/* raiw design system.

   Canva's palette, applied with enterprise restraint: one solid violet does all
   the functional work, cyan appears only as a small accent, and neutrals carry
   the layout. Gradients are reserved for the brand mark — never on controls,
   where they read as consumer marketing rather than software.

   Scale: 4px spacing grid, 13px UI base, 6/8/10px radii, near-invisible shadows. */

:root {
  /* brand */
  --primary: #7d2ae8;
  --primary-hover: #6b1fd0;
  --primary-active: #5c18b8;
  --primary-weak: #f4edff;
  --primary-weak-2: #e9dcff;
  --primary-ring: rgba(125, 42, 232, 0.18);
  --accent: #00949a;
  --accent-weak: #e2f6f7;
  /* The brand ramp, painted onto the wordmark text beside the artwork. It is also
     the canonical definition that the CYAN and VIOLET constants in
     scripts/make_mark.py mirror, so the type and the mark stay the same two
     colours — change it here first, then re-run that script. */
  --brand-grad: linear-gradient(135deg, #00c4cc 0%, #7d2ae8 100%);

  /* neutrals */
  --n0: #ffffff;
  --n25: #fcfcfd;
  --n50: #f8f9fb;
  --n100: #f1f2f6;
  --n150: #e9ebf1;
  --n200: #e1e4ec;
  --n300: #cdd1dd;
  --n400: #98a0b3;
  --n500: #6b7385;
  --n600: #545c6d;
  --n700: #3d4453;
  --n900: #16191f;

  /* status */
  --ok: #067a55;
  --ok-weak: #e4f5ef;
  --warn: #a55a09;
  --warn-weak: #fdf0df;
  --err: #c62a30;
  --err-weak: #fdebec;
  --info: #0d5bb9;
  --info-weak: #e7f0fd;

  --r-sm: 5px;
  --r: 7px;
  --r-md: 9px;
  --r-lg: 13px;

  --sh-xs: 0 1px 1px rgba(22, 25, 31, 0.04);
  --sh-sm: 0 1px 2px rgba(22, 25, 31, 0.06), 0 1px 1px rgba(22, 25, 31, 0.04);
  --sh:
    0 4px 10px -2px rgba(22, 25, 31, 0.08),
    0 2px 4px -2px rgba(22, 25, 31, 0.05);
  --sh-lg:
    0 16px 36px -8px rgba(22, 25, 31, 0.18),
    0 4px 10px -4px rgba(22, 25, 31, 0.1);

  --sidebar-w: 244px;
  --sidebar-w-rail: 60px;
  --topbar-h: 56px;
}

* {
  box-sizing: border-box;
}
html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
}

body {
  background: var(--n50);
  color: var(--n900);
  font:
    13.5px/1.5 -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    Inter,
    "Helvetica Neue",
    Arial,
    sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

h1,
h2,
h3 {
  margin: 0;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: var(--n900);
}
h1 {
  font-size: 19px;
  line-height: 1.25;
}
h2 {
  font-size: 14px;
}
h3 {
  font-size: 13px;
}

code,
pre,
.mono {
  font-family:
    ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
}

::selection {
  background: var(--primary-weak-2);
}

/* ---------------------------------------------------------------- layout */

.shell {
  display: flex;
  min-height: 100vh;
}

.sidebar {
  width: var(--sidebar-w);
  flex: 0 0 var(--sidebar-w);
  background: var(--n0);
  border-right: 1px solid var(--n200);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  transition:
    width 0.16s ease,
    flex-basis 0.16s ease;
}

.main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.content {
  padding: 20px 24px 56px;
  flex: 1;
  max-width: 1400px;
  width: 100%;
}

/* ---------------------------------------------------------------- brand */

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  height: var(--topbar-h);
  padding: 0 14px;
  border-bottom: 1px solid var(--n200);
  font-weight: 650;
  font-size: 15px;
  letter-spacing: -0.02em;
  color: var(--n900);
  flex: 0 0 auto;
}
.brand:hover {
  text-decoration: none;
}

/* The brand and the About entry share the top row. The row owns the height and
   the rule beneath it, so the icon sits on the wordmark's own line rather than
   under it, and .side-toggle — anchored to the sidebar's edge at half a topbar
   down — still lands on the same centre. Scoped to .brand-row so the sign-in and
   invite pages, which use .brand on its own, are untouched. */
.brand-row {
  display: flex;
  align-items: center;
  height: var(--topbar-h);
  padding-right: 12px;
  border-bottom: 1px solid var(--n200);
  flex: 0 0 auto;
}
.brand-row .brand {
  flex: 1 1 auto;
  min-width: 0;
  border-bottom: 0;
}

/* The brand is one image now — see scripts/make_mark.py. wordmark.svg draws R,
   A, the arrow and W as one run of letters in the cyan -> violet ramp, so it
   replaces the mark-and-text lockup outright: the name is no longer set in type
   anywhere, which is what stops the two halves from drifting apart as the
   gradient text and the artwork used to.

   Vectors rather than rasters, so the R's counter and the arrowhead stay clean
   at 30px and at 104px alike; the PNGs alongside are only for the favicon slots,
   which can't all take an SVG. Both files are cropped to their ink, so height is
   the only axis worth setting — width follows the vector's own aspect, and there
   is no transparent margin to cancel out with negative margins. */
.brand-word-mark {
  width: auto;
  height: 30px;
  flex: 0 0 auto;
  display: block;
}

/* The rail is 60px wide. Four letters would have to shrink well past reading
   size to fit, so the collapsed sidebar drops to the A and its arrow — the same
   trade the favicons make. */
.brand-rail-mark {
  width: auto;
  height: 26px;
  flex: 0 0 auto;
  display: none;
}

/* The name set in type, beside the artwork that already spells it. The two are
   deliberately different registers: the mark is the leaning display cut, this is
   the name in the UI's own face at reading weight.

   It carries the mark's cyan -> violet ramp so the lockup reads as one object,
   and both lockups share this rule rather than repeating the ramp, so the
   sidebar and the login panel can't drift apart. Their fallbacks differ because
   their backgrounds do: violet on the sidebar's white, inherited white on the
   login panel's near-black (see .auth-logo).

   The @supports guard matters: background-clip painting the text needs a
   transparent fill, so without it an unsupporting engine would render a gradient
   block with invisible letters. */
.sidebar .brand-word {
  color: var(--primary);
}

@supports (background-clip: text) or (-webkit-background-clip: text) {
  .sidebar .brand-word,
  .auth-wordmark {
    background: var(--brand-grad);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

/* ---------------------------------------------------------------- sidebar */

.side-scroll {
  flex: 1;
  overflow-y: auto;
  padding: 10px 8px;
}

.side-group {
  margin-bottom: 14px;
}

.side-group-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px 5px;
}
.side-group-title {
  font-size: 10.5px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--n400);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
  min-width: 0;
}

.side-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  margin-bottom: 1px;
  border-radius: var(--r);
  color: var(--n700);
  font-size: 13px;
  font-weight: 450;
  cursor: pointer;
  line-height: 1.4;
}
.side-item:hover {
  background: var(--n100);
  text-decoration: none;
  color: var(--n900);
}
.side-item.active {
  background: var(--primary-weak);
  color: var(--primary);
  font-weight: 550;
}
.side-item .label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.side-item .count {
  font-size: 11px;
  color: var(--n400);
  font-variant-numeric: tabular-nums;
  background: var(--n100);
  border-radius: 4px;
  padding: 0 5px;
  line-height: 16px;
  min-width: 20px;
  text-align: center;
}
.side-item.active .count {
  background: var(--primary-weak-2);
  color: var(--primary);
}
.side-item:hover .count {
  background: var(--n150);
}

.side-icon {
  flex: 0 0 15px;
  width: 15px;
  height: 15px;
  color: var(--n400);
}
.side-item.active .side-icon {
  color: var(--primary);
}

.side-empty {
  padding: 5px 8px;
  font-size: 12.5px;
  color: var(--n400);
  font-style: italic;
}

/* --- the gear on a row, and the menu it opens ---------------------------- */

/* The row wraps the link so the gear can sit on top of it without becoming part
   of the link's hit area — a gear nested inside the <a> would navigate to the
   workspace on its way to opening the menu. */
.side-row {
  position: relative;
  display: flex;
  align-items: center;
  border-radius: var(--r);
}
.side-row .side-item {
  flex: 1;
  min-width: 0;
  margin-bottom: 0;
}
.side-row.active .side-item {
  background: var(--primary-weak);
  color: var(--primary);
  font-weight: 550;
}
.side-row.active .side-icon {
  color: var(--primary);
}
.side-row.active .side-item .count {
  background: var(--primary-weak-2);
  color: var(--primary);
}

/* Absolute, so revealing it does not reflow the label mid-hover — a name that
   re-truncates as the pointer arrives reads as a glitch. It covers the count
   instead: at this width there is room for one of them, and while the pointer
   is on the row the count is the less useful. */
.side-cog {
  position: absolute;
  right: 4px;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--n400);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.side-cog svg {
  width: 13px;
  height: 13px;
}

/* Focus and the open state pin it visible: a control that only exists under the
   pointer is one a keyboard can tab to and not see. */
.side-row:hover .side-cog,
.side-row:focus-within .side-cog,
.side-cog[aria-expanded="true"] {
  opacity: 1;
}
.side-cog:hover {
  background: var(--n150);
  color: var(--n700);
}
.side-cog:focus-visible {
  opacity: 1;
  outline: 2px solid var(--primary-ring);
  outline-offset: 1px;
}
.side-row:hover .side-item .count,
.side-row:focus-within .side-item .count {
  visibility: hidden;
}

/* The org head's gear is in normal flow — that row has no count to cover — but
   it still only appears on hover, so a sidebar at rest is a list of names. */
.side-group-head .side-menu-wrap .side-cog {
  position: static;
  opacity: 0;
}
.side-group-head:hover .side-cog,
.side-group-head:focus-within .side-cog,
.side-group-head .side-cog[aria-expanded="true"] {
  opacity: 1;
}

.side-menu-wrap {
  position: relative;
  display: flex;
  align-items: center;
}

.side-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 40;
  min-width: 176px;
  padding: 4px;
  background: var(--n0);
  border: 1px solid var(--n200);
  border-radius: var(--r-md);
  box-shadow: var(--sh);
}
.side-menu[hidden] {
  display: none;
}

.side-menu-head {
  padding: 6px 8px 5px;
  font-size: 10px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--n400);
  border-bottom: 1px solid var(--n150);
  margin-bottom: 4px;
}
/* The name under the level, so a menu floating over a list of ten workspaces
   still says which one it belongs to. */
.side-menu-name {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  font-weight: 550;
  text-transform: none;
  letter-spacing: 0;
  color: var(--n700);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.side-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--n700);
}
.side-menu-item svg {
  width: 14px;
  height: 14px;
  color: var(--n400);
}
.side-menu-item:hover {
  background: var(--n100);
  color: var(--n900);
  text-decoration: none;
}
.side-menu-item:hover svg {
  color: var(--primary);
}

.side-foot {
  flex: 0 0 auto;
  border-top: 1px solid var(--n200);
  padding: 8px;
}

.side-user {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: var(--r);
  min-width: 0;
}
.avatar {
  width: 24px;
  height: 24px;
  border-radius: 5px;
  background: var(--primary-weak-2);
  color: var(--primary);
  display: grid;
  place-items: center;
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: 0.02em;
  flex: 0 0 24px;
}
.side-user-meta {
  min-width: 0;
  flex: 1;
}
/* display:block is load-bearing, not tidiness: these are <span>s, and
   overflow/text-overflow do nothing on an inline box — so a long email ran past
   the meta column instead of truncating, and collided with the gear once there
   was something to its right. */
.side-user-name {
  display: block;
  font-size: 12.5px;
  font-weight: 550;
  color: var(--n900);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.side-user-mail {
  display: block;
  font-size: 11px;
  color: var(--n400);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Settings entry point, sat next to the user in the sidebar foot. A 26px square
   rather than a .btn so it reads as an icon affordance and keeps the same
   footprint in the collapsed rail, where the labels and the sign-out button are
   gone and it is the only thing left beside the avatar. */
.side-gear {
  flex: 0 0 26px;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  color: var(--n400);
  transition:
    color 0.12s ease,
    background 0.12s ease;
}
.side-gear:hover {
  background: var(--n100);
  color: var(--n900);
  text-decoration: none;
}
.side-gear:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}
.side-gear.active {
  background: var(--primary-weak);
  color: var(--primary);
}
.side-gear svg {
  width: 15px;
  height: 15px;
}

/* The same affordance beside a page title. Sits inside the <h1>, so it inherits
   the line box and stays glued to the name; the vertical nudge keeps it on the
   optical centre of the text rather than the baseline. Sized independently of
   the heading because an h1-sized gear reads as a second heading. */
.title-gear {
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  margin-left: 6px;
  vertical-align: -2px;
  border-radius: var(--r-sm);
  color: var(--n400);
  transition:
    color 0.12s ease,
    background 0.12s ease;
}
.title-gear:hover {
  background: var(--n100);
  color: var(--n900);
  text-decoration: none;
}
.title-gear:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}
.title-gear svg {
  width: 15px;
  height: 15px;
}

/* ------------------------------------------------------ sidebar collapse

   The state is a class on <html>, set by an inline script in base.html before
   first paint — set it from app.js instead and a collapsed sidebar flashes open
   on every page load.

   It collapses to a rail rather than to nothing so every destination stays one
   click away; labels drop out, icons and the brand chip stay. */

.side-toggle {
  /* Anchored to the sidebar's right edge, vertically on the brand row: the one
     position that survives both states. .sidebar is sticky, hence positioned,
     so it is the containing block. z-index clears the sticky topbar (30). */
  position: absolute;
  top: calc(var(--topbar-h) / 2);
  right: -11px;
  transform: translateY(-50%);
  z-index: 40;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--n200);
  border-radius: 50%;
  background: var(--n0);
  color: var(--n500);
  cursor: pointer;
  transition:
    color 0.12s ease,
    border-color 0.12s ease,
    background 0.12s ease;
}
.side-toggle:hover {
  color: var(--n900);
  border-color: var(--n300);
  background: var(--n50);
}
.side-toggle:focus-visible {
  outline: 2px solid var(--primary-ring);
  outline-offset: 1px;
}

.side-toggle-icon {
  width: 12px;
  height: 12px;
  transition: transform 0.16s ease;
}
html.sidebar-collapsed .side-toggle-icon {
  transform: rotate(180deg);
}

html.sidebar-collapsed .sidebar {
  width: var(--sidebar-w-rail);
  flex-basis: var(--sidebar-w-rail);
}

/* Anything that needs room for words. The workspace rows keep their title
   attribute, so the icons stay identifiable on hover. */
html.sidebar-collapsed .brand-word-mark,
html.sidebar-collapsed .brand-word,
html.sidebar-collapsed .side-group-head,
html.sidebar-collapsed .side-empty,
html.sidebar-collapsed .side-item .label,
html.sidebar-collapsed .side-item .count,
html.sidebar-collapsed .side-user-meta,
html.sidebar-collapsed .side-user form {
  display: none;
}

/* At 60px there is no room for a gear beside an icon, and a menu anchored to a
   rail would open over the content. The rows still lead to the workspace, whose
   own page carries the same three destinations in its header. */
html.sidebar-collapsed .side-cog {
  display: none;
}
html.sidebar-collapsed .side-row {
  justify-content: center;
}

html.sidebar-collapsed .brand {
  justify-content: center;
  padding: 0;
  gap: 0;
}

/* At 60px the mark takes the whole row, so About drops out of it rather than
   crowding the one thing the rail is meant to keep legible. It is not lost: the
   legal footer carries the same link on every page. */
html.sidebar-collapsed .brand-row {
  padding-right: 0;
}
html.sidebar-collapsed .brand-info {
  display: none;
}
html.sidebar-collapsed .brand-rail-mark {
  display: block;
}
html.sidebar-collapsed .side-item,
html.sidebar-collapsed .side-user {
  justify-content: center;
  padding: 6px 0;
}

/* At 60px the avatar and the gear cannot sit side by side, so the foot stacks.
   The gear stays because settings is a destination and the rail's whole premise
   is that destinations remain one click away; sign-out is hidden above. */
html.sidebar-collapsed .side-user {
  flex-direction: column;
  gap: 6px;
}

/* The group heads were the only thing separating one org from the next. */
html.sidebar-collapsed .side-group + .side-group {
  border-top: 1px solid var(--n150);
  padding-top: 10px;
}

@media (prefers-reduced-motion: reduce) {
  .sidebar,
  .side-toggle-icon {
    transition: none;
  }
}

/* ---------------------------------------------------------------- topbar */

.topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: var(--topbar-h);
  padding: 10px 24px;
  border-bottom: 1px solid var(--n200);
  background: var(--n0);
  position: sticky;
  top: 0;
  z-index: 30;
}
.topbar-title {
  min-width: 0;
  flex: 1;
}
.topbar-title h1 {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.topbar-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex: 0 0 auto;
}

.crumb {
  font-size: 11.5px;
  color: var(--n400);
  margin-bottom: 1px;
  display: flex;
  align-items: center;
  gap: 5px;
}
.crumb a {
  color: var(--n500);
}
.crumb a:hover {
  color: var(--primary);
  text-decoration: none;
}

/* --- scope header -------------------------------------------------------- */

/* The chip that names the level a page is editing. Colour carries as much of
   the message as the word does: violet for a workspace, teal for the
   organization above it, matching .badge-org everywhere else. Somebody
   administering both should be able to tell them apart before reading. */
.scope-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 18px;
  padding: 0 6px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.scope-chip.is-workspace {
  background: var(--primary-weak);
  color: var(--primary);
}
.scope-chip.is-org {
  background: var(--accent-weak);
  color: var(--accent);
}

/* The three pages of one scope, as tabs. Wraps rather than scrolls, because on
   a narrow window a hidden tab is a page nobody finds. */
.scope-tabs {
  flex-wrap: wrap;
  justify-content: flex-end;
}
.scope-tabs .btn.active {
  background: var(--primary-weak);
  border-color: var(--primary-weak-2);
  color: var(--primary);
  font-weight: 550;
}
.scope-tabs .btn svg {
  color: var(--n400);
}
.scope-tabs .btn.active svg {
  color: var(--primary);
}

.metaline {
  font-size: 12px;
  color: var(--n500);
  margin-top: 2px;
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
}
.metaline .sep {
  color: var(--n300);
}

/* ---------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 30px;
  padding: 0 11px;
  border-radius: var(--r);
  border: 1px solid var(--n200);
  background: var(--n0);
  color: var(--n700);
  font-size: 12.5px;
  font-weight: 550;
  font-family: inherit;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background 0.1s ease,
    border-color 0.1s ease,
    color 0.1s ease;
}
.btn:hover {
  background: var(--n50);
  border-color: var(--n300);
  color: var(--n900);
  text-decoration: none;
}
.btn:active {
  background: var(--n100);
}
.btn:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}

.btn-primary {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
  box-shadow: var(--sh-xs);
}
.btn-primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
  color: #fff;
}
.btn-primary:active {
  background: var(--primary-active);
}

.btn-ghost {
  background: transparent;
  border-color: transparent;
  color: var(--n500);
}
.btn-ghost:hover {
  background: var(--n100);
  border-color: transparent;
  color: var(--n900);
}

.btn-danger {
  background: var(--n0);
  border-color: var(--n200);
  color: var(--err);
}
.btn-danger:hover {
  background: var(--err-weak);
  border-color: #f3c9cb;
  color: var(--err);
}

.btn-danger-solid {
  background: var(--err);
  border-color: var(--err);
  color: #fff;
}
.btn-danger-solid:hover {
  background: #ad2126;
  border-color: #ad2126;
  color: #fff;
}

.btn-sm {
  height: 26px;
  padding: 0 9px;
  font-size: 12px;
}
.btn-lg {
  height: 36px;
  padding: 0 16px;
  font-size: 13.5px;
}
.btn-block {
  width: 100%;
}
.btn[disabled],
.btn.disabled {
  opacity: 0.5;
  pointer-events: none;
}

.btn svg {
  flex: 0 0 auto;
}

/* ---------------------------------------------------------------- forms */

label {
  font-size: 12px;
  font-weight: 550;
  color: var(--n700);
  display: block;
  margin-bottom: 5px;
}

input[type="text"],
input[type="email"],
input[type="password"],
select,
textarea {
  width: 100%;
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--n300);
  border-radius: var(--r);
  background: var(--n0);
  color: var(--n900);
  font: inherit;
  font-size: 13px;
  transition:
    border-color 0.1s ease,
    box-shadow 0.1s ease;
}
textarea {
  height: auto;
  padding: 9px 10px;
  resize: vertical;
  min-height: 150px;
  line-height: 1.6;
}
input::placeholder,
textarea::placeholder {
  color: var(--n400);
}
input:hover,
select:hover,
textarea:hover {
  border-color: var(--n400);
}
input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-ring);
}
select {
  cursor: pointer;
  appearance: none;
  padding-right: 26px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%236b7385' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
}

.field {
  margin-bottom: 12px;
}
.field:last-child {
  margin-bottom: 0;
}
.hint {
  font-size: 11.5px;
  color: var(--n500);
  margin-top: 5px;
  line-height: 1.45;
}

.checkline {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.checkline label {
  margin: 0;
  font-weight: 450;
  color: var(--n600);
  font-size: 12.5px;
}
.checkline input {
  width: auto;
  height: auto;
  accent-color: var(--primary);
}

/* ---------------------------------------------------------------- surfaces */

.panel {
  background: var(--n0);
  border: 1px solid var(--n200);
  border-radius: var(--r-md);
  box-shadow: var(--sh-xs);
  margin-bottom: 14px;
}
.panel-head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--n150);
}
.panel-head h2 {
  flex: 1;
  min-width: 0;
}
.panel-body {
  padding: 14px;
}
.panel-body.tight {
  padding: 10px 14px;
}
.panel.flush .panel-body {
  padding: 0;
}

.section-title {
  font-size: 10.5px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--n400);
  margin-bottom: 8px;
}

.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 372px;
  gap: 14px;
  align-items: start;
}
@media (max-width: 1180px) {
  .split {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------------------------------------------------------------- toolbar */

.toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.toolbar .spacer {
  flex: 1;
}
.toolbar h2 {
  font-size: 14px;
}

.segmented {
  display: inline-flex;
  background: var(--n100);
  border-radius: var(--r);
  padding: 2px;
  gap: 2px;
}
.segmented a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0 10px;
  height: 24px;
  border-radius: 5px;
  font-size: 12px;
  font-weight: 550;
  color: var(--n500);
  cursor: pointer;
}
.segmented a:hover {
  text-decoration: none;
  color: var(--n900);
}
.segmented a.on {
  background: var(--n0);
  color: var(--n900);
  box-shadow: var(--sh-xs);
}

/* ---------------------------------------------------------------- doc grid */

.docgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(198px, 1fr));
  gap: 12px;
}

.doccard {
  background: var(--n0);
  border: 1px solid var(--n200);
  border-radius: var(--r-md);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  position: relative;
  transition:
    border-color 0.12s ease,
    box-shadow 0.12s ease;
}
.doccard:hover {
  border-color: var(--n300);
  box-shadow: var(--sh);
}

.doccard-thumb {
  display: block;
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--n50);
  border-bottom: 1px solid var(--n150);
  overflow: hidden;
}
.doccard-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
}

.doccard-fallback {
  width: 100%;
  height: 100%;
  /* Top padding clears the absolutely-positioned format chip. */
  padding: 30px 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  background: var(--n25);
}
.doccard-fallback .snip {
  font-size: 10.5px;
  line-height: 1.55;
  color: var(--n500);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 6;
  -webkit-box-orient: vertical;
}
.doccard-fallback .none {
  margin: auto;
  color: var(--n400);
  font-size: 11.5px;
  font-weight: 500;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.doccard-body {
  padding: 9px 11px 11px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.doccard-name {
  font-size: 12.5px;
  font-weight: 550;
  color: var(--n900);
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.doccard-name:hover {
  text-decoration: none;
  color: var(--primary);
}
.doccard-meta {
  font-size: 11px;
  color: var(--n400);
  display: flex;
  align-items: center;
  gap: 5px;
  flex-wrap: wrap;
}

.doccard-actions {
  position: absolute;
  top: 7px;
  right: 7px;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.doccard:hover .doccard-actions,
.doccard:focus-within .doccard-actions {
  opacity: 1;
}
.doccard-actions .btn {
  background: rgba(255, 255, 255, 0.96);
  box-shadow: var(--sh);
}

.fmt-chip {
  position: absolute;
  top: 7px;
  left: 7px;
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid var(--n200);
  border-radius: 4px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--n600);
  padding: 1px 5px;
  backdrop-filter: blur(3px);
}

/* status dot instead of a loud badge in dense views */
.dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex: 0 0 6px;
  background: var(--n300);
}
.dot-indexed {
  background: var(--ok);
}
.dot-failed {
  background: var(--err);
}
.dot-uploaded {
  background: var(--n300);
}
.dot-extracted,
.dot-extracting,
.dot-embedding {
  background: var(--warn);
}

/* ---------------------------------------------------------------- table */

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
}
.table thead th {
  text-align: left;
  font-size: 10.5px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--n400);
  padding: 8px 12px;
  border-bottom: 1px solid var(--n200);
  background: var(--n25);
  white-space: nowrap;
}
.table td {
  padding: 8px 12px;
  border-bottom: 1px solid var(--n150);
  color: var(--n600);
}
.table tbody tr:last-child td {
  border-bottom: none;
}
.table tbody tr:hover {
  background: var(--n25);
}
.table .name {
  font-weight: 550;
  color: var(--n900);
}
.table .name a {
  color: inherit;
}
.table .name a:hover {
  color: var(--primary);
  text-decoration: none;
}
.table .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.table .actions {
  text-align: right;
  white-space: nowrap;
  width: 1%;
}
.table .actions .btn {
  opacity: 0;
}
.table tr:hover .actions .btn,
.table tr:focus-within .actions .btn {
  opacity: 1;
}

/* ---------------------------------------------------------------- badges */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 19px;
  padding: 0 7px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  background: var(--n100);
  color: var(--n600);
  white-space: nowrap;
}
.badge-indexed {
  background: var(--ok-weak);
  color: var(--ok);
}
.badge-failed {
  background: var(--err-weak);
  color: var(--err);
}
.badge-uploaded {
  background: var(--n100);
  color: var(--n500);
}
.badge-extracted,
.badge-extracting,
.badge-embedding {
  background: var(--warn-weak);
  color: var(--warn);
}
.badge-fmt {
  background: var(--accent-weak);
  color: var(--accent);
}
.badge-primary {
  background: var(--primary-weak);
  color: var(--primary);
}
.badge-stub {
  background: var(--warn-weak);
  color: var(--warn);
}
.badge-count {
  background: var(--n100);
  color: var(--n500);
  font-variant-numeric: tabular-nums;
}
/* Organization authority, wherever it reaches down a level: an enforced setting
   on a workspace panel, or the pool a workspace's credits fall back to. Teal
   rather than violet, so "the org decided this" never reads as "you did". */
.badge-org {
  background: var(--accent-weak);
  color: var(--accent);
}
/* Semantic variants, named for meaning rather than for a document status. */
.badge-violet {
  background: var(--primary-weak);
  color: var(--primary);
}
.badge-ok {
  background: var(--ok-weak);
  color: var(--ok);
}
.badge-warn {
  background: var(--warn-weak);
  color: var(--warn);
}
.badge-err {
  background: var(--err-weak);
  color: var(--err);
}
/* A price. Deliberately quiet — the same weight as the mode label it sits
   beside, not an alert. It reports what something costs, and a cost badge
   styled like a warning would make every paid technique look like a mistake. */
.badge-cost {
  background: var(--n100);
  color: var(--n600);
  font-variant-numeric: tabular-nums;
  cursor: help;
}
/* The same technique's price with "embed only what I describe" ticked — a
   conditional cost, so it is quieter than the one that applies right now.
   Both are shown because the checkbox is a second control and no static markup
   can total a combination the user has not made yet. */
.badge-muted {
  background: transparent;
  color: var(--n400);
  border: 1px dashed var(--n250, var(--n200));
  font-variant-numeric: tabular-nums;
  cursor: help;
}

/* The countable inputs behind a price, under a mode's description. Quieter
   than the description above it: this is evidence for the badge, not a second
   thing to read. */
.cost-breakdown {
  font-variant-numeric: tabular-nums;
  color: var(--n400);
}

/* The balance line under a panel that is about to spend from it. */
.costfoot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
  padding: 9px 14px;
  border-top: 1px solid var(--n150);
  background: var(--n25);
  font-size: 11.5px;
  color: var(--n500);
}
.costfoot strong {
  color: var(--n900);
  font-variant-numeric: tabular-nums;
}
.costfoot a {
  color: var(--n500);
  font-weight: 550;
}
.costfoot a:hover {
  color: var(--primary);
  text-decoration: none;
}
.costfoot .sep {
  color: var(--n300);
}

/* ---------------------------------------------------------------- upload */

.upload-strip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border: 1px dashed var(--n300);
  border-radius: var(--r-md);
  background: var(--n0);
  margin-bottom: 14px;
  cursor: pointer;
  transition:
    border-color 0.12s ease,
    background 0.12s ease;
}
.upload-strip:hover {
  border-color: var(--primary);
  background: var(--primary-weak);
}
.upload-strip .icon {
  color: var(--n400);
  flex: 0 0 auto;
  display: flex;
}
.upload-strip:hover .icon {
  color: var(--primary);
}
.upload-strip .t {
  font-size: 12.5px;
  font-weight: 550;
  color: var(--n700);
}
.upload-strip .d {
  font-size: 11.5px;
  color: var(--n400);
}

/* full-area drop overlay — appears only while dragging files */
.drop-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: rgba(125, 42, 232, 0.06);
  backdrop-filter: blur(2px);
  display: none;
  place-items: center;
  pointer-events: none;
}
.drop-overlay.on {
  display: grid;
}
.drop-overlay .card {
  background: var(--n0);
  border: 2px dashed var(--primary);
  border-radius: var(--r-lg);
  padding: 28px 40px;
  box-shadow: var(--sh-lg);
  text-align: center;
}
.drop-overlay .card strong {
  display: block;
  font-size: 15px;
  margin-bottom: 3px;
}
.drop-overlay .card span {
  font-size: 12.5px;
  color: var(--n500);
}

/* ---------------------------------------------------------------- job strip */

.jobstrip {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  background: var(--n0);
  border: 1px solid var(--n200);
  border-radius: var(--r-md);
  margin-bottom: 14px;
  flex-wrap: wrap;
}
.jobstrip .lead {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.jobstrip .title {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--n900);
}
.jobstrip .counts {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 12px;
  color: var(--n500);
  font-variant-numeric: tabular-nums;
}
.jobstrip .counts b {
  color: var(--n900);
  font-weight: 600;
}
.jobstrip .counts .ok b {
  color: var(--ok);
}
.jobstrip .counts .warn b {
  color: var(--warn);
}
.jobstrip .counts .bad b {
  color: var(--err);
}

.progress {
  height: 4px;
  border-radius: 2px;
  background: var(--n150);
  overflow: hidden;
  flex: 1;
  min-width: 120px;
}
.progress .bar {
  height: 100%;
  background: var(--ok);
  border-radius: 2px;
}
.progress .bar.partial {
  background: var(--warn);
}
.progress .bar.failed {
  background: var(--err);
}

.joblist {
  border-top: 1px solid var(--n150);
  margin-top: 10px;
  padding-top: 8px;
  width: 100%;
}
.jobrow {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 5px 0;
  font-size: 12px;
  color: var(--n600);
}
.jobrow .fname {
  flex: 0 0 auto;
  font-weight: 500;
  color: var(--n900);
}
.jobrow .detail {
  flex: 1;
  min-width: 0;
  color: var(--n400);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------------------------------------------------------- empty */

.empty {
  text-align: center;
  padding: 44px 24px;
  border: 1px solid var(--n200);
  border-radius: var(--r-md);
  background: var(--n0);
}
.empty .icon {
  width: 38px;
  height: 38px;
  border-radius: var(--r-md);
  background: var(--n100);
  color: var(--n400);
  display: grid;
  place-items: center;
  margin: 0 auto 12px;
}
.empty h2 {
  margin-bottom: 4px;
  font-size: 14px;
}
.empty p {
  margin: 0 auto 16px;
  font-size: 12.5px;
  color: var(--n500);
  max-width: 380px;
  line-height: 1.55;
}

/* ---------------------------------------------------------------- modal */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(22, 25, 31, 0.4);
  display: grid;
  place-items: center;
  z-index: 400;
  padding: 20px;
}
.modal {
  background: var(--n0);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-lg);
  width: 100%;
  max-width: 440px;
  overflow: hidden;
}
.modal-head {
  padding: 16px 18px 0;
}
.modal-head h2 {
  font-size: 15px;
  margin-bottom: 3px;
}
.modal-head .sub {
  color: var(--n500);
  font-size: 12.5px;
  line-height: 1.5;
}
.modal-body {
  padding: 16px 18px;
  /* .modal clips its children so the rounded corners hold, which means a body
     taller than the viewport is simply cut off with no way to reach the rest.
     Scrolling the body is what keeps a long picker readable on a short screen. */
  max-height: min(70vh, 620px);
  overflow-y: auto;
}
.modal-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  padding: 12px 18px;
  background: var(--n25);
  border-top: 1px solid var(--n150);
}
[hidden] {
  display: none !important;
}

/* ---------------------------------------------------------------- toasts */

.toasts {
  position: fixed;
  right: 16px;
  bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  z-index: 600;
  max-width: 380px;
}
.toast {
  background: var(--n900);
  color: #fff;
  border-radius: var(--r);
  padding: 9px 13px;
  box-shadow: var(--sh-lg);
  font-size: 12.5px;
  line-height: 1.45;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  animation: toast-in 0.16s ease;
}
.toast::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--n400);
  flex: 0 0 5px;
  margin-top: 6px;
}
.toast.success::before {
  background: #34d399;
}
.toast.error::before {
  background: #f87171;
}
.toast.warning::before {
  background: #fbbf24;
}
@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ---------------------------------------------------------------- alerts */

.alert {
  padding: 9px 12px;
  border-radius: var(--r);
  font-size: 12.5px;
  line-height: 1.5;
  border: 1px solid transparent;
}
.alert-error {
  background: var(--err-weak);
  color: #8f1f24;
  border-color: #f5cdcf;
}
.alert-info {
  background: var(--info-weak);
  color: #0a4a97;
  border-color: #cfe1fb;
}
.alert-warn {
  background: var(--warn-weak);
  color: #7d4407;
  border-color: #f6ddb9;
}
.alert code {
  background: rgba(0, 0, 0, 0.06);
  padding: 1px 4px;
  border-radius: 3px;
}

/* ---------------------------------------------------------------- doc detail */

.preview {
  border: 1px solid var(--n200);
  border-radius: var(--r-md);
  background: var(--n50);
  overflow: hidden;
}
.preview iframe {
  width: 100%;
  height: 520px;
  border: 0;
  background: #fff;
  display: block;
}
.preview .none {
  color: var(--n400);
  font-size: 12.5px;
  padding: 48px 24px;
  text-align: center;
}
.preview img {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 0 auto;
  background: var(--n100);
}

.doctext {
  white-space: pre-wrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  line-height: 1.7;
  background: var(--n25);
  border: 1px solid var(--n200);
  border-radius: var(--r);
  padding: 12px;
  max-height: 340px;
  overflow-y: auto;
  color: var(--n600);
}
.doctext::selection,
.doctext *::selection {
  background: var(--primary-weak-2);
}

.chunk {
  border-bottom: 1px solid var(--n150);
  padding: 10px 14px;
}
.chunk:last-child {
  border-bottom: none;
}
.chunk-head {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 10.5px;
  color: var(--n400);
  font-weight: 600;
  margin-bottom: 4px;
}
.chunk-head .ord {
  background: var(--n100);
  border-radius: 3px;
  padding: 0 5px;
  color: var(--n500);
  font-variant-numeric: tabular-nums;
}
.chunk-text {
  font-size: 12px;
  color: var(--n600);
  line-height: 1.6;
}

/* mode picker */
.modelist {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* The picker asks two questions in order: how the document is read, and how much
   of it to keep. Grouping is what makes that order visible — a flat list of six
   options reads as six unrelated choices. */
.modegroup + .modegroup {
  margin-top: 18px;
}
.modegroup-h {
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--n500);
  margin: 0 0 2px;
}
.modegroup-d {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--n500);
  margin: 0 0 8px;
}
.modeopt {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  border: 1px solid var(--n200);
  border-radius: var(--r);
  padding: 9px 11px;
  cursor: pointer;
  transition:
    border-color 0.1s ease,
    background 0.1s ease;
}
.modeopt:hover {
  border-color: var(--n300);
  background: var(--n25);
}
.modeopt.sel {
  border-color: var(--primary);
  background: var(--primary-weak);
}
.modeopt input {
  margin: 2px 0 0;
  width: auto;
  height: auto;
  flex: 0 0 auto;
  accent-color: var(--primary);
}
.modeopt .t {
  font-size: 12.5px;
  font-weight: 550;
  color: var(--n900);
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.modeopt .d {
  font-size: 11.5px;
  color: var(--n500);
  margin-top: 2px;
  line-height: 1.45;
}
.modeopt.na {
  opacity: 0.78;
}

/* The second axis of the picker: the techniques above decide how a document is
   read, this decides how much of the result to keep. Styled as a sibling of
   .modeopt rather than one of them, because it composes with whichever is
   selected instead of competing with them. */
.checkopt {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  border: 1px solid var(--n200);
  border-radius: var(--r);
  padding: 9px 11px;
  margin-top: 14px;
  cursor: pointer;
  transition:
    border-color 0.1s ease,
    background 0.1s ease;
}
.checkopt:hover {
  border-color: var(--n300);
  background: var(--n25);
}
.checkopt:has(input:checked) {
  border-color: var(--primary);
  background: var(--primary-weak);
}
.checkopt input {
  margin: 2px 0 0;
  width: auto;
  height: auto;
  flex: 0 0 auto;
  accent-color: var(--primary);
}
.checkopt .t {
  font-size: 12.5px;
  font-weight: 550;
  color: var(--n900);
  display: block;
}
.checkopt .d {
  font-size: 11.5px;
  color: var(--n500);
  margin-top: 2px;
  line-height: 1.45;
  display: block;
}
.checkopt.off {
  opacity: 0.45;
  cursor: not-allowed;
}

.mode-extra {
  margin-top: 8px;
  padding: 11px;
  border-radius: var(--r);
  background: var(--n25);
  border: 1px solid var(--n200);
}
.mode-extra textarea {
  width: 100%;
  font-size: 12.5px;
  line-height: 1.5;
  resize: vertical;
}

.kv {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 5px 12px;
  font-size: 12px;
}
.kv dt {
  color: var(--n400);
  font-weight: 500;
}
.kv dd {
  margin: 0;
  color: var(--n700);
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ------------------------------------------------------------ ask / answer */

.askbox {
  margin-bottom: 14px;
}
.askbox .panel-body {
  padding: 10px 12px;
}
.ask-icon {
  flex: 0 0 32px;
  height: 32px;
  display: grid;
  place-items: center;
  color: var(--n400);
}
/* Overrides the global input width:100% so the button can sit beside it. */
.ask-input {
  flex: 1;
  min-width: 0;
}
.askbox:focus-within .ask-icon {
  color: var(--primary);
}

/* States which agent and model the next question will run as. Indented to line
   up with the input rather than the icon, so it reads as a note on the box. */
.ask-foot {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding: 0 12px 10px 52px;
  font-size: 11.5px;
  color: var(--n400);
}
.ask-foot a {
  color: var(--n500);
  font-weight: 550;
}
.ask-foot a:hover {
  color: var(--primary);
  text-decoration: none;
}
.ask-foot .sep {
  color: var(--n300);
}

/* Answer prose. white-space: pre-wrap because a model's paragraph breaks are
   meaningful and the text is rendered as plain text, not markdown — escaping is
   Django's default, and this keeps it that way rather than trusting model output
   through |safe. */
.answer-text {
  white-space: pre-wrap;
  font-size: 13.5px;
  line-height: 1.62;
  color: var(--n900);
}
.answer-text::selection {
  background: var(--primary-weak-2);
}

/* One retrieved passage, collapsed. Closed by default: the answer is the thing
   to read, and eight expanded chunks would bury it. */
.cite {
  border: 1px solid var(--n200);
  border-radius: var(--r);
  margin-bottom: 6px;
  background: var(--n25);
}
.cite:last-child {
  margin-bottom: 0;
}
.cite > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  cursor: pointer;
  font-size: 12px;
  color: var(--n600);
  list-style: none;
}
.cite > summary::-webkit-details-marker {
  display: none;
}
.cite > summary:hover {
  color: var(--n900);
}
.cite[open] > summary {
  border-bottom: 1px solid var(--n150);
}
.cite-n {
  flex: 0 0 18px;
  height: 18px;
  display: grid;
  place-items: center;
  border-radius: 4px;
  background: var(--primary-weak-2);
  color: var(--primary);
  font-size: 10.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.cite-doc {
  font-weight: 550;
  color: var(--n900);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 320px;
}
a.cite-doc:hover {
  color: var(--primary);
  text-decoration: none;
}
.cite-meta {
  color: var(--n400);
}
.cite-score {
  color: var(--n500);
  font-variant-numeric: tabular-nums;
}
.cite-text {
  padding: 10px;
  font-size: 12px;
  line-height: 1.65;
  color: var(--n600);
  white-space: pre-wrap;
  max-height: 240px;
  overflow-y: auto;
}

/* ------------------------------------------------------------ about / donate */

.about {
  max-width: 720px;
}

/* Jump-link targets. The topbar is sticky at the top of the scroll container,
   so an anchor landing flush at y=0 puts the panel head underneath it — the
   link would appear to jump to the middle of the previous section.

   The margin clears the bar and then some. --topbar-h is a floor, not the
   height: signed out this page carries a "Sign in" crumb above the title, which
   pushes the bar to ~63px. Budgeting the padding on top of the token leaves the
   panel clear in both states rather than only the shorter one. */
.about-anchor {
  scroll-margin-top: calc(var(--topbar-h) + 20px);
}

/* Not a panel: this is navigation about the page rather than a section of it,
   so it stays lighter than the three cards it points into and doesn't read as
   a fourth one. */
.about-toc {
  margin-bottom: 14px;
}
.about-toc ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.about-toc a {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 7px 11px;
  border: 1px solid var(--n200);
  border-radius: 999px;
  background: var(--n0);
  box-shadow: var(--sh-xs);
  font-size: 12.5px;
  font-weight: 550;
  color: var(--n700);
  white-space: nowrap;
}
.about-toc a:hover {
  border-color: var(--primary);
  color: var(--primary);
  text-decoration: none;
}
.about-toc a svg {
  width: 13px;
  height: 13px;
  color: var(--primary);
}

/* Smooth only where it is wanted. A jump this short is easy to lose track of
   without the travel, but motion is exactly what a reduced-motion setting is
   asking not to have, so the default stays instant and smooth is opted into. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

.about-intro {
  display: flex;
  gap: 14px;
  align-items: flex-start;
}

/* The portrait is a binarized trace — black paths on transparency — so it takes
   a light field rather than the black chip the brand mark sits on, where it
   would disappear into the background. 52x73 is the source's 2500:3510 to five
   decimals, so `contain` has nothing to letterbox; re-derive both if the artwork
   is ever re-traced at another crop. */
.about-portrait {
  flex: 0 0 auto;
  width: 52px;
  height: 73px;
  display: block;
  object-fit: contain;
  background: var(--n100);
  border-radius: var(--r);
}
.about-name {
  font-size: 15px;
  margin-bottom: 4px;
}
.about-lede {
  margin: 0 0 12px;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--n600);
  max-width: 56ch;
}

/* The two RAG steps, numbered because they are ordered — retrieve happens
   before generate, and the point of the section is that sequence. Numbers are
   drawn rather than left to the list marker so they can carry the brand tint
   and stay aligned with the text beside them at any wrap width. */
.rag-steps {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
  max-width: 56ch;
}
.rag-steps li {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--n600);
}
.rag-step-n {
  flex: 0 0 auto;
  width: 19px;
  height: 19px;
  /* Nudged down so the digit centres on the first line of body text rather than
     on the cap line, which sits higher than the numeral's own optical middle. */
  margin-top: 2px;
  border-radius: 50%;
  background: var(--n100);
  color: var(--primary);
  font-size: 11px;
  font-weight: 650;
  display: flex;
  align-items: center;
  justify-content: center;
}
.rag-steps strong,
.rag-uses strong {
  color: var(--n700);
  font-weight: 650;
}

/* The takeaway between the mechanism and the uses. Ruled on the left rather
   than boxed: it is an aside to the paragraph above it, not a warning. */
.rag-point {
  margin: 0 0 16px;
  padding-left: 11px;
  border-left: 2px solid var(--n200);
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--n600);
  max-width: 56ch;
}

.rag-uses {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 60ch;
}
.rag-uses li {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--n600);
}
/* Sized here rather than on the element, so the sprite's 16-unit viewBox scales
   to the run of text instead of to whatever the icon last inherited. */
.rag-uses svg {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  margin-top: 4px;
  color: var(--primary);
}

.donate-grid {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.donate-tile {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 13px;
  border: 1px solid var(--n200);
  border-radius: var(--r-md);
  background: var(--n0);
  color: var(--n900);
  transition:
    border-color 0.12s ease,
    background 0.12s ease,
    transform 0.12s ease;
}
.donate-tile:hover {
  border-color: var(--primary);
  background: var(--primary-weak);
  text-decoration: none;
  transform: translateY(-1px);
}
.donate-tile:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}
@media (prefers-reduced-motion: reduce) {
  .donate-tile {
    transition: none;
  }
}

.donate-icon {
  flex: 0 0 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: var(--r);
  background: var(--n100);
  color: var(--n500);
}
.donate-tile:hover .donate-icon {
  background: var(--primary-weak-2);
  color: var(--primary);
}
.donate-icon svg {
  width: 17px;
  height: 17px;
}

.donate-text {
  flex: 1;
  min-width: 0;
}
.donate-label {
  display: block;
  font-size: 13px;
  font-weight: 550;
}
.donate-note {
  display: block;
  font-size: 11.5px;
  color: var(--n400);
  margin-top: 1px;
}

.donate-amount {
  flex: 0 0 auto;
  font-size: 13.5px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--n500);
}
.donate-tile:hover .donate-amount {
  color: var(--primary);
}

.donate-custom {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--n150);
}

/* The $ sits inside the field rather than beside it, so the number reads as an
   amount at a glance instead of as an arbitrary integer. */
.donate-currency {
  position: relative;
  display: block;
  width: 150px;
}
.donate-symbol {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--n400);
  font-size: 13px;
  pointer-events: none;
}
.donate-currency input {
  padding-left: 22px;
}
/* Spinners fight the currency framing and are useless for a one-off amount. */
.donate-currency input::-webkit-outer-spin-button,
.donate-currency input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.donate-currency input {
  -moz-appearance: textfield;
  appearance: textfield;
}

/* ---------------------------------------------------------------- members */

.mrow {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--n150);
}
.mrow:last-child {
  border-bottom: none;
}
.mrow .who {
  flex: 1;
  min-width: 0;
}
.mrow .em {
  font-size: 12.5px;
  font-weight: 550;
  color: var(--n900);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mrow .nm {
  font-size: 11.5px;
  color: var(--n400);
  margin-top: 1px;
}
.mrow form {
  display: flex;
  gap: 6px;
  align-items: center;
  margin: 0;
}
.mrow select {
  width: auto;
  height: 26px;
  font-size: 12px;
  padding: 0 24px 0 8px;
}

.invite-link {
  display: flex;
  gap: 6px;
  align-items: center;
  background: var(--n25);
  border: 1px solid var(--n200);
  border-radius: var(--r-sm);
  padding: 3px 3px 3px 9px;
  margin-top: 6px;
  max-width: 520px;
}
.invite-link code {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--n500);
  font-size: 11px;
}

/* ---------------------------------------------------------------- auth */

.auth {
  min-height: 100vh;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
@media (max-width: 900px) {
  .auth {
    grid-template-columns: minmax(0, 1fr);
  }
  .auth-aside {
    display: none;
  }
}

.auth-aside {
  background: var(--n900);
  color: #fff;
  padding: 48px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: relative;
  overflow: hidden;
}
.auth-aside::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(
      620px 340px at 12% 8%,
      rgba(0, 196, 204, 0.22),
      transparent 62%
    ),
    radial-gradient(
      680px 380px at 88% 92%,
      rgba(125, 42, 232, 0.34),
      transparent 62%
    );
  pointer-events: none;
}
.auth-aside > * {
  position: relative;
  z-index: 1;
}
.auth-aside .brand {
  border: 0;
  padding: 0;
  height: auto;
  color: #fff;
}

/* The login panel carries the wordmark at full size: this is the one place with
   room for it, and the R's counter and the W's joins only really open up above
   ~80px. The panel is hidden outright below 900px wide, so the fixed height
   never has to survive a narrow viewport. */
.auth-hero {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.auth-logo {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  color: #fff;
  /* Whole gap, no correction: the vector is cropped to its ink, so there is no
     transparent margin inside the image to discount. */
  gap: 18px;
}
.auth-logo:hover {
  text-decoration: none;
}
/* Height only, as in the sidebar: the vector is cropped to its ink, so the
   letters sit flush with the tagline below without a correcting margin. */
.auth-logo .brand-word-mark {
  height: 104px;
}
/* Two thirds of the artwork's height rather than the half it was set at beside
   the old square mark: the name is the label here, and the leaning logotype
   beside it is the thing meant to be read first. Colour comes from the shared
   gradient rule in the brand section; the white is only the fallback. */
.auth-wordmark {
  font-size: 40px;
  font-weight: 650;
  letter-spacing: -0.035em;
  line-height: 1;
}
/* Beats .auth-aside p, which is sized for body copy. */
.auth-aside .auth-tagline {
  margin: 0;
  max-width: 30ch;
  font-size: 16px;
  font-weight: 550;
  line-height: 1.45;
  letter-spacing: -0.01em;
  color: rgba(255, 255, 255, 0.92);
}

/* .auth-aside clips rather than scrolls, so on short viewports the lockup gives
   up size before the value prop below it gets cut off. */
@media (max-height: 720px) {
  .auth-logo .brand-word-mark {
    height: 74px;
  }
  .auth-wordmark {
    font-size: 30px;
  }
  .auth-hero {
    gap: 14px;
  }
}
.auth-aside h2 {
  color: #fff;
  font-size: 27px;
  line-height: 1.25;
  letter-spacing: -0.025em;
  margin-bottom: 12px;
  max-width: 15ch;
}
.auth-aside p {
  color: rgba(255, 255, 255, 0.66);
  font-size: 14px;
  line-height: 1.6;
  max-width: 42ch;
  margin: 0;
}
.auth-points {
  display: flex;
  flex-direction: column;
  gap: 11px;
  margin-top: 26px;
}
.auth-point {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.8);
}
.auth-point .tick {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.13);
  color: #fff;
  display: grid;
  place-items: center;
  flex: 0 0 16px;
  margin-top: 1px;
}
.auth-foot {
  font-size: 11.5px;
  color: rgba(255, 255, 255, 0.4);
}

.auth-main {
  display: grid;
  place-items: center;
  padding: 40px 24px;
  background: var(--n0);
}
.auth-form {
  width: 100%;
  max-width: 336px;
}
.auth-form h1 {
  font-size: 21px;
  margin-bottom: 5px;
}
.auth-form .sub {
  color: var(--n500);
  font-size: 13px;
  margin: 0 0 24px;
}

.btn-google {
  width: 100%;
  height: 38px;
  background: var(--n0);
  border: 1px solid var(--n300);
  color: var(--n900);
  font-size: 13.5px;
  font-weight: 550;
}
.btn-google:hover {
  background: var(--n50);
  border-color: var(--n400);
}

.sep {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--n400);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 18px 0;
}
.sep::before,
.sep::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--n200);
}

.devnote {
  margin-top: 20px;
  padding-top: 14px;
  border-top: 1px solid var(--n150);
  font-size: 11px;
  color: var(--n400);
  line-height: 1.5;
}

/* ---------------------------------------------------------------- htmx */

.htmx-indicator {
  display: none;
}
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  display: inline-flex;
}
.htmx-request.btn {
  pointer-events: none;
  opacity: 0.7;
}

.spinner {
  width: 12px;
  height: 12px;
  border: 1.5px solid rgba(255, 255, 255, 0.35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
  display: inline-block;
}
.spinner.dark {
  border-color: var(--n200);
  border-top-color: var(--primary);
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* ---------------------------------------------------------------- utils */

.muted {
  color: var(--n500);
}
.small {
  font-size: 11.5px;
}
.row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.wrap {
  flex-wrap: wrap;
}
.spacer {
  flex: 1;
}
.stack {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.nowrap {
  white-space: nowrap;
}

/* ---------------------------------------------------------------- billing

   The credit balance, the plan picker, and the ledger. Kept out of the modal
   block above because the plan picker is also the widest thing in the app —
   Stripe's embedded iframe has a minimum comfortable width, and a 440px dialog
   squeezes its card fields into two cramped columns. */

.modal-wide {
  max-width: 560px;
}
/* Checkout is taller than the viewport on a laptop once the card fields,
   address, and wallet buttons are all up. Scroll the dialog rather than the
   page behind it. */
.modal-wide .modal-body {
  max-height: min(72vh, 640px);
  overflow-y: auto;
}

.notice {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 11px;
  border-radius: var(--r);
  font-size: 12.5px;
  line-height: 1.45;
  border: 1px solid var(--n200);
  background: var(--n50);
  color: var(--n600);
}
.notice-info {
  border-color: #c7dcfa;
  background: var(--info-weak);
  color: var(--info);
}
.notice-warn {
  border-color: #f2ddbb;
  background: var(--warn-weak);
  color: var(--warn);
}

/* --- balance --- */

.balance-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}
.balance {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.balance-value {
  font-size: 30px;
  font-weight: 680;
  letter-spacing: -0.02em;
  color: var(--n900);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.balance-unit {
  font-size: 12.5px;
  color: var(--n500);
}

.subline {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--n150);
  font-size: 12.5px;
  color: var(--n600);
}
/* .sep is a full-width labelled divider elsewhere; here it is a dot, the same
   override .metaline makes. */
.subline .sep {
  display: inline;
  margin: 0;
  color: var(--n300);
  font-size: inherit;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
}
.subline .sep::before,
.subline .sep::after {
  content: none;
}

/* --- plan picker --- */

.planlist {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.planopt {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  margin: 0;
  border: 1px solid var(--n200);
  border-radius: var(--r);
  background: var(--n0);
  cursor: pointer;
  transition:
    border-color 0.12s ease,
    background 0.12s ease;
}
.planopt:hover {
  border-color: var(--n300);
  background: var(--n25);
}
.planopt.sel {
  border-color: var(--primary);
  background: var(--primary-weak);
}
.planopt:focus-within {
  outline: 2px solid var(--primary-ring);
  outline-offset: 1px;
}
@media (prefers-reduced-motion: reduce) {
  .planopt {
    transition: none;
  }
}

.planopt input {
  margin: 0;
  width: auto;
  height: auto;
  flex: 0 0 auto;
  accent-color: var(--primary);
}
.plan-main {
  flex: 1;
  min-width: 0;
}
.plan-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--n900);
}
.plan-note {
  display: block;
  font-size: 11.5px;
  color: var(--n500);
  margin-top: 2px;
  line-height: 1.45;
}
.plan-price {
  text-align: right;
  flex: 0 0 auto;
}
.plan-amount {
  display: block;
  font-size: 14px;
  font-weight: 680;
  color: var(--n900);
  font-variant-numeric: tabular-nums;
}
.plan-credits {
  display: block;
  font-size: 11px;
  color: var(--n500);
  margin-top: 1px;
}

/* --- ledger --- */

.ledger {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
}
.ledger td {
  padding: 8px 4px;
  border-bottom: 1px solid var(--n100);
  vertical-align: top;
}
.ledger tr:last-child td {
  border-bottom: 0;
}
.ledger-when {
  color: var(--n500);
  white-space: nowrap;
  width: 1%;
}
.ledger-what {
  color: var(--n700);
}
.ledger-delta,
.ledger-after {
  text-align: right;
  white-space: nowrap;
  width: 1%;
  font-variant-numeric: tabular-nums;
}
.ledger-delta {
  font-weight: 620;
}
.ledger-delta.up {
  color: var(--ok);
}
.ledger-delta.down {
  color: var(--n600);
}
.ledger-after {
  color: var(--n400);
}

/* ------------------------------------------------------------ legal pages */

/* Policy documents are long-form prose, unlike the rest of the app, which is
   panels of controls. They need the two things body copy always needs and that
   a dense UI stylesheet has no reason to provide: a line height that is
   readable over paragraphs rather than over labels, and vertical rhythm between
   headings and the text under them. */
.legal-body {
  line-height: 1.65;
}

.legal-body p {
  margin: 0 0 12px;
}

.legal-body h3 {
  margin: 26px 0 10px;
}

.legal-body h4 {
  margin: 18px 0 6px;
  font-size: 13px;
  color: var(--n700);
}

.legal-body hr {
  margin: 28px 0;
  border: 0;
  border-top: 1px solid var(--n150);
}

/* Bolded text inside a policy is doing real work: it marks the clauses that
   allocate risk (the warranty disclaimer, the liability cap, the AI accuracy
   warning). Some jurisdictions expect those to be conspicuous, so this makes
   them so rather than leaving it to the default weight. */
.legal-body strong {
  color: var(--n900);
}

/* Tables carry the Article 30 processing record, the Annex I description, and
   the subprocessor list. All three are wide, so every one is wrapped in an
   overflow-x container by the template and this only has to style the contents:
   the page itself must never scroll sideways. */
.legal-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  margin: 4px 0 12px;
}

.legal-table th,
.legal-table td {
  padding: 8px 10px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--n150);
}

.legal-table thead th {
  font-weight: 600;
  color: var(--n600);
  background: var(--n50);
  white-space: nowrap;
}

.legal-table tbody th {
  font-weight: 600;
  color: var(--n700);
  white-space: nowrap;
}

/* The AI accuracy notice under every generated answer. Deliberately not styled
   as an alert: an alert is dismissible-looking and reads as an error state,
   while this is a permanent property of the output it sits under. Quiet, always
   present, and attached to the thing it qualifies. */
.ai-notice {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin-top: 12px;
  padding: 8px 10px;
  border: 1px solid var(--n150);
  border-radius: 6px;
  background: var(--n25);
  font-size: 12px;
  line-height: 1.5;
  color: var(--n600);
}

.ai-notice svg {
  flex: 0 0 auto;
  margin-top: 2px;
  color: var(--n400);
}

/* The footer that carries the policy links. Every page needs them reachable:
   a privacy notice nobody can find is not notice, and Stripe requires the
   links to be present on the site. */
.legal-foot {
  margin: 28px 0 8px;
  padding-top: 14px;
  border-top: 1px solid var(--n150);
  font-size: 12px;
  color: var(--n500);
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  align-items: center;
}

.legal-foot a {
  color: var(--n500);
  text-decoration: none;
}

.legal-foot a:hover {
  color: var(--n700);
  text-decoration: underline;
}

/* ------------------------------------------------------------ consent gate */

/* One outstanding document: its title, a Read button, and the slot the text
   loads into. */
.consent-doc {
  border: 1px solid var(--n200);
  border-radius: 8px;
  margin-bottom: 10px;
  overflow: hidden;
}

.consent-doc-head {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 10px 12px;
  background: var(--n25);
}

.policy-reader-head {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  padding: 8px 12px;
  border-top: 1px solid var(--n150);
  background: var(--n50);
  font-size: 12px;
}

/* Bounded and scrollable rather than expanded inline. These documents run to
   hundreds of lines, and letting one push the checkbox below the fold is the
   surest way to guarantee nobody ever reaches it. The reader keeps the text
   and the control that depends on it visible at the same time. */
.policy-reader-body {
  max-height: 340px;
  overflow-y: auto;
  padding: 14px 16px;
  font-size: 13px;
  background: var(--n0);
}

/* The focus ring matters here: the region is tabindex="0" so it can be
   scrolled from the keyboard, and a focusable thing with no visible focus is
   unusable for anyone not using a mouse. */
.policy-reader-body:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}

.consent-field {
  margin: 14px 0 12px;
}

/* Checkbox and its text side by side, with the box aligned to the first line
   rather than centred against a paragraph of legalese. */
.consent-label {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  line-height: 1.55;
  cursor: pointer;
}

.consent-label input[type="checkbox"] {
  flex: 0 0 auto;
  margin-top: 3px;
}

/* A disabled checkbox with no visual difference reads as a broken page rather
   than as a precondition, so the label dims with it and the cursor says so. */
.consent-label input[type="checkbox"]:disabled {
  cursor: not-allowed;
}

.consent-label:has(input:disabled) {
  color: var(--n500);
  cursor: not-allowed;
}

.consent-pending {
  display: flex;
  gap: 6px;
  align-items: center;
  margin: 0 0 4px;
  color: var(--n600);
}

.consent-pending svg {
  flex: 0 0 auto;
  color: var(--n400);
}
