/* Palette takes its brand values from the marketing site's own tokens
   (tensormux.com's src/app/globals.css): orange #f97316 as the accent, and
   the warm "paper" greys (--paper #faf9f7) that give the brand its
   character, over near-neutral surfaces. Colours used as *text* are tuned
   per mode — a single value cannot clear contrast on both grounds, see
   --accent-text.

   The surface tokens are split by ROLE, not by the first place they were
   used. --sidebar-bg once backed the rail, the table header and every
   hover state at once, which meant lightening the sidebar silently made
   hover feedback invisible. Now:
     --panel-bg    raised surfaces (cards, table body)
     --sidebar-bg  the rail, and nothing else
     --surface-2   quiet standing surfaces (table header, disabled fields)
     --accent-bg   hover feedback — must stay perceptible against --bg */
:root {
  --bg: #ffffff;
  --panel-bg: #ffffff;
  --sidebar-bg: #faf9f7;
  --surface-2: #f5f3f0;
  --border: #e7e3dd;
  --text: #0a0a0a;
  --text-muted: #57534e;

  /* Brand orange, for fills and indicators — the active-nav marker, status
     dots, accented icons — where it sits on a neutral or tinted surface
     rather than being read as prose. */
  --accent: #f97316;
  --accent-hover: #ea580c;
  /* #f97316 on white is only 2.8:1, below the 4.5:1 needed for text, so
     anything rendered AS text uses a darker orange (5.2:1). Dark mode has
     no such problem and keeps a brighter value. */
  --accent-text: #c2410c;
  --accent-soft: rgba(249, 115, 22, 0.1);
  /* Hover feedback. Deliberately NOT the brand colour — it backs nav, row
     and button hovers, all of which would be garish in orange. */
  --accent-bg: #f1efeb;

  /* Same hue families as the site's --tm-success/warning/danger, one step
     darker so they stay legible as text on their own tints. */
  --green: #15803d;
  --green-bg: #e7f6ec;
  --amber: #b45309;
  --amber-bg: #fdf2e3;
  /* The site's own --tm-danger. Softer than the oxblood it replaces while
     still clearing AA on white at 4.9:1 — one step lighter (#ef4444) drops
     to 3.8:1, which is too weak for a destructive action's label. */
  --red: #dc2626;
  /* Lightened alongside it: the badge sets --red as text ON this tint, and
     the previous #fdeaea left that pairing just under 4.5:1. */
  --red-bg: #fef2f2;
  /* Code samples stay a dark surface in light mode by design. */
  --code-bg: #0d0d0d;
  --radius: 8px;
}

:root[data-theme="dark"] {
  --bg: #0a0a0a;
  --panel-bg: #161513;
  /* Light mode sets the rail slightly darker than the page; dark mode
     slightly lighter. Either way the rail is offset from the page rather
     than merging with it. */
  --sidebar-bg: #0f0e0d;
  --surface-2: #1c1a18;
  --border: #2a2724;
  --text: #fafaf9;
  --text-muted: #a8a29e;

  --accent: #f97316;
  /* Hover brightens here rather than darkening as it does in light mode —
     on a near-black surface #ea580c would recede instead of respond. */
  --accent-hover: #fb923c;
  --accent-text: #fb923c;
  --accent-soft: rgba(249, 115, 22, 0.16);
  --accent-bg: #232120;

  --green: #22c55e;
  --green-bg: #10281a;
  --amber: #facc15;
  --amber-bg: #2e2408;
  --red: #ef4444;
  --red-bg: #2e1414;
  /* Lifted off the page here — #0d0d0d would merge with --bg. */
  --code-bg: #1c1a18;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
}

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

/* Sidebar */
.sidebar {
  width: 220px;
  flex-shrink: 0;
  background: var(--sidebar-bg);
  border-right: 1px solid var(--border);
  padding: 20px 12px;
  display: flex;
  flex-direction: column;
  transition: width 0.15s ease;
  /* Without this, .sidebar stretches to match .main's height (default flex
     stretch), so growing page content (e.g. expanding every model card)
     pushes .sidebar-footer's margin-top:auto position off-screen instead of
     keeping it pinned to the viewport. */
  position: sticky;
  top: 0;
  align-self: flex-start;
  height: 100vh;
}

.sidebar.collapsed {
  width: 64px;
  padding: 20px 8px;
}

.sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 4px 4px 20px 6px;
}

/* Collapsed, the rail shows the mark alone. A permanently visible toggle
   would read as a fifth nav item once the wordmark is gone — same size and
   spacing as the icons below it, but the only one that doesn't navigate.
   Instead the header swaps: hovering (or tabbing into) it exchanges the
   mark for the toggle in place, so the logo's own position is the expand
   affordance. The rule below it separates brand from navigation. */
.sidebar.collapsed .sidebar-header {
  position: relative;
  justify-content: center;
  padding: 4px 0 14px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--border);
}

.sidebar.collapsed .collapse-btn {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  opacity: 0;
}

/* opacity, not display:none — the button has to stay focusable or the
   keyboard route to re-expanding the sidebar disappears with it.

   :focus-visible, NOT :focus-within. Clicking the button to collapse leaves
   it focused, and :focus-within would then hold the swap open after the
   pointer left — so the mark never reappeared until the next navigation
   cleared focus. :focus-visible matches keyboard focus but not a mouse
   click, which is exactly the distinction needed here. */
/* pointer-events:none matters now that .brand is a link. Faded to zero it
   would still sit in the hit area and swallow the click, so hovering the
   collapsed logo would navigate home instead of expanding the sidebar. */
.sidebar.collapsed .sidebar-header:hover .brand,
.sidebar.collapsed .sidebar-header:has(.collapse-btn:focus-visible) .brand {
  opacity: 0;
  pointer-events: none;
}

.sidebar.collapsed .sidebar-header:hover .collapse-btn,
.sidebar.collapsed .collapse-btn:focus-visible {
  opacity: 1;
}

/* An <a> to the app's default page. Styled to read as the wordmark, not as
   a link — no underline, inherited colour. */
.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
}

.brand:hover .brand-name {
  opacity: 0.75;
}

.brand-mark {
  width: 22px;
  height: auto;
  flex-shrink: 0;
}

/* Both sides of the collapsed hover swap fade rather than cut. */
.brand,
.collapse-btn {
  transition: opacity 0.12s ease;
}

/* Collapsed, the mark stays and only the wordmark goes — the same trade the
   nav items make with their labels, so the rail keeps an identity anchor
   instead of showing nothing but a toggle. */
.sidebar.collapsed .brand-name {
  display: none;
}

.sidebar.collapsed .brand {
  justify-content: center;
}

.collapse-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  border-radius: 6px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  position: relative;
  padding: 0;
}

.collapse-btn:hover {
  background: var(--accent-bg);
  border-color: var(--border);
  color: var(--text);
}

.collapse-btn svg {
  width: 16px;
  height: 16px;
}

.collapse-btn[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(-4px);
  background: var(--text);
  color: var(--bg);
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
  padding: 6px 10px;
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.1s ease, transform 0.1s ease;
  z-index: 10;
}

.sidebar.collapsed .collapse-btn[data-tooltip]::after {
  left: 0;
  transform: translateY(-4px);
}

.sidebar.collapsed .collapse-btn[data-tooltip]:hover::after {
  transform: translateY(0);
}

.collapse-btn[data-tooltip]:hover::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

.nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius);
  color: var(--text-muted);
  text-decoration: none;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
}

.nav-item:hover {
  background: var(--accent-bg);
  color: var(--text);
}

.nav-item.active {
  background: var(--accent-soft);
  color: var(--text);
  position: relative;
}

/* The one place the chrome spends brand colour: a marker on the page you
   are on. It sits inside the item's own left edge rather than outside it —
   .nav-item is overflow:hidden (it clips the label as the sidebar
   collapses), so anything at a negative offset would be clipped away. The
   18px height keeps it clear of the 8px corner radius. */
.nav-item.active::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 18px;
  border-radius: 0 3px 3px 0;
  background: var(--accent);
}

.nav-item.active svg {
  color: var(--accent);
}

.nav-item svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.sidebar.collapsed .nav-item {
  justify-content: center;
  padding: 8px;
}

.sidebar.collapsed .nav-item .label {
  display: none;
}

/* User footer */
/* The rule sets the account area apart from navigation. Without it the
   footer just floats at the bottom of a large gap and reads as one more
   nav row rather than a different kind of thing. */
.sidebar-footer {
  margin-top: auto;
  position: relative;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

/* 8px, not 4: the account button and the theme toggle are unrelated
   controls, and at 4px the chevron and the moon crowd into one another. */
.footer-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.sidebar.collapsed .footer-row {
  flex-direction: column;
  gap: 6px;
}

.theme-toggle-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  border-radius: 6px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  position: relative;
  padding: 0;
}

.theme-toggle-btn:hover {
  background: var(--accent-bg);
  border-color: var(--border);
  color: var(--text);
}

.theme-toggle-btn svg {
  width: 16px;
  height: 16px;
}

.theme-toggle-btn[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  background: var(--text);
  color: var(--bg);
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
  padding: 6px 10px;
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  transform: translateY(4px);
  transition: opacity 0.1s ease, transform 0.1s ease;
  z-index: 10;
}

.theme-toggle-btn[data-tooltip]:hover::after {
  opacity: 1;
  transform: translateY(0);
}

.user-trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
  padding: 8px 10px;
  border: none;
  border-radius: var(--radius);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
}

.user-trigger:hover {
  background: var(--accent-bg);
}

.sidebar.collapsed .user-trigger {
  justify-content: center;
  padding: 8px;
}

.sidebar.collapsed .user-trigger .user-name {
  display: none;
}

.avatar {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  /* Inverts with the theme, so the initials stay high-contrast in both
     modes. Deliberately not the brand orange: the active-nav marker is the
     one place the chrome spends that colour, and white-on-orange would not
     clear contrast at 11px anyway. */
  background: var(--text);
  color: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
  flex-shrink: 0;
}

.user-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Required for the ellipsis to ever appear: a flex item defaults to
     min-width:auto, which refuses to shrink below the text's own width, so
     a long name would push the chevron out and overflow the rail instead
     of truncating. */
  min-width: 0;
}

/* Deliberately does NOT grow. Pushed to the trigger's right edge the
   chevron ends up nearer the theme toggle than the name it belongs to, and
   the two read as a pair of icons. Sitting directly after the name it reads
   as part of it. Shrinking stays enabled so long names still ellipsize. */
.user-trigger .user-name {
  flex: 0 1 auto;
}

/* The affordance. Hover alone told nobody this opens a menu — and on a
   touch device there is no hover at all, so the trigger read as a plain
   label despite already carrying aria-haspopup. */
.user-chevron {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  color: var(--text-muted);
  transition: transform 0.12s ease;
}

.user-trigger[aria-expanded="true"] .user-chevron {
  transform: rotate(180deg);
}

/* Hidden when collapsed, like the wordmark and the nav labels — in a 64px
   rail it would just be noise beside the avatar. */
.sidebar.collapsed .user-trigger .user-chevron {
  display: none;
}

.user-menu {
  display: none;
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  width: 240px;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
  padding: 6px;
  z-index: 20;
}

.user-menu.open {
  display: block;
}

.user-menu-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px 10px;
}

.user-menu-header .user-name {
  font-weight: 600;
}

.user-menu hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 6px 0;
}

.user-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--text);
  text-decoration: none;
  font: inherit;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
}

.user-menu-item:hover,
.user-menu-item.active {
  background: var(--accent-bg);
}

.user-menu-item svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.theme-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.user-menu-item.danger {
  color: var(--red);
}

.user-menu-item.danger:hover {
  background: var(--red-bg);
}

/* Main content */
.main {
  flex: 1;
  padding: 40px 48px;
  max-width: 1100px;
}

.page-title {
  font-size: 28px;
  font-weight: 500;
  margin: 0 0 6px;
}

.page-subtitle {
  color: var(--text-muted);
  margin: 0 0 32px;
}

.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 32px;
}

.page-header .page-title,
.page-header .page-subtitle {
  margin-bottom: 0;
}

/* Plan limit stated under a page title, e.g. the API Keys one-key rule. */
.tier-note {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 10px 0 0;
  font-size: 13px;
  color: var(--text-muted);
}

.tier-note a {
  color: var(--accent-text);
  text-decoration: none;
  font-weight: 500;
}

.tier-note a:hover {
  text-decoration: underline;
}

.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 16px;
  border: none;
  border-radius: var(--radius);
  background: var(--text);
  color: var(--bg);
  font: inherit;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  flex-shrink: 0;
}

.btn-primary:hover {
  opacity: 0.85;
}

.btn-primary:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.btn-primary svg {
  width: 16px;
  height: 16px;
}

.btn-revoke {
  border: none;
  background: none;
  color: var(--red);
  font: inherit;
  font-weight: 500;
  font-size: 13px;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 6px;
}

.btn-revoke:hover {
  background: var(--red-bg);
}

.btn-danger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 16px;
  border: none;
  border-radius: var(--radius);
  background: var(--red);
  color: #fff;
  font: inherit;
  font-weight: 500;
  cursor: pointer;
}

.btn-danger:hover {
  opacity: 0.85;
}

/* Settings */
.settings-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 20px;
}

.settings-card-body {
  padding: 20px;
}

.settings-card-title {
  font-size: 16px;
  font-weight: 600;
  margin: 0 0 16px;
}

.settings-card-footer {
  display: flex;
  justify-content: flex-end;
  padding: 12px 20px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  border-radius: 0 0 var(--radius) var(--radius);
}

.form-group {
  margin-bottom: 16px;
}

.form-group:last-child {
  margin-bottom: 0;
}

.form-row {
  display: flex;
  gap: 16px;
}

.form-row .form-group {
  flex: 1;
}

.form-label {
  display: block;
  font-size: 13px;
  font-weight: 500;
  margin-bottom: 6px;
}

.form-input {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
}

.form-input:disabled {
  color: var(--text-muted);
  background: var(--surface-2);
}

.form-hint {
  font-size: 12px;
  color: var(--text-muted);
  margin: 6px 0 0;
}

.form-hint a {
  color: inherit;
}

/* Model list */
.model-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.model-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.model-card-header {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  padding: 18px 20px;
  border: none;
  background: none;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.model-card-header:hover {
  background: var(--accent-bg);
}

.model-card-top {
  display: flex;
  align-items: center;
  gap: 12px;
}

.model-icon {
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  border-radius: 7px;
  background: var(--text);
  color: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
}

.model-name {
  font-size: 15px;
  font-weight: 600;
}

.model-card-top .chevron {
  margin-left: auto;
  width: 18px;
  height: 18px;
  color: var(--text-muted);
  flex-shrink: 0;
  transition: transform 0.15s ease;
}

.model-card.expanded .chevron {
  transform: rotate(180deg);
}

.model-desc {
  color: var(--text-muted);
  font-size: 13px;
  font-weight: 400;
  padding-left: 42px;
}

.model-card-body {
  display: none;
  padding: 0 20px 20px;
  border-top: 1px solid var(--border);
  padding-top: 18px;
}

.model-card.expanded .model-card-body {
  display: block;
}

.endpoint-line {
  margin: 0 0 18px;
}

.quickstart-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.quickstart-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
  color: var(--text-muted);
}

.lang-select {
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.stream-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
}

.stream-toggle .toggle-pill {
  font-size: 11px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-muted);
}

.stream-toggle.on .toggle-pill {
  background: var(--green-bg);
  color: var(--green);
}

.code-block {
  position: relative;
  background: var(--code-bg);
  border-radius: var(--radius);
  overflow: hidden;
}

.code-block pre {
  margin: 0;
  padding: 18px 44px 18px 20px;
  overflow-x: auto;
}

.code-block code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.6;
  color: #e6e6e6;
  white-space: pre;
}

.copy-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  border: 1px solid transparent;
  background: rgba(255, 255, 255, 0.06);
  color: #c8c8c8;
  cursor: pointer;
}

.copy-btn:hover {
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
}

.copy-btn svg {
  width: 14px;
  height: 14px;
}

.copy-btn .icon-check {
  display: none;
}

.copy-btn.copied .icon-copy {
  display: none;
}

.copy-btn.copied .icon-check {
  display: block;
  color: var(--green);
}

/* Table */
.table-wrap {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

/* Explicit proportions rather than letting the browser share out the spare
   width, which distributed it unevenly and left a ~460px gap mid-row.
   Percentages (not shrink-to-fit) because the empty state has no data cells
   to size against — sizing to content collapses the header row into the
   left quarter of the table. */
thead th:nth-child(1) {
  width: 26%;
}

thead th:nth-child(2) {
  width: 34%;
}

thead th:nth-child(3) {
  width: 22%;
}

/* The trailing action reads as the row's control, so it sits at the table's
   right edge instead of floating at the left of an oversized last cell.
   :not(.table-empty) matters — the empty state is a single colspan cell,
   which is also its row's last child, and this rule outweighs
   .table-empty's centring on specificity. */
thead th:last-child,
tbody tr:not(.table-empty) td:last-child {
  text-align: right;
}

table {
  width: 100%;
  border-collapse: collapse;
}

thead th {
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  background: var(--surface-2);
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
}

tbody td {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
}

tbody tr:last-child td {
  border-bottom: none;
}

tbody tr:hover {
  background: var(--accent-bg);
}

.mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  color: var(--text-muted);
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 500;
}

.badge-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
}

.badge-active {
  background: var(--green-bg);
  color: var(--green);
}
.badge-active .badge-dot {
  background: var(--green);
}

.badge-inactive {
  background: var(--red-bg);
  color: var(--red);
}
.badge-inactive .badge-dot {
  background: var(--red);
}

/* Plan/brand label, as distinct from the status badges above. Green means
   a healthy running thing (see the Models list); the tier you are on is
   not a health state, so it takes the accent and leaves green its
   meaning. */
.badge-brand {
  background: var(--accent-soft);
  color: var(--accent-text);
}
.badge-brand .badge-dot {
  background: var(--accent);
}

/* Create API key modal */
.hidden {
  display: none !important;
}

.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  align-items: center;
  justify-content: center;
  z-index: 100;
}

.modal-overlay.open {
  display: flex;
}

.modal {
  width: 420px;
  max-width: calc(100vw - 32px);
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}

.modal-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: 20px 20px 0;
}

.modal-heading {
  display: flex;
  align-items: center;
  gap: 8px;
}

.modal-icon {
  display: inline-flex;
  color: var(--text);
}

.modal-icon svg {
  width: 20px;
  height: 20px;
}

.modal-title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
}

.modal-subtitle {
  margin: 4px 0 20px;
  color: var(--text-muted);
  font-size: 13px;
}

.modal-close {
  border: none;
  background: none;
  color: var(--text-muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 4px;
}

.modal-close:hover {
  color: var(--text);
}

.modal-body {
  padding: 0 20px 20px;
}

.modal-error {
  display: none;
  margin: 12px 0 0;
  font-size: 13px;
  color: var(--red);
}

.modal-error.visible {
  display: block;
}

.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 16px 20px;
  border-top: 1px solid var(--border);
}

/* .btn-secondary carries margin-left:auto for the Playground toolbar; in a
   modal footer the buttons are already right-aligned as a group. */
.modal-footer .btn-secondary {
  margin-left: 0;
}

.required-mark {
  color: var(--red);
  margin-left: 2px;
}

.key-warning {
  border-left: 3px solid var(--red);
  padding-left: 12px;
  margin-bottom: 20px;
}

.key-warning-title {
  margin: 0 0 4px;
  color: var(--red);
  font-weight: 600;
  font-size: 14px;
}

.key-warning-text {
  margin: 0;
  color: var(--text);
  font-size: 13px;
}

.key-reveal-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--border);
  background: var(--bg);
  border-radius: 6px;
}

.key-reveal-row .key-reveal-value {
  flex: 1;
  word-break: break-all;
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text-muted);
  cursor: pointer;
}

.icon-btn:hover {
  background: var(--accent-bg);
  color: var(--text);
}

.icon-btn.copied {
  color: var(--green);
  border-color: var(--green);
}

.icon-btn svg {
  width: 16px;
  height: 16px;
}

.table-empty td {
  text-align: center;
  color: var(--text-muted);
  padding: 24px 16px;
}

/* Cards */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
  margin-bottom: 32px;
}

.card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
}

.card-label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  margin: 0 0 12px;
}

.card-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 8px;
}

.card-value {
  font-size: 15px;
  font-weight: 600;
}

.card-limit {
  font-size: 13px;
  color: var(--text-muted);
}

.progress-track {
  height: 6px;
  border-radius: 999px;
  background: var(--surface-2);
  overflow: hidden;
  margin-top: 10px;
}

.progress-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--text);
}

/* The Usage page is two groups with different stakes. Free credit is what
   actually cuts an account off — permanently, once spent or expired — so it
   keeps the full-contrast fill. RPM/TPM are transient, clearing at the top of
   every minute, so they read as informational.
   Declared BEFORE .warn deliberately: equal specificity means source order
   decides, and a rate limit about to 429 someone must stay visible whatever
   group it belongs to. */
/* A section heading and its note are one unit.
   These previously reused .page-subtitle, which is page-level: body-sized
   type and a 32px bottom margin against the heading's 12px. The note ended up
   with more air below it than above, so it drifted toward the cards and read
   as a second page-level statement rather than as a caption for the heading
   directly above it. Smaller type and a negative top margin pull it back. */
/* No max-width: these notes are kept short enough to sit on one line, and a
   measure cap was wrapping them into two on a wide viewport for no benefit.
   Narrow viewports still wrap them naturally, which is the right behaviour. */
.section-note {
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-muted);
  margin: 0 0 16px;
}

.section-heading + .section-note {
  margin-top: -8px;
}

/* .page-title's 6px bottom margin assumes a .page-subtitle follows. Where a
   section heading follows the title directly, it needs the breathing room the
   subtitle would otherwise have provided. */
.page-title + .section-heading {
  margin-top: 28px;
}

.progress-fill.muted {
  background: var(--text-muted);
  opacity: 0.5;
}

.card-grid.secondary .card-value {
  color: var(--text-muted);
  font-weight: 500;
}

.progress-fill.warn {
  background: var(--amber);
}

.section-heading {
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  margin: 0 0 12px;
}

/* Playground */

/* Playground-only (no other page uses .main-wide). The chat is this page's
   work surface, so the panels run to the bottom of the viewport instead of
   ending at their content height and leaving dead space beneath. .main is
   border-box, so 100vh already accounts for its own padding, and the
   sidebar is already pinned at 100vh — so the page itself never scrolls
   and the message list scrolls internally, which is what makes it read as
   an app rather than a document. */
.main-wide {
  max-width: 1320px;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.playground-layout {
  display: flex;
  align-items: stretch;
  gap: 24px;
  /* Takes the height left over by the title block. min-height:0 is what
     lets it actually shrink — without it a flex item floors at its content
     size and the panel would push past the viewport. */
  flex: 1;
  min-height: 0;
}

/* Model picker — a listbox rather than a native <select> so each model can
   carry its vendor as a second line. Deliberately no search field or
   provider grouping: with a handful of models those would be scaffolding
   for a catalogue that doesn't exist yet. Both fit into this same
   component later without reworking it. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.model-picker {
  position: relative;
  width: 100%;
}

.model-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.model-trigger:hover {
  background: var(--accent-bg);
}

.model-trigger[aria-expanded="true"] {
  border-color: var(--accent);
}

.model-trigger-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}

.model-name {
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.model-vendor {
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.model-chevron {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  color: var(--text-muted);
  transition: transform 0.12s ease;
}

.model-trigger[aria-expanded="true"] .model-chevron {
  transform: rotate(180deg);
}

/* Fixed, not absolute: .playground-options scrolls, and a scroll container
   clips absolutely-positioned descendants. Coordinates are set from the
   trigger's rect when it opens (see script.js). */
.model-menu {
  display: none;
  position: fixed;
  max-height: 280px;
  overflow-y: auto;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
  padding: 5px;
  z-index: 20;
}

.model-menu.open {
  display: block;
}

.model-option {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 9px;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.model-option:hover,
.model-option:focus-visible {
  background: var(--accent-bg);
}

.model-option[aria-selected="true"] {
  background: var(--accent-soft);
}

.model-option .model-check {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  color: var(--accent);
  opacity: 0;
}

.model-option[aria-selected="true"] .model-check {
  opacity: 1;
}

.model-menu-empty {
  padding: 9px;
  font-size: 12px;
  color: var(--text-muted);
}

.playground-options {
  width: 280px;
  flex-shrink: 0;
  /* Stretches with the chat panel (inherited align-items:stretch) so the
     two columns end on the same line. Scrolls internally on viewports too
     short for the full control set rather than overflowing the page. */
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
  /* Column layout so the system-prompt field can absorb whatever height is
     left over — otherwise the controls stack at the top and leave the
     bottom of the panel empty. */
  display: flex;
  flex-direction: column;
}

/* Everything except the system prompt keeps its natural height; only that
   one field grows. Without this the sliders would compress on short
   viewports instead of the panel scrolling. */
.playground-options > * {
  flex-shrink: 0;
}

.options-heading-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 20px 0 16px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.options-heading {
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

.reset-btn {
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  padding: 4px 10px;
  border-radius: 6px;
}

.reset-btn:hover {
  background: var(--accent-bg);
  border-color: var(--text-muted);
}

.param-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 6px;
}

.param-label {
  font-size: 13px;
  font-weight: 500;
}

.param-value-input {
  width: 68px;
  padding: 5px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  text-align: right;
}

.param-full {
  width: 100%;
}

/* The one control that takes the panel's leftover height. A system prompt
   is the field people actually write multiple lines into, so it is the
   right place to spend the space. Capped so it stays a sane shape on tall
   monitors rather than becoming a wall of empty textarea. */
.param-textarea {
  resize: vertical;
  border-radius: 8px;
  margin-bottom: 22px;
  flex: 1 1 auto;
  min-height: 92px;
  max-height: 300px;
}

.param-slider-group {
  margin-bottom: 22px;
}

.param-slider {
  width: 100%;
  accent-color: var(--text);
}

#paramStop {
  margin-bottom: 18px;
}

.param-switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.switch {
  width: 36px;
  height: 20px;
  flex-shrink: 0;
  padding: 0;
  border: none;
  border-radius: 999px;
  background: var(--border);
  position: relative;
  cursor: pointer;
}

.switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  transition: transform 0.15s ease;
}

.switch.on {
  background: var(--green);
}

.switch.on::after {
  transform: translateX(16px);
}

.playground-chat-col {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.chat-panel-header {
  display: flex;
  justify-content: flex-end;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}

.chat-meta {
  margin-top: 6px;
  font-size: 11px;
  color: var(--text-muted);
}

.playground-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
}

.btn-secondary {
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-weight: 500;
  cursor: pointer;
  margin-left: auto;
}

.btn-secondary:hover {
  background: var(--accent-bg);
}

.chat-panel {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.chat-messages {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

.chat-messages::-webkit-scrollbar {
  width: 6px;
}

.chat-messages::-webkit-scrollbar-track {
  background: transparent;
}

.chat-messages::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 999px;
}

.chat-messages::-webkit-scrollbar-thumb:hover {
  background: var(--text-muted);
}

.chat-empty {
  margin: auto;
  text-align: center;
  color: var(--text-muted);
  max-width: 320px;
}

.chat-empty-title {
  font-weight: 600;
  color: var(--text);
  margin: 0 0 6px;
}

.chat-empty-sub {
  font-size: 13px;
  margin: 0;
}

.chat-message {
  max-width: 70%;
  padding: 10px 14px;
  border-radius: 14px;
  font-size: 14px;
  line-height: 1.5;
  white-space: pre-wrap;
}

.chat-message.user {
  align-self: flex-end;
  background: var(--text);
  color: var(--bg);
  border-bottom-right-radius: 4px;
}

.chat-message.assistant {
  align-self: flex-start;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-bottom-left-radius: 4px;
}

.chat-typing {
  display: flex;
  gap: 4px;
  padding: 4px 2px;
}

.chat-typing span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-muted);
  animation: chat-typing-bounce 1.2s infinite ease-in-out;
}

.chat-typing span:nth-child(2) {
  animation-delay: 0.15s;
}

.chat-typing span:nth-child(3) {
  animation-delay: 0.3s;
}

@keyframes chat-typing-bounce {
  0%, 80%, 100% { transform: translateY(0); opacity: 0.4; }
  40% { transform: translateY(-4px); opacity: 1; }
}

.chat-input-bar {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  padding: 12px;
  border-top: 1px solid var(--border);
}

.chat-input {
  flex: 1;
  resize: none;
  overflow-y: hidden;
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 9px 16px;
  font: inherit;
  font-size: 14px;
  background: var(--bg);
  color: var(--text);
  max-height: 160px;
  line-height: 1.4;
}

.chat-input:focus {
  outline: none;
  border-color: var(--text-muted);
}

.chat-send-btn {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border-radius: 50%;
  border: none;
  background: var(--text);
  color: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.chat-send-btn:hover {
  opacity: 0.85;
}

.chat-send-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.chat-send-btn svg {
  width: 16px;
  height: 16px;
}

.placeholder {
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 60px 24px;
  text-align: center;
  color: var(--text-muted);
}

/* Login (pages/login.html)
   Standalone centered layout — this page has no .sidebar, since every part
   of it (nav, user menu, theme toggle) assumes a signed-in session. */
.auth-layout {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.auth-wrap {
  width: 100%;
  max-width: 380px;
}

.auth-header {
  text-align: center;
  margin-bottom: 24px;
}

/* The mark sits on its own line above the heading. width/height are set on
   the element too, so the layout doesn't shift once the image loads. */
.auth-logo {
  display: block;
  width: 52px;
  height: auto;
  margin: 0 auto 20px;
}

/* Retained for the text wordmark should it be needed elsewhere; the login
   header now leads with .auth-logo instead. */
.auth-brand {
  display: inline-block;
  font-size: 20px;
  margin-bottom: 24px;
}

.auth-title {
  font-size: 24px;
  font-weight: 500;
  letter-spacing: -0.02em;
  margin: 0 0 6px;
}

.auth-subtitle {
  color: var(--text-muted);
  margin: 0;
}

.auth-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel-bg);
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Full-width provider button. Same visual language as .btn-secondary, but
   block-level and centered — these are the page's only actions, so they
   read as targets rather than as a toolbar. */
.btn-oauth {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 11px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
}

.btn-oauth:hover {
  background: var(--accent-bg);
}

/* Explicit focus ring: sign-in is frequently keyboard-driven, and the
   default outline is nearly invisible against the dark theme's panel. */
.btn-oauth:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

.btn-oauth svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.auth-error {
  border: 1px solid var(--red);
  background: var(--red-bg);
  color: var(--red);
  border-radius: var(--radius);
  padding: 10px 14px;
  margin-bottom: 16px;
  font-size: 13px;
  text-align: center;
}

.auth-note {
  font-size: 12px;
  color: var(--text-muted);
  text-align: center;
  margin: 4px 0 0;
}

.auth-footnote {
  font-size: 12px;
  color: var(--text-muted);
  text-align: center;
  margin: 20px 0 0;
}

/* Coming soon — roadmap models on the Models page.
   Deliberately a different, lighter shape from .model-card above. Those are
   interactive (expand -> pick a language -> copy a snippet); these are not
   available yet, so giving them the same affordance would invite a click
   that ends in a 404. Dashed border + muted text reads as "placeholder"
   before the label is even read, and the denser two-column grid keeps seven
   upcoming models from visually outweighing the handful that actually work. */
.coming-soon {
  margin-top: 40px;
}

.coming-soon .section-title {
  font-size: 17px;
  font-weight: 600;
  margin-bottom: 4px;
}

.coming-soon-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 12px;
  margin-top: 16px;
  padding: 0;
  list-style: none;
}

.coming-soon-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
}

/* Muted rather than solid --text, so these read as pending against the
   filled icons on the live model cards directly above. */
.coming-soon-card .model-icon {
  background: var(--text-muted);
  color: var(--bg);
  opacity: 0.7;
}

.coming-soon-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.coming-soon-card .model-name {
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.coming-soon-vendor {
  font-size: 12px;
  color: var(--text-muted);
  opacity: 0.8;
}

/* The dedicated-serving CTA sits inside .page-subtitle, which is muted by
   design — without this the link would be muted too and read as body text
   rather than as the action it is. */
.coming-soon .page-subtitle a {
  color: var(--accent-text);
  font-weight: 500;
  text-decoration: none;
}

.coming-soon .page-subtitle a:hover {
  text-decoration: underline;
}

/* Page-level "talk to us" line. Visually mirrors .tier-note (the one that
   already lives inside the API Keys card) so the CTAs read as one pattern
   across the app, but it is a separate class because this one sits under a
   page title rather than inside a card, and .tier-note's name is about plan
   limits specifically. */
.contact-note {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin: -4px 0 24px;
  font-size: 13px;
  color: var(--text-muted);
}

.contact-note a {
  color: var(--accent-text);
  text-decoration: none;
  font-weight: 500;
}

.contact-note a:hover {
  text-decoration: underline;
}
