/* One phone, one hand, often in poor light in a plant room. Big targets, high
   contrast, nothing that needs a second tap to read. */
/* ─── THIS IS THE REDAI DESIGN SYSTEM, NOT A PALETTE OF ITS OWN ──────────────
 *
 * `C:\RedAI Studio\design-system\` holds `redai-tokens.css` and
 * `redai-product-design-language.md`. Every value below comes from them.
 *
 * It did not, until 20 Sep. This file carried `--accent: #A8E05F`, a green
 * invented here, with a comment in this very spot arguing that "a red primary
 * button in a dark interface reads as delete". The design language had already
 * answered that — §1.2, "the red collision — resolved": interactive and danger
 * are separated by LIGHTNESS and by a mandatory ICON, never by hue. So the
 * argument was sound and the conclusion was a local invention, and the app
 * ended up wearing neither RedAI's colours nor Redmatic's. Shane, 20 Sep: "it
 * still doesnt use the redmatic colour themses?"
 *
 * ─── WHAT THE PRODUCT ACCENTS ARE FOR, WHICH IS NOT THIS ────────────────────
 *
 * ReCeipt `#FF5964`, RedMatic `#FF9A3D`, ALbot `#8E9BFF`, ReDeal `#FFD166`,
 * ReTrack `#5FD8BD`, RedAI `#FFB27A`. §1.1: "remain DETAIL ONLY. In product UI
 * their only sanctioned use is data-visualisation series and product-identity
 * chips. They never fill a button, a card, or a nav."
 *
 * So ReCeipt's own accent lives on the mark and wordmark and nowhere else in
 * here. Painting the buttons RedMatic orange instead would have broken the
 * same rule the old green broke.
 *
 * ReCeipt's accent is RED since 22 Sep — Shane: "the Ceipt needs to be red,
 * im not sure where green is coming from?". The green was picked on 18 Sep as
 * the hue the other products had not used; nobody asked for it. It is now
 * Signal Red, the same red as the interactive accent, which is why the token
 * below points at it rather than repeating the value.
 *
 * ─── TRANSLATED TO A DARK SURFACE, AND THE TRANSLATION IS CHECKED ───────────
 *
 * `redai-tokens.css` is written for Paper. Ink is a sanctioned surface (§1.1,
 * "Ink #0B0D11 — text, dark surfaces") but the muted and semantic values are
 * chosen against white and several are unreadable on it — slate-600 muted,
 * amber-700 warning, emerald-700 success. Each one below says what it is and
 * what it measures, because an unchecked translation is how the last palette
 * ended up with a 1.61:1 control edge.
 *
 * Ratios are WCAG 2.1 against the surface the value is actually used on.
 */
:root {
  /* ---- Surfaces. Ink is the house value; the old #12161D was lighter. ---- */
  --bg: #0B0D11;          /* Ink. Never #000 */
  --panel: #12161D;       /* level 1: cards, list rows */
  --panel-2: #1A1F28;     /* level 2: inputs, pressed surfaces */

  /* ---- Edges. Two, and the split is the point (§8, WCAG 1.4.11) -----------
   * A divider you are not meant to notice and the edge that tells you where a
   * control IS are different jobs with different floors. */
  --line: #232B39;        /* decoration: dividers, card edges */
  --edge: #5A6880;        /* control boundaries — 3.22:1 on --panel */

  /* ---- Text ------------------------------------------------------------- */
  --ink: #F7F8FA;         /* Paper — 18.3:1 on Ink */
  --ink-dim: #94A3B8;     /* slate-400 — 7.58:1 on Ink, 7.07:1 on --panel.
                             The house muted (slate-600) is 1.8:1 here */

  /* ---- Interactive. One colour, everywhere (§1.1) -------------------------
   * Signal Red: primary fill, links, focus ring, selection, active nav.
   * §8 warns it is ~3.0:1 on Paper and fails as body text there. On Ink it is
   * 6.36:1 and passes — the warning is about the surface, not the colour. */
  --accent: #FF5964;
  --on-accent: #0B0D11;   /* Ink on Signal Red — 6.36:1 */
  --brand: #D42438;       /* Brand Red: hover on primary, large brand fills */

  /* The mark's own gradient, from Branding/assets/svg. Used by the header
     brandmark and the icons, which are placed artwork rather than UI. */
  --brand-hi: #FF6E6A;
  --brand-lo: #C31832;
  --receipt: var(--accent); /* ReCeipt's product accent — Signal Red; mark and wordmark */

  /* ---- Semantic ----------------------------------------------------------
   * §1.2: danger is Deep Red, separated from the interactive accent by
   * lightness, and ALWAYS carries an icon. Three reds, three lightnesses:
   *
   *   interactive  Signal Red  #FF5964  L≈0.29   fills, links, focus
   *   danger       Deep Red    #C31832  L≈0.12   destructive fill and border
   *   danger text  light red   #FF9AA2  L≈0.47   the same job Brand Red does
   *                                              for text on Paper, mirrored
   *
   * Deep Red is 3.02:1 on --panel: enough for a boundary, NOT enough for
   * text, which is why the third one exists rather than being decoration. */
  --bad: #C31832;         /* fills and borders — 3.02:1 on --panel */
  --bad-ink: #FF9AA2;     /* danger text — 8.98:1 on --panel */

  /* No house dark values for these two; added here rather than written into a
     component (§1.4, "if a needed colour has no token, add the token first"). */
  --warn: #FFB020;        /* 9.9:1 on --panel; house amber-700 is 2.1:1 */
  --good: #3DD68C;        /* 9.66:1 on --panel; house emerald-700 is 2.4:1.
                             Kept clear of ReTrack's #5FD8BD, so it reads as
                             "it worked" and not as a product */

  /* ---- Shape (§4): 8px controls, 12px cards and tables, 9999px pills ----- */
  --radius: 12px;         /* cards, panels, list rows */
  --radius-control: 8px;  /* buttons, inputs, segments */

  /* ---- Type (§2.1) -------------------------------------------------------
   * The handoff specifies Inter, self-hosted as a latin-subset woff2. It is
   * NOT shipped here yet, so this falls through to the system stack the house
   * list already names as the fallback. Outstanding, and written down rather
   * than left looking deliberate. */
  --font-sans: "Inter var", "Inter", ui-sans-serif, system-ui, -apple-system,
    "Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "JetBrains Mono", Menlo, monospace;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  /* 17px body, not the house 14px. §3.2 sets a different bar for a screen "a
     tradesperson uses one-handed", and this is that screen in a van. The
     scale below it is the house scale. */
  font: 17px/1.5 var(--font-sans);
  -webkit-text-size-adjust: 100%;
}

header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: max(16px, env(safe-area-inset-top)) 16px 16px;
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  background: var(--bg);
  z-index: 5;
}

h1 { font-size: 18px; margin: 0; letter-spacing: 0.2px; white-space: nowrap;
     min-width: 0; overflow: hidden; text-overflow: ellipsis; }
h2 { font-size: 19px; margin: 0 0 8px; }

/* A long address must shorten itself rather than push the title onto two lines
   and the sign-out link onto three. Tested at 375 px, which is the phone in
   the van. */
.who {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
  font-size: 12px;
  color: var(--ink-dim);
}
.who span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who a { color: var(--accent); white-space: nowrap; }

/* The body is a column so the footer sits at the bottom of a short page
   rather than floating halfway up an empty desktop viewport. */
body { min-height: 100dvh; display: flex; flex-direction: column; }
main { padding: 16px 16px 28px; max-width: 640px; margin: 0 auto; width: 100%;
       box-sizing: border-box; flex: 1 0 auto; }

/* ─── THE HEADER SAYS WHAT THE APP IS, NOT ONLY WHOSE IT IS ──────────────
   Signed in, the header showed the COMPANY name and nothing else, so there was
   nothing on screen saying what the app was. The mark goes beside it; the
   company name is what `app.js` replaces. */
.brandmark { display: flex; align-items: center; gap: 8px; min-width: 0; }
.brandmark .mark { flex: none; display: block; border-radius: 7px; }

/* ─── THE FOOTER ───────────────────────────────────────────
   Privacy and Terms are not decoration here: they are the two documents Google
   required before the OAuth app could be published at all, and a product that
   hides them looks like one that would rather you did not read them. */
.site-foot {
  flex: none;
  border-top: 1px solid var(--line);
  padding: 16px 16px calc(16px + env(safe-area-inset-bottom));
  max-width: 640px; margin: 0 auto; width: 100%; box-sizing: border-box;
  color: var(--ink-dim); font-size: 13px;
}
.site-foot p { margin: 0 0 8px; }
/* ─── THE WORDMARKS ARE PLACED ARTWORK ───────────────────────────────────
   Branding/wordmark-assets/READ-ME.txt and design language §2.1: "Tektur is
   never typed. It exists only as placed SVG." Generated by
   tools/make-wordmark.mjs, which rebuilds the delivered wordmarks from the
   font first and refuses to write if they do not come back identical.

   The white variant, because this is a dark app and an <img> cannot inherit
   currentColor. */
.brand { margin: 0; line-height: 0; }
.wordmark { display: block; height: 20px; width: auto; }

/* In a line of text an image sits on the baseline by its BOTTOM edge, and the
   bottom of each box is padding and descender, not baseline. These drop each
   one by exactly that much, so the letters share the text's baseline:
     ReCeipt  (8 pad + 20 descender) / 137.4 box × 13px = 2.65px
     RedAI    8.6 pad / 118 box × 11px              = 0.80px */
.foot-brand img { display: inline-block; width: auto; }
.foot-brand .wm-receipt { height: 13px; vertical-align: -2.65px; }
/* Three wordmarks, three boxes, one baseline. Each drop is its box's padding
   below the letters, scaled to the height it is shown at:
     RedMatic 8 pad / 117.4 box x 11px = 0.75px
   The RedAI Studios line sits under the ReCeipt-by-RedMatic line rather than
   beside it: product, then who sells it, then who makes it. */
.foot-brand .wm-redmatic { height: 11px; vertical-align: -0.75px; }
.foot-brand .by { color: var(--ink-dim); }
.foot-maker { font-size: 12px; color: var(--ink-dim); }
.foot-maker img { display: inline-block; width: auto; height: 11px; vertical-align: -0.8px; }
.site-brand { margin-top: 24px; }
/* Two classes, not one: on the prose pages this sits inside `.doc`, and
   `.doc p` (0,1,1) sets 16px and outranks a lone class (0,1,0) — which made
   "by" and "product" bigger than the wordmarks either side of them. Seen at
   375px on privacy and connect. Same trap as the button that once rendered blue
   on blue on the connect page. */
.site-brand .foot-brand { font-size: 13px; line-height: 1.5; margin: 0 0 8px; }
.site-brand .foot-maker { font-size: 12px; line-height: 1.5; margin: 0; }
.foot-links { display: flex; flex-wrap: wrap; gap: 16px; }
.foot-links a { color: var(--accent); text-decoration: none; }
.foot-links a:hover { text-decoration: underline; }
.foot-links a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 3px; }
.foot-version { font-size: 12px; opacity: 0.75; }

.lead { font-size: 17px; margin: 4px 0 16px; }
.note { color: var(--ink-dim); font-size: 14px; margin: 8px 0; }

.banner {
  margin: 0;
  padding: 8px 16px;
  background: var(--panel-2);
  border-bottom: 1px solid var(--line);
  font-size: 14px;
  color: var(--warn);
}
.banner.offline { color: var(--warn); }
.banner.working { color: var(--accent); }

/* ── capture ─────────────────────────────────────────────────────────── */
.capture { display: block; position: relative; }
.capture input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; }
.capture-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 44px 16px;
  background: var(--panel);
  border: 2px dashed var(--edge);
  border-radius: var(--radius);
  text-align: center;
}
/* An SVG, not an emoji. The OS draws an emoji, so Apple's camera, Google's and
   Samsung's are three different pictures and none of them is ours. */
.capture-icon { color: var(--accent); display: block; }
.capture-label { font-size: 19px; font-weight: 600; }
.capture-sub { color: var(--ink-dim); font-size: 14px; }

/* ── fields ──────────────────────────────────────────────────────────── */
.field { display: block; margin: 0 0 16px; border: 0; padding: 0; }
.field-label { display: block; font-size: 14px; color: var(--ink-dim); margin-bottom: 8px; }
.field-help { display: block; font-size: 13px; color: var(--ink-dim); margin-top: 8px; }

input[type=text], input[type=email] {
  width: 100%;
  padding: 16px;
  font-size: 18px;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: var(--radius-control);
}
input[type=text]:focus-visible, input[type=email]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-color: var(--accent);
}

.seg { display: flex; gap: 8px; }
.seg-btn {
  flex: 1;
  padding: 13px 8px;
  font-size: 15px;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
}
.seg-btn[aria-pressed=true] { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 500; }

.pick { margin-top: 16px; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.chip {
  display: inline-flex;
  align-items: center;
  /* 44px, like every other control meant for a thumb. These were 35px, which is
     small for a man standing in a supplier's car park holding a docket. */
  min-height: 44px;
  padding: 8px 16px;
  font-size: 15px;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.chip[aria-pressed=true] { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 500; }
/* On is not carried by colour alone. Drawn in CSS rather than set as a glyph
   for the same reason the camera and the tick elsewhere are SVG: a character is
   drawn by whatever font the phone falls back to, and a checkmark that renders
   as a box is worse than no checkmark. */
.chip[aria-pressed=true]::before {
  content: '';
  flex: none;
  width: 5px;
  height: 10px;
  margin-right: 9px;
  border: solid currentColor;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translate(-1px, -1px);
}
/* Keyboard focus. The links and inputs above already carried this; the buttons
   and chips did not, so they fell back to whatever outline the browser draws on
   a near-black background. Same token and offset as the rest of the file. */
.btn:focus-visible, .chip:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
.chip.recent { border-style: dashed; }

/* ── preview, the whole point of the confirm screen ──────────────────── */
.preview {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 16px;
  font-size: 14px;
  color: var(--ink-dim);
  min-height: 62px;
}
.preview .filename {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 15px;
  color: var(--ink);
  word-break: break-all;
  margin-bottom: 8px;
}
.preview .routing { color: var(--ink); }
.preview .adjust { color: var(--warn); margin-top: 8px; }
.preview .bad { color: var(--bad-ink); }

/* A save that worked. Green, like agreement on the amount line, so "it went
   through" reads the same way everywhere in the app. */
.note-box.good { border-color: var(--good); color: var(--good); }
.note-box.good strong { color: var(--good); }

/* The reader is still working. An INDETERMINATE bar on purpose: the read takes
   between three and eight seconds and nothing here knows which, so a bar that
   filled up would be inventing a number. This one travels, says "still going",
   and claims nothing about how far along it is. */
.reading-bar {
  /* A span by default is inline, and height on an inline box does nothing —
     the first version of this rendered 0x4 and was invisible. */
  display: block;
  position: relative;
  height: 4px;
  margin-top: 10px;
  border-radius: 999px;
  background: var(--panel-2);
  overflow: hidden;
}
.reading-bar::after {
  content: '';
  position: absolute;
  inset: 0;
  width: 40%;
  border-radius: 999px;
  background: var(--accent);
  animation: reading-travel 1.1s ease-in-out infinite;
}
@keyframes reading-travel {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}
/* Somebody who has asked not to be moved gets a steady bar instead of a
   travelling one. It still says "working"; it just stops sliding. */
@media (prefers-reduced-motion: reduce) {
  .reading-bar::after { animation: none; width: 100%; opacity: 0.55; }
}

.note-box, .warn-box {
  border-radius: 12px;
  padding: 12px 16px;
  font-size: 14px;
  margin-bottom: 16px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink-dim);
}
.warn-box { border-color: var(--warn); color: var(--warn); }
.note-box strong, .warn-box strong { color: var(--ink); }

/* ── what the reader made of the total ─────────────────────── */
/* Sits under the Amount box, because that is where the decision is made - a
   figure to check in a banner at the top of the screen is a figure nobody
   compares. It is never written INTO the box; renderAmountRead() says why.

   Three states, and each says it in words as well as colour: a reading to
   check, agreement, and disagreement. */
.read-line {
  display: block;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-dim);
  margin-top: 8px;
  padding-left: 12px;
  border-left: 2px solid var(--edge);
}
/* Currency lines up under currency. */
.read-line strong { color: var(--ink); font-variant-numeric: tabular-nums; }
.read-line.agree { border-left-color: var(--good); }
.read-line.agree strong { color: var(--good); }
.read-line.differ { border-left-color: var(--warn); color: var(--warn); }
.read-line.differ strong { color: var(--warn); }

/* ── buttons ─────────────────────────────────────────────────────────── */
.row-actions { display: flex; gap: 8px; align-items: center; margin: 16px 0; flex-wrap: wrap; }
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 15px 20px;
  font-size: 17px;
  border-radius: var(--radius-control);
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  text-decoration: none;
  font-weight: 500;
  min-height: 52px;
  flex: 1 1 auto;
  transition: background-color 120ms ease-out, border-color 120ms ease-out;
}
/* §7: press feedback on every button. */
.btn:active { transform: scale(0.98); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn.primary:hover { background: var(--brand); border-color: var(--brand); }
.btn.primary:disabled { opacity: 0.45; }
.btn.ghost { background: transparent; }

/* ── saved ───────────────────────────────────────────────────────────── */
/* An SVG, for the same reason as the camera: a character is drawn by whatever
   font the phone falls back to, and a 44px tick is not somewhere to find out
   which one that is. */
.tick { color: var(--good); line-height: 0; margin-bottom: 8px; }
.saved-filename {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 16px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px;
  word-break: break-all;
}

/* ── lists ───────────────────────────────────────────────────────────── */
.item {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px 16px;
  margin-bottom: 8px;
}
.item .name {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px;
  word-break: break-all;
}
.item .meta { color: var(--ink-dim); font-size: 13px; margin-top: 4px; }
.item .meta.locked { color: var(--warn); }
.item .actions { margin-top: 8px; display: flex; gap: 8px; }
.item .actions .btn { padding: 8px 16px; font-size: 14px; min-height: 44px; flex: 0 0 auto;
  gap: 8px; }   /* 44px: §3.2's floor for a one-handed screen */

.pending { border-color: var(--warn); }
.pending .meta { color: var(--warn); }

img#thumb {
  width: 100%;
  max-height: 210px;
  object-fit: contain;
  background: #000;
  border-radius: 12px;
  margin-bottom: 16px;
}

.spinner {
  width: 34px; height: 34px;
  margin: 24px auto 16px;
  border: 3px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.toggle { font-size: 14px; color: var(--ink-dim); flex: 0 0 auto; }

@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 3s; }
}

/* ── people ──────────────────────────────────────────────────────────────
   Who can use ReCeipt, and the two controls for ending it. Built on `.item`
   rather than beside it: this is a list of things with a name, a line of
   detail and some actions, which is what `.item` already is. */
.item.is-blocked { border-color: var(--bad); }
.item.is-blocked .meta.locked { color: var(--bad-ink); }
.item.is-out { border-color: var(--warn); }

/* Colour never carries the meaning on its own — every badge has a word in
   it, and the row's state is in the meta line as a sentence as well. */
.badge {
  display: inline-block;
  font-size: 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 8px;
  margin-left: 8px;
  color: var(--ink-dim);
  vertical-align: 1px;
  white-space: nowrap;
}
.badge.you { border-color: var(--edge); color: var(--ink); }
.badge.out { border-color: var(--warn); color: var(--warn); }
.badge.blocked { border-color: var(--bad); color: var(--bad-ink); }

/* §1.2: a destructive action defaults to the OUTLINE danger variant, and
   always carries an icon — Deep Red is 3.02:1 here, enough for the border that
   says "this one is different" and not enough for the label, which is why the
   label is Paper and the meaning is carried by the icon and the word. */
.btn.danger { border-color: var(--bad); color: var(--ink); }
.btn.danger svg { color: var(--bad-ink); flex: none; }
/* "Solid Deep Red is reserved for the confirming button inside a destructive
   dialog — the last click before something is gone." This app has no dialog;
   the second tap IS that click. Paper on Deep Red is 5.65:1. */
.btn.armed { background: var(--bad); border-color: var(--bad); color: var(--ink); }
.btn.armed svg { color: var(--ink); }
/* "A destructive control is never placed adjacent to the primary action in the
   same button group. Put it at the opposite end of the row." */
.item .actions .btn.danger { margin-left: auto; }

/* ── loading ─────────────────────────────────────────────────────────────
   Shaped like the rows it stands in for, so the screen does not jump when
   the answer arrives. A centred spinner would say "something is happening";
   this says what is coming. */
.skeleton { background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; padding: 16px; margin-bottom: 8px; }
.skeleton .bar {
  height: 12px; border-radius: 6px;
  background: linear-gradient(90deg, var(--panel-2), var(--line), var(--panel-2));
  background-size: 200% 100%;
  animation: shimmer 1.2s linear infinite;
}
.skeleton .bar + .bar { margin-top: 8px; width: 55%; }
@keyframes shimmer { to { background-position: -200% 0; } }

@media (prefers-reduced-motion: reduce) {
  .skeleton .bar { animation: none; }
}
