/*
 * The visual language, shared by every surface on the origin.
 *
 * ADR-0005 §6 asks for one visual language across the landing, the portal and
 * the cabinet, held in one stylesheet rather than repeated per page. Caddy
 * serves this file at /styles/tokens.css on the same origin as all three
 * (ADR-0005 §1), so the cabinet's server-rendered pages link it directly and
 * the three cannot drift apart without somebody editing this file.
 *
 * What belongs here: colour, type, radius, the border weight, and the base
 * element rules that follow from them. What does not: the layout of any one
 * page. A rule that only the landing needs lives in landing.css, and a rule
 * that only the cabinet needs lives with the cabinet.
 *
 * The palette is stated in oklch because the light and the dark set are the
 * same hues at different lightness, and that relationship is legible in oklch
 * and invisible in hex.
 *
 * Three of the light values are a step darker than the design was drawn with,
 * and that is deliberate rather than drift. Measured against the light
 * background, the drawn accent came to 3.96:1, the drawn --ok to 4.03:1 and the
 * drawn --warn to 4.27:1 — under the 4.5:1 that ordinary text needs, in the
 * theme most people read in. Dropping the lightness to the values below brings
 * them to 4.68, 4.96 and 5.06 while leaving hue and chroma untouched, so the
 * page still looks like the drawing. The dark set already passed and is
 * unchanged.
 */

:root {
  --bg: oklch(0.985 0.003 90);
  --surface: oklch(1 0 0);
  --raised: oklch(0.972 0.004 90);
  --line: oklch(0.895 0.006 90);
  --fg: oklch(0.24 0.012 260);
  --muted: oklch(0.52 0.012 260);
  --accent: oklch(0.56 0.14 55);
  --ok: oklch(0.53 0.13 155);
  --warn: oklch(0.54 0.16 25);

  /* The hairline of the design carries 1.31:1 against the page, which is right
     for a rule between two cells and not enough for the edge of something you
     are meant to click — a control's boundary needs 3:1 before it reads as a
     control at all. --line draws the first, --line-strong the second. */
  --line-strong: oklch(0.65 0.008 90);

  /* Text laid on --accent. Fixed rather than derived: the accent is dark in
     the light theme and light in the dark one, so a single near-white would
     fail the contrast it needs in one of them. */
  --on-accent: oklch(0.99 0 0);

  --font-sans: "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --radius: 3px;
  --hairline: 1px;
}

/*
 * The dark set, in the two places a reader can ask for it.
 *
 * There are three states rather than two. With nothing chosen the page follows
 * the operating system, which is the first block; a reader who has chosen gets
 * what they chose, which is the second, and the `:not()` above is what makes a
 * choice of light survive a system that is dark.
 *
 * The values are written twice and that is the cost of doing it in plain CSS: a
 * media query cannot be part of a selector, so the same block cannot serve both
 * conditions. `light-dark()` would state each colour once and was not taken —
 * a browser that does not know the function drops every declaration containing
 * it, and the front door of the product would render with no colours at all
 * rather than with the wrong ones. The pair below drifts apart the moment
 * somebody edits one of them, which is the reason this paragraph is here.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: oklch(0.165 0.012 260);
    --surface: oklch(0.205 0.013 260);
    --raised: oklch(0.235 0.013 260);
    --line: oklch(0.31 0.013 260);
    --line-strong: oklch(0.52 0.013 260);
    --fg: oklch(0.94 0.004 90);
    --muted: oklch(0.67 0.011 260);
    --accent: oklch(0.74 0.13 55);
    --ok: oklch(0.72 0.12 155);
    --warn: oklch(0.7 0.15 25);
    --on-accent: oklch(0.19 0.012 260);
  }
}

:root[data-theme="dark"] {
  --bg: oklch(0.165 0.012 260);
  --surface: oklch(0.205 0.013 260);
  --raised: oklch(0.235 0.013 260);
  --line: oklch(0.31 0.013 260);
  --line-strong: oklch(0.52 0.013 260);
  --fg: oklch(0.94 0.004 90);
  --muted: oklch(0.67 0.011 260);
  --accent: oklch(0.74 0.13 55);
  --ok: oklch(0.72 0.12 155);
  --warn: oklch(0.7 0.15 25);
  --on-accent: oklch(0.19 0.012 260);
}

/*
 * What the browser paints that this stylesheet does not: form controls, the
 * scrollbar, the caret. Told rather than left to guess, or a light page keeps a
 * dark scrollbar down its side.
 */
:root {
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  /* The dark theme's page background has to reach past the document box, or a
     rubber-band scroll on a phone exposes a white strip under a dark page. */
  background: var(--bg);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.mono {
  font-family: var(--font-mono);
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius);
}

h1,
h2,
h3 {
  margin: 0;
  font-weight: 600;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

p {
  margin: 0;
  text-wrap: pretty;
}

/*
 * What the cabinet needs on top of the shared visual language.
 *
 * ADR-0005 §6 asks for one visual language across the landing, the portal and
 * the cabinet, held in one stylesheet rather than repeated per page. That file
 * is `apps/landing/public/styles/tokens.css`, and it is the one this branch
 * used to carry a second copy of — with the pre-contrast-fix values, which is
 * exactly how "one visual language" becomes two that look almost alike. The
 * copy is gone: the cabinet serves the shared file itself, ahead of this one,
 * so there is a single set of tokens on disk and no way for the two surfaces to
 * drift without somebody editing the shared file.
 *
 * So nothing here declares a colour, a family, a radius or a base element rule.
 * What is here is the cabinet's own layout — its frame, its tables, its state
 * dots and its controls — which is what tokens.css means by "a rule that only
 * the cabinet needs lives with the cabinet".
 */

/*
 * The one thing the cabinet takes back from the shared base, and why.
 *
 * tokens.css sets 16px/1.6, which is right for a landing somebody reads. This
 * is a console with six-column tables that a merchant scans rather than reads,
 * and at that size a row of them does not fit a laptop without scrolling
 * sideways. The design was drawn at 14px for that reason.
 */
body {
  font-size: 14px;
  line-height: 1.5;
}

/* The page heading, at the size the console was drawn at. Declared before
   `.gate h1` below, so that the more specific rule is the one that wins. */
h1 {
  font-size: 22px;
}

/* --- the frame ---------------------------------------------------------- */

.page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 0 32px;
  border-bottom: var(--hairline) solid var(--line);
  background: var(--surface);
  flex-wrap: wrap;
}

.brand {
  display: flex;
  align-items: center;
  gap: 36px;
  flex-wrap: wrap;
}

.wordmark {
  font-family: var(--font-mono);
  font-size: 15px;
  font-weight: 500;
}

.tabs {
  display: flex;
  gap: 4px;
}

.tabs a,
.tabs span {
  padding: 18px 14px;
  color: var(--muted);
}

.tabs .here {
  color: var(--fg);
  font-weight: 500;
  box-shadow: inset 0 -2px 0 var(--accent);
}

.whoami {
  display: flex;
  align-items: center;
  gap: 20px;
  font-size: 13px;
}

/*
 * The way out to the documentation, and the counterpart of the way out of it
 * (portal/.vitepress/theme/coinslot.css, same class name). Both are muted
 * until they are hovered, so that neither reads as one of the places the bar
 * navigates to; both stop at one line, because a bar that reflows around a
 * link is a bar that moves under the cursor. Everything else about it —
 * no underline at rest, one on hover — is the cabinet's own convention for a
 * link, taken from tokens.css the way the portal's half takes VitePress's.
 */
.way-out {
  color: var(--muted);
  white-space: nowrap;
}

.way-out:hover {
  color: var(--fg);
}

.lede {
  padding: 32px 32px 0;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

.lede p {
  margin-top: 6px;
  color: var(--muted);
  max-width: 68ch;
}

.actions {
  display: flex;
  gap: 10px;
  align-items: center;
}

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

/*
 * A real table, so a merchant's browser can read the columns out and their
 * find-in-page still works. The scroller around it is what keeps a wide row
 * from pushing the whole page sideways on a narrow screen.
 */
.scroller {
  margin: 24px 32px 32px;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow-x: auto;
}

table {
  width: 100%;
  min-width: 720px;
  border-collapse: collapse;
  text-align: left;
}

thead th {
  background: var(--raised);
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
  padding: 12px 24px;
  border-bottom: var(--hairline) solid var(--line);
  white-space: nowrap;
}

tbody td {
  padding: 15px 24px;
  border-bottom: var(--hairline) solid var(--line);
  vertical-align: middle;
}

/*
 * The first column carries the name of the thing and needs room for it. Left to
 * share the table's minimum evenly it collapses on a narrow screen and a title
 * wraps one word per line, which is unreadable long before the row is.
 */
tbody td:first-child,
thead th:first-child {
  min-width: 220px;
}

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

tbody tr.off td {
  background: var(--raised);
}

tbody tr.needs-you td {
  background: color-mix(in oklab, var(--warn) 7%, transparent);
}

td.amount,
td.key,
td.ident {
  font-family: var(--font-mono);
  white-space: nowrap;
}

td.key,
td.ident {
  font-size: 13px;
  color: var(--muted);
}

.title {
  font-weight: 500;
}

.under {
  color: var(--muted);
  font-size: 13px;
  margin-top: 3px;
}

.under.mono {
  font-size: 12.5px;
}

td.quiet {
  color: var(--muted);
  white-space: nowrap;
}

/* --- the state dot ------------------------------------------------------ */

.state {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
}

.dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex: none;
  background: var(--muted);
}

.state.ok {
  color: var(--ok);
}
.state.ok .dot {
  background: var(--ok);
}

.state.warn {
  color: var(--warn);
}
.state.warn .dot {
  background: var(--warn);
}

.state.busy {
  color: var(--accent);
}
.state.busy .dot {
  background: var(--accent);
}

/*
 * The mark on a sum that was not real money. It sits beside the amount rather
 * than in a column of its own so that it cannot be scrolled out of sight on a
 * narrow screen while the number it qualifies stays visible.
 */
.tag {
  font-family: var(--font-sans);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--warn);
  border: var(--hairline) solid var(--warn);
  border-radius: 2px;
  padding: 1px 4px;
  margin-left: 6px;
  vertical-align: 1px;
}

/* --- controls ----------------------------------------------------------- */

button {
  font: inherit;
  cursor: pointer;
  border-radius: var(--radius);
  padding: 6px 12px;
  font-size: 13px;
  /* --line-strong and not --line: a hairline is right for a rule between two
     cells and too faint for the edge of something you are meant to click. */
  border: var(--hairline) solid var(--line-strong);
  background: var(--surface);
  color: var(--fg);
}

button:hover {
  border-color: var(--fg);
}

button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

button.wide {
  padding: 9px 16px;
  font-size: 13.5px;
}

form.inline {
  display: inline;
}

td.control {
  text-align: right;
}

.filter {
  display: flex;
  gap: 6px;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  padding: 3px;
}

.filter a,
.filter span {
  padding: 6px 14px;
  font-size: 13px;
  border-radius: 2px;
  color: var(--muted);
}

.filter .here {
  background: var(--raised);
  color: var(--fg);
  font-weight: 500;
}

/* --- notes and summaries ------------------------------------------------ */

.note {
  margin: 0 32px 32px;
  display: flex;
  gap: 12px;
  align-items: flex-start;
  border: var(--hairline) dashed var(--line-strong);
  border-radius: var(--radius);
  padding: 16px 20px;
  color: var(--muted);
  font-size: 13.5px;
  line-height: 1.55;
}

.note .mark {
  color: var(--accent);
}

.callout {
  margin: 0 32px 32px;
  border: var(--hairline) solid var(--line);
  border-left: 2px solid var(--warn);
  border-radius: var(--radius);
  padding: 16px 20px;
  background: var(--surface);
  line-height: 1.55;
}

.callout .what {
  font-weight: 500;
  /*
   * An order identifier is a long unbroken run of characters in a monospaced
   * face, and this box has no scroller of its own the way the tables do. Left
   * alone it is the one thing on the page that scrolls the whole document
   * sideways on a phone.
   */
  overflow-wrap: anywhere;
}

.callout .why {
  color: var(--muted);
  margin-top: 4px;
  font-size: 13.5px;
}

.surface {
  margin: 0 32px 32px;
  border: var(--hairline) solid var(--line);
  border-left: 2px solid var(--warn);
  border-radius: var(--radius);
  padding: 16px 20px;
  background: var(--surface);
  line-height: 1.55;
}

.surface-words {
  margin: 0;
  color: var(--muted);
  font-size: 13.5px;
}

.summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin: 24px 32px 0;
}

.tile {
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 20px 24px;
}

.tile .label {
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
}

.tile .figure {
  font-family: var(--font-mono);
  font-size: 26px;
  margin-top: 10px;
  letter-spacing: -0.02em;
}

.tile .figure.ok {
  color: var(--ok);
}

.tile .figure.busy {
  color: var(--accent);
}

.tile .aside {
  color: var(--muted);
  font-size: 13px;
  margin-top: 4px;
}

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

/* --- signing in --------------------------------------------------------- */

.gate {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
}

.gate form {
  width: 100%;
  max-width: 420px;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 28px;
}

.gate h1 {
  font-size: 18px;
}

.gate p {
  color: var(--muted);
  margin-top: 8px;
}

.gate label {
  display: block;
  margin-top: 20px;
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
}

.gate input {
  font-family: var(--font-mono);
  font-size: 14px;
  width: 100%;
  margin-top: 8px;
  padding: 9px 12px;
  border: var(--hairline) solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--fg);
}

.gate button {
  margin-top: 20px;
  width: 100%;
  padding: 10px;
  font-size: 14px;
}

.problem {
  margin-top: 16px;
  color: var(--warn);
  font-size: 13.5px;
}

/* The address of the person signed in, in the corner of every page. Until
   ADR-0009 there was nobody to name: the cabinet held a merchant key and the
   screen could not say whose hand was on the control that stops all selling.
   It is a link because pressing your own name means "show me my account", and
   it leads to the settings, where the account is. The colour is the muted one
   so that it reads as a label rather than as a fifth tab. */
.who {
  color: var(--muted);
  text-decoration: none;
  border-bottom: var(--hairline) solid var(--line);
}

.who:hover {
  color: var(--fg);
}

/* The way back from the password page, under the form. `td.quiet` above is a
   table cell and does not apply here. */
p.quiet {
  margin-top: 16px;
  font-size: 13px;
  color: var(--muted);
}

/* The note beside the address, on every page (ADR-0014 §4). It borrows the
   pill above rather than a shape of its own, and takes the warn colour back
   out of it: what that colour is for on these screens is money that did not
   move, and a page that painted this the same amber would spend a merchant's
   attention on a fact about their account every time they looked at a
   receipt. */
.tag.plain {
  color: var(--muted);
  border-color: var(--line-strong);
}

/* --- the keys screen ---------------------------------------------------- */

/*
 * The heading over the form that issues a key, under the list of the ones there
 * are. Smaller than the page's own heading and larger than a table's, because
 * it opens a second half of one screen rather than a second screen.
 */
h2 {
  font-size: 16px;
}

/*
 * The form that issues a key: one box and one button, side by side, sitting
 * where a table would.
 *
 * The input is styled here rather than borrowing `.gate input`, which is a
 * whole-width box inside a sign-in card and would run the length of the page.
 */
.issue {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
  padding: 20px 32px 32px;
}

.issue label {
  display: block;
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
  margin-bottom: 8px;
}

.issue input {
  font: inherit;
  width: 340px;
  max-width: 100%;
  padding: 6px 12px;
  border: var(--hairline) solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--fg);
}

/*
 * The key itself, on the one page it is ever shown.
 *
 * Given a line of its own and set in the monospaced family so that it can be
 * selected in one gesture and read back character by character — it is copied
 * by hand at least once, and the shared palette's fallback stack is what makes
 * that true on a machine with no fonts of ours.
 */
.secret {
  padding: 28px 24px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 15px;
  overflow-wrap: anywhere;
}

/* The way back, under the key. On the password page this sits inside the
   sign-in card and takes that card's padding; here it is a direct child of the
   page frame and needs the frame's own gutter, or it lands against the edge of
   the window. */
.page > p.quiet {
  padding: 0 32px;
}

/* --- the payout address on the settings screen -------------------------- */

/*
 * The address a merchant's money arrives at, shown back to them.
 *
 * Forty characters is more than anybody reads in a run, so it is set in the
 * monospaced family and broken into groups of four the way a long number is.
 * The gaps are this rule's and not the text's: the groups are spans with
 * nothing between them in the markup, so a merchant who selects the address and
 * pastes it into a wallet pastes an address rather than a spaced-out copy of
 * one that no wallet will take.
 *
 * `overflow-wrap` is here for the same reason it is on the callout above: this
 * block has no scroller of its own, and an unbroken forty-character run would
 * be the one thing on the page that scrolls the whole document sideways on a
 * phone. Wrapping happens between groups, which is where a person reading it
 * against their wallet would break it anyway.
 */
.saved {
  margin: 0 32px 24px;
  padding: 16px 20px;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

.saved .label {
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
}

.saved .address {
  margin-top: 10px;
  font-family: var(--font-mono);
  font-size: 15px;
  overflow-wrap: anywhere;
}

.saved .address .lead {
  color: var(--muted);
}

.saved .address .quad {
  margin-right: 0.5em;
}

.saved .under {
  color: var(--muted);
  font-size: 13px;
  margin-top: 10px;
  max-width: 70ch;
}
