/* One stylesheet for the whole site.

   Mobile first, because the person who uses this most is standing next to a
   truck holding a receipt. The dashboard and admin pages are desktop first in
   layout but never scroll sideways on a phone: wide tables live inside
   .scroll, which is the only element allowed to overflow horizontally.

   Light and dark come from the same variables. Nothing below hard-codes a
   colour outside :root, and the six page stylesheets hard-code none at all --
   so the palette is the two blocks below and nowhere else.

   --- the palette, and why it is the colour it is ---------------------------

   ReceiptSonar is mildly nautical: deep water, slate, a cooler grey. The word
   is *mildly*. There is no rope, no anchor, no compass rose, no wheel and no
   porthole anywhere in this file, and no maritime word in any sentence the
   interface says -- nobody wants their expense report to greet them. What the
   theme is allowed to be is a set of hues: the greys are cooled towards blue,
   the accent is a deep harbour blue rather than the brighter one it was, and
   the warning colour is a brass rather than a plain amber. That is the whole
   of it.

   Sonar is the half of the metaphor worth leaning on, because it is already
   what the product does: it finds one thing in a noisy picture. So the accent
   is the colour of the thing found -- links, the primary button, the focus
   ring, the crop outline, the progress fill -- and everything around it got
   quieter than it used to be.

   Four rules held the palette in place while it was being chosen, and all
   four are worth restating, because each is easier to break than to notice:

   1. **The receipt itself is never tinted.** `--paper` and `--paper-line`
      exist for exactly that and are documented on their own lines below.
   2. **Green, amber and red keep meaning what people read them as.** `--ok`,
      `--warn` and `--bad` moved towards the palette -- deeper, and in the
      green's case a shade cooler -- and not across it. "Over limit" is not
      blue.
   3. **Every text-on-background pair was computed, not eyeballed.** The pairs
      that decided the palette are `--muted` on `--panel-2` (5.29:1 light,
      6.36:1 dark) and `--muted` on `--bg` (5.66:1 and 7.59:1), which is the
      pair a blue shift usually breaks: cooling a grey lowers its luminance,
      and the ground it stands on was cooled by the same amount.
   4. **Light and dark are two designs, not one design and its inverse.**
      Light is paper under a north window: a white card on a pale blue-grey
      field. Dark is the water itself: a near-black navy field with cards
      lifted off it far enough to read as objects. An inverted scheme looks
      like a bug, so neither was derived from the other.

   PLACEHOLDER: the single signal-flag accent. The brief invited "perhaps a
   single restrained brass or signal-flag accent for one state" and did not say
   which state earns it. Nothing here ships one, on purpose: the obvious brass
   sits within five degrees of hue of `--warn`, so a variable holding it would
   be a second amber that means something else -- the exact confusion the three
   status colours exist to prevent. The brass note is therefore inside `--warn`
   itself, where amber already lives. If a distinct flag colour is wanted, the
   decision needed is *which* state carries it -- the reviewer's keyboard
   cursor in `dashboard.css` and the "exported" badge are the two that read as
   blue-on-blue now -- and the colour then has to clear 3:1 against `--panel`,
   `--panel-2` and `--bg` in both schemes. */

/* Daylight. A white card standing on a pale blue-grey field, and the card is
   white on purpose: it is where the numbers are read, and a tint under a
   column of money buys nothing. The theme is carried by everything around
   it. */
:root {
  /* The field the cards stand on. Cooled from the old #f4f6f8, which was a
     grey with a hint of blue in it, to a pale blue-grey that is unmistakably
     the latter without becoming a colour anybody would name. */
  --bg: #e9eef4;
  --panel: #ffffff;
  /* The secondary surface: chips, badges, code, the trough of a progress bar,
     the hover state of a table row. Deeper than it was and plainly blue, so a
     chip still reads as a chip against a white card now that the card is the
     only white thing on the page. */
  --panel-2: #dfe7f0;
  /* The decorative hairline: the rule under a table row, the edge of a card,
     the border of a badge. Deliberately faint -- 1.57:1 on `--panel` --
     because a table of forty receipts drawn with rules at 3:1 is a page of
     grid and not a page of receipts. Anything a person has to find the edge
     of in order to use it takes `--edge` instead.

     `--line` is what almost every border in the six page stylesheets asks
     for, and those are separators and groupings. They were left as they
     are. */
  --line: #c3d0de;
  /* The interactive border, and the reason there are two line colours now.

     This is the one variable that is new rather than recoloured, and it is
     here because of the measurement rather than because of the theme. WCAG
     1.4.11 wants the boundary of a control -- a text box, a select, a button,
     the trough of a bar -- at 3:1 against what is behind it, and the old
     single `--line` was at 1.35:1 against every ground on the site: each text
     box was a box you found by guessing where it ended. Making `--line`
     itself reach 3:1 would have fixed the text boxes by turning every table
     into graph paper, so the two jobs got two variables.

     5.18:1 on `--panel`, 4.44:1 on `--bg`, 4.15:1 on `--panel-2`. A slate
     rather than a neutral grey, so the controls carry the theme rather than
     sitting outside it. */
  --edge: #5b6f85;
  /* Near-black with a navy cast rather than a warm one. 17.26:1 on a card,
     which is far more headroom than the 4.5:1 body-text rule asks for and is
     spent deliberately: this is the colour a total is printed in. */
  --ink: #0e1c29;
  /* The quiet text: hints, table headings, the footer, timestamps. The pair a
     blue shift usually breaks, so it was chosen last and against the worst of
     its three grounds -- 5.29:1 on `--panel-2`, 5.66:1 on `--bg`, 6.61:1 on
     `--panel`. */
  --muted: #4b5f72;
  /* Green, amber, red. Deepened to sit with the rest of the palette, not
     shifted across the wheel: a person reads these three before they read the
     word beside them, and a green that has gone teal or an amber that has
     gone brass-red is a status light that lies. The amber is where the brass
     note of the theme lives -- see the placeholder at the top of this file for
     why there is no separate variable holding one. */
  --ok: #10714a;
  --ok-bg: #dff0e7;
  --warn: #86590a;
  --warn-bg: #f7ecd6;
  --bad: #a52f2c;
  --bad-bg: #f8e4e2;
  /* Harbour blue: deeper and less electric than the #1b6fc4 it replaces, so a
     page of links reads as text and the primary button reads as the one thing
     to press. 7.07:1 as link text on a card, and the same 7.07:1 the other way
     round for the white label on the button. */
  --accent: #0a5a9e;
  --accent-ink: #ffffff;
  /* --- the receipt's own ground, and the rule that outranks the theme -------

     A photograph of a receipt is what somebody is squinting at to check a
     total. A blue panel behind it, or a blue frame around it, shifts the
     apparent colour of the paper and makes faint thermal print harder to
     read, so every surface that holds a receipt image uses these two and
     never `--panel-2` or `--line`. The seven rules that do are named in
     `receipts.css`, `scan.css` and `dashboard.css`.

     In this scheme `--paper` happens to equal `--panel`. That is a
     coincidence of both wanting to be white, not a reason to drop the
     variable: in dark they are necessarily different, and the day somebody
     tints the cards is the day this rule has to hold anyway. `--paper-line` is
     a neutral grey rather than the slate `--line` for the same reason -- a
     coloured frame is the oldest way there is to make a white thing look
     tinted. */
  --paper: #ffffff;
  --paper-line: #d5d6d8;
  /* The card shadow, tinted with the same navy as the ink rather than with a
     neutral black, so a card looks lifted off water rather than off paper. */
  --shadow: 0 1px 2px rgba(10, 26, 43, .07), 0 4px 14px rgba(10, 26, 43, .05);
  --radius: 12px;
}

/* Night, and not the light scheme inverted. The same room after dark: a
   near-black navy field, cards lifted off it by being lighter rather than by a
   shadow -- there is no shadow at all here, because a shadow on a dark ground
   is a smudge -- and the accent brightened to a ping so it is still the thing
   the eye lands on first. */
@media (prefers-color-scheme: dark) {
  :root {
    /* Deep water. Darker than the old #0f1216 and now plainly blue, which is
       most of what makes this scheme read as the same product as the light
       one. */
    --bg: #0a121a;
    --panel: #101b26;
    --panel-2: #17242f;
    --line: #2b3b4a;
    /* The same job as in light and the same 3:1 floor: 4.26:1 on `--panel`,
       4.61:1 on `--bg`, 3.87:1 on `--panel-2`. Lighter than its ground here
       rather than darker, because on a dark field the edge of a control has
       to be the bright thing. */
    --edge: #6a8096;
    --ink: #dce7f1;
    --muted: #92a7ba;
    /* The same three meanings, lightened for a dark ground rather than
       re-chosen. Each tinted background is its own hue at very low lightness,
       so a flash message is a coloured band and not a grey one. */
    --ok: #3fc78a;
    --ok-bg: #0c2620;
    --warn: #dda94a;
    --warn-bg: #2a2113;
    --bad: #ec7570;
    --bad-bg: #2c1a19;
    /* The ping. Bright enough to be the one saturated thing on a dark page
       (7.08:1 on a card) and dark enough underneath that `--accent-ink`, the
       label on the button, still clears 4.5:1 the other way at 7.69:1. */
    --accent: #5cabf0;
    --accent-ink: #04121e;
    /* Neutral, and neutral on purpose: red, green and blue within five of
       each other, so nothing tints the photograph. Not white -- a white
       light-box on a dark page is a torch in the face at night -- and not the
       navy of the card either, which is the mistake this variable exists to
       prevent. A receipt in the dark scheme is a bright object on a dark grey
       mount, which is how a photograph is looked at. */
    --paper: #1f2124;
    --paper-line: #34363b;
    --shadow: none;
  }
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.55 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
}

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

h1, h2, h3, h4 { line-height: 1.2; margin: 0 0 12px; }
h1 { font-size: clamp(24px, 4.5vw, 34px); }
h2 { font-size: 20px; }
h3 { font-size: 17px; }
p { margin: 0 0 14px; }

code, pre, .mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .9em;
}

pre {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px 14px;
  overflow-x: auto;
}

/* --- icons ---------------------------------------------------------------

   The drawings are in `web/templates/_icons.html`, one macro, one copy each;
   this is only how big they are and where they sit. Two properties carry it,
   and both are about not having to think about them at a call site.

   **The size is in `em` rather than pixels.** An icon is almost always next to
   words, and the words on this site are 15 px in a button, 14 px in a small
   one and 13 px in a hint. A pixel size would be right in one of those and
   wrong in the other two, and the way that goes wrong is a 20 px icon sitting
   beside 13 px text looking like a mistake. 1.15em is a shade taller than the
   capital height of the type beside it, which is what makes the two look the
   same weight.

   **`flex: none` is load-bearing.** Every container an icon goes into here is
   a flex row -- buttons, the navigation, the message bands -- and a flex item
   with an intrinsic size is a flex item that gets squashed when the row runs
   out of room. Without this, a long merchant name in a narrow column turns
   the icon beside it into an oval.

   Colour is not set here at all. The macro draws with `currentColor`, so an
   icon is whatever colour the text around it is. The two places that want
   something else -- the brand mark, and the icon in a message band -- set
   `color` on the icon itself further down and let that inheritance do the
   work.

   **There is no size modifier here and that is deliberate.** A `.icon-lg`
   nothing used would be a rule in the shared stylesheet waiting for somebody
   to need it, which is how this stylesheet ends up with blocks nobody ever
   filled. A page that wants a bigger one -- the icon over an empty state is
   the case -- sizes it from its own stylesheet, where the rest of that
   screen's layout already is, or simply puts it somewhere with a larger
   `font-size`, because both dimensions above are in `em`. `.brand-mark` below
   is the one exception the shell itself needs. */
.icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  /* For the few icons that sit in running text rather than in a flex row.
     Ignored in a flex container, which is where most of them are. */
  vertical-align: -.18em;
}

/* --- hidden means hidden -------------------------------------------------- */

/* `hidden` is the attribute this interface hides things with, and eleven
   elements across the two phone screens shipped permanently visible for want
   of the rule below.

   The browser hides `[hidden]` from its own user-agent stylesheet, and an
   author rule that sets `display` beats a user-agent one however much less
   specific it is. So `.sc-queue { display: flex }` un-hid the upload badge,
   `.sc-error` and `.notice` each un-hid the error region -- twice over --
   `.sc-progress` un-hid "Straightening the photograph and reading it", and
   `.button` un-hid its Try again. What somebody saw on a screen they had not
   yet photographed anything with was a dead button, an empty red alert, and a
   progress bar for work that was not happening.

   `!important` is the point of this rule rather than a shortcut around it: it
   exists to win against author `display` declarations and has no job if it
   does not. The alternative -- taking `display` off five classes and revealing
   steps with a class instead -- argues with `scan.css`'s own design note,
   which keeps `hidden` an attribute rather than a class because that takes an
   element out of the accessibility tree as well as out of the layout, and
   because `app.js` should reveal a step by removing an attribute rather than
   by agreeing with a stylesheet about the name of a class. That design is
   right. It needed the one rule that makes the platform keep its promise. */

[hidden] {
  display: none !important;
}

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

header.site,
footer.site {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  padding: 12px 20px;
  padding-left: max(20px, env(safe-area-inset-left));
  padding-right: max(20px, env(safe-area-inset-right));
}

header.site { position: sticky; top: 0; z-index: 20; }

footer.site {
  border-bottom: 0;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 14px;
  padding-bottom: max(12px, env(safe-area-inset-bottom));
}

footer.site a { color: var(--muted); }

.brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-weight: 700;
  font-size: 18px;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

/* The mark, and the one place on the page where the accent is used on
   something that is not a control.

   It is sized in pixels rather than in `em` -- the one icon on the site that
   is -- because it is not labelling the words beside it, it is standing next
   to them: the name is 18 px and the mark is 26, which is the proportion a
   logotype and its mark usually sit in. `color` rather than `fill`, because
   the macro draws in `currentColor`; and `--accent`, so the mark is the ping
   in both schemes without this rule knowing there are two. */
.brand-mark {
  width: 26px;
  height: 26px;
  color: var(--accent);
}

.grow { flex: 1 1 auto; }

/* The two gaps are the grouping, and the difference between them is the whole
   mechanism: 4 px between the entries of one group, 14 px between one group
   and the next. `gap: 4px 14px` is row-gap then column-gap, so a bar that
   wraps onto three rows keeps its rows tight and its groups apart. */
header.site nav {
  display: flex;
  align-items: center;
  gap: 4px 14px;
  flex-wrap: wrap;
}

/* One kind of entry, drawn as a row that wraps inside itself.

   A group wraps rather than being held on one line, because at 375 px the
   Manage group of an administrator is within a few pixels of the width and
   `nowrap` would push it off the side -- and this file's one rule about
   phones is that nothing but `.scroll` overflows horizontally. When a group
   does wrap, its label stays at the head of it and the rest of the group
   follows underneath, still inside the group's own box. */
.navgroup {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
}

/* The kind, said in the smallest type on the page.

   Not a heading element: these label five groups of a navigation, and five
   `<h*>` in the header would rewrite the document outline of every page in
   the application. The grouping is announced instead through `role="group"`
   and `aria-labelledby` pointing at this span, which `base.html` explains.

   `--muted` rather than `--ink`, because a label the same weight as the
   entries under it competes with them: it is read once, on the way to the
   thing being looked for. It is 5.29:1 on `--panel-2` and 6.61:1 on
   `--panel` in light, 6.36:1 and better in dark -- the pair `app.css` chose
   the palette against -- so this stays legible in both schemes at 11 px,
   which is the size that made the measurement worth keeping. */
.navgroup-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  padding: 0 2px;
}

header.site nav a,
header.site nav button {
  display: inline-flex;
  align-items: center;
  /* The gap an icon sits in when an entry has one. Sign out is the only entry
     that does today; the gap costs nothing on the others, because a flex row
     with one child has no gap to apply. */
  gap: 7px;
  min-height: 40px;
  padding: 6px 10px;
  border-radius: 8px;
  text-decoration: none;
  color: var(--ink);
  font-size: 15px;
}

header.site nav a:hover { background: var(--panel-2); }

main {
  flex: 1 0 auto;
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  padding: 22px 20px 40px;
  padding-left: max(20px, env(safe-area-inset-left));
  padding-right: max(20px, env(safe-area-inset-right));
}

main.wide { max-width: 1320px; }
main.narrow { max-width: 460px; }
main.prose { max-width: 720px; }

/* --- flash and inline messages ------------------------------------------- */

.flash,
.notice {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 11px 14px;
  margin: 0 0 18px;
  font-size: 15px;
}

/* A band that has an icon becomes a row, so that the second line of a wrapped
   message lines up under the first rather than under the icon.

   **Only a band that has one**, and that is what `:has()` is doing here rather
   than a modifier class somebody has to remember. Eight of the twelve message
   bands in this tree are in other templates and have no icon, and two of those
   hold a second element -- `app/scan.html` a retry button, `exports/status.html`
   the provider's own error string -- which a flex row would stand beside the
   sentence in a column of its own instead of letting it flow. Turning every
   band into a row to style three of them is how a shared component gets broken
   from a distance.

   A browser too old for `:has()` drops this rule and gets the fallback the
   `.icon` rule above is written for: an inline icon on the first line, which
   is imperfect on a wrap and perfectly readable. Nothing about the band's
   meaning depends on it.

   The nudge is the difference between the icon's 1.15em box and the 1.55em
   line box it sits in, so it centres on the first line. `align-items: center`
   would centre it against the whole message and float it halfway down a
   four-line one. */
.flash:has(> .icon),
.notice:has(> .icon) {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.flash > .icon,
.notice > .icon { margin-top: 2px; }

.flash-ok, .notice-ok { background: var(--ok-bg); border-color: var(--ok); color: var(--ink); }
.flash-warn, .notice-warn { background: var(--warn-bg); border-color: var(--warn); color: var(--ink); }
.flash-error, .notice-error { background: var(--bad-bg); border-color: var(--bad); color: var(--ink); }

/* The band's text is `--ink` -- a sentence somebody has to read is not
   coloured -- and its icon is the status colour, which is the same division of
   labour `.badge-ok` and its two neighbours already use, against the same
   backgrounds. The pairs are therefore already known to hold in both schemes:
   `--ok` on `--ok-bg`, `--warn` on `--warn-bg`, `--bad` on `--bad-bg`. */
.flash-ok > .icon, .notice-ok > .icon { color: var(--ok); }
.flash-warn > .icon, .notice-warn > .icon { color: var(--warn); }
.flash-error > .icon, .notice-error > .icon { color: var(--bad); }

.error-text {
  color: var(--bad);
  font-size: 15px;
  margin: 0 0 14px;
}

/* --- cards and sections --------------------------------------------------- */

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px;
  margin: 0 0 18px;
  box-shadow: var(--shadow);
}

.card > :last-child { margin-bottom: 0; }

.card h2 { font-size: 16px; }

.card-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.card-head h2 { margin: 0; }

.muted { color: var(--muted); }
.small { font-size: 14px; }
.tiny { font-size: 13px; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.stack > * + * { margin-top: 14px; }

.grid {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}

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

form { margin: 0; }

.field { margin: 0 0 14px; }

label {
  display: block;
  font-size: 14px;
  font-weight: 600;
  margin: 0 0 5px;
}

label.inline {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-weight: 500;
  font-size: 15px;
  min-height: 44px;
  padding: 8px 0;
  cursor: pointer;
}

label.inline input[type="checkbox"],
label.inline input[type="radio"] {
  width: 20px;
  height: 20px;
  margin: 2px 0 0;
  flex: 0 0 auto;
}

input, select, textarea, button { font: inherit; }

/* 16px minimum stops Safari zooming the page when a field takes focus. */
input[type="text"],
input[type="email"],
input[type="tel"],
input[type="password"],
input[type="number"],
input[type="search"],
select,
textarea {
  width: 100%;
  font-size: 16px;
  min-height: 44px;
  padding: 10px 12px;
  color: var(--ink);
  background: var(--panel);
  /* `--edge`, not `--line`: a control has to show where it ends. See the
     comment on the variable. The four page stylesheets that repeat this rule
     for a `date` or `month` input, which app.css does not name, were changed
     with it, so a filter row is not two boxes in two different greys. */
  border: 1px solid var(--edge);
  border-radius: 9px;
}

textarea { min-height: 220px; resize: vertical; line-height: 1.5; }

input:focus-visible,
select:focus-visible,
textarea:focus-visible,
button:focus-visible,
a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.hint { font-size: 13px; color: var(--muted); margin: 5px 0 0; }

.row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: flex-end;
}

.row > .field { flex: 1 1 180px; margin-bottom: 0; }

.actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 16px;
}

.actions.tight { margin-top: 0; }

/* --- buttons -------------------------------------------------------------- */

.button,
button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* The space between an icon and the word it belongs to, on every button on
     the site. A button with no icon has one child and no gap to apply, so this
     line is free where it is not used. */
  gap: 8px;
  min-height: 44px;
  padding: 11px 17px;
  border-radius: 10px;
  /* `--edge` for the same reason as the inputs above: a secondary button is a
     panel-coloured rectangle and its border is the only thing that says it is
     a button at all. `.primary` and `.danger` override this with their own
     colour two rules down and are unaffected. */
  border: 1px solid var(--edge);
  background: var(--panel);
  color: var(--ink);
  font-weight: 600;
  font-size: 15px;
  text-decoration: none;
  cursor: pointer;
}

.button:hover, button:hover { background: var(--panel-2); }

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

.button.primary:hover, button.primary:hover { filter: brightness(1.07); }

.button.danger, button.danger { color: var(--bad); border-color: var(--bad); }

.button.link, button.link {
  background: none;
  border: 0;
  color: var(--accent);
  padding: 6px 4px;
  min-height: 40px;
  font-weight: 500;
}

.button.small, button.small {
  min-height: 38px;
  padding: 7px 12px;
  font-size: 14px;
}

.button.block, button.block { width: 100%; }

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

.scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin: 0 0 6px;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
  min-width: 520px;
}

table.auto { min-width: 0; }

th, td {
  text-align: left;
  padding: 9px 12px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

th {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
  font-weight: 650;
  white-space: nowrap;
}

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

td.right, th.right { text-align: right; }

/* --- badges and chips ----------------------------------------------------- */

.badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
}

.badge-ok { background: var(--ok-bg); border-color: var(--ok); color: var(--ok); }
.badge-warn { background: var(--warn-bg); border-color: var(--warn); color: var(--warn); }
.badge-bad { background: var(--bad-bg); border-color: var(--bad); color: var(--bad); }

.chips { display: flex; flex-wrap: wrap; gap: 7px; margin: 0; padding: 0; list-style: none; }

.pill-list { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; padding: 0; list-style: none; }

.pill-list li {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  border-radius: 999px;
  padding: 4px 6px 4px 14px;
  font-size: 14.5px;
}

.pill-list li.archived { opacity: .55; }

/* --- the draft state ------------------------------------------------------

   Being a draft is a fact about a receipt, not a sentence at the bottom of the
   card that files it. `web/templates/_draft.html` draws it and argues the
   whole design there; this is where it stands and what colour it is.

   **Here rather than in `receipts.css` or `scan.css`.** A draft is met on the
   receipt page, on the scan screen, and -- the day somebody adds it -- in the
   list and on the desk, and those are four stylesheets. One component in the
   shared file is what makes "the same thing looks the same everywhere" a
   property of the code rather than of whoever copied it last.

   **Amber, because that is what `--warn` means here.** Nothing is wrong,
   something has not happened, and it has a consequence with a date on it.
   Green would say the receipt is done and red would say it is broken, and it
   is neither -- until the day it runs out, which is the one case below that
   really is red.

   **It does look like a warn band, and that was chosen rather than settled
   for.** The first draft of this rule gave the strip a tint and no border, so
   that a state could not be mistaken for a message. Measured, that is a strip
   with no edge: `--warn-bg` and `--bg` come out at 1.00:1 in the light scheme
   -- identical luminance, different hue -- so on the receipt page, which is
   where this actually renders, the block dissolved into the field for anybody
   who does not separate cream from pale blue. A boundary that is only there
   for some readers is worse than a familiar shape. So it takes the border,
   and what tells it apart from the bands is everything else: the square
   leading edge under a 4 px bar, the state's name set as a label rather than
   as the first word of a sentence, smaller type, and the fact that it says
   what the receipt *is* rather than what just happened to it. The two never
   appear together in any case -- a receipt is a draft or it is approved and
   locked, never both.

   The text is `--ink` throughout, for the reason the message bands give at
   greater length: a sentence a person has to read is not coloured. The three
   parts are told apart by weight and size, and the colour is spent on the two
   marks -- the bar and the icon -- and on the countdown once it has run out. */

.draft-state {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* Two gaps: the row's, and a smaller one for the lines it breaks into on a
     phone, where "Only you can see it" lands under the countdown. */
  gap: 3px 10px;
  margin: 0 0 18px;
  padding: 9px 14px;
  border: 1px solid var(--warn);
  border-left-width: 4px;
  /* Square where the bar is, rounded everywhere else, so the bar reads as an
     edge of the strip rather than as a rule standing beside it. */
  border-radius: 0 10px 10px 0;
  background: var(--warn-bg);
  color: var(--ink);
  font-size: 14.5px;
}

.draft-state > .icon { color: var(--warn); }

/* The state's name, set the way this stylesheet already sets the name of a
   thing rather than the thing itself -- the same treatment as a table heading
   and a figure's caption. It is what stops "Draft" being read as the first
   word of the sentence after it. */
.draft-state-lead {
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
}

/* The one part of this that changes while nobody is looking, so it carries the
   weight. It takes no colour of its own: inside the strip it is `--ink` like
   everything else there, and on a row of a list of drafts it is the muted grey
   of the line it sits in. That is deliberate -- twenty rows of amber is twenty
   rows of nothing -- and it is why this is a class of its own rather than a
   child of `.draft-state`, since on those rows it has no strip around it. */
.draft-life { font-weight: 700; }

.draft-state-note { font-size: 13.5px; }

/* Past the day the nightly sweep acts on. Red, because at this point the thing
   is not waiting for the person any more: it goes on the next run unless they
   file it now. On a list row this is the only red on the screen, which is the
   whole point of the countdown being there.

   `:has()` rather than a second modifier the macro has to remember, so that
   `_draft.html` works the deadline out in exactly one place and the strip
   follows what the words inside it say. A browser too old for `:has()` drops
   the two rules below and keeps the amber, around a countdown that reads "Due
   to be deleted" in red -- degraded, and still true. `.flash:has(> .icon)`
   above makes the same trade for the same reason. */
.draft-life-due { color: var(--bad); }

.draft-state:has(.draft-life-due) {
  border-color: var(--bad);
  background: var(--bad-bg);
}

.draft-state:has(.draft-life-due) > .icon { color: var(--bad); }

/* --- key figures ---------------------------------------------------------- */

.figures {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  margin: 0 0 18px;
}

.figure {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
}

.figure .n { font-size: 26px; font-weight: 700; line-height: 1.1; }
.figure .k { font-size: 13px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }

/* --- definition rows ------------------------------------------------------ */

dl.facts { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: 6px 18px; }
dl.facts dt { color: var(--muted); font-size: 14px; }
dl.facts dd { margin: 0; font-size: 15px; overflow-wrap: anywhere; }

/* --- landing page --------------------------------------------------------- */

.hero { padding: 26px 0 6px; }
/* A short measure keeps the headline from running the full width of a
   desktop window. 15ch broke it into three two-word lines, which reads as
   cramped; 20ch gives two lines on a laptop and still wraps sensibly on a
   phone, where the clamp has already reduced the size. */
.hero h1 { max-width: 20ch; }
.lead { font-size: 18px; color: var(--muted); max-width: 62ch; margin: 0 0 20px; }

.steps ol { padding-left: 22px; margin: 22px 0; max-width: 62ch; }
.steps li { margin: 9px 0; }

.features { margin: 18px 0 30px; }
.features .card p { color: var(--muted); }

/* --- prose (legal documents) ---------------------------------------------- */

.doc { max-width: 68ch; }
.doc h1 { margin-bottom: 22px; }
.doc h2 { margin-top: 30px; font-size: 19px; }
.doc h3 { margin-top: 22px; }
.doc ul { padding-left: 22px; }
.doc li { margin: 6px 0; }
.doc code { background: var(--panel-2); padding: 1px 5px; border-radius: 5px; }

/* --- secret display (TOTP setup) ------------------------------------------ */

.secret {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 18px;
  letter-spacing: .08em;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px 14px;
  overflow-wrap: anywhere;
}

.codes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 8px;
  margin: 0 0 14px;
  padding: 0;
  list-style: none;
}

.codes li {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 8px 10px;
  text-align: center;
  font-size: 15px;
}

/* --- switcher ------------------------------------------------------------- */

.switcher {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}

.switcher select { width: auto; min-width: 150px; max-width: 240px; }

/* Permission checkboxes inside the people table. Narrow column, one line per
   flag, so the labels stay readable without widening the table. */
.perm-list {
  list-style: none;
  margin: 0;
  padding: 0;
  min-width: 200px;
}

.perm-list li { margin: 0; }

.perm-list label.inline {
  min-height: 32px;
  padding: 3px 0;
  font-size: 14px;
  gap: 8px;
}

.perm-list label.inline input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin-top: 1px;
}

/* The view-as banner.

   This is the one rule in this file that exists for a security control rather
   than for a screen, and it is here rather than in a page stylesheet for the
   same reason the markup is in base.html: `app.css` is linked outside
   `{ block styles }`, so no child template can discard it, and a banner a page
   could style away is a banner that is missing exactly where it matters.

   Sticky, and above the header on purpose. `header.site` is sticky at top: 0
   with z-index 20; this sits at 30, so when the two would occupy the same
   strip the one that must never be missed is the one that wins.

   Loud without hard-coding a colour, which is this file's own rule: --bad is
   the error red both themes already define, and --accent-ink is the colour
   each theme uses for text on a saturated ground. Even with the stylesheet
   gone the banner still reads, because the sentence is in the DOM above
   everything else -- that is the point of four independent mechanisms rather
   than one good one. */
.viewas {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 12px;
  padding: 10px 16px;
  background: var(--bad);
  color: var(--accent-ink);
  font-size: 14.5px;
  line-height: 1.4;
}

.viewas strong {
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: 12.5px;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid currentColor;
}

.viewas form { flex: 0 0 auto; }

/* The Stop button has to be legible against the banner rather than against a
   panel, so it takes the banner's own ink and a transparent ground instead of
   the shared button colours. */
.viewas button {
  background: transparent;
  border-color: currentColor;
  color: inherit;
}

/* `filter` rather than a hover colour, because this file hard-codes no
   colour outside :root and there is no variable for "a shade darker than the
   banner". `.button.primary:hover` does the same thing for the same reason. */
.viewas button:hover { background: transparent; filter: brightness(1.12); }

@media (max-width: 560px) {
  /* The Stop button keeps its own line on a phone rather than squeezing the
     sentence into two words a column wide. */
  .viewas { padding: 9px 14px; font-size: 14px; }
  .viewas .grow { display: none; }
  header.site { gap: 8px; padding: 10px 14px; }
  header.site nav a, header.site nav button { padding: 6px 8px; font-size: 14.5px; }
  main { padding: 18px 14px 32px; }
  .card { padding: 15px 16px; }
  .switcher select { min-width: 0; flex: 1 1 120px; }
}

/* --- the lock line ---------------------------------------------------------
   Where a feature's controls would be, on a plan without it: one line, the
   lock, the sentence, the way there (docs/BILLING_CONTRACT.md 10.2). The
   same strip as `.draft-state`, in the accent rather than the warning colour,
   because nothing is wrong. */
.plan-lock {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-left: 4px solid var(--accent);
  border-radius: 0 10px 10px 0;
  background: var(--panel-2);
  color: var(--ink);
  font-size: 13.5px;
}

.plan-lock > .icon { flex: none; color: var(--accent); }

.plan-lock-text { min-width: 0; }

/* The confirmation page's two lists: a feature's name, then what it is. */
.plan-features { margin: 0; padding-left: 20px; display: grid; gap: 8px; }

/* The invite request form's trap (docs/INVITE_ONLY_CONTRACT.md 5): a field no
   person sees and a script fills in. Off screen rather than `display: none`,
   which the simplest scripts already know to skip; the markup takes it out of
   the tab order and out of the accessibility tree. */
.trap {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
