/* style.css - shared styling for the Qurak front end.
 *
 * Layout note: the old sheet hard-coded `max-width: 480px` on .card,
 * .top-bar and .banner, which made every page - including the evaluate
 * workbench, where the whole point is looking at code and results side by
 * side - a narrow single column. Width is a token now, chosen per page by
 * the container class rather than baked into every component:
 *
 *   .container-wide   1200px  the evaluate workbench and the history table
 *   .container        760px   settings, billing, tokens - forms read badly wide
 *   .container-auth   420px   the login card, which genuinely wants to be small
 *
 * The palette is unchanged; this is a layout change, not a re-skin.
 */

:root {
  color-scheme: dark;
  --bg: #0f1115;
  --panel: #171a21;
  --panel-raised: #1d212a;
  --border: #2a2f3a;
  --border-strong: #39404f;
  --text: #e6e9ef;
  --muted: #8b92a3;
  --accent: #5b8cff;
  --danger: #ff6b6b;
  --success: #4fd18b;
  --warning: #f0b429;
  /* Cards on a dark ground are drawn with borders, not shadows; the
     light palette below swaps that for a soft shadow, so it is a token. */
  --card-shadow: none;

  --content-wide: 1200px;
  --content: 760px;
  --content-auth: 420px;
  --gutter: clamp(1rem, 4vw, 2.5rem);
  --radius: 10px;
}

/* ---------------------------------------------------------- light palette
   The site was dark-only, which suits a function reference read beside a
   terminal at night and suits nobody reading a pricing page at noon. The
   palette is a set of tokens, so the light one is the same tokens with
   other values: it follows the reader's system setting by default, and a
   `data-theme` on <html> - stamped by the server from the `theme` cookie
   the footer's switcher sets - wins over the system in either direction.
   No script anywhere: the switch is a form and the memory is a cookie. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
    --bg: #f6f7f9;
    --panel: #ffffff;
    --panel-raised: #f0f2f6;
    --border: #d9dde6;
    --border-strong: #b9c0cf;
    --text: #1a1e27;
    --muted: #5d6577;
    --accent: #2f63e0;
    --danger: #c92a2a;
    --success: #1f8a4c;
    --warning: #9a6700;
    --card-shadow: 0 1px 2px rgba(20, 24, 40, 0.06);
  }
}
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f6f7f9;
  --panel: #ffffff;
  --panel-raised: #f0f2f6;
  --border: #d9dde6;
  --border-strong: #b9c0cf;
  --text: #1a1e27;
  --muted: #5d6577;
  --accent: #2f63e0;
  --danger: #c92a2a;
  --success: #1f8a4c;
  --warning: #9a6700;
  --card-shadow: 0 1px 2px rgba(20, 24, 40, 0.06);
}
:root[data-theme="dark"] { color-scheme: dark; }

/* ------------------------------------------------------------ scripts
   Nine of the twenty-two interface languages are not Latin. A generic
   sans-serif stack lets the browser pick a face per glyph, which on a
   bare system means a fallback that was never designed for the script.
   Named faces per language first, the generic stack after. */
:lang(ar), :lang(ckb), :lang(fa) { font-family: "Vazirmatn", "Noto Naskh Arabic", "Noto Sans Arabic", "Segoe UI", Tahoma, Arial, sans-serif; }
:lang(hi) { font-family: "Noto Sans Devanagari", "Nirmala UI", "Mangal", Arial, sans-serif; }
:lang(te) { font-family: "Noto Sans Telugu", "Nirmala UI", "Gautami", Arial, sans-serif; }
:lang(th) { font-family: "Noto Sans Thai", "Leelawadee UI", "Tahoma", Arial, sans-serif; }
:lang(ja) { font-family: "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Noto Sans JP", "Yu Gothic UI", "Meiryo", sans-serif; }
:lang(zh-Hans), :lang(zh-hans) { font-family: "PingFang SC", "Noto Sans SC", "Microsoft YaHei", "Hiragino Sans GB", sans-serif; }
:lang(zh-Hant), :lang(zh-hant) { font-family: "PingFang TC", "Noto Sans TC", "Microsoft JhengHei", "Hiragino Sans", sans-serif; }
/* Expressions never mirror, whatever the page's language. */
:lang(ar) code, :lang(ckb) code, :lang(fa) code, :lang(ar) pre, :lang(ckb) pre, :lang(fa) pre,
:lang(ar) .result, :lang(ckb) .result, :lang(fa) .result,
:lang(ar) .log-input, :lang(ckb) .log-input, :lang(fa) .log-input,
:lang(ar) .try-row input, :lang(ckb) .try-row input, :lang(fa) .try-row input,
:lang(ar) .stat-value, :lang(ckb) .stat-value, :lang(fa) .stat-value {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  direction: ltr;
  unicode-bidi: isolate;
  text-align: left;
}

* { box-sizing: border-box; }

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

/* ---------------------------------------------------------------- shell */

.app-header {
  border-bottom: 1px solid var(--border);
  background: var(--panel);
  position: sticky;
  top: 0;
  z-index: 10;
}

.app-header-inner {
  max-width: var(--content-wide);
  margin: 0 auto;
  padding: 0 var(--gutter);
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: 56px;
  flex-wrap: wrap;
}

.brand {
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--text);
  text-decoration: none;
  font-size: 1.05rem;
}

.app-nav {
  display: flex;
  gap: 0.25rem;
  flex: 1;
  flex-wrap: wrap;
}

.app-nav a {
  color: var(--muted);
  text-decoration: none;
  padding: 0.35rem 0.7rem;
  border-radius: 6px;
  font-size: 0.9rem;
}

.app-nav a:hover { color: var(--text); background: var(--panel-raised); }

.app-nav a.active {
  color: var(--text);
  background: var(--panel-raised);
  box-shadow: inset 0 -2px 0 var(--accent);
}

.app-header-meta {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.85rem;
  color: var(--muted);
}

/* ------------------------------------------------------------ containers */

.container, .container-wide, .container-auth {
  margin: 0 auto;
  padding: 2rem var(--gutter) 4rem;
  width: 100%;
}
.container-wide { max-width: var(--content-wide); }
.container      { max-width: var(--content); }
.container-auth { max-width: var(--content-auth); padding-top: 4rem; }

.page-title { margin: 0 0 0.35rem; font-size: 1.35rem; }
.page-sub { margin: 0 0 1.75rem; color: var(--muted); font-size: 0.9rem; }

/* ----------------------------------------------------------------- cards */

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  width: 100%;
  box-shadow: var(--card-shadow);
}

/* Cards stack with a gap between them - but only where they are *stacked*.
 *
 * As a bare sibling rule this also fired inside every grid and flex
 * container that happens to hold cards, where the container's own `gap`
 * is already doing the spacing. On the workbench that put a 1.25rem
 * margin on the second grid item and nothing on the first, so the editor
 * and the result panel sat twenty pixels out of line with each other for
 * no reason anyone could see in the markup. `:not()` on the layout
 * parents keeps the rule where it belongs. */
.card + .card { margin-top: 1.25rem; }
.workbench > .card + .card,
.plan-grid > .card + .card,
.stat-grid > .card + .card { margin-top: 0; }

/* The one card on a page whose contents cannot be undone by clicking
   again. Red edge, red heading, and enough space above it that it does not
   read as the next thing to do after the card before it.

   `.card.card-danger`, not `.card-danger`: `.card + .card` above sets the
   spacing between cards and would otherwise outrank a single class. */
.card.card-danger {
  border-color: var(--danger);
  margin-top: 2.5rem;
}
.card h2 { margin: 0 0 0.35rem; font-size: 1.05rem; }
.card h2 + .muted { margin-top: 0; margin-bottom: 1rem; }
.card-danger h2 { color: var(--danger); }

/* ------------------------------------------------------- workbench split */

.workbench {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
  /* stretch, not start: the editor is naturally the taller column, and a
   * result panel that hugs its content leaves a ragged gap beside it. */
  align-items: stretch;
}

@media (min-width: 1000px) {
  .workbench { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

.workbench-output { min-height: 12rem; }

/* A card's opening heading, whatever element carries it.
 *
 * The workbench's two columns open with different elements - the editor's
 * heading is the textarea's own <label>, the result's is an <h2> - and
 * they have to occupy the same height for the boxes underneath them to
 * line up. One class, applied to both, is what guarantees that; matching
 * two separate rules by eye is what did not. */
.card-heading {
  display: block;
  margin: 0 0 0.35rem;
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.5;
  color: var(--text);
}

/* The editor's controls: the buttons on one line, the pickers on the
   next, each row wrapping on its own. */
.workbench-controls { display: flex; flex-direction: column; gap: 0.6rem; }
.workbench-controls .field-row { margin: 0; }
.workbench-controls select { margin-bottom: 0; }

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

label {
  display: block;
  font-size: 0.85rem;
  color: var(--muted);
  margin-bottom: 0.3rem;
}

input, textarea, select {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  padding: 0.55rem 0.7rem;
  font-size: 0.95rem;
  font-family: inherit;
  margin-bottom: 0.9rem;
}

input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--accent);
}

/* What an autofilled field looks like.
 *
 * WebKit and Blink paint their own opaque background behind a field the
 * browser filled - a pale yellow chosen for a white page, which on this
 * one arrives as a bright block with unreadable text in it, and which
 * `background-color` cannot override at all. An inset shadow the height of
 * the field is the one thing that paints over it, and `-webkit-text-fill-
 * color` is the only way to reach the text.
 *
 * This is presentation only. The fields that should not be filled in the
 * first place say so themselves, with autocomplete="off" and the password
 * managers' own opt-out attributes; this is what the ones that *are*
 * legitimately filled look like afterwards. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
select:-webkit-autofill {
  -webkit-text-fill-color: var(--text);
  caret-color: var(--text);
  box-shadow: inset 0 0 0 100px var(--bg);
  border-color: var(--border);
  /* The wash is applied on a delay of its own; outrunning it is not
     possible, so the transition is stretched out past any page's life. */
  transition: background-color 100000s ease-in-out 0s;
}
input:-webkit-autofill:focus { border-color: var(--accent); }

textarea {
  min-height: 80px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  resize: vertical;
}

textarea.editor { min-height: 220px; font-size: 0.9rem; line-height: 1.6; }

button {
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 6px;
  padding: 0.55rem 1rem;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
}

button:hover { filter: brightness(1.08); }
button:disabled { opacity: 0.5; cursor: not-allowed; filter: none; }

button.secondary { background: transparent; border: 1px solid var(--border-strong); color: var(--text); }
button.danger {
  background: transparent; border: 1px solid var(--danger); color: var(--danger);
  padding: 0.3rem 0.6rem; font-size: 0.8rem;
}
button.link {
  background: none; border: none; color: var(--accent); padding: 0;
  font-size: 0.8rem; font-weight: 500; text-decoration: underline; cursor: pointer;
}

.button-row { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; }
.button-row .grow { flex: 1; }

/* A form that is one button, sitting where a button used to sit.
 * The server-rendered pages post their mutations as real forms so they
 * work with scripting off (src/admin/pages.rs); a form is block-level, so
 * without this every action button in a table cell would take its own
 * line. `inline-flex` rather than `inline` so the button keeps its box. */
.inline-form { display: inline-flex; margin: 0; vertical-align: middle; }
.inline-form + .inline-form { margin-inline-start: 0.35rem; }
/* A link dressed as a secondary button, for a navigation that would be a
   button if it changed anything (the organizations list's "Open"). */
.button-link {
  display: inline-block; background: transparent; border: 1px solid var(--border-strong);
  color: var(--text); border-radius: 6px; padding: 0.3rem 0.6rem;
  font-size: 0.8rem; font-weight: 600; text-decoration: none;
}
.button-link:hover { filter: brightness(1.08); }
/* A field row that is itself a form keeps the row layout rather than
   becoming one flex item inside another row. */
form.field-row { display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: flex-start; }

/* A settings form: its fields on one row, its Save under them on the left.
 *
 * Replaces the `<div class="button-row" style="align-items:end">` that
 * every one of these forms carried inline. That trick aligned the button
 * with the *bottom of the tallest field*, which is the bottom of a
 * select - so the button sat level with the control while the label above
 * it pushed the whole column down, and two settings forms in one card
 * produced two buttons at two different heights. Here the button is on
 * its own line: predictable, and the same in every card that has one. */
.settings-form { margin-bottom: 1.25rem; }
.settings-form:last-child { margin-bottom: 0; }
.settings-form .field-row { margin-bottom: 0.35rem; }
/* The select is the last thing in its field, and inputs already carry a
   bottom margin; without this the gap under the row doubles. */
.settings-form .field-row select,
.settings-form .field-row input { margin-bottom: 0; }
.settings-form .form-actions { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; }

/* A row of tabs that becomes a block of tabs when the row runs out.
 *
 * `flex: 1` with no wrap was the bug: a flex item will not shrink below
 * its content's width by default, so six client names (Claude Code, Claude
 * Desktop, Cursor, VS Code, Gemini, Other) forced the strip wider than the
 * card on a phone. The card grew with it, the page picked up a horizontal
 * scrollbar, and every other card on the page was pushed out of line.
 *
 * Wrapping is the fix, and it only works with a flex-basis small enough
 * that a row can hold more than one tab - see the rule below. */
.tabs { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1rem; }
.tabs button {
  /* `auto` basis, so a row that has room keeps every tab on one line at
     its natural width - which is what the strip did on a desktop before,
     and worth keeping. `min-width: 0` is the backstop for the other end:
     a label longer than the whole line (a translation, a narrow phone)
     wraps inside its own button instead of pushing past the card. */
  flex: 1 1 auto;
  min-width: 0;
  /* Narrower than a standalone button's 1rem sides. Six client names at
     the default padding come to 634px inside a 630px card, so the strip
     overflowed its own card on a *desktop* too - by four pixels, which is
     why it read as a rendering quirk rather than the same bug. */
  padding: 0.55rem 0.75rem;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
}
.tabs button.active { background: var(--accent); color: #fff; border-color: var(--accent); }

.checkbox-label {
  display: flex; align-items: center; gap: 0.5rem;
  font-size: 0.85rem; color: var(--text); margin-bottom: 0.2rem; cursor: pointer;
}
.checkbox-label input[type="checkbox"] { width: auto; margin: 0; }
.checkbox-label input[type="checkbox"]:disabled { cursor: not-allowed; }

.field-row { display: flex; gap: 0.75rem; flex-wrap: wrap; }
/* Grow-0: a row of two selects shouldn't each take half the container.
 * They size to ~16rem and wrap on narrow screens instead. */
.field-row > * { flex: 0 1 16rem; min-width: 9rem; }
/* The search box on the function reference: one field that should take the
   room the fixed-width filter next to it doesn't want. */
.field-row > .grow { flex: 1 1 20rem; }
/* A pair of compact fields that belong to each other - the Profile page's
   date format and time zone. The narrower basis is what keeps them beside
   each other in a 760px column instead of wrapping at the first one, and
   the grow is what makes each fill the width once they do wrap on a
   phone. */
/* A capped half-width field. The cap is what keeps a settings form with
   one control the same width as one with two beside it: without it the
   lone Language picker stretched the full column while the Format and
   Time zone pair below it sat at half, and the card read as three
   different layouts. */
.field-row > .grow-half { flex: 1 1 13rem; max-width: 20rem; }

/* -------------------------------------------------------------- feedback */

.error { color: var(--danger); font-size: 0.85rem; margin: -0.5rem 0 0.9rem; min-height: 1.1em; }
.muted { color: var(--muted); font-size: 0.8rem; }
/* A status badge for a value that resolved but was not chosen - the
   Mail card's derived sender. Neither "fine" (.muted) nor "broken"
   (.error), because a derived value is a guess that has not been
   tested yet. Matches .muted's metrics so a table column keeps its
   rhythm when a row changes state. */
.badge-warn { color: var(--warning); font-size: 0.8rem; }
.hint { color: var(--muted); font-size: 0.8rem; margin: -0.5rem 0 0.9rem; }

.result {
  margin-top: 0.75rem;
  padding: 0.75rem;
  border-radius: 6px;
  background: var(--bg);
  border: 1px solid var(--border);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem;
  white-space: pre-wrap;
  word-break: break-word;
}
.result.status-success { border-color: var(--success); }
.result.status-error { border-color: var(--danger); }
/* A run that answered but lost its plot to the size cap: neither of the
   above, and it has a border of its own so it doesn't read as a failure. */
.result.status-warning { border-color: var(--warning); }

/* The engine diagnostic and the fallback guidance used to be concatenated
 * into one blob with \n\n. They are separate blocks now - the first line
 * is what went wrong, the rest is advice, and running them together made
 * both harder to read. */
.result-guidance {
  margin-top: 0.5rem;
  padding: 0.6rem 0.75rem;
  border-inline-start: 2px solid var(--border-strong);
  color: var(--muted);
  font-size: 0.8rem;
  white-space: pre-wrap;
  font-family: inherit;
}

.banner { padding: 0.75rem 1rem; border-radius: 8px; font-size: 0.85rem; margin-bottom: 1.25rem; }
.banner-success { background: rgba(79, 209, 139, 0.12); border: 1px solid var(--success); color: var(--success); }
.banner-muted { background: var(--panel); border: 1px solid var(--border); color: var(--muted); }
.banner-warning { background: rgba(240, 180, 41, 0.1); border: 1px solid var(--warning); color: var(--warning); }

/* ------------------------------------------------------------ plot output */

.plot-frame {
  margin-top: 0.75rem;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.5rem;
  overflow-x: auto;
}
.plot-frame img { display: block; max-width: 100%; height: auto; margin: 0 auto; }

/* ----------------------------------------------------------- run metadata */

.run-meta {
  display: flex; gap: 1rem; flex-wrap: wrap;
  margin-top: 0.75rem; font-size: 0.75rem; color: var(--muted);
}
.run-meta span strong { color: var(--text); font-weight: 600; }

/* A verdict on the answer above it. Three states and three colours, because
 * the middle one is the one that must not be mistaken for a pass: an
 * inconclusive check learned nothing, and it is deliberately muted rather
 * than green. */
.verdict { font-size: 0.8rem; font-weight: 600; margin: 0.75rem 0 0.4rem; }
.verdict-verified { color: var(--success); }
.verdict-refuted { color: var(--danger); }
.verdict-inconclusive { color: var(--muted); }
#verify-result .hint { margin: 0 0 0.4rem; }

/* ------------------------------------------------------------- key/token */

.key-row {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  padding: 0.6rem 0; border-bottom: 1px solid var(--border); font-size: 0.85rem;
}
.key-row:last-child { border-bottom: none; }
.key-row code { color: var(--muted); font-size: 0.8rem; }

.key-token {
  margin-top: 0.75rem; padding: 0.75rem; border-radius: 6px;
  background: var(--bg); border: 1px dashed var(--success);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem; word-break: break-all;
}

/* ---------------------------------------------------------------- tables */

.log-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
.log-table th {
  text-align: start; color: var(--muted); font-weight: 500;
  padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--border); white-space: nowrap;
}
.log-table td { padding: 0.5rem; border-bottom: 1px solid var(--border); vertical-align: top; }
.log-table tr:last-child td { border-bottom: none; }
.log-input { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; word-break: break-word; overflow-wrap: anywhere; }
.log-input.log-nowrap { white-space: nowrap; word-break: normal; }
.log-table tr.clickable:hover { background: var(--panel-raised); cursor: pointer; }

.table-scroll { overflow-x: auto; }

.log-status { display: inline-block; padding: 0.1rem 0.4rem; border-radius: 4px; font-size: 0.72rem; white-space: nowrap; }
.log-status.status-success { color: var(--success); background: rgba(79, 209, 139, 0.12); }
.log-status.status-error { color: var(--danger); background: rgba(255, 107, 107, 0.12); }
.log-status.status-warning { color: var(--warning); background: rgba(240, 180, 41, 0.12); }
/* An optional thing that isn't set - present in the list so its absence is
   accounted for, but not a problem to fix. */
.log-status.status-muted { color: var(--muted); background: var(--panel-raised); }

/* --------------------------------------------------------------- pricing */

/* The plan grid.
 *
 * `align-items: stretch` plus a column layout inside each plan is what
 * makes the row read as a row: every card is the height of the tallest,
 * every price sits on the same line, and every action is on the floor
 * rather than floating wherever its own feature list happened to end.
 * The cards used to be plain `.card`s in a grid sized `minmax(200px,1fr)`,
 * which gave three plans an orphan on a second row and left the shortest
 * one two hundred pixels of blank space. */
.plan-grid {
  display: grid;
  gap: 1rem;
  /* 12rem, not 15: three plans have to fit one row inside the 760px
     column this page uses, and at 15rem the third wrapped onto a line of
     its own beside an empty half-row. Still wide enough that a phone gets
     one plan per line. */
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  align-items: stretch;
}
.plan {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 1.25rem; background: var(--bg);
}
/* The plan this account is on. Named, not just outlined: a border colour
   alone is a fact only someone comparing all three cards can read. */
.plan.current { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.plan-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.plan h3 { margin: 0; font-size: 1.05rem; }
.plan-tag {
  font-size: 0.68rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--accent); border: 1px solid var(--accent); border-radius: 999px;
  padding: 0.1rem 0.45rem; white-space: nowrap;
}
/* The price line is a fixed slot so the feature lists start level even
   when one plan is quoted and the next costs $19. */
.plan-price { margin: 0.75rem 0 0.25rem; min-height: 2.5rem; }
.plan-price .amount { font-size: 1.6rem; font-weight: 700; line-height: 1.1; }
.plan-price .per { font-size: 0.82rem; font-weight: 400; color: var(--muted); }
.plan-price .quoted { font-size: 1.05rem; font-weight: 600; color: var(--text); }
.plan-price .free { font-size: 1.6rem; font-weight: 700; }
.plan-note { color: var(--muted); font-size: 0.78rem; margin: 0 0 0.25rem; min-height: 1.1rem; }
.plan ul { margin: 0.75rem 0 1rem; padding-inline-start: 1.1rem; color: var(--muted); font-size: 0.82rem; }
.plan li { margin-bottom: 0.25rem; }
/* Pushes the action to the bottom of whichever card it is in, so a row of
   plans has one line of buttons rather than a staircase. */
.plan-actions { margin-top: auto; display: flex; flex-direction: column; gap: 0.4rem; }
.plan-actions button, .plan-actions .cta { width: 100%; }
.plan-actions .inline-form { display: block; margin: 0; }
/* A quoted plan's action is a link that scrolls to the sales form, and a
   period that is not on sale is a sentence rather than a dead button. */
.plan .cta {
  display: block; text-align: center; text-decoration: none;
  background: transparent; border: 1px solid var(--border-strong);
  color: var(--text); border-radius: 6px;
  padding: 0.55rem 1rem; font-size: 0.9rem; font-weight: 600;
}
.plan .cta:hover { filter: brightness(1.08); }

/* The plan name at the top of the Current plan card - the one fact on the
   card that should be readable from across a desk. */
.plan-name { font-size: 1.35rem; font-weight: 700; margin: 0 0 0.35rem; }
/* A plan that is running out says so in the warning colour rather than in
   the same grey as everything else on the card. */
.warning-line { color: var(--warning); margin: 0 0 0.5rem; }
.card-brand { font-weight: 600; }
.card-digits { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.plan-current-note { margin: 0; }

/* Where Stripe's form is mounted.
 *
 * A minimum height so the card does not collapse to a line and then jump
 * when the iframe arrives, and a background of its own because Stripe's
 * form paints its own surface and should sit *on* the page rather than
 * float in it. */
.stripe-mount {
  min-height: 22rem;
  margin-top: 0.75rem;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
}

/* A postal address is written the way it is posted: on its own lines. */
.postal-address { white-space: pre-wrap; }

/* A secondary route under a row of buttons - "or pay by post". Quieter
   than the buttons above it, and centred under them so it reads as an
   alternative to the whole group rather than to the last one. */
.quiet-link {
  display: block; text-align: center; margin-top: 0.15rem;
  font-size: 0.78rem; color: var(--muted);
}
.quiet-link:hover { color: var(--accent); }

/* One choice in the purchase flow - a billing period or a payment method.
   Block rather than inline so the options stack into a list the eye can
   run down, and so the whole row is a click target for the radio. */
.choice { display: block; padding: 0.35rem 0; cursor: pointer; }
.choice input { width: auto; margin-inline-end: 0.5rem; cursor: pointer; }
.choice input:disabled, .choice input:disabled + * { cursor: not-allowed; }

/* What goes in the envelope. Set apart from the surrounding prose because
   it is meant to be copied exactly, and printed on its own. */
.mail-instructions {
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  padding: 0.75rem 1rem; margin: 0.75rem 0;
}
.mail-instructions p { margin: 0.5rem 0; }
.mail-instructions ul { margin: 0.25rem 0 0.5rem; padding-inline-start: 1.1rem; }

/* The invoice receipt exists only on paper: printReceipt() fills it in,
   puts print-receipt on <body>, prints, and takes the class off again. */
#receipt { display: none; }

/* Printing this page means printing the mailing instructions - nothing
   else in a browser chrome-free sheet of paper is any use in an envelope. */
@media print {
  .app-header, .sub-nav, .button-row, #plan-grid, #manage-card, #danger-card { display: none !important; }
  .card { border: none; padding: 0; }
  /* ...unless a receipt is being printed, in which case the receipt is
     the sheet of paper and everything else stays off it. */
  body.print-receipt .container > :not(#receipt) { display: none !important; }
  body.print-receipt #receipt { display: block; }
}

kbd {
  background: var(--panel-raised); border: 1px solid var(--border-strong);
  border-radius: 4px; padding: 0.05rem 0.35rem; font-size: 0.75rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* The strip under a page title: the pages of the section this one is in,
   or - in the admin area, which has fourteen of them and is too many for
   the header nav shared with signed-out readers - the admin pages. */
/* A section strip that rendered nothing - a public page viewed signed out,
   where every sibling needs a session - should take no room. */
.sub-nav:empty { display: none; }

.sub-nav {
  display: flex;
  /* Wraps rather than scrolls. The admin area has fourteen pages, which
     is wider than the column even on a desktop, and a horizontal scroller
     was hiding the last two behind an edge nobody thinks to drag: the
     Webhooks and Audit log tabs were simply not there unless you knew to
     look. A second line costs one line and hides nothing. */
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 0 0 1.5rem;
}
.sub-nav a {
  flex: 0 0 auto;
  padding: 0.35rem 0.7rem;
  border-radius: 6px;
  font-size: 0.82rem;
  color: var(--muted);
  text-decoration: none;
  border: 1px solid transparent;
  white-space: nowrap;
}
.sub-nav a:hover { color: var(--text); background: var(--panel-raised); }
.sub-nav a.active {
  color: var(--text);
  background: var(--panel-raised);
  border-color: var(--border-strong);
}
/* The "(beta)" tag on a pre-release strip entry (see shell.js's `beta`
   flag). Quieter than the label so the word reads as a caveat, not part
   of the name. */
.sub-nav .beta-tag { font-style: italic; font-size: 0.72rem; color: var(--muted); }
.page-title .beta-tag { font-style: italic; color: var(--muted); }

/* The timeline row whose activity the filmstrip card is showing. */
.log-table tr.active-row { background: var(--panel-raised); }

/* Overview tiles on the admin landing page. */
.stat-grid {
  display: grid;
  gap: 0.85rem;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.stat {
  background: var(--panel-raised);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.85rem 1rem;
}
.stat .stat-value { font-size: 1.5rem; font-weight: 700; line-height: 1.2; }
.stat .stat-label { color: var(--muted); font-size: 0.78rem; margin-top: 0.15rem; }

/* Site notices (0017): the admin-configured strip shell.js renders
   directly below the header. Informational, not an error state - the
   accent tint is what says "notification" rather than "content", and the
   message sits centered with the dismiss pinned to the right edge so it
   doesn't skew the centering. */
.site-notices {
  border-bottom: 1px solid var(--border);
  background: rgba(91, 140, 255, 0.12);
}
.site-notice {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  max-width: var(--content-wide);
  margin: 0 auto;
  padding: 0.55rem 3rem;
  font-size: 0.88rem;
  text-align: center;
}
.site-notice a { color: var(--accent); white-space: nowrap; }
.site-notice .site-notice-dismiss {
  position: absolute;
  inset-inline-end: 1rem;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.15rem 0.4rem;
}
.site-notice .site-notice-dismiss:hover { color: var(--text); }

/* Language footer (i18n): static markup on public pages, translated by
   the server like everything else (src/i18n.rs). */
.site-footer {
  max-width: var(--content-wide);
  margin: 2rem auto 1rem;
  padding: 0 1.25rem;
  color: var(--muted);
  font-size: 0.8rem;
  text-align: center;
}
.site-footer a { color: var(--muted); }
.site-footer .active { color: var(--text); }

/* ------------------------------------------------------------------- rtl
 *
 * Direction comes from <html dir>, set by the server per language
 * (src/i18n.rs locale_dir). Layout above uses logical properties, so it
 * mirrors on its own; what needs saying explicitly is that code never
 * mirrors. Expressions, results, tokens and identifiers are Wolfram
 * Language - Latin script - and an RTL context would reorder their
 * brackets and operators into nonsense. isolate keeps the embedded LTR
 * run from bleeding direction into the surrounding RTL text. */
[dir="rtl"] textarea.editor,
[dir="rtl"] .result,
[dir="rtl"] .key-token,
[dir="rtl"] .log-input,
[dir="rtl"] code,
[dir="rtl"] pre,
[dir="rtl"] kbd {
  direction: ltr;
  unicode-bidi: isolate;
  text-align: left;
}

/* /languages.html: the list the "Other languages" footer link opens. */
.language-list {
  list-style: none;
  margin: 0;
  padding: 0;
  text-align: center;
}
.language-list li { padding: 0.4rem 0; font-size: 1rem; }
.language-list a { color: var(--accent); }
.language-list .active { color: var(--text); font-weight: 600; }

/* The documentation hub: five doors, each a whole card that is a link. */
.door-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.door-grid > .card + .card { margin-top: 0; }
a.door { display: block; color: inherit; text-decoration: none; }
a.door:hover, a.door:focus-visible { border-color: var(--accent); }
a.door h2 { margin-top: 0; }

/* Breadcrumbs: the trail under the header on every public page but home.
   Semantic list, visually a line; the current page is text, not a link. */
.crumbs { max-width: 1100px; margin: 0.75rem auto 0; padding: 0 1rem; font-size: 0.85rem; color: var(--muted); }
.crumbs ol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.35rem; }
.crumbs li + li::before { content: "\203A"; margin-inline-end: 0.35rem; color: var(--border-strong); }
.crumbs a { color: var(--muted); }
.crumbs a:hover { color: var(--text); }
.crumbs [aria-current="page"] { color: var(--text); }

/* Filter chips on the reference index, and the letter row. */
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin: 0.5rem 0; }
.chip { display: inline-block; padding: 0.2rem 0.6rem; border: 1px solid var(--border); border-radius: 999px; font-size: 0.85rem; color: var(--text); text-decoration: none; background: var(--panel); }
.chip:hover { border-color: var(--accent); }
.chip.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.chip.active .muted { color: rgba(255,255,255,0.8); }
.ref-search { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; margin: 0.75rem 0; }
.ref-search input[type="search"] { flex: 1; min-width: 12rem; max-width: 28rem; }

/* Alphabetical neighbours at the foot of a symbol page. */
.prevnext { display: flex; justify-content: space-between; gap: 1rem; margin: 1rem 0; font-size: 0.95rem; }
.prevnext a[rel="next"] { margin-inline-start: auto; }

/* Availability shown on the index links themselves, so a scan of the
   wall already tells the reader which ones run here. */
.status-warning-link { border-bottom: 1px dotted var(--muted); }
.status-error-link { color: var(--muted); }

/* The anonymous try box: a form, an answer page, no script. */
.try-box label { font-weight: 600; }
.try-row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; margin-top: 0.35rem; }
.try-row input[type="text"] { flex: 1; min-width: 14rem; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.try-answer .result { font-size: 1.05rem; }
.hero .anchor { font-size: 1.05rem; margin: 1rem 0 0.5rem; }

/* /why: the plausible answer beside the computed one. */
.why-expr { font-size: 1.05rem; margin-bottom: 0.75rem; }
.why-columns { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.why-col h3 { margin: 0 0 0.4rem; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.why-plausible .result { border-inline-start: 3px solid #d29922; }
.why-computed .result { border-inline-start: 3px solid #3fb950; }

/* The site footer: four columns, one partial. */
.site-footer { max-width: 1100px; margin: 2.5rem auto 1.5rem; padding: 1.5rem 1rem 0; border-top: 1px solid var(--border); font-size: 0.9rem; }
.footer-grid { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.footer-col h3 { margin: 0 0 0.5rem; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.footer-col ul { list-style: none; margin: 0; padding: 0; }
.footer-col li { margin: 0.25rem 0; }
.footer-col a { color: var(--text); text-decoration: none; }
.footer-col a:hover { color: var(--accent); }
.footer-meta { margin: 1.25rem 0 0; color: var(--muted); }

/* /docs/functions: availability on the link itself. */
.button-row a.status-success-link { border-bottom: 1px solid transparent; }

/* The theme switcher in the footer: three buttons, one form, a cookie. */
.theme-switch { display: inline-flex; gap: 0.25rem; align-items: center; }
.theme-switch button { padding: 0.15rem 0.55rem; font-size: 0.8rem; border-radius: 999px; }
.theme-switch button.active { background: var(--accent); color: #fff; border-color: var(--accent); }

/* Footer closing lines: three short lines under the columns, with a
   little air between them. The last is the copyright. */
.footer-line { margin: 0.5rem 0 0; color: var(--muted); font-size: 0.85rem; }
.footer-grid + .footer-line { margin-top: 1.4rem; }
.footer-copyright { color: var(--muted); }
.footer-meta { display: none; }

/* The try box: the hint sits under the row, never under the button. */
.try-box .hint { margin: 0.6rem 0 0; }
.try-row { margin-bottom: 0.25rem; align-items: stretch; }
.try-row button { flex: 0 0 auto; white-space: nowrap; }

/* Code, presented one way. A sample a reader copies is a code box
   (render::code_box, and the same markup on the static app pages);
   inline and tabular code - names, statements, ids, digests - is
   `.log-input`; live output is `.result`, a result rather than a sample.
   Inline `code` and any `pre` outside a box keep the box's face and
   metrics, so a sample that escapes the component still reads as code
   and scrolls sideways rather than breaking the page. */
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; overflow-wrap: anywhere; }
pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.88rem; line-height: 1.5; margin: 0.6rem 0 1rem; overflow-x: auto; }

/* Code boxes: one component for every block a reader copies. The copy
   button is `hidden` until static/copy.js unhides it. */
.code-box { border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel-raised); margin: 0.6rem 0 1rem; overflow: hidden; }
.code-box-bar { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; padding: 0.35rem 0.6rem 0.35rem 0.85rem; border-bottom: 1px solid var(--border); background: var(--panel); }
.code-box-label { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.78rem; color: var(--muted); }
.code-box pre { margin: 0; padding: 0.85rem 1rem; overflow-x: auto; font-size: 0.88rem; line-height: 1.5; background: transparent; border: 0; }
.code-box pre code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; background: transparent; padding: 0; }
.code-box .copy { padding: 0.15rem 0.6rem; font-size: 0.78rem; border-radius: 999px; }
.code-box .copy.done { background: var(--success); border-color: var(--success); color: #fff; }

/* CSS-only tabs (the public MCP page): radios carry the state. */
.tabset > input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0; }
.tabset .tabs label { flex: 1 1 auto; min-width: 0; padding: 0.55rem 0.75rem; border: 1px solid var(--border); border-radius: var(--radius); color: var(--muted); text-align: center; cursor: pointer; font-size: 0.9rem; }
.tabset .tabs label:hover { color: var(--text); }
.tabset .tab-panel { display: none; }
#client-cc:checked ~ .tabs label[for="client-cc"],
#client-cd:checked ~ .tabs label[for="client-cd"],
#client-cursor:checked ~ .tabs label[for="client-cursor"],
#client-vscode:checked ~ .tabs label[for="client-vscode"],
#client-any:checked ~ .tabs label[for="client-any"] { background: var(--accent); color: #fff; border-color: var(--accent); }
#client-cc:checked ~ .tab-panels > [data-panel="cc"],
#client-cd:checked ~ .tab-panels > [data-panel="cd"],
#client-cursor:checked ~ .tab-panels > [data-panel="cursor"],
#client-vscode:checked ~ .tab-panels > [data-panel="vscode"],
#client-any:checked ~ .tab-panels > [data-panel="any"] { display: block; }
.tabset > input:focus-visible ~ .tabs label { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The sign-in page's brand goes home, and looks like a title doing it. */
.brand-home { color: inherit; text-decoration: none; }
.brand-home:hover { color: var(--accent); }

/* The terms as one document: clauses inside one card. */
.legal-document h3 { margin: 1.4rem 0 0.4rem; font-size: 1.05rem; }
.legal-document h3:first-child { margin-top: 0; }

/* Illustrations: inline SVG that follows the theme. */
.illus { margin: 0; }
.illus svg { width: 100%; height: auto; display: block; border-radius: var(--radius); }
.illus figcaption { color: var(--muted); font-size: 0.82rem; margin-top: 0.4rem; }
.illus-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
