/* Geodesix V1 Publisher: the approvals platform's own stylesheet.

   This is a port of geodesix-approvals/src/app.css rather than a wireframe
   drawn to look like it. The approvals platform is built and live at
   geodesix.pages.dev, so the useful thing for this surface to show is what is
   actually on screen there: the same tokens, the same frame, the same card,
   table, badge, stat, form and login primitives. Nothing is shared between the
   two repos; drift is fine and is corrected by re-porting.

   This surface is therefore the one exception to the repo's grayscale rule,
   taken deliberately. docs/design-system.md records what the port contains and
   docs/ux-patterns.md records the product decisions that come with it, so the
   other surfaces can be brought onto the same system from those two files
   rather than by reading this one.

   Two local additions follow the port, at the end of the file: the V1 version
   banner, which every V1 page carries and the platform has no equivalent of,
   and the tab panel rule the wireframes' own tab controller needs. The fonts
   are named but not loaded, because a wireframe page opens from file:// with
   no remote assets; the stack falls back to the system face. */

/* Geodesix approvals. Ported from the main app's (app)/globals.css: the
   same tokens, the same frame (sidebar + topbar + content column), the
   same card, table, badge, stat, form and login primitives, so the two
   read as one product. Nothing is shared; drift is fine. */

:root {
  --pink-primary: #e91e8c;
  --pink-dark: #c4177a;
  --pink-press: #a51466;
  --pink-light: #f472b6;
  --red-accent: #e53935;
  --ink: #1a1a2e;
  --ink-2: #34344e;
  --muted: #5b626e;
  --faint: #69707d;
  --paper: #ffffff;
  --soft: #f9fafb;
  --soft-2: #f3f4f6;
  --pink-soft: #fdf2f8;
  --line: #e5e7eb;
  --line-strong: #d1d5db;
  --control-border: #848b97;
  --on-dark: #ffffff;
  --on-dark-muted: #c7c8d6;
  --ok: #15803d;
  --warn: #b45309;
  --bad: #b91c1c;
  --radius: 10px;
  --sidebar-w: 232px;
  --topbar-h: 72px;
}

* {
  box-sizing: border-box;
}
html,
body {
  margin: 0;
  padding: 0;
}
body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
  color: var(--ink);
  background: var(--soft);
  font-size: 14.5px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
a {
  color: inherit;
}
[hidden] {
  display: none !important;
}
h1,
h2,
h3,
h4 {
  font-weight: 700;
  letter-spacing: -0.4px;
  margin: 0;
}
p {
  margin: 0 0 10px;
}
:focus-visible {
  outline: 3px solid var(--pink-primary);
  outline-offset: 2px;
  border-radius: 4px;
}
.skiplink {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: var(--on-dark);
  padding: 10px 16px;
  z-index: 100;
  border-radius: 0 0 8px 0;
}
.skiplink:focus {
  left: 0;
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0.01ms !important;
    /* The logo's flicker is `infinite`, and a 0.01ms infinite animation
       is still an animation. Capping the count is what actually leaves
       the mark as static navy dots. */
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0.01ms !important;
  }
}
.muted {
  color: var(--muted);
}
.small {
  font-size: 13px;
}
.nowrap {
  white-space: nowrap;
}
.link {
  color: var(--pink-dark);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.link:hover {
  color: var(--pink-press);
}

/* ---------- App frame ---------- */

.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  min-height: 100vh;
}
.sidebar {
  background: var(--paper);
  border-right: 1px solid var(--line);
  padding: 0 14px 18px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}
.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  padding: 4px 8px 14px;
}
.sidebar .brand-row {
  flex: none;
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin: 0 -14px 10px;
  padding: 0 22px;
  border-bottom: 1px solid var(--line);
}
.sidebar .brand-row .brand {
  padding: 0;
  min-width: 0;
}
.brand .name {
  font-family: 'Sarabun', 'Inter', sans-serif;
  font-weight: 700;
  font-size: 17px;
  line-height: 1.15;
}
.brand .name small {
  display: block;
  font-family: 'Inter', sans-serif;
  font-weight: 500;
  font-size: 12px;
  color: var(--muted);
  letter-spacing: 0.3px;
}
.nav-section h4 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--faint);
  margin: 14px 8px 4px;
  font-weight: 600;
}
.nav-section + .nav-section h4 {
  margin-top: 30px;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-radius: 8px;
  text-decoration: none;
  color: var(--ink-2);
  font-size: 13.5px;
}
.nav-item:hover {
  background: var(--soft);
}
.nav-item.active {
  background: var(--pink-soft);
  color: var(--pink-dark);
  font-weight: 600;
}
.nav-item .nav-count {
  margin-left: auto;
  font-size: 12px;
  font-weight: 700;
  line-height: 20px;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  text-align: center;
  border-radius: 999px;
  background: var(--pink-dark);
  color: var(--on-dark);
}
.main-col {
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.topbar {
  flex: none;
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 0 28px;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}
.topbar-start {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.topbar .title-wrap {
  min-width: 0;
}
.topbar .title-wrap h1 {
  font-size: 17px;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.topbar-end {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
}
.mobile-nav-toggle {
  display: none;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  color: var(--ink);
  font-size: 15px;
  cursor: pointer;
}
.mobile-nav-toggle:hover {
  background: var(--soft);
}
.mobile-nav-close {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  cursor: pointer;
}
.mobile-nav-close:hover {
  background: var(--soft);
  color: var(--ink);
}
.nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 30;
  border: 0;
  padding: 0;
  cursor: default;
  background: rgba(26, 26, 46, 0.35);
}
.content {
  padding: 24px 28px 48px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  max-width: 1240px;
  width: 100%;
}

/* ---------- User chip ---------- */

.userchip {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 12px 5px 6px;
  background: var(--paper);
  cursor: pointer;
  font-size: 13px;
}
.userchip .logo,
.userchip .logo-dots {
  flex: none;
}
.userchip .logo {
  border-radius: 50%;
  overflow: hidden;
}
.userchip .who {
  min-width: 0;
}
.entity-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
}
.userchip .who b {
  display: block;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chipmenu {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  min-width: 240px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(26, 26, 46, 0.12);
  padding: 6px;
  z-index: 40;
}
.chipmenu a,
.chipmenu button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 8px 10px;
  border-radius: 8px;
  text-decoration: none;
  font: inherit;
  font-size: 13px;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}
.chipmenu a:hover,
.chipmenu button:hover {
  background: var(--soft);
}
.cm-sep {
  border-top: 1px solid var(--line);
  margin: 6px 4px;
}
.chipmenu .cm-pick {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: 8px;
  background: none;
  font: inherit;
  font-size: 13px;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
/* A light line between accounts, so the list reads as rows rather than
   one block. The set is what is separated, so the first row has none. */
.chipmenu .cm-accounts .cm-pick + .cm-pick {
  border-top: 1px solid var(--line);
}
.chipmenu .cm-pick:hover {
  background: var(--soft);
}
/* The account being shown is the largest thing in the list and sits on
   its own ground, which is the whole marking; there are no checkmarks
   (Evan, 2026-09-03). */
.chipmenu .cm-pick.current {
  background: var(--soft-2);
  font-weight: 700;
  font-size: 14px;
  padding: 11px 10px;
}
.chipmenu .cm-pick.current:hover {
  background: var(--line);
}
.cm-who {
  padding: 8px 10px;
  font-size: 13px;
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* ---------- Cards, stats, sections ---------- */

.card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px;
}
.card > h3 {
  font-size: 15px;
  margin-bottom: 10px;
}
/* A second heading in the same card belongs to what follows it, not to
   the group above, so it opens a gap the heading's own 10px does not
   close (Evan, 2026-09-08). Notifications is three settings and one
   Save in one card. */
.card > .pickgroup + h3 {
  margin-top: 20px;
}
.card > h3 + p,
.card > .row-between + p,
.card > h3 + .muted,
.card > .row-between + .muted {
  margin-top: 0;
  margin-bottom: 14px;
}
.row-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.row-between h3 {
  font-size: 15px;
}
.section-h {
  font-size: 15px;
  margin-bottom: 0;
}
.statgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 14px;
}
.stat {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
}
.stat .label {
  color: var(--muted);
  font-size: 12px;
}
.stat .value {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.5px;
}
.stat .sub {
  color: var(--faint);
  font-size: 12px;
}
a.stat {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: border-color 0.12s ease, background 0.12s ease;
}
a.stat:hover,
a.stat:focus-visible {
  border-color: var(--control-border);
  background: var(--soft);
}
/* .stat.urgent no longer colors its figure: no number is red on any V1
   page (Evan, 2026-09-22). The class stays as a marker only. */
.cardcols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: 18px;
}
.narrow {
  max-width: 640px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
/* A form that runs the width of the page: the same column of cards as
   .narrow without the ceiling, for a form with a table in it (Evan,
   2026-09-08). */
.wide {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* ---------- Tables ---------- */

.tablewrap {
  overflow-x: auto;
}
table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
  background: var(--paper);
}
table.data th {
  text-align: left;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--muted);
  font-weight: 600;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line-strong);
  white-space: nowrap;
}
table.data th button.th-sort {
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  color: inherit;
  cursor: pointer;
}
table.data th button.th-sort:hover {
  color: var(--ink);
}
table.data th .sort-ind {
  font-size: 9px;
  color: var(--faint);
}
table.data th .sort-ind.active {
  color: var(--pink-dark);
}
table.data td {
  padding: 11px 12px;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
table.data tr[data-href] {
  cursor: pointer;
}
table.data tr[data-href]:hover td {
  background: var(--soft);
}
table.data td.actions {
  text-align: right;
  white-space: nowrap;
}
/* Inside an expansion row's own nested table, the Placement column lines up
   left under its own left-aligned header, rather than the page-level
   .actions right-alignment meant for a lone trailing button (Evan,
   2026-09-22). */
tr.expansion table.data td.actions {
  text-align: left;
}
table.data td.num {
  white-space: nowrap;
}
/* A figure that opens something is a link without an underline; the
   underline comes back on hover so it still reads as one (Evan,
   2026-09-22). */
table.data td.num a {
  text-decoration: none;
}
table.data td.num a:hover,
table.data td.num a:focus-visible {
  text-decoration: underline;
}
/* The amount reads as the figure, the unit as its label under it. */
table.data td.rate-cell {
  white-space: nowrap;
}
table.data td.rate-cell .unit {
  display: block;
  font-size: 12px;
  color: var(--muted);
}
table.data .cell-empty {
  text-align: center;
  color: var(--muted);
  padding: 32px 12px;
}
/* The split under a status badge: "2 accepted, 1 waiting". */
.status-cell .split {
  display: block;
  margin-top: 3px;
  white-space: nowrap;
}
.row-between .status-cell {
  text-align: right;
}

/* ---------- Opening rows ----------
   The [+] column at the left of a table whose rows can open, and the
   full-width row that follows an opened one. The opened row loses its
   bottom rule so the two read as one. */
table.data th.expander,
table.data td.expander {
  width: 28px;
  padding-right: 0;
}
.card .tablewrap table.data td.expander + td,
.card .tablewrap table.data th.expander + th {
  padding-left: 8px;
}
.expand {
  appearance: none;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 1px solid var(--control-border);
  border-radius: 6px;
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
  display: inline-grid;
  place-items: center;
  cursor: pointer;
}
.expand:hover,
.expand[aria-expanded='true'] {
  background: var(--soft-2);
}
table.data tr.opened td {
  border-bottom: 0;
}
/* The panel's own cell, and only it: a descendant selector here also
   caught every td of the table inside the panel, which left those cells
   indented 58px under headers that were not (Evan, 2026-09-08). */
table.data tr.expansion > td {
  background: var(--soft);
  padding: 4px 12px 14px 50px;
  cursor: default;
}
.card .tablewrap table.data tr.expansion > td {
  padding-left: 58px;
}

/* ---------- Citations ----------
   A request's citations: waiting first, then accepted, then declined.
   The same list opens under a table row (compact) and sits on the
   detail page. */
.citations {
  display: grid;
  gap: 14px;
}
.cite-heading {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--muted);
  font-weight: 600;
  margin: 0 0 6px;
}
.cite-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 8px;
}
.cite {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 6px 14px;
  align-items: center;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
}
.cite.pending {
  border-color: var(--pink-light);
}
.cite-body {
  min-width: 0;
}
.cite-text {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink);
}
.cite-article {
  margin-top: 3px;
}
.cite-when {
  display: block;
  margin-top: 3px;
  font-size: 12px;
  color: var(--muted);
}
.cite-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}
.citations.compact .cite {
  padding: 8px 10px;
}
.citations.compact .cite-text {
  font-size: 13px;
}
table.data td.cite-cell {
  max-width: 520px;
}
table.data td.cite-cell .cite-text {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
table.data td.article-cell {
  min-width: 200px;
  max-width: 360px;
}
/* A whole article URL under a title or a passage. One line, cut with an
   ellipsis, because a real article URL is longer than any column it
   belongs in (Evan, 2026-09-08). */
.url-line {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  color: var(--muted);
}
.url-line a {
  color: inherit;
}

/* A citations table inside an opened row: the list primitive on the
   panel's own ground, without the card edges it would get inside a
   card. */
tr.expansion table.data {
  background: transparent;
}
/* The row rule of table.data is #e5e7eb, which all but vanishes on the
   panel's own #f9fafb ground, so the citations ran together as one
   block of text (Evan, 2026-09-08). The stronger token, the one the
   header rule already uses, is the separator here. */
tr.expansion table.data td {
  border-bottom-color: var(--line-strong);
}
/* The last citation keeps its rule, unlike the last row of a table in a
   card, where the card's own edge closes the list. The panel has no
   edge, so without this a panel holding one citation showed no
   separator at all and its rows ran into the request under them (Evan,
   2026-09-08). */
tr.expansion table.data tbody tr:last-child td {
  border-bottom: 1px solid var(--line-strong);
}
table.data td.cite-cell .url-line {
  margin-top: 2px;
}
table.data td.num .unit {
  display: block;
  font-size: 12px;
  color: var(--muted);
}

/* The email preview: the message rendered by the sender's own template,
   in a frame so its styles and the app's cannot reach each other. */
.mailframe {
  display: block;
  width: calc(100% + 40px);
  margin: 4px -20px -18px;
  border: 0;
  border-top: 1px solid var(--line);
  background: var(--soft);
}
table.data td.article-cell .clamp {
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.card table.data {
  margin: 0 -20px -18px;
  width: calc(100% + 40px);
}
.card .tablewrap {
  margin: 0 -20px -18px;
}
.card .tablewrap table.data {
  margin: 0;
  width: 100%;
}
.card .tablewrap table.data th:first-child,
.card .tablewrap table.data td:first-child {
  padding-left: 20px;
}
.card .tablewrap table.data th:last-child,
.card .tablewrap table.data td:last-child {
  padding-right: 20px;
}
/* The outer table only: `>` all the way down, because the table inside
   an opened row is a `.card .tablewrap table.data` too, and this rule
   was taking the rule off its last citation as well (Evan,
   2026-09-08). */
.card > .tablewrap > table.data > tbody > tr:last-child > td {
  border-bottom: 0;
}
.entity {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.entity .ent-name {
  font-weight: 600;
  display: block;
  line-height: 1.25;
  white-space: nowrap;
}
.entity .ent-sub {
  display: block;
  color: var(--muted);
  font-size: 12px;
}
.entity .ent-sub a {
  color: inherit;
}
.logo {
  flex: none;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--paper);
  object-fit: contain;
  display: inline-grid;
  place-items: center;
}
.logo.placeholder {
  background: var(--soft-2);
  color: var(--muted);
  font-weight: 700;
}
/* The widest free-text column, and the one that has to yield when a
   Publisher column joins the row. Capped low enough that the actions at
   the end of the row stay on screen at a laptop width. */
.usage-cell {
  min-width: 150px;
  max-width: 260px;
  color: var(--ink-2);
}
.usage-cell .clamp {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ---------- Badges ---------- */

.badge {
  display: inline-block;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 1px 9px;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-2);
  background: var(--paper);
  white-space: nowrap;
}
.badge.dim {
  color: var(--muted);
  background: var(--soft);
  border-color: var(--line);
}
.badge.solid {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--on-dark);
}
.badge.pink {
  background: var(--pink-soft);
  border-color: var(--pink-light);
  color: var(--pink-dark);
}
.badge.ok {
  border-color: var(--ok);
  color: var(--ok);
}
.badge.bad {
  border-color: var(--bad);
  color: var(--bad);
}
/* Mixed: a citation request answered both ways. */
.badge.warn {
  border-color: var(--warn);
  color: var(--warn);
}

/* ---------- Filter bar and tabs ---------- */

.filterbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.filterbar input[type='search'] {
  flex: 0 1 280px;
  padding: 8px 12px;
}
.filterbar select {
  padding: 8px 10px;
}
.filterbar .actions {
  margin-left: auto;
  display: flex;
  gap: 8px;
}
/* The Filters disclosure: one button that opens a panel of selects, so a
   list has a single control rather than a row of loose dropdowns. */
details.filters {
  position: relative;
}
details.filters > summary {
  list-style: none;
  cursor: pointer;
  border: 1px solid var(--control-border);
  border-radius: 8px;
  padding: 7px 12px;
  background: var(--paper);
  font-size: 13.5px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
details.filters > summary::-webkit-details-marker {
  display: none;
}
details.filters > summary::before {
  content: '';
  width: 14px;
  height: 14px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Cpath d='M22 3H2l8 9.46V19l4 2v-8.54L22 3z'/%3E%3C/svg%3E")
    center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Cpath d='M22 3H2l8 9.46V19l4 2v-8.54L22 3z'/%3E%3C/svg%3E")
    center / contain no-repeat;
}
.filters-panel {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 30;
  min-width: 320px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(26, 26, 46, 0.12);
  padding: 14px;
}
.filters-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 4px;
}
.tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 14px;
}
.tabs button {
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 8px 12px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
}
.tabs button:hover {
  color: var(--ink);
}
.tabs button.active {
  color: var(--pink-dark);
  border-bottom-color: var(--pink-dark);
}

/* ---------- Buttons ----------

   Four roles, each its own color, so what a button does is legible
   before the label is read (Evan, 2026-09-03):

     .btn.primary    the one pink fill on a screen, the thing to do
     .btn.secondary  pink outline, the other thing you might do
     .btn.cancel     recessive gray, backing out of something
     .btn.danger     red outline, and only ever behind a confirmation

   A bare .btn is the plain neutral for navigation that is none of
   those. .quiet is kept as the old name for .secondary. */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--control-border);
  background: var(--paper);
  color: var(--ink);
  border-radius: 8px;
  padding: 8px 16px;
  font: inherit;
  font-weight: 600;
  font-size: 13.5px;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}
.btn:hover {
  background: var(--soft);
}
.btn.primary {
  border-color: var(--pink-dark);
  background: var(--pink-dark);
  color: #fff;
}
.btn.primary:hover {
  background: var(--pink-press);
  border-color: var(--pink-press);
}
.btn.secondary,
.btn.quiet {
  border-color: var(--pink-primary);
  background: var(--paper);
  color: var(--pink-dark);
}
.btn.secondary:hover,
.btn.quiet:hover {
  background: var(--pink-soft);
}
.btn.cancel {
  border-color: var(--line-strong);
  background: var(--soft-2);
  color: var(--muted);
}
.btn.cancel:hover {
  background: var(--line);
  color: var(--ink);
}
.btn.danger {
  background: var(--paper);
  color: var(--bad);
  border-color: var(--bad);
}
.btn.danger:hover {
  background: #fef2f2;
}
.btn.sm {
  padding: 5px 11px;
  font-size: 12.5px;
}
.btn:disabled {
  opacity: 0.6;
  cursor: default;
}
.btn-row {
  display: inline-flex;
  gap: 8px;
  align-items: center;
}
/* The confirm step asks a question and offers Cancel and Yes. The
   question sits above the two buttons so a longer one, "Accept the
   remaining 2?", does not widen the column it is in. */
.btn-row.confirming {
  flex-wrap: wrap;
  justify-content: flex-end;
  row-gap: 4px;
}
.btn-row.confirming .ask {
  flex-basis: 100%;
  text-align: right;
  white-space: nowrap;
}
.form-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 18px;
}
.form-actions .primary {
  margin-left: auto;
}

/* The admin's viewing-as bar, above the topbar and across the whole main
   column, the main app's .viewas (Evan, 2026-09-03). Ink, so there is no
   mistaking whose screen is on show. */
.viewas {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--ink);
  color: var(--on-dark);
  padding: 10px 28px;
  font-size: 13px;
}
.viewas > span span {
  color: var(--on-dark-muted);
  margin-left: 8px;
}

/* A way back that sits above the thing it leaves, rather than a button
   crowding the user chip in the topbar (Evan, 2026-09-03). */
.backlink {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 14px;
  color: var(--muted);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
}
.backlink:hover {
  color: var(--pink-dark);
}
.backlink .arrow {
  font-size: 15px;
  line-height: 1;
}

/* A page's own action, right aligned above its content. */
.pageactions {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 14px;
}

/* ---------- Forms ---------- */

input[type='text'],
input[type='url'],
input[type='email'],
input[type='number'],
input[type='search'],
input[type='date'],
input[type='file'],
input[type='password'],
input:not([type]),
select,
textarea {
  padding: 9px 12px;
  border: 1px solid var(--control-border);
  border-radius: 8px;
  font: inherit;
  color: var(--ink);
  background: var(--paper);
  max-width: 100%;
  min-width: 0;
  width: 100%;
}
/* A date input's calendar icon and a file input's own "Choose file" button
   stay native; the box around either one is styled to match so a form does
   not visibly switch systems partway down. */
input[type='date'] {
  color-scheme: light;
}
input[type='file'] {
  font-size: 13px;
}
/* A URL field paired with a fetch action, used when adding an article: the
   field takes the row, the button holds its width and never wraps under it. */
.inline-fetch {
  display: flex;
  gap: 8px;
  align-items: center;
}
.inline-fetch input {
  flex: 1;
  min-width: 0;
}
.inline-fetch .btn {
  flex: none;
  white-space: nowrap;
}
textarea {
  resize: vertical;
}
.formgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: 0 12px;
}
@media (min-width: 520px) {
  .formgrid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
/* An identity form (a person's own name, email, password) reads better as
   one field per row than as two across; a bulk data-entry form keeps the
   default two columns (Evan, 2026-09-22). */
.formgrid.stacked {
  grid-template-columns: 1fr;
}
.field {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-bottom: 14px;
  min-width: 0;
}
.field label {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
}
.field .hint {
  color: var(--muted);
  font-size: 12px;
}
.field.check {
  flex-direction: row;
  align-items: center;
  gap: 8px;
}
.field.check input {
  width: auto;
}
/* One field's complaint, under the control that caused it. */
.field-error {
  color: var(--bad);
  font-size: 12px;
  font-weight: 600;
}
.form-error {
  background: #fef2f2;
  border: 1px solid #fecaca;
  color: var(--bad);
  border-radius: 8px;
  padding: 10px 14px;
  font-size: 13px;
}
.notice {
  display: flex;
  align-items: center;
  gap: 12px;
  border: 1px solid var(--ok);
  border-left-width: 4px;
  background: #f0fdf4;
  color: #14532d;
  border-radius: 8px;
  padding: 11px 14px;
  font-size: 13.5px;
  font-weight: 600;
}
.notice.bad {
  border-color: var(--bad);
  background: #fef2f2;
  color: var(--bad);
}
.notice.info {
  border-color: var(--line-strong);
  background: var(--soft-2);
  color: var(--ink-2);
}
.notice span {
  flex: 1;
}
.notice a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.notice button {
  border: 0;
  background: none;
  font: inherit;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  color: var(--muted);
  padding: 0 2px;
}
.notice button:hover {
  color: var(--ink);
}

/* ---------- Publisher picker ----------
   The grant list on Access: publishers grouped by organization, each
   group with a whole-company shortcut. A fieldset per group, because
   that is what a group of related checkboxes is, and a screen reader
   then reads the company name before each title. */

.picker {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: 14px;
  margin-top: 4px;
}
.pickgroup {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 14px 12px;
  margin: 0;
  min-width: 0;
}
.pickgroup legend {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 0 6px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--faint);
}
.pickitem {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 5px 0;
  font-size: 13.5px;
  font-weight: 400;
  color: var(--ink);
  cursor: pointer;
}
.pickitem input {
  width: auto;
  flex: none;
  margin: 0;
}
/* One column, for a list read top to bottom: the citation picker on a
   request, and the notification choices. */
.picker.stack {
  grid-template-columns: 1fr;
}
.picker.stack .pickitem {
  align-items: flex-start;
}
.picker.stack .pickitem input {
  margin-top: 3px;
}
.pickitem .cite-text {
  font-size: 13.5px;
}
.pickgroup legend .badge {
  text-transform: none;
  letter-spacing: 0;
}
.linkbtn {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--pink-dark);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.linkbtn:hover {
  color: var(--pink-press);
}
/* Several small badges in one cell, wrapping rather than stretching the
   column: an editor with four titles is a normal row. */
.badges-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-width: 320px;
}

/* ---------- Detail page ---------- */

/* A field list: one field per row, the label then its value, on every V1
   page. The wrapping div is flattened so each pair takes its own grid row
   rather than two pairs sitting side by side as stacked label over value.
   No rule between rows, tight spacing, and the label's colon comes from here
   rather than the markup, so no page can drop it (Evan, 2026-09-22).
   `.kv.rows` is the older name for the same thing and still works. */
.kv {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 6px 16px;
  align-items: baseline;
  margin: 0;
  font-size: 13.5px;
}
.kv > div {
  display: contents;
}
.kv dt {
  color: var(--muted);
  font-weight: 600;
  font-size: 12.5px;
}
.kv dt::after {
  content: ':';
}
.kv dd {
  margin: 0;
  min-width: 0;
}
.kv dd.big {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.3px;
}
.timeline {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}
.timeline li {
  display: grid;
  grid-template-columns: 10px 1fr;
  gap: 10px;
  align-items: start;
  font-size: 13.5px;
}
.timeline li::before {
  content: '';
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--line-strong);
  margin-top: 6px;
}
.timeline li.accepted::before {
  background: var(--ok);
}
.timeline li.declined::before {
  background: var(--bad);
}
.timeline li.reopened::before {
  background: var(--warn);
}
.timeline .when {
  color: var(--muted);
  font-size: 12px;
  display: block;
}
.confirm {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  background: var(--soft-2);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 13.5px;
}
.confirm span {
  flex: 1 1 200px;
}

/* ---------- Empty ---------- */

.empty {
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
  padding: 36px 24px;
  text-align: center;
  color: var(--muted);
  background: var(--paper);
}
.empty b {
  display: block;
  color: var(--ink-2);
  margin-bottom: 4px;
}

/* ---------- Login ---------- */

.login-wrap {
  min-height: 100vh;
  display: grid;
  grid-template-rows: 1fr auto;
  background:
    radial-gradient(60% 50% at 85% -10%, rgba(233, 30, 140, 0.08), transparent),
    var(--soft);
}
.login-center {
  display: grid;
  place-items: center;
  padding: 24px;
}
.login-card {
  width: 100%;
  max-width: 400px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 32px;
  box-shadow: 0 20px 50px rgba(26, 26, 46, 0.08);
}
/* The mark and the word Geodesix on one line, the same on all three V1
   logins, with no audience name after it (Evan, 2026-09-22). */
.login-card .auth-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 0 0 28px;
}
.login-card .auth-brand h1 {
  font-family: 'Sarabun', 'Inter', sans-serif;
  font-size: 24px;
  line-height: 1;
}
.login-card form > .btn {
  width: 100%;
  justify-content: center;
  padding: 11px 16px;
  margin-bottom: 4px;
}
.login-card .sent {
  text-align: center;
  font-size: 14px;
  color: var(--ink-2);
}
.login-card .sent b {
  display: block;
  margin-bottom: 4px;
}
.login-card .after {
  text-align: center;
  margin: 24px 0 0;
  font-size: 13.5px;
}
/* "Not someone@example.com?" over the link that changes it. The address
   wraps rather than pushing the card wide, since a long one is common. */
.login-card .after .muted {
  display: block;
  font-size: 13px;
  overflow-wrap: anywhere;
}
.login-card .after .muted + br {
  display: none;
}
.login-card .after a,
.login-card .after button {
  display: inline-block;
  padding: 8px 12px;
  color: var(--pink-dark);
  background: none;
  border: 0;
  font: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ---------- Footer ---------- */

.appfooter {
  margin-top: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 4px 24px;
  padding: 16px 28px 20px;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 13px;
}
.appfooter nav {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
}
.appfooter a {
  color: var(--muted);
  text-decoration: none;
  padding: 6px 0;
}
.appfooter a:hover,
.appfooter a:focus-visible {
  color: var(--ink);
  text-decoration: underline;
}


/* ---------- Logo flicker ----------
   Ported from the main app's globals.css. One 12 second cycle, four
   pairs lighting up in turn at 0, 16, 32 and 48 percent, each pair one
   red dot and one gray, so at most two of the eight are colored at any
   moment and the mark reads as itself throughout. */

.logo-dots {
  flex: none;
}
.logo-dot {
  transition: fill 0.2s ease;
}
.dot-a {
  animation: flickerA 12s infinite;
}
.dot-b {
  animation: flickerB 12s infinite;
}
.dot-c {
  animation: flickerC 12s infinite;
}
.dot-d {
  animation: flickerD 12s infinite;
}
.dot-e {
  animation: flickerE 12s infinite;
}
.dot-f {
  animation: flickerF 12s infinite;
}
.dot-g {
  animation: flickerG 12s infinite;
}
.dot-h {
  animation: flickerH 12s infinite;
}
@keyframes flickerA {
  0%,
  2% {
    fill: #1a1a2e;
  }
  4%,
  10% {
    fill: #e53935;
  }
  12%,
  100% {
    fill: #1a1a2e;
  }
}
@keyframes flickerE {
  0%,
  2% {
    fill: #1a1a2e;
  }
  4%,
  10% {
    fill: #9ca3af;
  }
  12%,
  100% {
    fill: #1a1a2e;
  }
}
@keyframes flickerC {
  0%,
  16% {
    fill: #1a1a2e;
  }
  18%,
  26% {
    fill: #9ca3af;
  }
  28%,
  100% {
    fill: #1a1a2e;
  }
}
@keyframes flickerF {
  0%,
  16% {
    fill: #1a1a2e;
  }
  18%,
  26% {
    fill: #e53935;
  }
  28%,
  100% {
    fill: #1a1a2e;
  }
}
@keyframes flickerB {
  0%,
  32% {
    fill: #1a1a2e;
  }
  34%,
  42% {
    fill: #e53935;
  }
  44%,
  100% {
    fill: #1a1a2e;
  }
}
@keyframes flickerG {
  0%,
  32% {
    fill: #1a1a2e;
  }
  34%,
  42% {
    fill: #9ca3af;
  }
  44%,
  100% {
    fill: #1a1a2e;
  }
}
@keyframes flickerD {
  0%,
  48% {
    fill: #1a1a2e;
  }
  50%,
  58% {
    fill: #9ca3af;
  }
  60%,
  100% {
    fill: #1a1a2e;
  }
}
@keyframes flickerH {
  0%,
  48% {
    fill: #1a1a2e;
  }
  50%,
  58% {
    fill: #e53935;
  }
  60%,
  100% {
    fill: #1a1a2e;
  }
}

/* ---------- Mobile ---------- */

@media (max-width: 880px) {
  .app {
    grid-template-columns: 1fr;
  }
  .mobile-nav-toggle {
    display: flex;
  }
  .nav-backdrop {
    display: block;
  }
  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 40;
    width: min(280px, 82vw);
    height: 100vh;
    height: 100dvh;
    padding-bottom: calc(18px + env(safe-area-inset-bottom, 0px));
    transform: translateX(-100%);
    visibility: hidden;
    transition:
      transform 0.2s ease,
      visibility 0s linear 0.2s;
  }
  .sidebar.open {
    transform: translateX(0);
    visibility: visible;
    transition: transform 0.2s ease;
  }
  .viewas {
    padding: 10px 16px;
  }
  .topbar {
    padding: 0 16px;
  }
  .userchip .who {
    display: none;
  }
  .userchip {
    padding: 4px;
  }
  .content {
    padding: 16px 16px 40px;
  }
  .appfooter {
    padding: 12px 16px 16px;
    font-size: 12px;
    gap: 4px 14px;
  }
  .appfooter nav {
    gap: 14px;
  }
  table.data tr.expansion > td,
  .card .tablewrap table.data tr.expansion > td {
    padding-left: 16px;
  }
  /* A table wider than the phone scrolls sideways; the opened row's
     panel stays in view instead of stretching with it. */
  .expansion-body {
    position: sticky;
    left: 0;
    max-width: calc(100vw - 72px);
  }
  .cite {
    grid-template-columns: 1fr;
  }
  .cite-side {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }
}


/* ---------- Wireframe additions ----------

   Everything above is the port. Everything below exists because this is a
   wireframe rather than the running app, and should be left behind when the
   port is taken anywhere else. */

/* The V1 banner, drawn by nav.js on every page of the surface. */
.versionbar {
  padding: 9px 28px;
  background: var(--soft-2);
  border-bottom: 1px solid var(--line);
}
.versionbar .vb-txt b {
  display: block;
  font-size: 13px;
}
.versionbar .vb-txt span {
  display: block;
  font-size: 11.5px;
  color: var(--muted);
}

/* A tabbed page draws one panel at a time. The bar is .tabs[data-tabs] and each
   panel is .tabpanel[data-tab]; nav.js switches them by setting the hidden
   attribute, which the [hidden] rule near the top of the port already handles.
   The platform's own tabs are <button> children of .tabs, so these pages use
   buttons too rather than the spans the grayscale surfaces use. */
.tabpanel {
  display: grid;
  gap: 18px;
}

/* The email preview's own column. The platform's .mailframe is an iframe that
   renders the sender's template, so the centered white message comes from
   inside it; a static page has no iframe, and this is that column drawn
   directly on the frame's ground. */
.mailcard {
  max-width: 560px;
  margin: 0 auto;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 22px;
}

/* A line chart. The approvals platform has none, so this is a wireframe
   addition rather than part of the port: the same primitive the grayscale
   surfaces use, drawn on the ported tokens. */
.linechart {
  height: 180px;
  position: relative;
  border-left: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
}
.linechart svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.chart-axis {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--muted);
  margin-top: 6px;
}

/* ---------- Date range ----------

   The segmented control the main app uses on stag.geodesix.com/admin, beside
   a section heading: the fixed windows as links, then Select date range, which
   opens a panel holding a start, an end and a month. The active window is the
   one pink fill on the row.

   Each option carries its long label and its short one, and the breakpoint
   swaps which is shown: below 880px the row reads 30d, 90d, Select date range,
   because that is as much as the width will take. */
/* The type filter beside a date range wears the same segmented control: two
   of them on one row, the filter at the left and the window at the right. */
.segbar,
.daterange {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
  padding: 3px;
  flex: none;
}
.seg,
.dr-opt {
  appearance: none;
  border: 0;
  background: none;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  text-decoration: none;
  padding: 6px 14px;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
}
.seg:hover,
.dr-opt:hover {
  color: var(--ink);
}
.seg.active,
.dr-opt.active {
  background: var(--pink-dark);
  color: var(--on-dark);
}
.dr-opt .short {
  display: none;
}
.dr-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 20;
  width: 300px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(26, 26, 46, 0.12);
  padding: 16px;
  text-align: left;
}
.dr-cal {
  margin-top: 14px;
}
.dr-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-weight: 700;
  font-size: 13.5px;
  margin-bottom: 8px;
}
.dr-head span {
  cursor: pointer;
  color: var(--muted);
  padding: 0 6px;
}
.dr-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
  text-align: center;
}
.dr-dow {
  font-size: 11px;
  color: var(--faint);
  font-weight: 600;
  padding: 4px 0;
}
.dr-day {
  font-size: 12.5px;
  padding: 6px 0;
  border-radius: 6px;
  cursor: pointer;
}
.dr-day:hover {
  background: var(--soft-2);
}
.dr-day.in {
  background: var(--pink-soft);
  color: var(--pink-dark);
}
.dr-day.edge {
  background: var(--pink-dark);
  color: var(--on-dark);
}
.dr-day.out {
  color: var(--line-strong);
}

@media (max-width: 880px) {
  .dr-opt .long {
    display: none;
  }
  .dr-opt .short {
    display: inline;
  }
  .dr-opt.dr-wide {
    display: none;
  }
}

/* The account chip menu, and the account switcher inside it.

   A person can be given access to several publishers or several retailers
   through an organization, so the chip is where they move between them. The
   menu is the platform's own panel with three parts added: the signed in
   address, a list of the accounts reachable from this login, and All accounts,
   which is a rollup rather than a place.

   The rollup is drawn rather than described: nav.js sets data-account="all" on
   <body>, which reveals the Account column every list keeps hidden while one
   account is selected, and swaps any figure carrying a data-all value. */
.chipmenu-who {
  display: block;
  padding: 9px 12px 8px;
  font-size: 12px;
  color: var(--faint);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.chipmenu-head {
  margin: 0;
  padding: 10px 12px 4px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--faint);
}
.chipmenu .acct {
  display: flex;
  align-items: center;
  gap: 9px;
}
.chipmenu .acct .logo {
  width: 22px;
  height: 22px;
  font-size: 9px;
  flex: none;
}
.chipmenu .acct .tick {
  margin-left: auto;
  color: var(--pink-primary);
  font-weight: 700;
}
.chipmenu .acct.current {
  font-weight: 600;
}
.chipmenu .acct.all {
  border-top: 1px solid var(--line);
}
.chipmenu .acct.all .logo {
  background: var(--soft-2);
  color: var(--faint);
  letter-spacing: -0.2px;
}

/* An account column is dead weight while one account is selected, so every
   list carries it hidden and the rollup is what reveals it. */
.acct-col {
  display: none;
}
body[data-account="all"] .acct-col {
  display: table-cell;
}

/* A rollup adds rows as well as a column: the other accounts' work is not on
   the page while one account is selected, so those rows are carried hidden the
   same way the Account column is. */
tr.acct-row {
  display: none;
}
body[data-account="all"] tr.acct-row {
  display: table-row;
}

/* An account's mark.

   Admin uploads a 50 by 50 logo for each publisher and retailer and that is what
   the directory lists and the account switcher show. A wireframe has no uploads,
   so it stands one in from Google's favicon service and keeps the initials
   underneath: a real domain resolves to the publication's own mark, a domain
   that does not exist resolves to a generic globe, which is what an account with
   no logo uploaded looks like, and a page opened from file:// with no network
   falls back to the initials.

   This is the one remote asset in the repo. Every other rule holds: no CDN
   scripts, no remote stylesheets, no web fonts. The page renders correctly with
   the image missing, which is why the exception is affordable. */
.logo {
  position: relative;
  overflow: hidden;
}
.logo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--paper);
  border-radius: inherit;
  /* Transparent until it has actually loaded, so a pending or blocked request
     shows the initials underneath rather than a white square. The onload in the
     markup is what reveals it. */
  opacity: 0;
}

/* The figure a chart ends on, shown once beside the title rather than as a label
   on every point. */
.chart-latest {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
}
.chart-latest .unit {
  font-size: 11px;
  font-weight: 500;
  color: var(--faint);
}

/* The bot check on the login card. A static placeholder: the real one is a
   Cloudflare Turnstile widget, which a file:// page cannot load. */
.turnstile {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--soft);
  padding: 10px 12px;
  font-size: 12px;
  color: var(--muted);
}
.turnstile .tsbox {
  flex: none;
  width: 18px;
  height: 18px;
  border: 1.5px solid var(--control-border);
  border-radius: 4px;
  background: var(--paper);
}

/* An entity row inside an email preview (profile.html's Notifications tab):
   the other party's logo as a rounded square the full height of the text
   beside it, so nothing, the Respond button included, sits under the logo
   (Evan, 2026-09-22). A row with more lines gets a bigger logo. */
.mail-ent {
  display: grid;
  grid-template-columns: var(--mail-logo, 44px) 1fr;
  gap: 14px;
  align-items: stretch;
}
.mail-ent.lg {
  --mail-logo: 96px;
}
.mail-ent > .logo {
  width: 100%;
  height: auto;
  min-height: var(--mail-logo, 44px);
  border-radius: 10px;
  font-size: 13px;
}
.mail-ent > .logo img {
  object-fit: contain;
  padding: 18%;
  box-sizing: border-box;
}
.mail-ent > div > .url-line,
.mail-ent > div > .cite-when {
  display: block;
}

/* Edit rate on earnings.html's Your rate cards: a disclosure that starts
   closed, the button its own summary (Evan, 2026-09-22). */
details.rate-edit {
  margin-top: 16px;
}
details.rate-edit > summary {
  list-style: none;
  display: inline-flex;
}
details.rate-edit > summary::-webkit-details-marker {
  display: none;
}
details.rate-edit[open] > summary {
  display: none;
}

/* Password requirements under a New password field: an open circle per rule
   that becomes a green check once the typed password meets it (V1 batch 12;
   the checking lives in nav.js). */
ul.pwreq {
  list-style: none;
  margin: 4px 0 0;
  padding: 0;
  display: grid;
  gap: 3px;
  font-size: 12.5px;
  color: var(--muted);
}
ul.pwreq li {
  display: flex;
  align-items: center;
  gap: 8px;
}
ul.pwreq li::before {
  content: '';
  flex: none;
  width: 12px;
  height: 12px;
  box-sizing: border-box;
  border: 1.5px solid var(--control-border);
  border-radius: 50%;
}
ul.pwreq li.met {
  color: var(--ok);
}
ul.pwreq li.met::before {
  content: '\2713';
  border: 0;
  font-size: 12px;
  line-height: 12px;
  text-align: center;
  font-weight: 700;
}

/* The back link and the page's own action (View as, Edit, a status) on one
   row, the link at the left and the action at the right (V1 batch 13). */
.backrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.backrow .backlink {
  margin: 0;
}
.backrow-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* A select is as tall as a text field beside it. The native control draws
   its box a few pixels shorter from the same padding, so the height is set
   rather than left to the padding (V1 batch 13). Filter bar and table
   selects keep their smaller padding and height. */
select {
  box-sizing: border-box;
  min-height: calc(1.55em + 20px);
}
.filterbar select,
table.data select {
  min-height: 0;
}

/* A tab bar wider than its column scrolls sideways instead of wrapping,
   so a detail page with many tabs keeps them on one row (V1 batch 13). */
.tabs {
  overflow-x: auto;
  scrollbar-width: thin;
}
.tabs button {
  flex: none;
  white-space: nowrap;
}
.tabs {
  gap: 0;
}
.tabs button {
  padding: 8px 10px;
}
