/* Palette roles. Dark mode is a selected set of steps, not an inverted flip. */
:root {
  color-scheme: light;
  --plane:          #f9f9f7;
  --surface:        #fcfcfb;
  --text-primary:   #0b0b0b;
  --text-secondary: #52514e;
  --text-muted:     #898781;
  --gridline:       #e1e0d9;
  --baseline:       #c3c2b7;
  --border:         rgba(11, 11, 11, 0.10);
  --series-1:       #2a78d6;
  --series-wash:    rgba(42, 120, 214, 0.10);
  --good:           #006300;
  --critical:       #d03b3b;
  --warning-bg:     rgba(250, 178, 25, 0.14);
  --surface-2:      #f1f1ec;
  --radius:         10px;
}

/* Dark mode is where this app actually lives day to day, so it carries the
   brand rather than being a neutral inversion: a green-black ground, panels
   lifted a step out of it, and the brand green as the accent. */
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --plane:          #071711;
    --surface:        #0d2119;
    --surface-2:      #123026;
    --text-primary:   #eef6f1;
    --text-secondary: #a9c3b7;
    --text-muted:     #7d9a8d;
    --gridline:       #17362a;
    --baseline:       #1e4636;
    --border:         rgba(255, 255, 255, 0.09);
    --series-1:       #03d47c;
    --series-wash:    rgba(3, 212, 124, 0.14);
    --good:           #03d47c;
    --critical:       #ff7a6b;
    --warning-bg:     rgba(250, 178, 25, 0.16);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--plane);
  color: var(--text-primary);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif;
}

a { color: var(--series-1); }
a:hover { text-decoration: underline; }

/* ---- shell ---- */

/* ---- the space the phone itself occupies ----------------------------
   Android 15 (targetSdk 35+) draws every app edge-to-edge whether it asks to
   or not, so the WebView starts at y=0 - underneath the status bar. iOS does
   the same around the notch and the home bar. These four are the only place
   that is described, so nothing has to guess.

   They read as 0 on a plain desktop browser, which is exactly right: the
   rules below then behave as though they were never there. */
:root {
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
}

.topbar {
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  position: sticky;
  top: 0;
  z-index: 20;
  /* The bar grows upward to fill the status-bar strip, so its own background
     sits behind the clock and the battery rather than the page content
     sliding under them. */
  padding-top: var(--safe-top);
}

.topbar-inner,
.page {
  max-width: 1120px;
  margin: 0 auto;
  /* A rotated phone puts the camera cutout beside the content rather than
     above it, so the side gutters have to be at least the inset. */
  padding: 0 max(24px, var(--safe-right)) 0 max(24px, var(--safe-left));
}

.topbar-inner {
  display: flex;
  align-items: center;
  gap: 24px;
  min-height: 58px;
  flex-wrap: wrap;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  text-decoration: none;
}
.brand:hover { text-decoration: none; }
.brand-logo { display: block; border-radius: 50%; flex: 0 0 auto; }

.nav { display: flex; gap: 4px; margin-left: auto; flex-wrap: wrap; }

.nav a {
  padding: 6px 12px;
  border-radius: 8px;
  color: var(--text-secondary);
  text-decoration: none;
  font-weight: 500;
}
.nav a:hover { background: var(--plane); text-decoration: none; }
.nav a[aria-current="page"] { background: var(--series-wash); color: var(--series-1); }

.page { padding-top: 28px; padding-bottom: 64px; }
.page-narrow { max-width: 440px; }

/* ---- account menu ---- */

.account-menu { display: flex; align-items: center; gap: 10px; }
.account-name { font-size: 13px; color: var(--text-secondary); text-decoration: none; }
.account-name:hover { color: var(--text-primary); }

.btn.small { padding: 4px 10px; font-size: 13px; }
.btn.wide { width: 100%; }

.link-ish { color: var(--series-1); }

/* ---- sign in ---- */

.signin { margin-top: 48px; }
.signin-logo { display: block; margin: 0 auto 14px; border-radius: 50%; }
.signin h1 { font-size: 26px; text-align: center; }
.signin-card { display: flex; flex-direction: column; gap: 14px; }
.signin-card input { min-width: 0; width: 100%; }

/* ---- user admin ---- */

.row-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.inline-form { display: flex; gap: 6px; align-items: center; }
.inline-form input { min-width: 150px; padding: 4px 8px; font-size: 13px; }

.checkbox {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-secondary);
  padding-bottom: 8px;
}

h1 { font-size: 24px; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 4px; }
h2 { font-size: 15px; font-weight: 600; margin: 0 0 2px; }
.subtitle { color: var(--text-secondary); margin: 0 0 24px; }
.section-note { color: var(--text-muted); font-size: 13px; margin: 0 0 16px; }

/* ---- cards ---- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
}

.card + .card { margin-top: 16px; }
.grid { display: grid; gap: 16px; }
.grid.two { grid-template-columns: 1fr 1fr; }

@media (max-width: 820px) {
  .grid.two { grid-template-columns: 1fr; }
}

/* ---- notices ---- */

/* Two names for the same thing: base.html draws Flask's flashed messages as
   .notice, while several pages write an inline message as .flash. Only .notice
   had rules, so every inline message rendered as bare unstyled text - a
   warning that does not look like a warning. Both are styled here rather than
   renaming across the templates, so either spelling works from now on. */
.notice,
.flash {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 16px;
  margin-bottom: 16px;
  background: var(--warning-bg);
  color: var(--text-primary);
  font-size: 14px;
}
.notice.error,
.flash.error { background: rgba(208, 59, 59, 0.12); }
.notice.ok,
.flash.ok { background: var(--series-wash); }
.notice code,
.flash code { font-size: 13px; }

/* ---- forms ---- */

.filters {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  flex-wrap: wrap;
  margin-bottom: 20px;
}

.field { display: flex; flex-direction: column; gap: 5px; }
.field label { font-size: 12px; color: var(--text-muted); font-weight: 500; }

input[type="text"], input[type="date"], input[type="search"],
input[type="email"], input[type="password"], input[type="number"],
input[type="tel"], input[type="url"], select, textarea {
  /* textarea and the typed inputs were missing from this list, so they fell
     back to the browser default - which for a textarea is monospace. The note
     box on a receipt rendered in Courier while everything around it did not. */
  font: inherit;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text-primary);
  min-width: 130px;
}
input[type="search"] { min-width: 260px; }
input:focus-visible, select:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--series-1);
  outline-offset: 2px;
}

.btn {
  font: inherit;
  font-weight: 500;
  padding: 7px 14px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-primary);
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
}
.btn:hover { background: var(--plane); text-decoration: none; }
.btn.primary { background: var(--series-1); border-color: var(--series-1); color: #fff; }
.btn.primary:hover { filter: brightness(1.06); }
.btn.danger { color: var(--critical); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* ---- tables ---- */

.table-wrap { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: 14px; }

th {
  text-align: left;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0 12px 8px 0;
  border-bottom: 1px solid var(--gridline);
  white-space: nowrap;
}

td {
  padding: 9px 12px 9px 0;
  border-bottom: 1px solid var(--gridline);
  vertical-align: top;
}

tbody tr:hover { background: var(--plane); }
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.muted { color: var(--text-muted); }
.nowrap { white-space: nowrap; }

.pill {
  display: inline-block;
  font-size: 12px;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--plane);
  border: 1px solid var(--border);
  color: var(--text-secondary);
}

.empty {
  text-align: center;
  padding: 48px 24px;
  color: var(--text-muted);
}

/* ---- stat tiles ---- */

.tiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  margin-bottom: 16px;
}

@media (max-width: 820px) { .tiles { grid-template-columns: repeat(2, 1fr); } }

.tile { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px 18px; }
.tile .label { font-size: 12px; color: var(--text-muted); font-weight: 500; }
.tile .value { font-size: 26px; font-weight: 600; letter-spacing: -0.02em; margin-top: 2px; }
.tile .value.hero { font-size: 34px; }
.tile .sub { font-size: 12px; color: var(--text-muted); margin-top: 2px; }

/* ---- column chart (monthly) ---- */

.plot {
  position: relative;
  height: 200px;
  margin: 8px 0 0;
}

.gridlines { position: absolute; inset: 0; }
.gridlines span {
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--gridline);
}
.gridlines span:last-child { background: var(--baseline); }

.columns {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  gap: 2px;
}

.column {
  flex: 1;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  position: relative;
  background: none;
  border: 0;
  padding: 0;
  cursor: default;
}

.column-fill {
  width: 100%;
  max-width: 24px;
  background: var(--series-1);
  border-radius: 4px 4px 0 0;
  min-height: 2px;
  transition: filter 0.12s ease;
}
.column-fill.zero { background: var(--gridline); }
.column:hover .column-fill, .column:focus-visible .column-fill { filter: brightness(1.12); }

.column-cap {
  position: absolute;
  bottom: calc(var(--h) + 6px);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
}

.x-axis {
  display: flex;
  gap: 2px;
  margin-top: 8px;
}
.x-axis span {
  flex: 1;
  min-width: 0;
  text-align: center;
  font-size: 11px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.y-legend {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  margin-bottom: 4px;
}

/* ---- horizontal bars (category / merchant) ---- */

.hbars { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }

.hbar { display: grid; grid-template-columns: 128px 1fr auto; gap: 12px; align-items: center; }
.hbar-label { font-size: 13px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hbar-track { background: var(--plane); border-radius: 4px; height: 16px; }
.hbar-fill { background: var(--series-1); height: 100%; border-radius: 0 4px 4px 0; min-width: 2px; }
.hbar-value { font-size: 13px; font-variant-numeric: tabular-nums; color: var(--text-secondary); white-space: nowrap; }

@media (max-width: 620px) {
  .hbar { grid-template-columns: 92px 1fr auto; }
}

/* ---- upload ---- */

.dropzone {
  /* It is a <label> (so a tap anywhere opens the file picker), and a label is
     inline by default: the dashed border was drawn around each line of text
     separately and the box looked torn. */
  display: block;
  cursor: pointer;
  margin-top: 12px;
  border: 1.5px dashed var(--baseline);
  border-radius: var(--radius);
  padding: 40px 24px;
  text-align: center;
  background: var(--surface);
  transition: background 0.12s ease, border-color 0.12s ease;
}
.dropzone.dragover { border-color: var(--series-1); background: var(--series-wash); }
.dropzone p { margin: 8px 0 0; color: var(--text-secondary); }
.dropzone .hint { font-size: 13px; color: var(--text-muted); }

.filelist { margin: 16px 0 0; padding: 0; list-style: none; font-size: 14px; }
.filelist li { padding: 6px 0; border-bottom: 1px solid var(--gridline); display: flex; justify-content: space-between; gap: 12px; }

.log {
  background: var(--plane);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  font: 13px/1.6 ui-monospace, "Cascadia Code", Consolas, monospace;
  white-space: pre-wrap;
  max-height: 320px;
  overflow-y: auto;
  margin-top: 12px;
}

.progress-track { height: 6px; background: var(--plane); border-radius: 999px; overflow: hidden; margin-top: 12px; }
.progress-fill { height: 100%; background: var(--series-1); border-radius: 999px; transition: width 0.3s ease; }

/* ---- detail ---- */

.detail-grid { display: grid; grid-template-columns: 1fr 340px; gap: 16px; align-items: start; }
@media (max-width: 900px) { .detail-grid { grid-template-columns: 1fr; } }


.kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 16px; font-size: 14px; margin: 0; }
.kv dt { color: var(--text-muted); }
.kv dd { margin: 0; }

.totals { margin-top: 16px; border-top: 1px solid var(--gridline); padding-top: 12px; }
.totals-row { display: flex; justify-content: space-between; padding: 3px 0; font-size: 14px; }
.totals-row.grand { font-weight: 600; font-size: 16px; border-top: 1px solid var(--gridline); margin-top: 6px; padding-top: 10px; }

/* ---- print: the reports page doubles as the printable report ---- */

@media print {
  .topbar, .filters, .no-print { display: none !important; }
  body { background: #fff; }
  .card, .tile { border-color: #ddd; break-inside: avoid; }
  .page { max-width: none; padding: 0; }
  a { color: inherit; text-decoration: none; }
}

/* ---- Android app only ---------------------------------------------------
   These elements are hidden in a browser and revealed by native.js, which
   adds .is-native to <body> when Capacitor is present. */
/* Superseded: visibility is driven by the class set on <html> in the head,
   further down this file. Waiting for body.is-native to be added by script was
   the jump itself. */

.queue-badge {
  display: inline-block;
  min-width: 20px;
  margin-left: 8px;
  padding: 1px 7px;
  border-radius: 999px;
  background: #b7791f;
  color: #fff;
  font-size: 12px;
  font-weight: 600;
}

/* ---- expense classification and confirmation --------------------------- */
.pill {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  vertical-align: 2px;
  background: #eef1f3;
  color: #4a5560;
}
.pill.ok   { background: #e7f4ec; color: #1c7c4a; }
.pill.warn { background: #fdf3e2; color: #98650d; }
.card.needs-attention { border-left: 3px solid #b7791f; }
tr.muted td { opacity: .55; }
.btn.small { padding: 5px 10px; font-size: 13px; }
td.right { text-align: right; white-space: nowrap; }

/* ---- the outstanding-jobs list ----------------------------------------- */
.todo-list { list-style: none; margin: 14px 0 0; padding: 0; }
.todo-item {
  display: grid;
  grid-template-columns: 26px 1fr;
  gap: 10px;
  padding: 12px 0;
  border-top: 1px solid var(--rule, #e6eaed);
  align-items: start;
}
.todo-item:first-child { border-top: none; }
.todo-mark {
  width: 20px; height: 20px;
  border-radius: 50%;
  border: 1.5px solid #b9c2c9;
  display: grid; place-items: center;
  font-size: 12px; font-weight: 700;
  color: #fff;
}
.todo-item.done .todo-mark { background: #1c7c4a; border-color: #1c7c4a; }
.todo-body { display: flex; flex-direction: column; gap: 3px; }
.todo-title { font-weight: 600; }
.todo-item.done .todo-title { color: #6b7780; text-decoration: line-through; }
.todo-why { font-size: 14px; color: #5d6a72; max-width: 64ch; }
.todo-actions { margin-top: 7px; display: flex; gap: 8px; flex-wrap: wrap; }
.nav-badge {
  display: inline-block; min-width: 18px; margin-left: 6px; padding: 0 6px;
  border-radius: 999px; background: #b7791f; color: #fff;
  font-size: 11px; font-weight: 700; line-height: 17px; text-align: center;
}

/* ---- PDF preview -------------------------------------------------------- */

/* Reimbursement buttons sit in a row and wrap on a narrow screen. Each one is
   its own form (a POST per action), so without this they stack awkwardly. */
.claim-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin-top: 4px;
}
.claim-actions form {
  display: flex;
  gap: 8px;
  align-items: center;
}
.claim-actions input[type="text"] {
  min-width: 0;
  width: 15rem;
  max-width: 100%;
}

/* =====================================================================
   Mobile-first shell: a bottom tab bar and a receipt list that reads as
   a list, not a spreadsheet.

   What was wrong: seven nav links in a horizontal row wrapped into a pile
   on a phone, and the receipt table had six columns - date, merchant,
   category, total, payment, source - which at 375px meant either a
   horizontal scroll or unreadably squeezed text. Neither told you the one
   thing you open the app for: what this receipt was, how much, and whether
   it still needs something from you.
   ===================================================================== */

:root {
  --tabbar-h: 62px;
  /* Anything a thumb has to hit. Below about this, people miss. */
  --touch: 44px;
}

/* ---- bottom tab bar (phones only) ---- */

.tabbar { display: none; }

@media (max-width: 820px) {
  /* The desktop nav and the account strip give way to the tab bar. */
  .topbar .nav { display: none; }
  .topbar-inner { justify-content: space-between; gap: 12px; }

  .tabbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 50;
    display: flex;
    align-items: stretch;
    justify-content: space-around;
    background: var(--surface);
    border-top: 1px solid var(--border);
    /* Clears the home-bar area on phones that have one. */
    padding-bottom: var(--safe-bottom);
    box-shadow: 0 -2px 12px rgba(11, 11, 11, 0.06);
  }

  .tab {
    position: relative;
    flex: 1 1 0;
    min-height: var(--tabbar-h);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 7px 2px;
    color: var(--text-secondary);
    text-decoration: none;
    font-size: 11px;
    line-height: 1;
  }
  .tab:hover { text-decoration: none; }
  .tab.current { color: var(--series-1); font-weight: 600; }

  .tab-icon svg { width: 23px; height: 23px; display: block; }
  .tab-label { white-space: nowrap; }

  /* The raised action. Adding a receipt is the whole point of the app on a
     phone, so it is a target you cannot miss rather than one link of five. */
  .tab-action { color: var(--text-secondary); }
  .tab-fab {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    margin-top: -18px;
    border-radius: 50%;
    background: var(--series-1);
    color: #fff;
    box-shadow: 0 3px 10px rgba(42, 120, 214, 0.4);
  }
  .tab-fab svg { width: 24px; height: 24px; display: block; }

  .tab-badge {
    position: absolute;
    top: 5px;
    left: 50%;
    margin-left: 6px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 9px;
    background: var(--critical);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    line-height: 17px;
    text-align: center;
  }

  /* Room so the last row is never trapped under the bar. */
  /* Clear the tab bar *and* the gesture bar underneath it. Without the
     inset this left one pixel of daylight on a phone with a home bar, so the
     last card on a page sat behind the tabs. */
  .has-tabbar { padding-bottom: calc(var(--tabbar-h) + var(--safe-bottom) + 26px); }
}

/* ---- the receipt list ---- */

.receipt-list {
  list-style: none;
  margin: 0;
  padding: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.receipt-row + .receipt-row { border-top: 1px solid var(--gridline); }

.receipt-link {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 16px;
  min-height: var(--touch);
  color: inherit;
  text-decoration: none;
}
.receipt-link:hover { background: var(--plane); text-decoration: none; }

.r-main { flex: 1 1 auto; min-width: 0; }

.r-merchant {
  display: block;
  font-weight: 600;
  font-size: 15px;
  /* A long merchant name truncates rather than shoving the amount off. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.r-meta {
  display: block;
  margin-top: 3px;
  font-size: 13px;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.r-dot { margin: 0 5px; }

.r-side {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 5px;
  text-align: right;
}

.r-amount {
  font-weight: 600;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* A receipt still wanting something from you is marked down the edge, so a
   list of thirty can be skimmed without reading a single word. */
.receipt-row.flagged .receipt-link { box-shadow: inset 3px 0 0 var(--critical); }

@media (max-width: 620px) {
  .receipt-link { padding: 12px 13px; }
  .r-merchant { font-size: 15.5px; }
  .r-amount { font-size: 16px; }
  /* Payment method is the first thing to go: it is the least useful of the
     three and the line has to fit. */
  .r-pay, .r-pay + .r-dot, .r-meta .r-dot:last-of-type { display: none; }
}

/* ---- search and the filter drawer ---- */

.searchbar { margin-bottom: 18px; }

.searchbar-row {
  display: flex;
  gap: 9px;
}
.searchbar-row input[type="search"] {
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--touch);
}
.searchbar-row .btn { flex: 0 0 auto; min-height: var(--touch); }

.filter-drawer {
  margin-top: 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}
.filter-drawer > summary {
  padding: 11px 14px;
  min-height: var(--touch);
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-secondary);
}
.filter-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  padding: 0 14px 4px;
}
.filter-actions {
  display: flex;
  gap: 9px;
  padding: 12px 14px 14px;
}

@media (max-width: 620px) {
  .filter-grid { grid-template-columns: 1fr; }
}

/* ---- touch targets everywhere else ---- */

@media (max-width: 820px) {
  .btn {
    min-height: var(--touch);
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .btn.small { min-height: 38px; }
  /* 16px stops iOS zooming the whole page in when a field is focused. */
  input, select, textarea { font-size: 16px; min-height: var(--touch); }

  /* Tighter gutters on a phone, but never tighter than the cutout the
     screen actually has - in landscape the camera sits beside the text. */
  .page { padding-left: max(15px, var(--safe-left));
          padding-right: max(15px, var(--safe-right)); }
  h1 { font-size: 23px; }
  .page-head { margin-bottom: 14px; }

  /* Wide tables that remain tables (reports, cards, people) must scroll in
     their own box rather than stretching the page sideways. */
  .table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .table-wrap table { min-width: 520px; }
}

/* ---- the "More" hub ---- */
/* Everything the phone tab bar has no room for. Rows rather than a grid of
   tiles: a list of six destinations is read, not scanned for pictures. */
.hub {
  margin: 0 0 22px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.hub-item {
  display: block;
  padding: 13px 16px;
  min-height: var(--touch);
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--gridline);
}
.hub-item:last-of-type { border-bottom: none; }
.hub-item:hover { background: var(--plane); text-decoration: none; }
.hub-title { display: block; font-weight: 600; font-size: 15px; }
.hub-why { display: block; margin-top: 2px; font-size: 13px; color: var(--text-muted); }
.hub-signout { padding: 14px 16px; border-top: 1px solid var(--gridline); }

/* The scan button is the primary action inside the app, so it is full width and
   unmissable on a phone rather than a normal-sized button among others. */
@media (max-width: 820px) {
  .btn-scan {
    width: 100%;
    min-height: 54px;
    font-size: 16px;
    font-weight: 600;
  }
}

/* The three parts of a receipt's meta line. Each earns a rule: a date must not
   break across lines, a long category must truncate rather than push the line
   wide, and the status sits tight under the amount. */
.r-date { white-space: nowrap; }
.r-cat {
  display: inline-block;
  max-width: 14ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
.r-status { line-height: 1; }

/* =====================================================================
   Feedback pass: capture states you can actually see, and the Expensify
   details - merchant avatars, the green create action.
   ===================================================================== */

/* The status line is now a toast pinned above the tab bar. As a line of small
   text at the top of the page it was invisible while your eyes were on the
   button you had just pressed - which read as "it does nothing". */
.native-status {
  position: fixed;
  z-index: 70;
  left: 12px;
  right: 12px;
  bottom: calc(var(--tabbar-h) + 16px + var(--safe-bottom));
  margin: 0;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 13px 15px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  font-size: 14.5px;
  line-height: 1.45;
  color: var(--text-primary);
  box-shadow: 0 8px 28px rgba(11, 11, 11, 0.22);
}
.native-status[hidden] { display: none; }
.native-status.busy::before {
  content: "";
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  border: 2.5px solid var(--gridline);
  border-top-color: var(--series-1);
  animation: native-spin 0.8s linear infinite;
}
@keyframes native-spin { to { transform: rotate(360deg); } }
.native-status.ok { border-left: 4px solid var(--good); }
.native-status.warn { border-left: 4px solid var(--critical); }

/* A stable colour block per merchant, the way Expensify marks each row.
   The hue comes from the merchant's name, so Amazon is always the same shade. */
.r-avatar {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: 700;
  font-size: 15px;
}

/* Expensify's green for the actions that create an expense. */
:root { --brand-green: #03a05e; }
.tab-fab {
  background: var(--brand-green);
  box-shadow: 0 3px 10px rgba(3, 160, 94, 0.4);
}
.btn.primary.btn-scan {
  background: var(--brand-green);
  border-color: var(--brand-green);
}

/* Belt and braces on icon size. The icons now carry width/height attributes
   in the files themselves, and these rules cap them regardless - so even a
   stale stylesheet can never again render them enormous. */
.tabbar svg, .tab-icon svg { width: 22px; height: 22px; }
.tab-fab svg { width: 24px; height: 24px; }

/* A refund is money coming back. Shown in the good colour so a credit is not
   mistaken for a cost at a glance down the list. */
.r-amount.credit { color: var(--good); }
.receipt-row.refund .receipt-link { box-shadow: inset 3px 0 0 var(--good); }

/* ---- the duplicate question ---- */
/* Shown as a card that needs an answer rather than a passive warning: the
   whole point is that a person decides, so the two answers are equal-weight
   buttons and the receipt it matched is right there to compare against. */
.dupe-other {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 14px;
  margin: 4px 0 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--plane);
  color: inherit;
  text-decoration: none;
}
.dupe-other:hover { text-decoration: none; border-color: var(--baseline); }
.dupe-other-main { flex: 1 1 auto; min-width: 0; }
.dupe-other-merchant { display: block; font-weight: 600; }
.dupe-other-meta {
  display: block;
  margin-top: 2px;
  font-size: 13px;
  color: var(--text-muted);
}
.dupe-other-amount {
  flex: 0 0 auto;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.dupe-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.dupe-actions form { margin: 0; }

/* A confirmed copy is dimmed in the list - still there as evidence, clearly
   not counted. */
.receipt-row.is-duplicate .receipt-link { opacity: 0.62; }
.receipt-row.is-duplicate .r-amount { text-decoration: line-through; }
.receipt-row.is-rereading .r-tile { opacity: 0.55; }

@media (max-width: 620px) {
  .dupe-actions { flex-direction: column; align-items: stretch; }
  .dupe-actions .btn { width: 100%; }
}

/* The camera card is for phone browsers. A label styled as a button is what
   lets one tap open the camera - a real <button> cannot trigger a file input
   on iOS without scripting the click, which Safari blocks outside a gesture. */
.browser-camera label.btn-scan { display: inline-flex; cursor: pointer; }
.section-note.ok { color: var(--good); }
.section-note.warn { color: var(--critical); }

/* ---- status tabs ---- */
/* Scrolls sideways on a narrow screen. Wrapping to a second row was what made
   the page jump down as it loaded. */
.tabs {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 2px 0 10px;
  margin-bottom: 6px;
}
.tabs::-webkit-scrollbar { display: none; }
.tab-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  min-height: 38px;
  padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text-secondary);
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}
.tab-chip:hover { text-decoration: none; border-color: var(--baseline); }
.tab-chip.current {
  background: var(--series-1);
  border-color: var(--series-1);
  color: #fff;
  font-weight: 600;
}

/* The original foreign amount, under the converted one. */
.r-original {
  display: block;
  font-size: 12px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* ---- platform, decided in the head before anything is drawn ----
   The page must be laid out correctly on the first paint. Anything toggled by
   script after load makes the content jump, which is what made the app feel
   unsteady. */
/* Anything that is not the Android app - a browser, or scripting disabled -
   gets the web layout. One rule, so there is no state where both or neither
   card is showing. */
html:not(.is-native) .native-only { display: none !important; }
html.is-native .web-only { display: none !important; }

/* Space held for things that appear later, so nothing below them moves.
   The file list and the progress card both grow into reserved room. */
.filelist:not([hidden]) { margin-top: 10px; }
#progress-card { scroll-margin-top: 16px; }

/* Images inside a receipt keep their box before the file loads. */

/* ---- nothing may push the page wider than the screen ----
   A single long token used to do exactly that: the Source row on a receipt
   holds a filename like 2026-01-06_THE_HOME_DEPOT_95016cf3.jpg, and a grid
   item will not shrink below its content unless told to. That one word propped
   the whole page open, so the phone showed it zoomed and you had to scroll
   sideways to reach the rest. */
.kv { min-width: 0; }
.kv dt, .kv dd { min-width: 0; overflow-wrap: anywhere; }

/* pre-wrap wraps at spaces but never inside a long run of characters, which
   raw OCR text is full of. */
.log { overflow-wrap: anywhere; word-break: break-word; }

/* The same guard everywhere it could happen again. */
.card, .receipt-row, .notice, .section-note { min-width: 0; }
.r-merchant, .r-meta, .dupe-other-merchant, .dupe-other-meta { overflow-wrap: anywhere; }

/* Last line of defence: the page itself never scrolls sideways. Applied to the
   scrolling element only, so position:sticky inside the page still works. */
html { overflow-x: hidden; }
body { overflow-x: clip; max-width: 100%; }

/* Wide things stay wide, but scroll inside their own box rather than dragging
   the page with them. */
.table-wrap, .tabs { max-width: 100%; }

@media (max-width: 620px) {
  /* A 260px minimum plus a button does not fit a 360px phone. */
  input[type="search"] { min-width: 0; }
  .filters .field { min-width: 0; }
}

/* ---- statement redaction ---- */
.redact-pages { display: grid; gap: 12px; margin: 12px 0; }
.redact-canvas {
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: #fff;
  cursor: crosshair;
  /* Without this the phone scrolls the page instead of drawing a box. */
  touch-action: none;
}
.redact-actions { display: flex; gap: 8px; margin-bottom: 8px; }

/* ---- statement lines ---- */
.stmt-list { list-style: none; margin: 12px 0 0; padding: 0; }
.stmt-line {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas: "main amount" "actions actions";
  gap: 6px 12px;
  padding: 12px 0;
  border-top: 1px solid var(--border);
}
.stmt-line:first-child { border-top: 0; }
.stmt-main { grid-area: main; min-width: 0; }
.stmt-desc { display: block; font-weight: 600; overflow-wrap: anywhere; }
.stmt-meta { display: block; font-size: 13px; color: var(--text-muted); overflow-wrap: anywhere; }
.stmt-amount {
  grid-area: amount;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.stmt-actions { grid-area: actions; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.stmt-actions form { display: flex; gap: 6px; margin: 0; align-items: center; }
.stmt-receipt { width: 110px; min-width: 0; }

@media (min-width: 760px) {
  .stmt-line {
    grid-template-columns: 1fr auto auto;
    grid-template-areas: "main amount actions";
    align-items: center;
  }
}

/* ---- duplicate comparison ---- */
/* Side by side wherever there is room, stacked on a phone. Both sides use the
   same macro so the fields always line up - comparing two receipts is the
   whole job, and mismatched rows make that harder than it needs to be. */
.dupe-compare {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
  margin: 4px 0 16px;
}
@media (min-width: 720px) {
  .dupe-compare { grid-template-columns: 1fr 1fr; }
}
.dupe-side {
  min-width: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  background: var(--plane);
}
.dupe-side-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
}
.dupe-open { font-size: 13px; white-space: nowrap; }
.dupe-image {
  display: block;
  width: 100%;
  height: auto;
  max-height: 340px;
  object-fit: contain;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: #fff;
}
.dupe-noimage {
  margin: 0 0 10px;
  padding: 22px 10px;
  text-align: center;
  font-size: 13px;
  color: var(--text-muted);
  border: 1px dashed var(--border);
  border-radius: 8px;
}
.dupe-kv { margin-top: 10px; font-size: 13.5px; }
.dupe-verdict { display: flex; flex-wrap: wrap; gap: 10px; }
.dupe-verdict form { margin: 0; }
@media (max-width: 620px) {
  .dupe-verdict { flex-direction: column; align-items: stretch; }
  .dupe-verdict .btn { width: 100%; }
}

/* =====================================================================
   Desktop shell: a sidebar instead of a crowded top strip.

   Eight links, a brand, a name and a sign-out button all sharing one 58px
   bar left nothing any room - which is what made the top of the page look
   cramped. Moving navigation into a column down the side gives the content
   the whole width and lets the links group themselves by what they are for.
   The phone keeps its bottom tab bar; this only applies from 900px up.
   ===================================================================== */

.sidebar { display: none; }

@media (min-width: 900px) {
  .topbar { display: none; }

  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 248px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 18px 12px;
    background: var(--surface);
    border-right: 1px solid var(--border);
    overflow-y: auto;
    z-index: 30;
  }

  .side-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 4px 8px 16px;
    color: var(--text-primary);
    font-weight: 650;
    letter-spacing: -0.01em;
    line-height: 1.25;
    text-decoration: none;
  }
  .side-brand:hover { text-decoration: none; }
  .side-brand img { border-radius: 50%; flex: 0 0 auto; }

  .side-nav { display: flex; flex-direction: column; gap: 18px; }
  .side-group { display: flex; flex-direction: column; gap: 2px; }

  .side-label {
    margin: 0 0 4px;
    padding: 0 10px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-muted);
  }

  .side-link {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 9px 10px;
    border-radius: 9px;
    color: var(--text-secondary);
    font-weight: 500;
    text-decoration: none;
    min-height: 40px;
  }
  .side-link:hover { background: var(--surface-2); color: var(--text-primary); text-decoration: none; }
  .side-link.current { background: var(--series-wash); color: var(--series-1); font-weight: 600; }
  .side-icon { display: flex; flex: 0 0 auto; }
  .side-icon svg { width: 20px; height: 20px; }
  .side-text { flex: 1 1 auto; min-width: 0; }

  .side-badge {
    flex: 0 0 auto;
    min-width: 20px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--series-1);
    color: #04150e;
    font-size: 11px;
    font-weight: 700;
    line-height: 19px;
    text-align: center;
  }

  /* Pinned to the bottom of the column, the way an account menu usually is. */
  .side-foot {
    margin-top: auto;
    padding-top: 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .side-account {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 8px;
    border-radius: 9px;
    color: var(--text-secondary);
    text-decoration: none;
    min-width: 0;
  }
  .side-account:hover { background: var(--surface-2); color: var(--text-primary); text-decoration: none; }
  .side-avatar {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--series-1);
    color: #04150e;
    font-weight: 700;
    font-size: 13px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .side-account-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13.5px;
  }

  /* The content sits beside the sidebar and gets the rest of the width. */
  .page.with-sidebar {
    max-width: none;
    margin: 0;
    padding: 30px 34px 64px calc(248px + 34px);
  }
}

@media (min-width: 1500px) {
  /* Very wide screens: stop the table stretching to an unreadable width. */
  .page.with-sidebar > * { max-width: 1180px; }
}

/* =====================================================================
   The receipt list as a real table.

   Previously each row was a free-form flex line: merchant and date bundled
   together on the left, amount and status stacked on the right. Nothing lined
   up down the page, so scanning twenty receipts meant reading each one. These
   are explicit columns on a wide screen - date, status, merchant, amount -
   and the same markup folds into a two-line card on a phone.
   ===================================================================== */

/* Title on the left, the action that matters on the right. */
.toolbar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}
.toolbar-title { min-width: 0; }
.toolbar-title h1 { margin-bottom: 2px; }
.toolbar-title .subtitle { margin: 0; font-size: 14px; }
.toolbar-actions { display: flex; gap: 10px; flex: 0 0 auto; }

.receipt-table {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.receipt-table .receipt-list {
  background: none;
  border: 0;
  border-radius: 0;
  overflow: visible;
}

.receipt-head { display: none; }

.receipt-link {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-areas:
    "tile merchant amount"
    "tile meta     status";
  align-items: center;
  column-gap: 12px;
  row-gap: 3px;
  padding: 12px 14px;
  min-height: var(--touch);
  color: inherit;
  text-decoration: none;
}
.receipt-link:hover { background: var(--surface-2); text-decoration: none; }

/* The tile stands in for a receipt thumbnail. A real one would be a separate
   authenticated request per row - about a second of server work each on this
   host - which is the load that used to time the site out. */
.r-tile {
  grid-area: tile;
  width: 38px;
  height: 38px;
  border-radius: 9px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: 700;
  font-size: 15px;
  flex: 0 0 auto;
  /* A merchant logo is fetched from the open web, so its real size is
     whatever that site happened to publish - a 128px favicon dropped in here
     spilled across the rows either side of it. Clipping means the worst a
     surprising image can do is look cropped, rather than break the page. */
  overflow: hidden;
}
.r-tile-wait {
  background: var(--surface-2);
  border: 1px dashed var(--baseline);
  position: relative;
}
.r-tile-wait::after {
  content: "";
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 2px solid var(--gridline);
  border-top-color: var(--series-1);
  animation: native-spin 0.9s linear infinite;
}

.r-merchant {
  grid-area: merchant;
  min-width: 0;
  font-weight: 600;
  font-size: 15px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.r-sub {
  display: block;
  font-weight: 400;
  font-size: 12.5px;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.r-date {
  grid-area: meta;
  font-size: 12.5px;
  color: var(--text-muted);
  white-space: nowrap;
}
.r-status { grid-area: status; justify-self: end; line-height: 1; }
.r-amount {
  grid-area: amount;
  justify-self: end;
  font-weight: 600;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-align: right;
}
.r-chev { display: none; }

.receipt-row + .receipt-row { border-top: 1px solid var(--gridline); }
.receipt-row.flagged .receipt-link { box-shadow: inset 3px 0 0 var(--critical); }
.receipt-row.is-queued .receipt-link { cursor: default; }
.receipt-row.is-queued:hover .receipt-link { background: none; }

@media (min-width: 900px) {
  /* Real columns, and the header that labels them. */
  .receipt-head,
  .receipt-link {
    display: grid;
    grid-template-columns: 38px 104px 148px minmax(0, 1fr) auto 16px;
    grid-template-areas: "tile date status merchant amount chev";
    align-items: center;
    column-gap: 16px;
    row-gap: 0;
  }
  .receipt-head {
    padding: 10px 14px;
    border-bottom: 1px solid var(--gridline);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
  }
  .rh-tile { grid-area: tile; }
  .rh-date { grid-area: date; }
  .rh-status { grid-area: status; }
  .rh-merchant { grid-area: merchant; }
  .rh-amount { grid-area: amount; text-align: right; }
  .rh-chev { grid-area: chev; }

  .r-date { grid-area: date; font-size: 13.5px; color: var(--text-secondary); }
  .r-status { grid-area: status; justify-self: start; }
  .r-merchant { grid-area: merchant; }
  .r-amount { grid-area: amount; font-size: 15px; }
  .r-chev {
    grid-area: chev;
    display: block;
    color: var(--text-muted);
    font-size: 19px;
    line-height: 1;
    text-align: right;
  }
  .receipt-link { padding: 11px 14px; }
}

/* The search row reads as one control, the way a toolbar search does. */
.searchbar-row { display: flex; gap: 8px; }
.searchbar-row input[type="search"] { flex: 1 1 auto; }

/* ---------------------------------------------------------------- notes
   Notes people leave on a receipt. Deliberately quieter than the receipt's
   own data: these are somebody's opinion about the receipt, not evidence
   from it, and the two should not read with the same weight. */
.comment-list { list-style: none; margin: 0 0 18px; padding: 0; }

.comment { padding: 10px 0; border-top: 1px solid var(--border); }
.comment:first-child { border-top: 0; }

.comment-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 0.86rem;
  margin-bottom: 4px;
}
.comment-head .muted { font-size: 0.82rem; }
.comment-remove { margin-left: auto; }

.comment-body {
  margin: 0;
  white-space: pre-wrap;   /* a note typed across several lines stays that way */
  overflow-wrap: anywhere; /* a pasted order number cannot widen the page */
}

/* A destructive action that should not look like a button competing with the
   real ones on the page. */
.linklike {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--text-muted);
  cursor: pointer;
  text-decoration: underline;
}
.linklike:hover { color: var(--text-primary); }

/* Visible to a screen reader, not on screen. For a label whose field is
   already obvious by position but which still needs naming. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.plain { list-style: none; margin: 0; padding: 0; }
.plain li { padding: 4px 0; }

/* --------------------------------------------------------- merchant logos
   The tile falls back to a letter, so the logo is layered over the same box
   rather than replacing it - a merchant whose logo fails to load still gets
   its initial rather than an empty square. */
.r-tile img,
.merchant-logo img {
  width: 100%;
  height: 100%;
  /* Belt and braces with the tile's overflow: these keep the image inside
     the box even where a percentage height cannot resolve. */
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: inherit;
  background: #fff;
  display: block;
}

/* ------------------------------------------------------------- assistant
   A transcript, not a messaging app. The two sides are told apart by weight
   and a rule rather than by opposing bubbles: these are questions and
   answers about accounts, and answers here run to tables. */
.chat { margin-bottom: 18px; }

.chat-turn { padding: 14px 0; border-top: 1px solid var(--border); }
.chat-turn:first-child { border-top: 0; padding-top: 0; }

.chat-who {
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-bottom: 6px;
  display: flex;
  gap: 8px;
  align-items: baseline;
}
.chat-who .muted { font-weight: 400; }

.chat-user .chat-body { color: var(--text-secondary); }
.chat-user .chat-body p { white-space: pre-wrap; margin: 0; }

.chat-body > :first-child { margin-top: 0; }
.chat-body > :last-child { margin-bottom: 0; }
.chat-body h2, .chat-body h3, .chat-body h4 { margin: 16px 0 8px; }
.chat-body table { width: 100%; }
.chat-body code {
  background: var(--surface-2);
  padding: 1px 5px;
  border-radius: 4px;
  font-size: 0.92em;
}

/* --------------------------------------------------------- the document
   The printable version. Deliberately plainer than the application around
   it: this is meant to leave the building. */
.print-actions { display: flex; gap: 10px; margin-bottom: 18px; }

.document {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 34px 38px;
  max-width: 820px;
}

.doc-head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-bottom: 18px;
  border-bottom: 2px solid var(--text-primary);
  margin-bottom: 26px;
}
.doc-logo { border-radius: 8px; }
.doc-brand {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}
.doc-meta { margin: 2px 0 0; font-size: 0.85rem; color: var(--text-muted); }

.doc-body table { width: 100%; margin: 14px 0; }
.doc-body h2, .doc-body h3 { margin: 22px 0 10px; }
.doc-body > :first-child { margin-top: 0; }

.doc-foot {
  margin-top: 32px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: 0.82rem;
  color: var(--text-muted);
}
.doc-foot p { margin: 0; }

@media print {
  /* Everything that is the application rather than the document. */
  .sidebar, .tabbar, .topbar, .no-print, .flash, .print-actions { display: none !important; }
  body, .document { background: #fff; color: #000; }
  .document { border: 0; padding: 0; max-width: none; }
  .doc-head { border-bottom-color: #000; }
  a { text-decoration: none; color: #000; }
}

/* A merchant group whose spellings have not been reconciled yet. */
tr.flagged td { box-shadow: inset 3px 0 0 var(--critical); }

/* =====================================================================
   Reviewing a receipt
   =====================================================================
   The scan on the left, what was read off it on the right. Checking a
   total means reading one and typing the other, and on the old layout
   those were hundreds of pixels apart with nine cards in between - which
   is how a wrong number gets confirmed without anybody noticing. */

.review { display: grid; gap: 18px; align-items: start; }

@media (min-width: 1100px) {
  .review {
    /* The scan gets the larger share: it is the thing being read. */
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  }
  /* Held in view while the data column scrolls past it, so the paper is
     still on screen when you reach the field you are typing into. */
  .review-scan { position: sticky; top: 16px; }

  /* Items and Details normally sit side by side. Inside the narrower
     review column there is no room for two, so they stack. */
  .review-data .detail-grid { grid-template-columns: minmax(0, 1fr); }
}

.review-scan, .review-data { min-width: 0; }

/* ------------------------------------------------------------ the viewer */

.viewer-card { padding-bottom: 14px; }

.viewer-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
  margin-bottom: 12px;
}
.viewer-head h2 { margin: 0; margin-right: auto; }

.viewer-tools { display: flex; flex-wrap: wrap; gap: 8px; }

.viewer-group {
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--surface-2);
  border-radius: 8px;
  padding: 2px;
}
.viewer-group .btn { border: 0; background: transparent; }
.viewer-group .btn:hover:not(:disabled) { background: var(--plane); }
.viewer-group .btn:disabled { opacity: 0.35; cursor: default; }

.viewer-count {
  font-size: 0.82rem;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;   /* the counter must not jitter */
  min-width: 3.6em;
  text-align: center;
}

.viewer { position: relative; }

.viewer-stage {
  position: relative;
  height: min(70vh, 760px);
  overflow: auto;
  background: var(--plane);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  /* Centre a page narrower than the stage; keep it at the top-left once it
     is larger, which is where reading starts. */
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 10px;
  overscroll-behavior: contain;   /* panning must not scroll the page behind */
  touch-action: pan-x pan-y;      /* leaves two-finger pinch to the script */
}
.viewer-stage:focus-visible { outline: 2px solid var(--series-1); outline-offset: 2px; }

.viewer-stage canvas,
.viewer-stage img {
  display: block;
  max-width: none;   /* deliberately: zoomed past the stage is the point */
  background: #fff;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.18);
  border-radius: 2px;
}

.viewer.is-zoomed .viewer-stage { cursor: grab; }
.viewer.is-panning .viewer-stage { cursor: grabbing; user-select: none; }

.viewer-status {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  text-align: center;
  margin: 0;
  pointer-events: none;
}
.viewer.is-broken .viewer-status { pointer-events: auto; }

.viewer-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 12px 0 0;
}
.viewer-foot .section-note { margin: 0; }

/* --------------------------------------------------------------- pages */

.viewer-thumbs {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 10px 2px 2px;
  scrollbar-width: thin;
}

.thumb {
  flex: 0 0 auto;
  border: 2px solid transparent;
  border-radius: 6px;
  background: var(--surface-2);
  padding: 3px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  font-size: 0.72rem;
  color: var(--text-muted);
  line-height: 1;
}
.thumb canvas {
  display: block;
  width: 48px;
  height: auto;
  background: #fff;
  border-radius: 2px;
}
.thumb:hover { border-color: var(--baseline); }
.thumb.current { border-color: var(--series-1); color: var(--text-primary); }

/* ---------------------------------------------------------- full screen */

.viewer.is-full {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: var(--plane);
  /* inset:0 means the whole screen, status bar and home bar included, so the
     padding has to put the page back inside the part you can actually see. */
  padding: calc(14px + var(--safe-top)) calc(14px + var(--safe-right))
           calc(14px + var(--safe-bottom)) calc(14px + var(--safe-left));
  display: flex;
  flex-direction: column;
}
.viewer.is-full .viewer-stage {
  height: auto;
  flex: 1 1 auto;
  border-radius: 8px;
}
body.viewer-open { overflow: hidden; }

/* The toolbar belongs to the card, which is behind the overlay - so lift it
   above, otherwise there is no way to zoom or leave without the keyboard. */
body.viewer-open .viewer-card {
  position: relative;
  z-index: 301;
}
body.viewer-open .viewer-card .viewer-head {
  position: fixed;
  top: calc(14px + var(--safe-top));
  left: calc(14px + var(--safe-left));
  right: calc(14px + var(--safe-right));
  z-index: 302;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 12px;
  margin: 0;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.22);
}
/* Room for the floating toolbar, which is itself pushed down by the inset. */
body.viewer-open .viewer.is-full { padding-top: calc(68px + var(--safe-top)); }

@media (max-width: 640px) {
  .viewer-stage { height: 58vh; }
  .viewer-head h2 { width: 100%; margin-right: 0; }
  .viewer-tools { width: 100%; }
  .viewer-group { flex: 1 1 auto; justify-content: center; }
}

@media print {
  .viewer-tools, .viewer-thumbs, .viewer-foot { display: none; }
  .viewer-stage { height: auto; overflow: visible; border: 0; }
}

/* ---- people & roles ---- */

.side-account-role { display: block; font-size: 11px; color: var(--text-muted); font-weight: 400; }

.people { display: grid; gap: 12px; margin-bottom: 16px; }
@media (min-width: 900px) { .people { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.person { margin: 0; }
.person.is-off { opacity: 0.65; }
.person-head { display: flex; align-items: center; gap: 12px; }
.person-who { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.person-who strong, .person-who span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.person-who span { font-size: 13px; }
.person-meta { margin: 8px 0 0; font-size: 13px; }
.pill.strong { background: var(--series-wash); color: var(--series-1); }
.person-manage { margin-top: 12px; border-top: 1px solid var(--gridline); padding-top: 8px; }
.person-manage summary { cursor: pointer; padding: 8px 0; font-weight: 500; min-height: 44px; display: flex; align-items: center; }
.person-row { display: grid; grid-template-columns: 1fr; gap: 6px; margin: 10px 0; }
.person-row label { font-size: 12px; color: var(--text-muted); }
@media (min-width: 620px) {
  .person-row { grid-template-columns: 110px 1fr auto; align-items: center; }
}

.stack-form { display: grid; gap: 14px; max-width: 560px; }
.role-choice { border: 0; margin: 0; padding: 0; display: grid; gap: 8px; }
.role-choice legend { font-size: 12px; color: var(--text-muted); font-weight: 500; margin-bottom: 6px; }
.role-option { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px;
  border: 1px solid var(--gridline); border-radius: var(--radius); cursor: pointer; }
.role-option:has(input:checked) { border-color: var(--series-1); background: var(--series-wash); }
.role-option input { margin-top: 3px; }
.role-option > span { display: flex; flex-direction: column; gap: 2px; }
.role-option .muted { font-size: 13px; }

@media (max-width: 620px) {
  input[type="search"] { min-width: 0; width: 100%; }
}
.avatar { flex: 0 0 auto; width: 36px; height: 36px; border-radius: 50%;
  background: var(--series-1); color: #04150e; font-weight: 700; font-size: 15px;
  display: flex; align-items: center; justify-content: center; }

/* ---- company branding ---- */
.brand-preview { display: flex; gap: 14px; align-items: flex-start; }
.brand-preview img { width: 56px; height: 56px; border-radius: 12px; object-fit: contain;
  background: var(--surface); border: 1px solid var(--gridline); flex: 0 0 auto; }
.brand-preview .field { flex: 1; min-width: 0; }
.brand-preview .hint { font-size: 12px; color: var(--text-muted); }
.brand-logo, .side-brand img, .signin-logo { object-fit: contain; border-radius: 8px; }
.stack-form > *, .brand-preview .field { min-width: 0; }
.stack-form input, .stack-form select { width: 100%; max-width: 100%; box-sizing: border-box; }
input[type="file"] { max-width: 100%; font-size: 14px; }
/* A grid column defaults to min-width:auto, so a wide items table inside it
   stretched the whole receipt card past the edge of a phone. */
.detail-grid > *, .review > *, .grid > * { min-width: 0; }

/* ---- location tabs ---- */
.location-tabs { display: flex; gap: 6px; margin: 0 0 16px; padding: 4px;
  background: var(--surface); border: 1px solid var(--gridline); border-radius: 999px;
  overflow-x: auto; scrollbar-width: none; }
.location-tabs::-webkit-scrollbar { display: none; }
.location-tabs form { margin: 0; flex: 1 0 auto; }
.location-tab { width: 100%; min-height: 40px; padding: 8px 16px; border: 0; border-radius: 999px;
  background: transparent; color: var(--text-secondary); font: inherit; font-weight: 600;
  font-size: 14px; white-space: nowrap; cursor: pointer; }
.location-tab.current { background: var(--series-1); color: #04150e; }
.location-tab:not(.current):hover { color: var(--text-primary, inherit); background: var(--series-wash); }
