@import url('/static/fonts/plex.css');
/* The Pharmacy Loop palette, shared with the counter app so the two halves of
   the system look like one. Two values depart from the prototype on purpose:
   an interactive border needs 3:1 to be seen at all, and the prototype's
   #D6E0E9 manages 1.34:1 on white - so rules and card edges use it, while
   inputs and buttons use a darker one from the same family. */
:root {
  color-scheme: light;
  --ink: #1C2430; --muted: #3A4653;
  /* The page is tinted and the panels sit pale on top of it - that separation
     is most of the design's character, and using near-white for both loses it. */
  --bg: #EDF2F6; --panel: #FCFCFA; --raised: #FFFFFF;
  --line: #E4EBF2; --line-strong: #D6E0E9;
  --field: #FFFFFF; --field-border: #7F94A9; --field-disabled: #F2F5F8;
  --accent: #0000FF; --accent-soft: #E6E6FF; --on-accent: #FFFFFF;
  --ok: #1F6B4A; --ok-bg: #E4F3EB;
  /* The NRx symbol's own red, as the Drugs Rules have it printed on the carton. */
  --nrx: #D00000;
  --warn: #7A5200; --warn-bg: #FFEBB8; --warn-ink: #5C3D00;
  --bad: #94382C; --bad-bg: #F6E5E1; --bad-soft: #FAF1EE;
  --free: #5B3FB8; --free-bg: #EFEBFA;
  --shadow: 0 1px 2px rgba(28,36,48,.05), 0 4px 14px rgba(28,36,48,.05);
}

/* Proportion ------------------------------------------------------------
   Content runs the full width of the window; what keeps it readable is the
   proportion of the parts, not an arbitrary cap. The horizontal padding grows
   with the viewport on a phi scale, and every two-column split is cut at
   1.618 : 1 - so the reading column and the column beside it hold the same
   relationship at any size. */
:root {
  --phi: 1.618;
  --gutter: clamp(18px, 2.618vw, 46px);   /* phi squared, so wide screens breathe */
  --gap: clamp(14px, 1.618vw, 26px);
}
/* The design is a light interface, so that is what loads. Dark is kept
   available but only when asked for - the operating system should not
   impose it on a screen meant to look like the design. */
:root[data-theme="dark"] {
    color-scheme: dark;
    /* The same hues carried into the dark, every pairing checked: the bright
       accent takes dark ink rather than white, which it cannot support. */
    --ink: #E8EBF0; --muted: #9AA5B4;
    --bg: #0F1318; --panel: #171C23; --raised: #1D232B;
    --line: #242A33; --line-strong: #333B46;
    --field: #0E1217; --field-border: #5F6C7B; --field-disabled: #1B212A;
    --accent: #8FA8FF; --accent-soft: #1C2440; --on-accent: #12161B;
    --ok: #57C98F; --ok-bg: #12291E;
    --nrx: #FF6B6B;
    --warn: #E9BE63; --warn-bg: #2E2510; --warn-ink: #E9BE63;
    --bad: #FF9C90; --bad-bg: #33191A; --bad-soft: #26161A;
    --free: #B8A2F0; --free-bg: #241C3A;
    --shadow: 0 1px 2px rgba(0,0,0,.45), 0 6px 18px rgba(0,0,0,.32);
  }
* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 14px/1.5 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, sans-serif;
  -webkit-font-smoothing: antialiased;
}
.mono, code { font-family: 'IBM Plex Mono', ui-monospace, Menlo, monospace; font-size: .92em; }
.small { font-size: .86em; } .r { text-align: right; } .strong { font-weight: 600; }
.mute { color: var(--muted); } .bad { color: var(--bad); } .free { color: var(--free); }

body { display: grid; grid-template-columns: 246px 1fr; min-height: calc(100vh / var(--z, 1)); }
.topbar {
  grid-row: 1 / -1; display: flex; flex-direction: column; align-items: stretch;
  gap: 0; padding: 0; background: var(--panel);
  border-right: 1px solid var(--line-strong); position: sticky; top: 0;
  /* Visible, not auto: the collapse button hangs over this edge, and a
     scrolling box would cut it off. The list of links scrolls on its own. */
  height: calc(100vh / var(--z, 1)); overflow: visible; z-index: 30;
}
.brand {
  font-weight: 600; text-decoration: none; color: var(--ink); font-size: 15px;
  letter-spacing: -0.01em; display: flex; align-items: center; gap: 9px;
  padding: 22px 20px 18px; border-bottom: 1px solid var(--line);
}
.mark {
  display: grid; place-items: center; width: 28px; height: 28px; border-radius: 7px;
  background: var(--accent); color: var(--on-accent); font-size: .8rem; font-weight: 700;
}
.topbar nav { display: flex; flex-direction: column; gap: 2px;
  padding: 16px 12px 24px; flex: 1; overflow: auto; }
.topbar nav a {
  display: flex; align-items: center; gap: 12px; flex: none; height: 40px;
  padding: 0 14px; border-radius: 8px; text-decoration: none;
  color: var(--ink); font-size: 14.5px;
}
.topbar nav a .ic {
  flex: none; display: grid; place-items: center; width: 20px; height: 20px;
  color: var(--muted);
}
.topbar nav a:hover .ic, .topbar nav a.on .ic { color: inherit; }
.topbar nav .heading {
  flex: none; margin: 18px 0 0; padding: 0 14px 10px; font-size: 11px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
/* The first one heads the list rather than parting it from anything, so it
   takes no room above itself and, folded, draws no rule across the top of the
   rail. */
.topbar nav .heading:first-child { margin-top: 2px; }
.topbar nav a:hover { background: var(--accent-soft); color: var(--accent); }
.topbar nav a.on { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

main { max-width: none; padding: var(--top) var(--gutter) 3.5rem; min-width: 0; }
h1 { font-size: 23px; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 5px; }
h2 { font-size: 1.02rem; margin: 0 0 .8rem; letter-spacing: .01em; }
h3 { font-size: .92rem; margin: 1.2rem 0 .5rem; color: var(--muted); }
.sub { color: var(--muted); margin: 0 0 1.4rem; }
.back { color: var(--muted); text-decoration: none; font-size: .9rem; display: inline-block; margin-bottom: .9rem; }
.back:hover { color: var(--accent); }
footer { grid-column: 2; max-width: none; padding: 0 var(--gutter) 2.5rem; color: var(--muted); font-size: .82rem; }

/* The last row of the register: where the next bill comes in. It reads as part
   of the table, not as a panel competing with it. */
.reg-drop {
  display: flex; align-items: center; gap: 14px;
  padding: 16px var(--pad, 18px);
  border-top: 1px dashed var(--line);
  background: var(--bg);
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.reg-drop:hover { background: var(--accent-soft); }
.reg-drop.over { border-color: var(--accent); background: var(--accent-soft); }
.reg-drop .dz-say { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.reg-drop .dz-say b { font-size: .95rem; }
.reg-drop .btn { flex: none; }
.dz-icon {
  flex: none; width: 34px; height: 34px; border-radius: 9px;
  display: grid; place-items: center; font-size: 19px;
  background: var(--accent-soft); color: var(--accent);
}
.hint.warn, .warn { color: var(--warn); }

.btn {
  display: inline-block; padding: .5rem .95rem; border-radius: 8px; border: 1px solid var(--line);
  background: var(--panel); color: var(--ink); font: inherit; font-size: .9rem;
  line-height: 1.5; cursor: pointer; text-decoration: none;
  -webkit-appearance: none; appearance: none;
}
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn[disabled] { opacity: .5; cursor: default; }
.btn:hover { border-color: var(--accent); color: var(--accent); }
/* A button that undoes nothing and writes nothing. The same box as the one it
   stands beside - same padding, same line-height, so the pair sit level in a
   row aligned on its bottom edge - and none of its weight. A cancel built out
   of .minibtn was a shorter control, and no amount of aligning makes two
   different heights look like one row. */
.btn.quiet { border-color: transparent; background: transparent; color: var(--muted); }
.btn.quiet:hover { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn.primary:hover { filter: brightness(1.08); color: var(--on-accent); }
.btn.ghost { background: transparent; }
.btn.danger { color: var(--bad); }
.btn.danger:hover { border-color: var(--bad); color: var(--bad); }

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: .85rem; margin: 1.4rem 0; }
.stat { background: var(--panel); border: 1px solid var(--line-strong); border-radius: 10px; padding: .9rem 1rem; box-shadow: var(--shadow); }
.stat .n { display: block; font-size: 1.5rem; font-weight: 650; font-variant-numeric: tabular-nums; }
.stat .l { display: block; color: var(--muted); font-size: .82rem; margin-top: .15rem; }
.stat.bad .n { color: var(--bad); }

.panel { background: var(--panel); border: 1px solid var(--line-strong); border-radius: 10px; padding: 1.15rem var(--gutter); margin-bottom: 1.1rem; box-shadow: var(--shadow); }
.panel-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: .9rem; flex-wrap: wrap; }
.panel-head h1, .panel-head h2 { margin: 0; }
.panel.split { display: grid; grid-template-columns: 1.618fr 1fr;   /* phi */ gap: calc(var(--gap) * var(--phi)); }
@media (max-width: 900px) { .panel.split { grid-template-columns: 1fr; } }
.count { color: var(--muted); font-weight: 400; font-size: .85rem; margin-left: .4rem; }
/* The things you can do to a panel, kept together at one end of its head. The
   head is spread on `space-between`, which is right for a title and a set of
   controls and wrong for three loose children - those get pushed to the
   corners with a button stranded in the middle of an empty rule. */
.headacts { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.headacts form { display: flex; }

/* A table wider than its panel scrolls sideways inside the panel. Without this
   a tablet either cuts the right-hand columns off or scrolls the whole page
   sideways, header and all. Paper has no scrollbar, so print lets it run. */
.scroll { overflow-x: auto; }
@media print { .scroll { overflow: visible; } }
/* A table of figures scrolls, and says so: overlay scrollbars vanish on a
   tablet, and a column cut off at the panel's edge with no bar looks like a
   fault rather than an invitation. Styling the bar makes it stay. */
.scroll::-webkit-scrollbar { height: 8px; }
.scroll::-webkit-scrollbar-track { background: var(--bg); border-radius: 4px; }
.scroll::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 4px; }
.scroll::-webkit-scrollbar-thumb:hover { background: var(--field-border); }
/* A table of text - mail, senders, column mappings - reflows instead: a
   filename, an address or a subject line breaks where it must rather than
   holding its column open past the screen. Figures never get this, because
   7,049.74 split over two lines is a different number. */
.scroll.reflow th { white-space: normal; }
.scroll.reflow td { overflow-wrap: anywhere; }
/* The mail, one line per address it came from. The line says enough - how
   many, how recent, what became of them - to know which one to open. */
.mailgroup { border-top: 1px solid var(--line); }
.mailgroup:last-child { border-bottom: 1px solid var(--line); }
.mailgroup > summary {
  cursor: pointer; list-style: none; display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: .35rem .8rem; padding: .6rem .25rem;
}
.mailgroup > summary::-webkit-details-marker { display: none; }
.mailgroup > summary:hover .mono { color: var(--accent); }
.mailgroup-who { display: flex; flex-wrap: wrap; align-items: baseline; gap: .15rem .5rem;
                 min-width: 0; overflow-wrap: anywhere; }
.mailgroup-who::before { content: '\25B8'; color: var(--muted); }
.mailgroup[open] .mailgroup-who::before { content: '\25BE'; }
.mailgroup-tally { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; }
/* Help beside a heading rather than under it: an info mark that shows its text
   on hover, and on focus so a keyboard and a touch screen both reach it. */
.info {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 1.05rem; height: 1.05rem; margin-left: .35rem; vertical-align: middle;
  border: 1px solid var(--line-strong); border-radius: 999px;
  color: var(--muted); background: var(--panel);
  font-size: .68rem; font-weight: 700; font-style: italic; line-height: 1; cursor: help;
}
.info:hover, .info:focus-visible { color: var(--accent); border-color: var(--accent); outline: none; }
.info > .tip {
  position: absolute; top: calc(100% + .4rem); left: 0; z-index: 40;
  width: max-content; max-width: min(24rem, 78vw);
  padding: .6rem .7rem; border: 1px solid var(--line-strong); border-radius: 8px;
  background: var(--raised); color: var(--ink); box-shadow: var(--shadow);
  font-family: inherit; font-size: .8rem; font-weight: 400; font-style: normal; line-height: 1.5;
  text-align: left; white-space: normal; cursor: auto;
  opacity: 0; visibility: hidden; transition: opacity .12s ease;
}
/* Beside something at the right of a row, where a tip opening rightwards would
   run off the panel. */
.info.end > .tip { left: auto; right: 0; }
.info:hover > .tip, .info:focus > .tip, .info:focus-within > .tip { opacity: 1; visibility: visible; }
/* On a phone there is no room beside the mark for a box of text, and one opening
   from a mark near the right edge would run off the screen. Pinned to the foot of
   the screen instead, where it is wide enough to read and always lands. */
@media (max-width: 720px) {
  .info > .tip { position: fixed; top: auto; bottom: .75rem; left: .75rem; right: .75rem;
                 width: auto; max-width: none; }
}
h1 > .info, h2 > .info, h3 > .info { vertical-align: 1px; }
/* ---- onboarding a sender ---------------------------------------------- */
/* The four steps the pages ahead take, said before the file is chosen. */
.tally { margin: -.8rem 0 1.4rem; font-size: .85rem; }
.onboard-steps {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: .5rem .9rem; margin: .2rem 0 1.1rem; padding: 0 0 0 1.1rem;
  color: var(--muted); font-size: .84rem;
}
.onboard-steps li { padding-left: .1rem; }
.onboard-steps b { color: var(--ink); }
.onboard-start { margin-bottom: .5rem; }
.onboard-stored { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.onboard-stored > summary { list-style: none; width: max-content; }
.onboard-stored > summary::-webkit-details-marker { display: none; }
.onboard-stored[open] > summary { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.onboard-stored .signoff-form { margin-top: .8rem; }
.onboard-stored select { min-width: 22rem; max-width: 100%; }
/* An address is a column of its own here, so the table asks for the width to
   print one rather than breaking it across three lines. */
.onboard-table { min-width: 56rem; }
.onboard-table td .mono { font-size: .8rem; }
/* A row's actions are a menu here, so the column is one button wide however
   long the supplier's name inside it. A full-width label naming that supplier
   was as wide as their name, and a table pays for that out of another column:
   it folded the address beside it to a character a line. The menu opens in the
   row rather than over it, because this table scrolls sideways and a scrolling
   box clips whatever is positioned outside it. */
.onboard-table td.small { min-width: 12rem; }
.onboard-table .rowmenu .menu {
  position: static; margin-top: 4px; box-shadow: none; min-width: 0;
}
.onboard-table .rowmenu .menu a,
.onboard-table .rowmenu .menu button { text-align: right; }

/* ---- the mailbox ----------------------------------------------------- */
/* A card of parts: a head naming the part, with a word at the right about what
   it counts, over a body. Tables inside run to the card's edges, so a row's
   hover and its rule reach the border rather than stopping short of it. */
.panel.mb-card { padding: 0; }
.mb-head {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: .3rem 1rem; min-height: 2.9rem; padding: .55rem 1.15rem;
  border-bottom: 1px solid var(--line);
}
.mb-head h2 { margin: 0; font-size: .9rem; font-weight: 600; letter-spacing: 0; }
.mb-head .count { margin-left: .1rem; color: var(--ink); font-weight: 600; font-size: inherit; }
.mb-aside { color: var(--muted); font-size: .8rem; }
.mb-body { padding: 1rem 1.15rem; margin: 0; }
/* Positioned, so a label hidden for screen readers - absolutely placed - is
   placed inside the scrolling box and not at the page's own edge, where on a
   phone it pushed the whole page sideways. */
.mb-card > .scroll { position: relative; border-radius: 0 0 10px 10px; }
.mb-foot { margin: 0; padding: .6rem 1.15rem; border-top: 1px solid var(--line); }

/* Whose mailbox, on one line with the way out of it. */
.mb-who { display: flex; align-items: center; flex-wrap: wrap; gap: .2rem .55rem;
          margin: 0; min-width: 0; font-size: .86rem; }
.mb-who .mono { overflow-wrap: anywhere; }
.livedot { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--ok); }

/* What the latest pull came to: the figure to act on, how it came to be, and
   the two things to do about it at the right. */
.mb-pull { display: flex; align-items: center; justify-content: space-between;
           flex-wrap: wrap; gap: .8rem 1.5rem; }
.mb-pull-say { flex: 1 1 24rem; min-width: 0; }
.mb-figure { display: flex; align-items: baseline; gap: .5rem; margin: 0; font-size: .9rem; }
.mb-figure b { font-size: 1.6rem; font-weight: 600; line-height: 1.1;
               font-variant-numeric: tabular-nums; }
.mb-line { margin: .2rem 0 0; color: var(--muted); font-size: .84rem; }
.mb-line .warn { color: var(--warn); }
.mb-line.bad { color: var(--bad); }
.mb-pull-acts { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }
.mb-pull-acts form { display: flex; margin: 0; }
.mb-totals {
  display: flex; flex-wrap: wrap; gap: .25rem 1.4rem;
  margin: .95rem 0 0; padding-top: .85rem; border-top: 1px solid var(--line);
  color: var(--muted); font-size: .84rem;
}
.mb-totals b { margin-right: .2rem; color: var(--ink); font-weight: 600;
               font-variant-numeric: tabular-nums; }

/* Fetching, and which mail is fetched, side by side at the golden cut. */
.mb-pair { display: grid; grid-template-columns: minmax(0, 1.618fr) minmax(0, 1fr);
           gap: var(--gap); align-items: start; margin-bottom: 1.1rem; }
.mb-pair > .panel { margin-bottom: 0; }
@media (max-width: 1100px) { .mb-pair { grid-template-columns: minmax(0, 1fr); } }
/* Side by side, the right card's help opens leftwards: rightwards it ran past
   the window, and even hidden it gave the page a sideways scroll. */
@media (min-width: 1101px) { .mb-pair > .panel + .panel .info > .tip { left: auto; right: 0; } }

.mb-acts { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; margin: 0; }
.mb-acts form { display: flex; flex-wrap: wrap; gap: .45rem; margin: 0; }
.mb-acts .btn { padding: .3rem .75rem; font-size: .86rem; }
.mb-acts .btn:not(.primary) { border-color: var(--field-border); }
.mb-acts .note { margin: 0; flex: 1 1 16rem; }

/* Everything older, folded behind one control under a rule. */
.mb-older { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.mb-older > summary { list-style: none; width: max-content; }
.mb-older > summary::-webkit-details-marker { display: none; }
.mb-older > summary::after { content: '+'; }
.mb-older[open] > summary::after { content: '\2212'; }
.mb-older-part h3 { margin: 1.1rem 0 .5rem; }

/* The search, shown as it is sent, with its editing folded under it. */
.mb-query {
  margin: 0 0 .75rem; padding: .5rem .7rem; border-radius: 6px;
  background: var(--bg); border: 1px solid var(--line);
  font-size: .84rem; line-height: 1.55; overflow-wrap: anywhere;
}
/* Whose mail the search is held to, under it: the directory's addresses. */
.mb-readable { margin: -.35rem 0 .75rem; color: var(--muted); font-size: .84rem; }
.mb-readable + .mb-edit, .mb-body > .banner + .mb-edit { margin-top: 0; }
/* A sender whose mail came in before the directory decided what is read. */
.mb-offlist { display: block; margin-top: .1rem; font-family: 'IBM Plex Sans', sans-serif;
              font-size: .74rem; color: var(--warn); }
.mb-edit > summary { list-style: none; display: flex; flex-wrap: wrap; align-items: center;
                     gap: .3rem .6rem; width: max-content; max-width: 100%;
                     cursor: pointer; font-size: .84rem; }
.mb-edit > summary::-webkit-details-marker { display: none; }
/* A fold's control stays pressed while what it opened is showing. */
.mb-older[open] .mb-toggle, .mb-edit[open] .mb-toggle {
  background: var(--accent-soft); border-color: var(--accent); color: var(--accent);
}
.mb-edit form { margin-top: .9rem; }

/* One height for every field and button in a form, so a row lines up along
   its foot instead of each control sitting at its own height. */
.mailbox .signoff-form input:not([type=checkbox]),
.mailbox .signoff-form select,
.mailbox .signoff-form .btn { height: 2.4rem; box-sizing: border-box; }
.mailbox .signoff-form .btn { display: inline-flex; align-items: center; padding-block: 0; }
.mailbox .signoff-form select { padding: 0 26px 0 .55rem; min-width: 17.5rem; }
/* A month, or a stretch of days: side by side where there is room. */
.mailwindow { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .7rem 1.2rem; }
.mailwindow .signoff-form { margin: 0; }
.mailwindow .or { color: var(--muted); font-size: .85rem; line-height: 2.4rem; }
@media (max-width: 720px) {
  .mailwindow { gap: .4rem; } .mailwindow .or { line-height: 1.4; }
  .mailbox .signoff-form select { min-width: 0; }
}

/* The senders and the syncs: figures read down their columns. */
.mb-table td { padding-block: .55rem; vertical-align: middle; }
.mb-table th:first-child, .mb-table td:first-child { padding-left: 1.15rem; }
.mb-table th:last-child, .mb-table td:last-child { padding-right: 1.15rem; }
.mb-table td.mono { font-size: .84rem; }
.mb-table td.nowrap { white-space: nowrap; }
.mb-pills { display: inline-flex; flex-wrap: wrap; gap: .3rem; }
.mb-senders tr.on > td { background: var(--accent-soft); }
.mb-senders tr.mb-opened:hover { background: none; }
.mb-senders tr.mb-opened > td { padding: .2rem 1.15rem .9rem; background: var(--bg); }
.mb-syncs td a { color: inherit; }
.mb-syncs tr.rowlink:hover td a { color: var(--accent); }
.mb-flagcol { width: 1.8rem; text-align: center; }
.mb-flag {
  display: inline-grid; place-items: center; width: 1.1rem; height: 1.1rem; border-radius: 50%;
  background: var(--bad-bg); color: var(--bad); font-size: .72rem; font-weight: 700; cursor: help;
}
.mb-flag.live { background: var(--accent-soft); color: var(--accent); }

/* One sender's mail, or a pull's: which file, and what became of it. Fixed
   proportions, so a long reason a file was not read cannot squeeze the name. */
.mb-mail { table-layout: fixed; min-width: 46rem; font-size: .84rem; }
.mb-mail .c-sent { width: 6.6rem; }
.mb-mail .c-file { width: 22%; }
.mb-mail .c-out { width: 24%; }
.mb-mail .c-go { width: 8.6rem; }
.mb-mail td { vertical-align: middle; }
.mb-mail td.mono { font-size: .8rem; }
.mb-card > .scroll > .mb-mail th:first-child, .mb-card > .scroll > .mb-mail td:first-child { padding-left: 1.15rem; }
.mb-card > .scroll > .mb-mail th:last-child, .mb-card > .scroll > .mb-mail td:last-child { padding-right: 1.15rem; }
.mb-detail { display: block; margin-top: .15rem; color: var(--muted); font-size: .78rem; }
.mb-from { display: block; margin-bottom: .1rem; font-size: .78rem; color: var(--muted); }
/* The supplier on file a group of unclaimed bills is probably from, under its line. */
.loosegroup .near { flex-basis: 100%; margin: 0 0 0 1rem; }
.mailgroup-tally .count { margin: 0 .2rem 0 0; }
.mailgroup .scroll { margin-bottom: .6rem; }
table { width: 100%; border-collapse: collapse; font-size: .89rem; }
th { text-align: left; color: var(--muted); font-weight: 550; font-size: .78rem;
     text-transform: uppercase; letter-spacing: .04em; padding: .4rem .5rem; border-bottom: 1px solid var(--line); white-space: nowrap; }
/* A heading that orders its table: the heading's own words as a button with
   none of a button's box - it reads as the heading until the pointer is on
   it, and then lights the way the desk's quiet button does, soft accent
   behind accent words, at the small button's corner. The box that lights is
   padding the words are given and then taken back out of the layout by the
   same margin, so the words stay on their column's edge and no column is a
   pixel wider for it.
   The mark for which way the table is ordered is drawn from the heading's own
   aria-sort and stands outside the words - after them, or before them over a
   column of figures - inside the same box, which is that much longer on that
   side. Set among the words it pushed "Amount" 12px in from the figures. */
.colsort {
  --pad: .4rem; --mark: .7rem;
  position: relative; padding: .2rem var(--pad); margin: -.2rem calc(var(--pad) * -1);
  border: 0; border-radius: 6px; background: none; cursor: pointer;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
  -webkit-appearance: none; appearance: none;
  transition: background .12s, color .12s;
}
/* The purchase register's headings are links, as its order is made a page at
   a time by the server; they are the same heading to look at. */
a.colsort { display: inline-block; text-decoration: none; }
[aria-sort] > .colsort {
  color: var(--ink);
  padding-right: calc(var(--pad) + var(--mark)); margin-right: calc((var(--pad) + var(--mark)) * -1);
}
.r[aria-sort] > .colsort {
  padding-right: var(--pad); margin-right: calc(var(--pad) * -1);
  padding-left: calc(var(--pad) + var(--mark)); margin-left: calc((var(--pad) + var(--mark)) * -1);
}
.colsort:hover { background: var(--accent-soft); color: var(--accent); text-decoration: none; }
.colsort:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
[aria-sort] > .colsort::after {
  content: "\25B2"; position: absolute; right: var(--pad); top: 50%; transform: translateY(-50%);
  font-size: .62em; line-height: 1;
}
[aria-sort="descending"] > .colsort::after { content: "\25BC"; }
.r[aria-sort] > .colsort::after { right: auto; left: var(--pad); }
td { padding: .45rem .5rem; border-bottom: 1px solid var(--line); vertical-align: top; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--bg); }
tfoot td { padding: .55rem .5rem; border-top: 2px solid var(--line); font-weight: 600; }
td.mono, th.r + td, .lines td.r { font-variant-numeric: tabular-nums; }
table a { color: var(--accent); text-decoration: none; }
table a:hover { text-decoration: underline; }
.row-bad { background: var(--bad-bg); }
.row-bad:hover { background: var(--bad-bg); }
.checks td.w1 { width: 62px; }
.kv td { padding: .35rem .5rem; }
.kv tr.total td { border-top: 2px solid var(--line); font-weight: 650; font-size: 1.02em; }
.empty { color: var(--muted); padding: 1rem 0; }
.note { color: var(--muted); font-size: .8rem; margin-top: .15rem; }
.notes { margin: .4rem 0 0; padding-left: 1.1rem; color: var(--muted); font-size: .88rem; }
.notes li { margin-bottom: .3rem; }
.tag { background: var(--accent-soft); color: var(--accent); border-radius: 4px; padding: 0 .3rem; font-size: .74rem; }

.pill { display: inline-block; padding: .12rem .5rem; border-radius: 999px; font-size: .72rem; font-weight: 600; white-space: nowrap;
  text-transform: uppercase; letter-spacing: .05em; }
.pill.big { font-size: .85rem; padding: .25rem .7rem; }
.pill.ok { background: var(--ok-bg); color: var(--ok); }
.pill.warn { background: var(--warn-bg); color: var(--warn); }
.pill.bad { background: var(--bad-bg); color: var(--bad); }
.pill.mute { background: var(--bg); color: var(--muted); }
/* What a credit note is marked with, here and in the register. */
.pill.free { background: var(--free-bg); color: var(--free); }

.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: .9rem 1.4rem; margin: 0 0 1.1rem; }
.facts dt { color: var(--muted); font-size: .76rem; text-transform: uppercase; letter-spacing: .04em; }
.facts dd { margin: .15rem 0 0; overflow-wrap: anywhere; }
/* A file name or mail address with no space in it is one word: a grid column
   does not shrink below its longest word, so it ran into the column beside it. */
.facts > div { min-width: 0; }
.actions { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.actions form { margin: 0; }

.results ul { list-style: none; margin: 0; padding: 0; }
.results li { display: flex; gap: .8rem; align-items: flex-start; padding: .7rem .9rem; border: 1px solid var(--line); border-radius: 10px; margin-bottom: .5rem; background: var(--panel); }
.results .fname { font-weight: 550; }
.results .detail { color: var(--muted); font-size: .85rem; margin-top: .1rem; }

.pct { color: var(--muted); font-weight: 500; font-size: .85em; }
/* Buttons and pills sharing a cell. `align-items` has to be said: a flex row
   defaults to stretch, so a pill - which is padded rather than a fixed height -
   grew to the row and left its text riding above the button beside it. */
.rowlinks { display: flex; gap: .35rem; align-items: center; white-space: nowrap; }
/* In a right-aligned cell the links go to its end. A flex box is block-level,
   so it quietly fills the cell and `text-align: right` on the cell reaches
   nothing inside it - the buttons stay at the left of a box that looks right-
   aligned until you notice they have not moved. */
td.r > .rowlinks { justify-content: flex-end; }
.minibtn {
  /* Used on both <a> and <button>: buttons inherit neither font nor background,
     so both are set explicitly or the two render as different controls. */
  display: inline-flex; align-items: center; justify-content: center; gap: .25rem;
  font: inherit; font-size: .78rem; font-weight: 550; line-height: 1.5;
  padding: .2rem .55rem; border-radius: 6px;
  border: 1px solid var(--field-border); background: var(--panel); color: var(--ink);
  text-decoration: none; cursor: pointer; white-space: nowrap;
  -webkit-appearance: none; appearance: none;
  transition: background .12s, border-color .12s, color .12s;
}
.minibtn:hover {
  background: var(--accent-soft); border-color: var(--accent);
  color: var(--accent); text-decoration: none;
}
.minibtn:active { transform: translateY(1px); }
.minibtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.minibtn[disabled], .minibtn[aria-disabled="true"] { opacity: .5; cursor: default; }
.minibtn[aria-disabled="true"]:hover { background: var(--panel); border-color: var(--field-border); color: var(--ink); }
.minibtn.solid {
  background: var(--accent); border-color: var(--accent); color: var(--on-accent);
}
.minibtn.solid:hover { filter: brightness(1.08); background: var(--accent); color: var(--on-accent); }
.minibtn.tint {
  background: var(--accent-soft); border-color: transparent; color: var(--accent);
}
.minibtn.tint:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.minibtn.quiet { border-color: transparent; background: transparent; }
.minibtn.quiet:hover { background: var(--accent-soft); border-color: transparent; }
/* Review: a filled light yellow - it marks the row as waiting on a person,
   which is a state, not a warning. */
/* The one thing left to do on the page, marked with a rule rather than a fill -
   the pill inside already says "Pending review". */
/* A verdict sits beside the figure it is about, so the two are read together. */
#checks-panel summary { cursor: pointer; list-style: none; }
/* .panel-head reserves space for what follows it. Closed, nothing follows. */
#checks-panel details:not([open]) summary { margin-bottom: 0; }
#checks-panel summary::-webkit-details-marker { display: none; }
#checks-panel summary::after {
  content: "\203A"; margin-left: auto; font-size: 1.2rem; color: var(--muted);
  transition: transform .15s;
}
#checks-panel details[open] summary::after { transform: rotate(90deg); }
#checks-panel summary:hover h2 { color: var(--accent); }

.verdict sup { font-size: .62em; font-weight: 600; margin-left: 1px; }
td.verdict { width: 1.4rem; padding-right: 0; text-align: center; }
.verdict.ok { color: var(--ok); font-weight: 700; }
.verdict.bad { color: var(--bad); font-weight: 700; }
tr:has(> td .verdict.bad) td { background: var(--bad-bg); }

.panel.spotlight {
  border-left: 3px solid var(--warn);
  scroll-margin-top: 24px;
}

.minibtn.amber {
  background: var(--warn-bg); border-color: var(--warn-bg); color: var(--warn-ink);
}
.minibtn.amber:hover {
  background: var(--warn-bg); border-color: var(--warn); color: var(--warn-ink);
}
/* Red is kept for the one action that destroys something. The reversible ones
   (Forget, Withdraw sign-off, revert) stay amber, or red stops meaning much. */
.minibtn.danger { color: var(--bad); border-color: var(--bad); }
.minibtn.danger:hover {
  background: var(--bad-bg); border-color: var(--bad); color: var(--bad);
}
.minibtn.warn { color: var(--warn); border-color: var(--warn); }
.minibtn.warn:hover { background: var(--warn-bg); border-color: var(--warn); color: var(--warn); }
.billhead h1 { margin-bottom: .1rem; }
.billhead .sub { margin: 0; }

@media print {
  :root {
    --bg: #fff; --panel: #fff; --ink: #000; --muted: #444; --line: #bbb;
    --accent: #000; --accent-soft: #eee; --ok: #060; --ok-bg: #eee;
    --warn: #660; --warn-bg: #eee; --warn-ink: #000; --bad: #900; --bad-bg: #f4f4f4; --shadow: none;
  }
  .topbar, footer, .noprint, .actions { display: none !important; }
  main { max-width: none; padding: 0; }
  .panel { border: 1px solid var(--line); box-shadow: none; break-inside: avoid; margin-bottom: .6rem; }
  .stats { break-inside: avoid; }
  .scroll { overflow: visible; }
  table { font-size: 8.5pt; }
  a { color: #000; text-decoration: none; }
  @page { margin: 12mm; }
}

/* ---- the NRx stock report ---------------------------------------------- */
/* Drawn as the counter's Sales > NRx tab is, from apps/shared/styles.css:
   counts in tiles, a segmented row of periods with the dates beside it, and
   the table in a card whose head carries Print and CSV. Under names this
   stylesheet had not used - `.seg` here is already the fieldset of radios. */
.tiles { display: flex; gap: var(--gap); flex-wrap: wrap; margin: 0 0 18px; }
.tile {
  flex: 1; min-width: 176px; padding: 13px 15px; background: var(--panel);
  border: 1px solid var(--line-strong); border-radius: 10px;
}
.tile b { display: block; font-size: 21px; font-weight: 600; font-family: 'IBM Plex Mono', monospace; }
.tile span { display: block; font-size: 11.5px; color: var(--muted); margin-top: 3px; }
.tile.bad b { color: var(--bad); }

.segbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
.segs {
  display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 3px; padding: 3px;
  background: var(--bg); border: 1px solid var(--line-strong); border-radius: 9px;
}
.segs > a {
  padding: 8px 12px; border-radius: 7px; text-align: center; white-space: nowrap;
  font-size: 13px; color: var(--muted); text-decoration: none;
}
.segs > a:hover { color: var(--ink); text-decoration: none; }
.segs > a.on { background: var(--raised); color: var(--ink); font-weight: 600; box-shadow: var(--shadow); }
.segs > a:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.segs.periods { width: 340px; }
/* A two-way switch in a row of small controls, at their height. */
.segs.mini > a { padding: 4px 10px; font-size: 12.5px; }
.daterange { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.daterange input { width: 150px; margin-top: 0; padding: 6px 8px; font: inherit; font-size: 13px; }

.card {
  background: var(--panel); border: 1px solid var(--line-strong); border-radius: 10px;
  margin-bottom: 1.1rem;
}
.card > header {
  padding: 13px var(--gap); border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.card > header h2 { font-size: 13.5px; font-weight: 600; margin: 0; }
.card > .note { margin: 0; padding: 10px var(--gap) 14px; border-top: 1px solid var(--line); }
.card > .empty { padding: 14px var(--gap); margin: 0; }
/* Under the card's head: a period nothing moved in, said before the rows. */
.card > .quietnote {
  margin: 0; padding: 10px var(--gap); font-size: 13px; color: var(--muted);
  border-bottom: 1px solid var(--line);
}
/* The heading and the Help button level with its top, not its middle: the
   line under the heading runs to two or three. */
.nrxhead { align-items: flex-start; flex-wrap: nowrap; }
.nrxhead > div { flex: 1 1 auto; min-width: 0; }
.helpbtn { flex: none; margin-top: .3rem; }
.helpmark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.05rem; height: 1.05rem; border-radius: 999px; border: 1px solid currentColor;
  font-size: .7rem; font-weight: 700; line-height: 1;
}

/* A drawer: help that slides in from the right over the page, leaving the
   report where it was being read. A native dialog, opened by app.js's
   data-dialog, so it takes the browser's own modal behaviour - and the UA's
   centring margins and size caps are undone here, or it lands mid-screen. */
dialog.drawer {
  position: fixed; inset: 0 0 0 auto; margin: 0;
  width: min(460px, calc(100vw / var(--z, 1))); max-width: none; height: auto; max-height: none;
  padding: 0; border: 0; border-left: 1px solid var(--line-strong);
  background: var(--panel); color: var(--ink); font: inherit;
  box-shadow: -10px 0 28px rgba(16, 22, 30, .18);
}
dialog.drawer::backdrop { background: rgba(16, 22, 30, .35); }
dialog.drawer[open] { animation: drawer-in .18s ease-out; }
@keyframes drawer-in { from { transform: translateX(100%); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) { dialog.drawer[open] { animation: none; } }
/* Its inside fills its height and scrolls on its own. Only while open: a
   display set on a closed dialog would show it. */
dialog.drawer[open] { display: flex; flex-direction: column; }
.drawer-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 18px 22px 26px; box-sizing: border-box; }
.drawer-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding-bottom: 12px; margin-bottom: 14px; border-bottom: 1px solid var(--line);
}
.drawer-head h2 { margin: 0; font-size: 1.05rem; }
.drawer ol { margin: 0; padding-left: 1.3rem; display: grid; gap: .7rem; font-size: .9rem; line-height: 1.55; }
.drawer li b { color: var(--ink); }

/* Bills still to be signed off: the count says enough until it is opened. */
.nrxunsigned { margin-bottom: 14px; }
.nrxunsigned summary { cursor: pointer; }
.nrxunsigned ul { margin: .5rem 0 0; padding-left: 1.1rem; max-height: 16rem; overflow-y: auto; }
.nrxunsigned li { margin: .15rem 0; font-size: .86rem; }

/* A table in a card is spaced as the counter's tables are - 10 by 12 px a
   cell, 13px type, the heads in small capitals on a firmer rule - so its
   columns have room between them and the two halves read alike. The desk's
   other tables keep their tighter spacing: they hold far more rows. */
.card table { font-size: 13px; }
.card th {
  padding: 9px 12px; font-size: 10.5px; font-weight: 500; letter-spacing: .07em;
  border-bottom: 1px solid var(--line-strong);
}
.card td { padding: 10px 12px; vertical-align: middle; }
/* Figures at the counter's size: the desk's .mono shrinks to .92em, which made
   the numbers smaller than the words beside them. */
.card td.r.mono { font-size: 13px; }
/* A table flush inside a card starts and ends where the card's header does:
   its first and last columns take the card's own inset, so the figures in
   the last column are not tighter to the edge than the buttons above them. */
.card > .scroll > table tr > :first-child { padding-left: var(--gap); }
.card > .scroll > table tr > :last-child { padding-right: var(--gap); }
/* On paper the room goes back to the figures, at the desk's printed size:
   A4 has to hold every column, and 13px type on paper pushed the last one
   off the page. */
@media print {
  .card table { font-size: 8.5pt; }
  .card th { letter-spacing: .03em; }
  .card th, .card td { padding: 4px 6px; }
  .card > .scroll > table tr > :first-child { padding-left: 6px; }
  .card > .scroll > table tr > :last-child { padding-right: 6px; }
  .card td.r.mono, .daybreak td b, .daybreak td.num { font-size: inherit; }
}

/* A day, or a product, heading the rows under it - the counter's day break:
   a row of the same table, so the figures stay under their columns. The tint
   is the row's, not its cells': a column pinned by freeze.js takes its row's
   background, and a tint on the cell left the pinned ones white. */
tr.daybreak { background-color: var(--bg); }
.daybreak td {
  border-top: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong); padding-top: 8px; padding-bottom: 8px;
  font-size: 12px; white-space: nowrap;
}
.daybreak td b { font-size: 13px; font-weight: 600; margin-right: 9px; }
.daybreak td .pill { margin-right: 9px; vertical-align: 1px; }
.daybreak td.num { font-weight: 600; font-family: 'IBM Plex Mono', monospace; font-size: 13px; }
/* The first day is under the table's head already; the ones after it need
   the air that says a new day, or product, has started. */
tbody > .daybreak:not(:first-child) td { padding-top: 16px; }
/* A bill or document number is one value: never broken across lines at its
   slashes and hyphens - the table scrolls instead. */
.nrxstock td.doc { white-space: nowrap; }
/* The product master's NRx answer, red and bold when it is NRx - and the
   same dropdown on the Add a product page and in a bill's review row. */
.cellsel.nrx-on,
select[name="nrx"]:has(option[value="yes"]:checked) { color: var(--nrx); font-weight: 700; }
/* The product master's own columns: a code is one value and never breaks at
   its hyphen, and a name keeps room to be read in a line or two - the fact
   columns beside it are form fields of a fixed width, and they were taking
   that room once the NRx column joined them. The table scrolls instead. */
.mastertable td.code { white-space: nowrap; }
.mastertable td.pname { min-width: 11rem; }
/* On paper too: the register is read against the carton. */
.sheet .nrxmark { color: #D00000; }
/* A batch sits indented under its product, by more than the card's inset. */
.card > .scroll > table.nrxstock tr > td.batchcell { padding-left: calc(var(--gap) + 16px); }
/* The line under a name - batch, note, who - at the counter's small size. */
.nrxstock .tiny { font-size: 11px; margin-top: 2px; }
/* What only the paper carries: the shop's letterhead over the report, and a
   line for the pharmacist to sign under it. */
.printonly { display: none; }
.printhead { text-align: center; margin-bottom: .8rem; }
.printhead h1 { margin: 0 0 .3rem; font-size: 18pt; text-transform: uppercase; }
.printhead p { margin: 0; }
.printhead .doc { margin-top: .4rem; letter-spacing: .08em; }
.printsigns { display: none; justify-content: space-between; margin-top: 2.6rem; }
.printsigns p { border-top: 1px solid #000; padding-top: 4px; min-width: 240px; text-align: center; }
@media print {
  .printonly { display: block; }
  .printsigns.printonly { display: flex; }
  tr.daybreak { background-color: #f2f2f2; }
  .card { border: 1px solid var(--line); }
}

/* ---- side-by-side PDF review ------------------------------------------- */
/* A full-bleed page fills the viewport beside the nav. The body grid has to
   survive: restacking it turns the sidebar into a full-width row that pushes
   the content out of an overflow-hidden viewport. */
/* The signed-out shell: one card, centred, no grid to hold a nav that is not
   there. */
.signer { display: flex; flex-direction: column; gap: .1rem; align-self: flex-end;
  padding-bottom: .3rem; }
.signer .tiny { text-transform: uppercase; letter-spacing: .04em; }

.logo { flex: none; display: block; border-radius: 8px; }
/* The brand is now a mark beside two lines, so it lays them out sideways. */
.brand { flex-direction: row; align-items: center; gap: 10px; }
.brandtext { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.brandtext b { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.brandtext span { font-size: 11px; color: var(--muted); }

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

body.plain { display: block; }
main.gate { display: grid; place-items: center; min-height: calc(100vh / var(--z, 1)); padding: var(--gutter); }
.gatecard { width: min(560px, 100%); margin: 0; }
.gatecard h1 { margin-bottom: .3rem; }
.gatecard .sub { margin-bottom: 1.2rem; }
.gatecard .steps { margin: 0 0 .4rem; padding-left: 1.2rem; line-height: 1.9; }
.gatecard .steps code, .gatecard .uri {
  background: var(--bg); border: 1px solid var(--line); border-radius: 5px;
  padding: .05rem .35rem; font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: .8rem; word-break: break-all;
}
.gatecard pre {
  background: var(--bg); border: 1px solid var(--line); border-radius: 8px;
  padding: .7rem .9rem; overflow-x: auto; font-size: .78rem; margin: .5rem 0;
}
.gatecard pre code { background: none; border: 0; padding: 0; }
.gbtn { display: inline-flex; align-items: center; gap: .55rem; }
.gatecard .foot { margin: 1rem 0 0; }
.gatecard h2 { font-size: .95rem; margin: 1.1rem 0 .4rem; }
.pwform { display: grid; gap: .7rem; }
.pwform label { display: grid; gap: .25rem; font-size: .85rem; color: var(--muted); }
.pwform input { width: 100%; }
.pwform .btn { justify-self: start; }
.gatecard .or { margin: 1.1rem 0 .6rem; }
.gatecard .err {
  background: var(--bad-bg); color: var(--bad); border-radius: 8px;
  padding: .6rem .8rem; margin: 0 0 1rem; font-size: .88rem;
}

body:has(main.fullbleed) { height: calc(100vh / var(--z, 1)); overflow: hidden; }
body:has(main.fullbleed) main {
  min-height: 0; overflow: hidden;
  display: flex; flex-direction: column; max-width: none; padding: 0;
}

.viewbar {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .5rem .9rem; background: var(--panel); border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.viewbar-left, .viewbar-right { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.viewbar .back { margin: 0; }
.viewbar-right { gap: .35rem; }

.splitview { flex: 1; min-height: 0; display: flex; background: var(--bg); }
.pane { min-width: 0; min-height: 0; overflow: auto; }
.pdfpane { flex: 0 0 52%; overflow: hidden; background: #52565c; }
.pdfpane object, .pdfpane embed, .pdfpane iframe { width: 100%; height: 100%; border: 0; display: block; }
/* On the viewer's fixed dark backdrop, so white regardless of theme. */
.pdf-fallback { padding: 2.5rem 1.5rem; text-align: center; color: #fff; }
.pdf-fallback p { margin-bottom: 1rem; }
.datapane { flex: 1 1 auto; }
.pane-inner { padding: 1rem 1.1rem 3rem; }
.pane-inner h2 { margin-bottom: .35rem; }

.splitter {
  flex: 0 0 6px; cursor: col-resize; background: var(--line);
  position: relative; transition: background .12s;
}
.splitter:hover, .splitter:focus, .splitter.dragging { background: var(--accent); outline: none; }
.splitter::after {
  content: ""; position: absolute; inset: 0 -4px; /* wider grab target than it looks */
}
.splitview.dragging { user-select: none; }
.splitview.dragging .pdfpane { pointer-events: none; }

.splitview.stacked { flex-direction: column; }
.splitview.stacked .pdfpane { flex: 0 0 55%; }
.splitview.stacked .splitter { flex-basis: 6px; cursor: row-resize; }

table.compact { font-size: .82rem; }
table.compact td, table.compact th { padding: .3rem .4rem; }
.facts.tight { gap: .5rem 1.1rem; margin-bottom: .9rem; }
.alert {
  background: var(--bad-bg); border: 1px solid var(--bad); border-radius: 9px;
  padding: .7rem .9rem; margin: .8rem 0; font-size: .88rem;
}
.alert .notes { color: inherit; margin-top: .3rem; }

@media (max-width: 820px) {
  .splitview { flex-direction: column; }
  .splitview .pdfpane { flex: 0 0 50%; }
  .splitter { cursor: row-resize; }
}

/* ---- flagged cells and inline row editing ------------------------------ */
.lines.editable td { position: relative; }
td.flag-warn { background: var(--warn-bg); box-shadow: inset 2px 0 0 var(--warn); }
td.flag-error { background: var(--bad-bg); box-shadow: inset 2px 0 0 var(--bad); }
td.flag-warn, td.flag-error { cursor: help; }
.flagdot {
  display: inline-block; width: 13px; height: 13px; border-radius: 50%;
  font-size: 9px; line-height: 13px; text-align: center; font-weight: 700;
  vertical-align: text-top; margin-left: .2rem; background: var(--warn);
  color: #fff;   /* always on a saturated warn/bad fill */
}
td.flag-error .flagdot { background: var(--bad); }
td.edited { text-decoration: underline dotted var(--accent) 2px; text-underline-offset: 3px; }
tr.has-edit td:first-child::before {
  content: "✎"; color: var(--accent); margin-right: .2rem; font-size: .8em;
}
.rowactions { white-space: nowrap; text-align: right; width: 1%; }
.rowactions .minibtn + .minibtn { margin-left: .25rem; }
/* Row controls stay fully legible at rest; hovering only warms the border, so
   they never read as disabled. */
.lines.editable tr:hover .rowactions .minibtn { border-color: var(--accent); }
.lines.editable tr.has-flag .edit-row { border-color: var(--accent); color: var(--accent); }
.flagline { display: inline-flex; gap: .3rem; align-items: center; }
.editnote { color: var(--accent); }
.minibtn.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
tr.hidden-row { display: none; }

tr.editing { background: var(--accent-soft) !important; }
tr.editing input {
  width: 100%; min-width: 52px; box-sizing: border-box; font: inherit; font-size: .82rem;
  padding: .15rem .3rem; border: 1px solid var(--accent); border-radius: 5px;
  background: var(--panel); color: var(--ink);
}
tr.editing input.wide { min-width: 130px; }
tr.editing input[type=number] { text-align: right; }
tr.editing td { vertical-align: middle; }
/* The same rule, for a table whose rows carry controls all the time rather
   than only while one is being edited. `td` is top-aligned everywhere else on
   purpose - a wrapped product name should start level with the figures beside
   it - but a row whose height is set by a button wants its contents centred in
   the band, not hanging from the top of it. */
.midrows td { vertical-align: middle; }
.editerr { color: var(--bad); font-size: .8rem; padding: .3rem .5rem; }
.saving { opacity: .5; pointer-events: none; }

.fixhint { font-size: .8rem; color: var(--bad); margin-top: .2rem; }
.linkbtn {
  font: inherit; font-size: inherit; background: none; border: 0; padding: 0;
  color: var(--accent); text-decoration: underline; cursor: pointer;
}
.kv td.edited { text-decoration: underline dotted var(--accent) 2px; text-underline-offset: 3px; }
#totals-table input {
  width: 100%; box-sizing: border-box; font: inherit; font-size: .85rem; text-align: right;
  padding: .12rem .3rem; border: 1px solid var(--accent); border-radius: 5px;
  background: var(--panel); color: var(--ink);
}
#totals-panel.editing { outline: 2px solid var(--accent); outline-offset: 2px; }
.totals-actions { display: flex; gap: .35rem; margin-top: .6rem; }

/* ---- sign-off ---------------------------------------------------------- */
.signoff { display: flex; flex-direction: column; gap: .8rem; }
.signoff-head { display: flex; align-items: flex-start; gap: .8rem; }
.signoff-form { display: flex; gap: .7rem; align-items: flex-end; flex-wrap: wrap; }
.signoff-form label {
  display: flex; flex-direction: column; gap: .2rem;
  font-size: .78rem; color: var(--muted); text-transform: uppercase; letter-spacing: .03em;
}
.signoff-form label.grow { flex: 1 1 260px; min-width: 0; }
/* A tick box and what it says on one line, level with the fields beside it -
   not a box given a text field's width and its words stacked under it. */
.signoff-form label.tick {
  flex-direction: row; align-items: center; gap: .45rem; padding-bottom: .45rem;
  font-size: .86rem; color: var(--ink); text-transform: none; letter-spacing: 0;
}
.signoff-form label.tick input { min-width: 0; width: auto; margin: 0; padding: 0; }
/* A select sizes itself to its longest option, and a stored bill's option is
   its filename, supplier and number - wider than a tablet. It takes the width
   its label has and clips the option text instead of stretching the page. */
.signoff-form select { width: 100%; max-width: 100%; }
select { max-width: 100%; }
.signoff-form input {
  font: inherit; font-size: .9rem; text-transform: none; letter-spacing: 0;
  padding: .35rem .55rem; border: 1px solid var(--line); border-radius: 7px;
  background: var(--panel); color: var(--ink); min-width: 160px;
}
.signoff-form input:focus { border-color: var(--accent); outline: none; }
.signoff-form .req { color: var(--bad); text-transform: none; }
#unconfirm-btn { align-self: flex-start; }
.banner {
  background: var(--warn-bg); border: 1px solid var(--warn); color: var(--warn);
  border-radius: 9px; padding: .6rem .9rem; font-size: .88rem; margin: 0 0 1.1rem;
}
.banner a { color: inherit; font-weight: 600; }

/* ---- form controls ------------------------------------------------------
   Buttons and inputs inherit neither font nor colour from the page, and the
   browser paints its own chrome (spinners, autofill, caret) unless told which
   scheme is in use. Everything below exists so a control looks the same in
   light and dark, and never falls back to greyed-out native styling. */
button, input, select, textarea {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}
button { -webkit-appearance: none; appearance: none; background: none; }
button:not(:disabled) { cursor: pointer; }

input[type="text"], input[type="number"], input[type="search"],
input[type="email"], input[type="password"], input[type="date"], select, textarea {
  background: var(--field);
  color: var(--ink);
  border: 1px solid var(--field-border);
  border-radius: 7px;
  padding: .35rem .55rem;
  -webkit-appearance: none; appearance: none;
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
}
/* A placeholder is not a value and must not be able to pass for one. Muted
   alone did not do it: --muted is the same ink the notes under a field are
   written in, so an example like "DOLO 650 TAB" sat in an empty box reading
   exactly like a filled one. Lighter, italic, and never bold however bold the
   field's own text is - the same slant the register uses for a form it has
   only read off a name rather than been told. */
input::placeholder, textarea::placeholder {
  color: var(--muted); opacity: .62; font-style: italic; font-weight: 400;
}
/* Which only works if what is typed is unmistakably darker than that. */
input, textarea, select { color: var(--ink); }
input:disabled, textarea:disabled, select:disabled,
input[readonly] { background: var(--field-disabled); color: var(--muted); cursor: default; }

/* Chrome forces a pale background on autofilled fields; paint over it. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ink);
  -webkit-box-shadow: 0 0 0 100px var(--field) inset;
  box-shadow: 0 0 0 100px var(--field) inset;
  caret-color: var(--ink);
}
/* The number spinners are drawn by the browser and cannot be themed; the value
   is what matters in a table cell, so hide them and keep the field full width. */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; }

/* The three editors reuse the shared field colours rather than the panel colour,
   so a field is always visibly recessed against the card behind it. */
tr.editing input, #totals-table input, .signoff-form input {
  background: var(--field); color: var(--ink); border-color: var(--accent);
}
.signoff-form input { border-color: var(--field-border); }
.signoff-form input:focus { border-color: var(--accent); }

/* A CSV has no rendering, so the raw file stands in for the page image. */
.csvpane { background: var(--bg); }
.rawcsv {
  margin: 0; padding: .9rem 1rem; height: 100%; overflow: auto;
  font-family: 'IBM Plex Mono', ui-monospace, Menlo, monospace;
  font-size: .74rem; line-height: 1.6; color: var(--ink);
  white-space: pre; tab-size: 4;
}

/* Where a bill came from matters: a CSV export names its supplier only in the
   filename, so it is worth seeing at a glance which records those are. */
.srcbadge {
  display: inline-block; padding: .05rem .4rem; border-radius: 5px;
  font-size: .7rem; font-weight: 700; letter-spacing: .04em;
  border: 1px solid var(--field-border); color: var(--muted); cursor: help;
}
.srcbadge.src-csv { color: var(--free); border-color: var(--free); }

.banner.ok-banner { background: var(--ok-bg); border-color: var(--ok); color: var(--ok); }
select {
  background: var(--field); color: var(--ink); border: 1px solid var(--field-border);
  border-radius: 7px; padding: .3rem .5rem; font: inherit; font-size: .85rem;
  -webkit-appearance: none; appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: right 12px center, right 7px center;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 26px;
}
input[type="file"] {
  font-size: .85rem; padding: .3rem; border: 1px dashed var(--field-border);
  border-radius: 7px; background: var(--field); color: var(--ink); width: 100%;
}
input[type="file"]::file-selector-button {
  font: inherit; font-size: .8rem; margin-right: .6rem; padding: .25rem .6rem;
  border: 1px solid var(--field-border); border-radius: 6px;
  background: var(--panel); color: var(--ink); cursor: pointer;
}

.inline-form { display: inline-block; margin: .5rem .4rem 0 0; }
.banner strong { display: inline; }

/* ---- Formats page: the file beside the decisions ------------------------ */
.formatlayout { display: grid; grid-template-columns: minmax(320px, 40%) 1fr;
  gap: 1.1rem; align-items: start; }
.formatlayout.solo { grid-template-columns: 1fr; }
.formatlayout.solo .formatsource { display: none; }
.formatwork { min-width: 0; }
.formatsource {
  position: sticky; top: calc(var(--bar) + 4.6rem);
  height: calc(100vh / var(--z, 1) - var(--bar) - 6rem);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: var(--shadow);
}
.sourcebar {
  display: flex; align-items: center; justify-content: space-between; gap: .6rem;
  padding: .5rem .7rem; border-bottom: 1px solid var(--line); font-size: .85rem;
}
.formatsource object, .formatsource embed { flex: 1; width: 100%; border: 0; background: #52565c; }
.formatsource .rawcsv { flex: 1; margin: 0; }
.formatsource .gridwrap { flex: 1; }

/* The file as a grid. A vendor export is 60-odd columns wide, so the table
   scrolls sideways inside its own pane - the page itself must never scroll
   horizontally - and the heading row and row numbers stay pinned, because a
   column is unidentifiable once its heading has scrolled out of sight. */
.gridwrap { overflow: auto; background: var(--bg); }
.csvgrid {
  border-collapse: separate; border-spacing: 0;
  font-family: 'IBM Plex Mono', ui-monospace, Menlo, monospace;
  font-size: .72rem; line-height: 1.5; color: var(--ink);
}
.csvgrid th, .csvgrid td {
  border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
  padding: .22rem .5rem; text-align: left; white-space: nowrap;
  max-width: 22ch; overflow: hidden; text-overflow: ellipsis;
}
.csvgrid thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--panel); color: var(--ink); font-weight: 700;
  vertical-align: bottom;
}
/* The row-number column pins both ways, so it has to outrank the heading row
   at the corner where the two overlap. */
.csvgrid .rownum {
  position: sticky; left: 0; z-index: 1;
  background: var(--panel); color: var(--muted); font-weight: 400;
  text-align: right; font-variant-numeric: tabular-nums;
}
.csvgrid thead .rownum { z-index: 3; }
.csvgrid tbody tr:hover td { background: var(--field); }
/* The heading's own position in the file, which is what the mapping form
   opposite is numbered by. */
.csvgrid .colnum {
  display: block; font-size: .62rem; font-weight: 400; color: var(--muted);
  letter-spacing: .04em;
}
.csvgrid th.undecided { color: var(--warn); }
.csvgrid th.undecided .colnum { color: var(--warn); }
.readsas { color: var(--ok); margin-top: .15rem; }
.dateorder { display: block; margin-top: .4rem; font-size: .78rem; color: var(--muted); }
.dateorder select { margin-top: .15rem; font-size: .8rem; }

.gridmore {
  margin: 0; padding: .5rem .7rem; font-size: .75rem; color: var(--muted);
  border-top: 1px solid var(--line); position: sticky; left: 0;
}
.floatshow { position: fixed; left: 1rem; bottom: 1rem; z-index: 20; }
@media (max-width: 900px) {
  .formatlayout { grid-template-columns: 1fr; }
  .formatsource { position: static; height: 60vh; }
}


/* The sidebar's contents, wrapped so a narrow screen can fold them into one
   menu. Wide, the wrapper has no box of its own and the button is not drawn:
   nav, switch and account are the sidebar's flex children exactly as before. */
.topmenu { display: contents; }
.kebab { display: none; }

/* The sidebar folded to a rail of icons, on a wide screen only. The button is
   a small disc astride the sidebar's edge, level with the mark - on the
   boundary it moves rather than inside the thing it folds. The choice is
   remembered like the appearance and set on <html> before first paint, so a
   page never opens wide and then jumps. */
.navcollapse {
  position: absolute; top: 23px; right: -13px; z-index: 1;
  width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-strong); border-radius: 50%; background: var(--panel);
  box-shadow: var(--shadow); color: var(--muted);
  font: inherit; font-size: 17px; line-height: 1; cursor: pointer;
}
.navcollapse:hover { color: var(--accent); border-color: var(--accent); }
/* Folding is animated: the column narrows over a quarter-second, the words
   fade out ahead of it and back in after, and the chevron turns round. The
   mark, the icons and the initial keep the same left edge in both states,
   so nothing jumps sideways while the width moves. A browser without
   display transitions cuts the words at once, which is what it did before;
   anyone who has asked for less motion gets the instant switch. */
body { transition: grid-template-columns .28s ease; }
.navcollapse span { display: inline-block; transition: transform .28s ease; }
:root[data-sidenav="collapsed"] .navcollapse span { transform: rotate(180deg); }
.topbar > .brand, .topbar nav a { white-space: nowrap; overflow: hidden; transition: padding .28s ease; }
.topbar nav .lbl {
  transition-property: opacity, display; transition-duration: .18s;
  transition-timing-function: ease; transition-behavior: allow-discrete;
}
/* Fading in applies only after a person has folded or unfolded the sidebar
   (the script marks the page): a fresh page load draws the words at once. */
@starting-style {
  :root[data-sidenav-anim] .topbar nav .lbl { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  body, .navcollapse span, .topbar nav .lbl,
  .topmenu, .topbar > .brand, .topbar nav a { transition: none; }
}
@media (min-width: 901px) {
  :root[data-sidenav="collapsed"] body { grid-template-columns: 72px 1fr; }
  :root[data-sidenav="collapsed"] .topbar nav { padding: 12px 16px 20px; }
  :root[data-sidenav="collapsed"] .topbar nav a { padding: 0 0 0 10px; }
  /* Folded, the word an icon stands for appears beside it on hover or focus.
     Drawn fixed from the icon's own place rather than positioned inside the
     link, so the list's scrolling box cannot cut it off at the rail's edge. */
  :root[data-sidenav="collapsed"] .topbar nav a::after {
    content: attr(data-title); position: fixed; left: 84px; z-index: 40;
    padding: 5px 9px; border-radius: 6px; white-space: nowrap; pointer-events: none;
    background: var(--ink); color: var(--panel); font-size: 12px; font-weight: 500;
    box-shadow: var(--shadow); opacity: 0; visibility: hidden;
    transition: opacity .12s ease, visibility .12s;
  }
  :root[data-sidenav="collapsed"] .topbar nav a:hover::after,
  :root[data-sidenav="collapsed"] .topbar nav a:focus-visible::after { opacity: 1; visibility: visible; }
  :root[data-sidenav="collapsed"] .topbar nav .lbl { display: none; opacity: 0; }
  /* A heading with nothing to head becomes the rule between the groups. */
  :root[data-sidenav="collapsed"] .topbar nav .heading {
    font-size: 0; margin: 8px 6px 4px; padding: 0; border-top: 1px solid var(--line); }
  :root[data-sidenav="collapsed"] .topbar nav .heading:first-child {
    margin-top: 0; border-top: 0; }
}
@media (max-width: 900px) { .navcollapse { display: none; } }

/* A narrow window drops the sidebar back to a bar across the top: the brand
   on the left, a menu button on the right, and the links, the appearance
   switch and the account behind it. Wrapped as a row of links the bar stood
   a quarter of a tablet's height tall before the page began. */
@media (max-width: 900px) {
  /* minmax(0, 1fr), not 1fr: a plain 1fr grows to its widest content, and the
     pharmacy's name in the bar pushed every page past the edge of a phone. */
  body { grid-template-columns: minmax(0, 1fr); }
  .topbar .brandtext b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .topbar { flex-direction: row; align-items: center; gap: 8px; height: auto;
    padding: 0 10px; position: sticky; top: 0; overflow: visible;
    border-right: 0; border-bottom: 1px solid var(--line-strong); }
  /* Qualified on .topbar: the base rules for these come later in this file
     and would otherwise win the tie. */
  .topbar .brand { padding: 10px; border-bottom: 0; min-width: 0; }
  .kebab {
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; margin-left: auto; flex: none;
    border: 1px solid var(--line-strong); border-radius: 8px; background: var(--panel);
    color: var(--ink); font: inherit; font-size: 20px; line-height: 1; cursor: pointer;
  }
  .kebab:hover, .topbar.open .kebab { border-color: var(--accent); color: var(--accent); }
  /* Opens with a short fade and settle from just above its resting place;
     closes the same way in reverse. */
  .topmenu {
    display: none; position: absolute; top: calc(100% + 6px); right: 10px; z-index: 30;
    width: min(300px, calc(100vw / var(--z, 1) - 20px)); max-height: calc(100vh / var(--z, 1) - 70px); overflow: auto;
    flex-direction: column; padding: 6px;
    background: var(--panel); border: 1px solid var(--line-strong);
    border-radius: 10px; box-shadow: var(--shadow);
    opacity: 0; transform: translateY(-6px);
    transition-property: opacity, transform, display; transition-duration: .16s;
    transition-timing-function: ease; transition-behavior: allow-discrete;
  }
  .topbar.open .topmenu { display: flex; opacity: 1; transform: none; }
  @starting-style { .topbar.open .topmenu { opacity: 0; transform: translateY(-6px); } }
  /* Crossing the 900px line turns the wrapper from sidebar contents into a
     menu, and that must not animate - the links would cross the bar for a
     frame. The script sets this class for the crossing. */
  .topbar.crossing .topmenu { transition: none; }
  .topbar nav { flex: none; padding: 4px 0; overflow: visible; }
  .topbar .themetoggle { width: calc(100% - 12px); margin: 4px 6px; }
  .topbar .whoami { margin: 4px 0 0; padding: 10px 12px 8px; border-top: 1px solid var(--line); }
  footer { grid-column: 1; }
}

/* ---- the design's inbound shell and register ------------------------- */
.brandtext { display: flex; flex-direction: column; gap: 3px; }
.brandtext b { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.brandtext > span { font-size: 11.5px; color: var(--muted); font-weight: 400; }
.topbar nav a .ic { width: 15px; display: inline-block; opacity: .75; }
.whoami {
  margin-top: auto; padding: 14px 20px 18px; border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 2px;
}
.whoami b { font-size: 12px; font-weight: 500; font-family: 'IBM Plex Mono', monospace; }
.whoami span { font-size: 11px; color: var(--muted); }
.whoami .ver { font-family: 'IBM Plex Mono', monospace; font-size: 10.5px; opacity: .8; }

/* state tiles, as the prototype draws them: label, count, one line of why */
.states { display: flex; gap: 12px; flex-wrap: wrap; margin: 0 0 20px; }
/* A tile at zero is still worth reading, but it is not news. */
.state-tile.quiet .dot { opacity: .3; }
.state-tile.quiet .count { color: var(--muted); }
a.state-tile { text-decoration: none; color: inherit; }
a.state-tile:hover { border-color: var(--accent); }
.state-tile.on { border-color: var(--accent); background: var(--accent-soft); }
.state-tile {
  /* Five of them now that credit notes are counted apart, so the width one
     asks for before the row wraps came down to fit them on a laptop. */
  flex: 1; min-width: 168px; text-align: left; display: flex; flex-direction: column;
  gap: 5px; padding: 14px 16px; background: var(--panel);
  border: 1px solid var(--line-strong); border-radius: 10px;
}
.state-tile .top { display: flex; align-items: center; gap: 7px; }
.state-tile .dot { width: 7px; height: 7px; border-radius: 50%; }
.state-tile .label {
  font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--muted);
}
.state-tile .count { font-size: 23px; font-weight: 600; font-family: 'IBM Plex Mono', monospace; line-height: 1; }
.state-tile .note { font-size: 11.5px; color: var(--muted); }

/* the register: a grid of rows, two lines to a cell where it earns it */
.register { background: var(--panel); border-top: 1px solid var(--line);
  border-radius: 0 0 10px 10px; overflow: hidden; margin: 0 calc(var(--gutter) * -1); }
.reg-head, .reg-row {
  display: grid;
  /* The two reading columns take the space; numbers and actions stay put, so a
     wider window widens the document and sender rather than the figures.

     Handed down from the route as `--reg-cols`, because which tracks there are
     depends on which columns somebody has left on. The full set is written out
     here as the fallback, for the grouped overview and for a page drawn before
     the choice is read. */
  grid-template-columns: var(--reg-cols,
    minmax(164px, 1.618fr) minmax(144px, 1fr) 104px 104px 104px 110px 130px 34px);
  gap: var(--gap); padding: 13px var(--gutter); align-items: center;
}
/* The row's menu ends on the row's own right edge, under the Columns box. */
.reg-row > .rowlinks { justify-content: flex-end; }
/* The headings are a table's (see `th`): its size, weight, spacing and rule,
   and as far under the rule over them and over the one under them as a
   supplier's transactions stand in their card. */
.reg-head {
  font-size: .78rem; font-weight: 550; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted); white-space: nowrap; border-bottom: 1px solid var(--line);
  padding-top: .8rem; padding-bottom: .4rem;
}
.reg-row { border-bottom: 1px solid var(--line); font-size: 13px; }
.reg-row:last-child { border-bottom: 0; }
.reg-row[data-open] { cursor: pointer; }
/* A row that just arrived, held long enough to find it and no longer. */
.reg-row.just-read {
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent);
  animation: just-read .5s ease-out;
}
@keyframes just-read { from { opacity: 0; transform: translateY(-4px); } }
.reg-row:hover { background: var(--bg); }
/* A row that takes money off the account rather than putting a purchase on it. */
.reg-row.reg-credit, tr.reg-credit { background: var(--free-bg); box-shadow: inset 3px 0 0 var(--free); }
.reg-row.reg-credit:hover, tr.reg-credit:hover { background: var(--free-bg); }
.reg-row.reg-credit .num, tr.reg-credit .num { color: var(--free); }

.reg-row .strong { font-weight: 500; }
.reg-row .under { font-size: 11px; color: var(--muted); margin-top: 2px; }
.reg-row a { color: inherit; text-decoration: none; }
.reg-row a:hover { color: var(--accent); }
/* A bill found as a whole - by its number, or by who sent it - marks what matched. */
.reg-row a.numhit, .reg-row .suphit {
  background: var(--accent-soft); color: var(--accent); border-radius: 4px;
  padding: 0 .25rem; margin-left: -.25rem;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
/* The register grouped: one row a supplier or a sender, which opens its own
   documents as the register below the bar naming it. */
.reg-ghead, .reg-group {
  display: grid; grid-template-columns: minmax(220px, 2fr) 96px 90px 96px 130px 120px 16px;
  gap: var(--gap); padding: 13px var(--gutter); align-items: center;
}
.reg-ghead {
  font-size: .78rem; font-weight: 550; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted); white-space: nowrap; border-bottom: 1px solid var(--line);
  padding-top: .8rem; padding-bottom: .4rem;
}
.reg-group { border-bottom: 1px solid var(--line); font-size: 13px; color: inherit; text-decoration: none; }
.reg-group:hover { background: var(--bg); text-decoration: none; }
.reg-group .under { display: block; font-size: 11px; color: var(--muted); margin-top: 2px; font-weight: 400; }
.reg-group .arrow { color: var(--muted); font-size: 1.1rem; }
.reg-group:hover .arrow { color: var(--accent); }
/* Nothing to list: said where the first row would be, lined up with the
   columns rather than against the panel's edge. */
.register > .reg-empty { margin: 0; padding: 22px var(--gutter); font-size: 13px; }
.register > .reg-empty b { color: var(--ink); font-weight: 600; }
.register > .reg-empty a { margin-left: .35rem; }
.reg-opened {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 8px 12px; padding: 12px var(--gutter); border-bottom: 1px solid var(--line-strong);
  background: var(--bg); font-size: 13px;
}
.vault {
  margin-top: 14px; padding: 0 var(--gutter) 14px; font-size: 11.5px; color: var(--muted); line-height: 1.6;
}
.vault code { font-size: 11px; }
@media (max-width: 1100px) {
  .reg-head { display: none; }
  .reg-row { grid-template-columns: 1fr 1fr; gap: 8px 16px; }
  .reg-ghead { display: none; }
  .reg-group { grid-template-columns: 1fr auto; gap: 6px 16px; }
}

/* theme switch ---------------------------------------------------------- */
.themetoggle {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: calc(100% - 20px); margin: 0 10px 10px; padding: 7px 10px;
  border: 1px solid var(--line-strong); border-radius: 7px; background: transparent;
  font: inherit; font-size: 12px; color: var(--muted); cursor: pointer;
}
.themetoggle:hover { border-color: var(--accent); color: var(--accent); }
a.themetoggle { text-decoration: none; }
a.themetoggle.on { border-color: var(--accent); color: var(--accent); }

/* The header across the top of the page: the mark and the shop's name at
   one end, and at the other the initial of whoever is signed in. The name
   headed the sidebar and folded away with it; the appearance, the reading
   aids and the account stood at the sidebar's foot, under eleven links. They
   are listed under the initial now, when the pointer is on it or the keyboard
   is in it, and the sidebar is the links alone, under the header.
   Fixed rather than sticky because all of it is still the sidebar's own
   markup - on a narrow screen the name is its bar and the three are in its
   menu, see .topmenu - so there is no box in the page for a header to stick
   in. --bar is its height, which the page starts under and everything that
   sticks to the top of the page stands clear of, and is nothing where there
   is no header: on a narrow screen and on paper. */
/* --top is how far the page stands in from what is over it. Under the header
   that is the gutter, the same as it stands in from its sides: with a rule
   drawn across the top of the page, 20px over 33px at each side read as a
   page pushed up against it. With no header it is the gap it always was. */
:root { --bar: 0px; --top: var(--gap); }
.pagebar, .acct, .pagemenu { display: contents; }
.avatar { display: none; }
@media screen and (min-width: 901px) {
  :root { --bar: 56px; --top: var(--gutter); }
  html { scroll-padding-top: var(--bar); }
  body { padding-top: var(--bar); }
  .topbar { top: var(--bar); height: calc(100vh / var(--z, 1) - var(--bar)); }
  .pagebar {
    position: fixed; top: 0; left: 0; right: 0; height: var(--bar);
    display: flex; align-items: stretch; justify-content: flex-end;
    padding: 0 var(--gutter); background: var(--panel);
    border-bottom: 1px solid var(--line-strong);
  }
  /* Over the header's left-hand end, at the inset it had in the sidebar. The
     same whether the sidebar is open or folded: it is not the sidebar's now. */
  .topbar > .brand {
    position: fixed; top: 0; left: 0; z-index: 1; height: var(--bar);
    padding: 0 20px; border-bottom: 0;
  }
  /* As tall as the bar, so the list hangs from the bar's own foot and there
     is no gap between the initial and it for the pointer to fall through. */
  .acct { position: relative; display: flex; align-items: center; }
  /* The disc the folded sidebar drew the initial in, now a button. */
  .avatar {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; padding: 0; border: 0; border-radius: 50%;
    background: var(--accent-soft); color: var(--accent);
    font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  }
  .avatar { position: relative; }
  .avatar img {
    position: absolute; inset: 0; width: 100%; height: 100%;
    border-radius: 50%; object-fit: cover;
  }
  .avatar:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  /* The narrow screen's menu, as much of it as these three are: its box, and
     the three in it as they stand there. */
  .pagemenu {
    display: none; position: absolute; top: 100%; right: 0; width: 236px;
    flex-direction: column; padding: 6px;
    background: var(--panel); border: 1px solid var(--line-strong);
    border-radius: 10px; box-shadow: var(--shadow);
  }
  .acct:hover .pagemenu, .acct:focus-within .pagemenu { display: flex; }
  .pagebar .themetoggle { width: calc(100% - 12px); margin: 4px 6px; }
  .pagebar .whoami { margin: 4px 0 0; padding: 10px 12px 8px; border-top: 1px solid var(--line); }
}


/* ---- Register row actions ----------------------------------------------
   One menu instead of four buttons. The row itself is the way to the bill, so
   the actions beside it should not compete with it for the eye or the width. */
.rowmenu { position: relative; }
.rowmenu > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-block; padding: .2rem .5rem; border-radius: 6px;
  border: 1px solid var(--line-strong); background: var(--panel);
  font-size: 11.5px; color: var(--muted); white-space: nowrap;
}
.rowmenu > summary::-webkit-details-marker { display: none; }
.rowmenu > summary::after { content: " \25BE"; }
.rowmenu > summary:hover { color: var(--ink); border-color: var(--muted); }
.rowmenu[open] > summary { color: var(--ink); border-color: var(--accent); }

.rowmenu .menu {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 30;
  min-width: 168px; padding: 4px; display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line-strong);
  border-radius: 9px; box-shadow: var(--shadow);
}
.rowmenu .menu a,
.rowmenu .menu button,
.rowmenu .menu span {
  display: block; width: 100%; text-align: left; padding: .38rem .55rem;
  border: 0; border-radius: 6px; background: none; font: inherit;
  font-size: 12.5px; color: var(--ink); text-decoration: none; cursor: pointer;
  white-space: nowrap;
}
.rowmenu .menu a:hover, .rowmenu .menu button:hover { background: var(--bg); }
.rowmenu .menu form { margin: 0; }
.rowmenu .menu .danger { color: var(--bad); }
.rowmenu .menu [aria-disabled="true"] { color: var(--muted); cursor: default; opacity: .72; }
.rowmenu .menu [aria-disabled="true"]:hover { background: none; }
/* Why it cannot be taken, on a line of its own under the label. After an
   em-dash the two ran together into a longer menu item. */
.rowmenu .menu [aria-disabled="true"] small {
  display: block; margin-top: 1px; font-size: 10.5px; line-height: 1.3;
  font-style: italic; color: var(--muted);
}

/* The last row has to be able to open its menu past the bottom of the panel.
   That was done by letting .register overflow visibly, which also let the rows
   themselves out: the grid's own minimum is about 1120px - the two reading
   columns at their floor plus five fixed ones, the gaps and the gutter - and
   below that the last columns spilled onto the page, where a row's background
   does not reach. The panel ended in its rounded corner and State and Actions
   sat on the grey behind it, which is also where the reading guide's band
   stopped.

   The menu escapes on its own now (`.rowmenu float` draws it in the top layer,
   which no ancestor can clip), so the panel can hold its rows again: it
   scrolls sideways instead, and the rows are at least as wide as their own
   minimum so a tint - or the guide - runs the full width of one. The child
   that forms the bottom edge still carries the corners; without it the drop
   zone's background squares them off. */
.register { overflow-x: auto; }
/* The floor every row stands on: the sum of the columns above at their minimum,
   with a gap between each and the gutter either side. Worked out in the route
   alongside the tracks, since hiding a column takes its width and one gap off
   the floor too; this is what stands when nothing has been chosen. */
.register { --reg-min: calc(894px + var(--gap) * 7 + var(--gutter) * 2); }
.reg-head, .reg-row, .reg-group { min-width: var(--reg-min); }
/* The drop zone and the empty line are not rows. They are the panel's own
   bottom edge, and nothing in them lines up with a column, so they do not
   travel sideways when the rows do - scrolled right, a row's figures should
   move under their headings while the place you drop a bill stays where it
   was put. Stuck to the left of the scrollport they keep the width that is on
   screen, which is also why they need no floor: at that width there is never
   a gap beside them to show the page through. */
.reg-drop, .register > .reg-empty { position: sticky; left: 0; }
.reg-drop { border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; }


/* Which columns the register's rows carry, behind one control on the row that
   already says how it is grouped and sorted. The menu is a form, so the boxes
   are ticked and then shown once rather than redrawing the register six times;
   it is wider than a row menu because a column's name sits beside its box. */
.colmenu .menu { min-width: 13rem; padding: 6px; gap: 1px; }
.colpick {
  display: flex; align-items: center; gap: .5rem; width: 100%;
  padding: .4rem .5rem; border-radius: 6px; font-size: 12.5px; cursor: pointer;
}
.colpick:hover { background: var(--bg); }
.colpick input { margin: 0; accent-color: var(--accent); }
.colpick > span:first-of-type { flex: 1 1 auto; }
/* A column that is always there says so rather than offering a box that does
   nothing: the document is the way into the bill, and Actions the way to its
   menu. */
.colpick.fixed { color: var(--muted); cursor: default; }
.colpick.fixed:hover { background: none; }
.colmenu .menu label span { display: inline; width: auto; padding: 0; white-space: nowrap; }
.colmenu .menu button.minibtn {
  display: flex; width: 100%; margin-top: 5px; justify-content: center;
  padding: .4rem .6rem; text-align: center;
  background: var(--accent); border: 1px solid var(--accent); color: var(--on-accent);
}
.colmenu .menu button.minibtn:hover { background: var(--accent); filter: brightness(1.08); }

/* The line count, sitting with the document it counts. Quiet on purpose - it
   is context for the bill number beside it, not a figure to be read across. */
.count-badge {
  display: inline-block; margin-left: .45rem; padding: .08rem .45rem;
  border: 1px solid var(--line-strong); border-radius: 999px;
  background: var(--bg); color: var(--ink);
  font-size: 10.5px; font-weight: 600; line-height: 1.45;
  text-transform: uppercase; letter-spacing: .05em;
  vertical-align: 1px; white-space: nowrap;
}


/* ---- The printed bill ---------------------------------------------------
   The shop's existing format, ruled the way it comes off their printer. Black
   on white and boxed on purpose: this is the one page that leaves the building
   in a customer's hand, so it does not follow the app's theme. */
.sheet-actions { display: flex; gap: .6rem; align-items: center; margin-bottom: 14px; }
.sheet {
  background: #fff; color: #000; border: 1px solid #000;
  max-width: 860px; margin: 0 auto;
  font-family: Arial, Helvetica, system-ui, sans-serif; font-size: 13px;
  line-height: 1.45;
}
.sheet p { margin: 0; }
.sheet-head { text-align: center; padding: 10px 14px; }
.sheet-head h1 {
  margin: 0 0 6px; font-size: 26px; font-weight: 700; letter-spacing: .2px;
  text-transform: uppercase; color: #000;
}

.sheet-meta {
  display: grid; grid-template-columns: 1.6fr 1fr; gap: 0 24px;
  border-top: 1px solid #000; border-bottom: 1px solid #000; padding: 8px 14px;
}
.sheet-meta dl { margin: 0; display: grid; gap: 2px; }
.sheet-meta dl > div { display: grid; grid-template-columns: 122px 1fr; }
.sheet-meta dt { color: #000; font-weight: 400; }
.sheet-meta dd { margin: 0; }

.sheet-lines { width: 100%; border-collapse: collapse; }
.sheet-lines th, .sheet-lines td {
  border: 1px solid #000; padding: 5px 7px; font-weight: 400; color: #000;
}
/* A figure, a code or a date is one value and never breaks across two lines -
   351.56 set as "351.5" over "6" is a misprint on a bill. The product's name is
   the column that wraps, and a name with no space in it breaks rather than
   widening the table past the frame. */
.sheet-lines td { white-space: nowrap; }
.sheet-lines td:nth-child(2) { white-space: normal; overflow-wrap: break-word; }
/* A screen narrower than the bill scrolls it inside its frame rather than
   letting the columns hang outside it. Paper is wide enough, and prints whole. */
@media screen { .sheet { overflow-x: auto; } }
.sheet-lines th { text-align: center; }
.sheet-lines td.c, .sheet-lines th.c { text-align: center; }
/* Fields sitting inside a table row on the product master. Sized to the
   column rather than to a form: this is a register being annotated, not a
   page being filled in, and a row of full-height inputs would bury the names
   the register is read by. */
/* What the medicine is, alongside what the bill said about it. Set off from
   the invoice's own columns because they answer to different records: correct
   a rate and you have corrected this bill, set a schedule and you have said
   something about the medicine that outlives it. */
.lines th.pfact, .lines td.pfact { background: var(--tint, rgba(0,0,0,.02)); }
.lines th.pfact:first-of-type, .lines td.pfact:first-of-type {
  border-left: 1px solid var(--line-strong);
}
/* Read off the name, not stated by anybody. Shown because a usable default
   beats four empty cells on every line of every delivery, and marked because
   a guess that looks like a decision is worse than no guess. */
.lines .guess { color: var(--ink-soft); font-style: italic; }
.lines td.pfact select { padding: 3px 4px; font-size: 12px; max-width: 100%; }

.cellsel, input.cellnum {
  padding: 4px 6px; font-size: 12px; border: 1px solid var(--line-strong);
  border-radius: 6px; background: var(--raised); color: inherit; font-family: inherit;
  max-width: 100%;
}
input.cellnum { width: 74px; text-align: right; }
/* Composition is a sentence, not a code - given room to be read back. */
input.celltext { width: 190px; }

/* The row the form above is holding. A tint alone reads as decoration in a
   long table, so it carries a bar down its leading edge as well - the same
   mark the till uses for a line that needs looking at, which is the one thing
   a highlighted row ever means. */
tr.marked td { background: var(--accent-soft); }
tr.marked td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
/* The tint pill is accent-on-accent-soft, which is the marked row's own
   background - so on the one row it is ever shown it was invisible. Given the
   panel colour instead, it reads as the chip it is. */
tr.marked .pill.tint { background: var(--panel); }
/* Landed on from the banner's "show it below", so it should not sit under the
   sticky page header when it gets there. */
tr.marked { scroll-margin-top: 90px; }


/* A file picker that reads as a button. The native control draws a "Choose
   file / no file selected" of its own that no styling reaches, and it sits
   beside two real buttons here - so the input is hidden and its label wears
   the button, which is what a label is for. */
.csvin { display: inline; }
.csvin input[type=file] {
  position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;
}
.csvin label { cursor: pointer; }
.csvin input[type=file]:focus-visible + label,
.csvin label:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cellsel:focus, input.cellnum:focus { outline: 2px solid var(--accent-bg); border-color: var(--accent); }

/* What the * against a loose line means, under the table it marks. */
/* Under the lines, set in from the frame as the foot below it is; ".sheet p"
   zeroes every paragraph's margin, so this has to say its own. */
.sheet p.sheet-note { margin: 0; padding: 8px 14px 2px; font-size: 12px; }
/* A plain asterisk after the name - not the app's blue .mark badge. */
.sheet-lines .loosemark { font-weight: 700; }
.sheet-lines td.r, .sheet-lines th.r { text-align: right; }
.sheet-lines tbody td { font-variant-numeric: tabular-nums; }
/* Only the outer frame is drawn on the sides, as on the printed original. */
.sheet-lines tr > *:first-child { border-left: 0; }
.sheet-lines tr > *:last-child { border-right: 0; }
/* The line under the last item. The app's tables drop their last row's rule
   ("tbody tr:last-child td") and that outranks the one above, so a printed
   bill's columns ran open into whatever was under them. */
.sheet-lines tbody tr:last-child td { border-bottom: 1px solid #000; }
/* And none over the headings: the block above them already rules them off,
   and the two lines together printed as one line twice as thick. */
.sheet-lines thead th { border-top: 0; }

.sheet-foot {
  display: grid; grid-template-columns: 1.6fr 1fr; gap: 0 24px;
  padding: 10px 14px; border-bottom: 1px solid #000;
}
.sheet-foot .tax .gap { display: inline-block; margin-left: 96px; }
/* The amount in words sits on the line of the net amount it spells out: the
   left column runs the height of the totals, and the words go to its foot. */
.sheet-foot .tax { display: flex; flex-direction: column; }
.sheet-foot .tax .words { margin-top: auto; padding-top: 8px; }
.sheet-foot .cancelled { margin-top: 8px; font-weight: 700; letter-spacing: .06em; }
.totals { margin: 0; display: grid; gap: 3px; }
.totals > div { display: grid; grid-template-columns: 1fr 12px 90px; align-items: baseline; }
.totals dt, .totals dd { margin: 0; color: #000; }
.totals dd.r { text-align: right; font-variant-numeric: tabular-nums; }
.totals .net dt, .totals .net dd { font-size: 20px; font-weight: 700; padding-top: 4px; }

.sheet-sign { text-align: center; padding: 26px 14px 12px; }

/* The Schedule X and NRx register on paper. Wider than a bill, because it
   names the patient, the prescriber and the drug on one line; the names and
   addresses are the columns that wrap, never a date or a batch. */
.sheet.wide { max-width: 1180px; }
/* A bill rules its headings off with the block of invoice details above them,
   so `.sheet-lines thead th` draws no top border. The register has no such
   block - its letterhead runs straight into the table - and the headings were
   left with no line over them at all. Drawn here, where it cannot double the
   bill's. */
.registerlines thead th { border-top: 1px solid #000; }
.registerlines td { vertical-align: top; }
.registerlines td.wrap { white-space: normal; overflow-wrap: break-word; }
/* Cancelled: printed, because the medicine did leave the shelf, and greyed so
   it is not read as a sale that stood. */
.registerlines tr.void td { color: #555; }
/* The day a run of sales was made on, across the whole width, over them. */
.registerlines tr.dayrow td { font-weight: 700; padding-top: 7px; }
/* Two signatures at the foot - the pharmacist's on the left, the shop's on the
   right - with room above each line to sign in. */
.sheet-signs {
  display: flex; justify-content: space-between; gap: 24px; padding: 46px 14px 12px;
}
.sheet-signs p { border-top: 1px solid #000; padding-top: 4px; min-width: 240px; text-align: center; }

@media print {
  @page { margin: 10mm; }
  body { background: #fff; }
  .noprint, .topbar, .sidebar, nav, footer { display: none !important; }
  .sheet { border: 1px solid #000; max-width: none; margin: 0; }
  /* Nothing else on the page should print, and nothing should be clipped. */
  body, main, .page, .wrap { padding: 0 !important; margin: 0 !important;
                             display: block !important; }
}

/* Vendors -------------------------------------------------------------
   The distributors, their orderbook and their bills. Built out of what the
   rest of this desk already uses - panels, pills and the sign-off form's
   field metrics - so a vendor page reads as another page of the same book. */
.banner.err-banner { background: var(--bad-bg); border-color: var(--bad); color: var(--bad); }
.pill.tint { background: var(--accent-soft); color: var(--accent); }

.crumb { margin: 0 0 .3rem; font-size: .82rem; }
.crumb a { color: var(--muted); text-decoration: none; }
.crumb a:hover { color: var(--accent); }

/* Same metrics as .signoff-form: these are the same kind of row, and two
   near-identical field styles on one desk is how they drift apart. */
.vform { display: flex; gap: .7rem; align-items: flex-end; flex-wrap: wrap; }
.vform label {
  display: flex; flex-direction: column; gap: .2rem;
  font-size: .78rem; color: var(--muted); text-transform: uppercase; letter-spacing: .03em;
}
.vform label.grow { flex: 1 1 260px; }
.vform input {
  font: inherit; font-size: .9rem; text-transform: none; letter-spacing: 0;
  padding: .35rem .55rem; border: 1px solid var(--field-border); border-radius: 7px;
  background: var(--field); color: var(--ink); min-width: 160px;
}
.vform input:focus { border-color: var(--accent); outline: none; }

/* Kept apart from the save row above it: forgetting is not a variant of
   saving, and a button that undoes the page should not sit in the same line
   as the one that keeps it. */
.danger-row {
  display: flex; align-items: center; gap: .7rem;
  margin-top: 1rem; padding-top: .9rem; border-top: 1px solid var(--line);
}
.banner.warn-banner { background: var(--warn-bg); border-color: var(--warn); color: var(--warn-ink); }
/* Two ways into the same step, so the row says they are alternatives. */
.importrow .orsep {
  align-self: center; padding-bottom: .4rem;
  font-size: .78rem; color: var(--muted); text-transform: uppercase; letter-spacing: .06em;
}
.importrow select, .importrow input[type=file] { min-width: 200px; }
.importrow input[type=file] {
  font: inherit; font-size: .85rem; text-transform: none; letter-spacing: 0;
}

/* ---- the supplier screen ------------------------------------------------
   The directory and one supplier's account, side by side. The list stays on
   screen because the question asked here is comparative - who is owed money,
   who is late - and a list you have to navigate back to is a list you stop
   checking. Cut on the phi scale like every other split on this desk: the
   directory to the account as 1 to phi squared, the step the gutter already
   takes. A rail is not a reading column, and at 1 : phi it took the width the
   transactions table needs and set it scrolling sideways. Never under the
   260px it has always had, so a narrow window is as it was, and never over
   phi times that - 421px - past which a list of names is only wider, not
   easier to read. */
.supscreen {
  --rail: 260px;
  display: grid;
  grid-template-columns:
    clamp(var(--rail),
          calc((100% - var(--gap)) / (1 + var(--phi) * var(--phi))),
          calc(var(--rail) * var(--phi)))
    minmax(0, 1fr);
  gap: var(--gap); align-items: start;
}
/* Folded away, the account has the window. Kept in this browser like the
   sidebar's own fold - see app.js - so it is on before the page paints and
   there is no round trip to open or close it. */
:root[data-supdir="closed"] .supscreen { grid-template-columns: 0px minmax(0, 1fr); column-gap: 0; }
:root[data-supdir="closed"] .supdir { display: none; opacity: 0; }
/* Folding is animated, the way the sidebar's fold is: the directory's column
   closes over a quarter-second and the account widens into it, the list
   fading out ahead of the column and back in behind it. A column can only
   narrow to nothing if it is still there, so folded it is a column of 0px
   with no gap rather than no column, and the account is placed in the second
   one by name - with the list gone from the page it would otherwise drop into
   the first. The list holds its narrowest width while its column moves, so
   its rows are not squeezed on the way. A browser without display transitions
   cuts the list at once, which is what it did before; anyone who has asked
   for less motion gets the instant switch. */
:root[data-supdir="closed"] .supscreen > .suppane { grid-column: 2; }
.supscreen { transition: grid-template-columns .28s ease, column-gap .28s ease; }
.supdir {
  transition: opacity .18s ease .1s, display .28s;
  transition-behavior: allow-discrete;
}
:root[data-supdir="closed"] .supdir { transition-delay: 0s; }
/* Fading in applies only after a person has folded or unfolded the list (the
   script marks the page): a fresh page load draws it at once. */
@starting-style { :root[data-supdir-anim] .supdir { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .supscreen, .supdir { transition: none; } }
/* The way back, in the account's heading. In the page whichever way the fold
   stands, so there is no flash of it on a screen where the list is open. */
.supdir-open { display: none; }
:root[data-supdir="closed"] .supdir-open {
  display: inline-flex; align-items: center; margin-bottom: .6rem;
  padding: 4px 8px; border: 1px solid var(--line-strong); border-radius: 7px;
  background: var(--panel); color: var(--muted);
  font: inherit; font-size: .8rem; cursor: pointer;
}
:root[data-supdir="closed"] .supdir-open:hover { color: var(--ink); border-color: var(--accent); }
.supdir-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.supscreen .tiny { font-size: .76rem; line-height: 1.45; }
.supscreen .lbl, .statement .lbl {
  margin: 0; font-size: .72rem; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em;
}

/* A heading inside the transactions, so an upload's bills are grouped with
   their account rather than listed above it. Sticky with the rest of the head
   would be one bar too many on a scrolling table, so it scrolls with its
   group. */
.lines tr.groupbar th {
  text-align: left; font-weight: 600; font-size: .74rem; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted); background: var(--bg);
  padding: .5rem .6rem; border-top: 1px solid var(--line); white-space: normal;
}
.lines tr.groupbar:first-child th { border-top: 0; }
.lines tr.groupbar th .warn { text-transform: none; letter-spacing: 0; }
/* The rows that came with it, held long enough to find them and no longer -
   the register marks a fresh upload the same way. */
.lines tr.just-read td { background: var(--accent-soft); }
.lines tr.just-read td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
/* A bill whose totals do not reconcile: the same edge, in red, and after the
   one above so that a bill just read that also needs a look shows the red. */
.lines tr.to-look td:first-child { box-shadow: inset 3px 0 0 var(--bad); }

/* Sticky rather than fixed: the page scrolls as one document, and only the
   list of suppliers scrolls inside itself once it is longer than the window. */
.supdir {
  position: sticky; top: calc(var(--bar) + var(--top));
  background: var(--panel); border: 1px solid var(--line-strong); border-radius: 10px;
  box-shadow: var(--shadow); overflow: visible; min-width: var(--rail);
  display: flex; flex-direction: column;
  max-height: calc(100vh / var(--z, 1) - var(--bar) - var(--top) * 2);
}
.supdir-head {
  padding: 14px 14px 12px; display: flex; flex-direction: column; gap: 10px;
  border-bottom: 1px solid var(--line);
}
.supdir-top { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.supdir-top h2 { margin: 0; font-size: 1rem; font-weight: 600; white-space: nowrap; }
.supdir-top h2 .count { margin-left: .3rem; }
.supdir-narrow { display: flex; align-items: center; justify-content: flex-end; gap: 4px; }
/* Set in the same pill as the filters inside the menu, and lit the same way,
   because they answer the same kind of question about the list. Words rather
   than glyphs: "by name" and "by date" have no icon anybody reads at 26px. */
.sortbtn {
  display: inline-flex; align-items: center; justify-content: center;
  height: 2.1rem; box-sizing: border-box; padding: 0 .6rem; border-radius: 8px;
  text-decoration: none; border: 1px solid var(--field-border);
  background: var(--panel); color: var(--ink-soft);
  font-size: 12px; font-weight: 600; white-space: nowrap;
}
.sortbtn:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.sortbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.sortbtn.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }


/* The fold. A chevron pair rather than a word: the column is 260px and the
   word for what this does is longer than the button beside it. */
.supdir-fold {
  position: absolute; top: 20px; right: -13px; z-index: 3;
  width: 26px; height: 26px; padding: 0; border-radius: 50%;
  border: 1px solid var(--line-strong); background: var(--panel);
  box-shadow: var(--shadow); color: var(--muted);
  font: inherit; font-size: 17px; line-height: 1; cursor: pointer;
}
.supdir-fold span, .supdir-open span { display: inline-block; transition: transform .28s ease; }
/* Shown only while the list is folded, so its chevron always points the way
   back - outwards, where the sidebar's points when that is folded too. */
.supdir-open span { transform: rotate(180deg); margin-right: .3rem; }
.supdir-fold:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
/* Sitting on the edge, its word goes out over the page rather than back over
   the list it would cover. */
.supdir-fold:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.supdir-fold::after {
  content: attr(data-tip); position: absolute; top: 50%; left: calc(100% + 10px);
  transform: translateY(-50%); z-index: 20;
  padding: 5px 9px; border-radius: 6px; white-space: nowrap; pointer-events: none;
  background: var(--ink); color: var(--panel); font-size: 12px; font-weight: 500;
  letter-spacing: 0; box-shadow: var(--shadow); opacity: 0; visibility: hidden;
  transition: opacity .12s ease, visibility .12s;
}
.supdir-fold:hover::after, .supdir-fold:focus-visible::after { opacity: 1; visibility: visible; }


/* No well behind them any more. The cards it was there to give an edge to are
   lines now, and a line needs no edge - the gap between two of them is 2px,
   and a tint under that reads as a second panel inside the first. */
.supdir-list {
  flex: 1 1 auto; overflow: auto; padding: 6px;
  display: flex; flex-direction: column; gap: 0; min-height: 0; min-width: 0;
}

/* The filter field: a box with the magnifier inside it rather than beside it,
   so the row is one control and not two things that happen to be adjacent. */
.supfind {
  display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px;
  border: 1px solid var(--field-border); border-radius: 9px; background: var(--field);
  color: var(--muted);
}
.supfind:focus-within { border-color: var(--accent); }
.supfind input {
  flex: 1; min-width: 0; border: 0; outline: none; background: none;
  font: inherit; font-size: 12.5px; color: var(--ink);
}
.supfind input::placeholder { color: var(--muted); }
.supfind svg { flex: none; }
/* A supplier's transactions and stock ledger, each searched above its table. */
.tablefind { max-width: 22rem; margin: 0 0 .6rem; }
/* In a card's head it is the head's own right-hand half, beside the heading
   that says what is being searched, and carries no margin of its own. */
.panel-head .tablefind { flex: 1 1 15rem; max-width: 24rem; margin: 0; }
/* With a button to narrow the table after it: the two are the head's
   right-hand half together, ending on the card's edge. The
   button is the directory's own, at the height of the box it stands beside -
   2.1rem is 33.6px, and the box is 34. */
.panel-head .tablenarrow { flex: 1 1 15rem; display: flex; justify-content: flex-end; align-items: center; gap: 6px; min-width: 0; }
.panel-head .tablenarrow .sortbtn { flex: none; height: 34px; }
/* A <button> inherits neither the page's face nor a pointer, and the rule was
   written for links. */
button.sortbtn { font-family: inherit; cursor: pointer; }
/* The product master's search and page size, on a line under its heading. */
.masterbar { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin: .2rem 0 .7rem; }
.masterfind { flex: 1 1 260px; max-width: 28rem; }
/* The purchase register is one card: its name and what is in it, the tabs,
   and one row that finds and arranges, then a rule and the rows, which run
   to the card's edges and are its foot. */
.panel.regcard { padding-bottom: 0; margin-bottom: 0; }
.regtop { padding-bottom: 1rem; }
.regcard > .banner { margin-bottom: 1rem; }
.regtop .panel-head { margin-bottom: .85rem; }
.regtop h1 { line-height: 1.25; }
.regtop .sub { margin: 0; font-size: .85rem; }
/* The rule under the tabs runs to the card's edges, and what is open is said
   by its colour and its count rather than by a second rule over that one. */
.regtop .tabrow {
  justify-content: flex-start; gap: 0; border-bottom-color: var(--line);
  margin: 0 calc(var(--gutter) * -1) 15px; padding: 0 var(--gutter);
}
.regtop .tabrow .tabs { flex: 0 1 auto; }
.regtop .tab { padding: .7rem .8rem; }
.regtop .tab.on { color: var(--accent); border-bottom-color: transparent; }
.regtop .tab .count, .suptab .tab .count {
  display: inline-block; margin-left: .5rem; padding: 0 .5rem; border-radius: 999px;
  background: var(--line); color: var(--muted);
  font-size: 12px; font-weight: 600; line-height: 1.7; font-variant-numeric: tabular-nums;
}
.regtop .tab.on .count, .suptab .tab.on .count { background: var(--accent-soft); color: var(--accent); }
.regtop .tab .count.bad { background: var(--bad-bg); color: var(--bad); }
/* The stock is no tab of the register, and a rule before it says so. */
.regtop .tabrow > .tab { position: relative; margin-left: 1.5rem; }
.regtop .tabrow > .tab::before {
  content: ""; position: absolute; left: -.75rem; top: 50%; height: 1.6rem;
  transform: translateY(-50%); border-left: 1px solid var(--line-strong);
}
/* Finding on the left, arranging on the right; each half wraps on its own. */
.regbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; }
/* Dropped to a line of their own they keep to the right, under Upload. */
.regbar > .rowlinks { gap: .5rem; flex-wrap: wrap; margin-left: auto; }
/* The search field gives way before the months drop under it. */
.regfind { flex: 1 1 28rem; flex-wrap: wrap; gap: 10px; min-width: 0; margin: 0; }
.regfind .supfind { flex: 1 1 12rem; max-width: 20rem; }
/* The expiry months as one box: what they are of, then from and to. The box
   is the search field's - its height, border and corners - and the two inputs
   inside carry none of their own. */
.rangebox {
  display: inline-flex; align-items: center; height: 34px; min-width: 0; max-width: 100%;
  border: 1px solid var(--field-border); border-radius: 9px; background: var(--field);
  font-size: 12.5px; color: var(--muted); white-space: nowrap;
}
.rangebox:focus-within { border-color: var(--accent); }
.rangebox > span:first-child {
  align-self: stretch; display: flex; align-items: center; padding: 0 10px;
  border-right: 1px solid var(--line);
}
.rangebox input {
  flex: 0 1 8.2rem; min-width: 0; height: 100%; padding: 0 4px 0 8px; border: 0; outline: none; background: none;
  font: inherit; color: var(--ink);
}
/* A choice shown as its name and what is chosen, in one box at the search
   field's height: the summary of a menu, which opens the app's own list of
   the choices under it rather than the browser's. */
.rowmenu > summary.pick {
  position: relative; display: inline-flex; align-items: center; gap: 6px;
  height: 34px; padding: 0 10px; box-sizing: border-box;
  border: 1px solid var(--line-strong); border-radius: 9px; background: var(--raised);
  font-size: 13px; color: var(--muted); white-space: nowrap; cursor: pointer;
}
.pick b { color: var(--ink); font-weight: 600; }
.pick .chev { flex: none; color: var(--ink); }
/* A word about the choice, in the choice's own case. */
.pick .pill { text-transform: none; letter-spacing: 0; }
.rowmenu > summary.pick:hover { border-color: var(--muted); }
.rowmenu > summary.pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.rowmenu[open] > summary.pick { border-color: var(--accent); }
.rowmenu > summary.pick::after { content: none; }
/* The choices, one a line and at least as wide as the box they hang from;
   the one chosen is said the way the directory's ordering says its own. A
   long list - a supplier of many years - scrolls inside the menu. */
.pickmenu .menu { min-width: 100%; max-height: 18rem; overflow-y: auto; }
.pickmenu .menu a { display: flex; align-items: center; padding: .45rem .6rem; font-size: 13px; }
.pickmenu .menu [aria-checked="true"] { font-weight: 600; }
.pickmenu .menu [aria-checked="true"]::after { content: "\2713"; margin-left: auto; padding-left: 1rem; color: var(--accent); }
.reg-row .under.match { color: var(--ink); }
.regspread { display: grid; gap: 3px; margin: 0 0 .6rem; }

.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
/* What narrows the directory and what orders it, on one line: the chips take
   the room they need and wrap, and the order sits at the end as one icon. A
   control spelled out in words took a line of its own in a column this narrow,
   and a line of its own under the chips reads as a heading for the cards.
   Aligned to the first row of chips, not the middle, so the icon stays put as
   chips come and go with the suppliers. */
/* Which suppliers are listed and in what order are two questions, so the one
   menu that answers them groups them under a heading each. `.sel-grouphead` is
   the counter's name for this; the desk had none, and a class the other half
   has styles nothing here. */
.menu-grouphead {
  margin: 6px 0 2px; padding: 0 .55rem; font-size: 10px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase; color: var(--muted);
}
.rowmenu .menu > .menu-grouphead:first-child { margin-top: 2px; }
/* The filters are menu items like the orders below them, not pills inside the
   menu: a row of tags among the lines reads as a different kind of control,
   where the question - which suppliers, in what order - is one. `display:
   contents` so the group that the filtering script looks for does not become
   a box between the menu and its items. */
.supsort .menu #supchips { display: contents; }
.supsort .menu .chip {
  display: block; width: 100%; padding: .38rem .55rem; border: 0; border-radius: 6px;
  background: none; color: var(--ink); font-size: 12.5px; font-weight: 400;
  text-align: left; white-space: nowrap;
}
.supsort .menu .chip:hover { background: var(--bg); border: 0; }
.supsort .menu .chip .count {
  display: inline; width: auto; padding: 0; margin-left: .3rem;
  font-size: inherit; font-weight: 400; color: var(--muted);
}
.supsort .menu [aria-checked="true"] { font-weight: 600; }
.supsort .menu [aria-checked="true"]::after { content: " \2713"; color: var(--accent); }
.chip {
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  padding: 5px 10px; border-radius: 99px;
  border: 1px solid var(--line-strong); background: var(--panel); color: var(--muted);
}
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

/* One supplier, as two lines rather than a card: the name and what they owe,
   then where they are and how they stand. The whole line is the link - at this
   size a name-only target is a miss waiting to happen, and the figure is what
   people aim at anyway. */
.sup {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 10px;
  padding: 9px 10px; border-radius: 8px; text-decoration: none; color: inherit;
}
/* A hairline between one supplier and the next. Every visible line but the
   first takes it: `:first-child` would put a rule under the first one the
   filter hides, because hiding here is `hidden` on the line and not a line
   taken out of the list. */
.sup:not([hidden]) ~ .sup:not([hidden]) { border-top: 1px solid var(--line-strong); }
.sup:hover { background: rgba(28, 36, 48, .045); }
.sup:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* The filter hides rows with the `hidden` property, and a display of its own
   would quietly outrank it - the row would stay on screen, filtered out. */
/* `!important`, and not for want of trying a selector. Hiding a line is the
   whole of the directory's search and its filters - the filter sets `hidden`
   and nothing else - and the stacked layouts below set `display: flex` on
   `.sup` to lay the list out as a rail. One of them outranks any selector that
   could be written here and the other ties and comes later, so under 1150px
   `hidden` stopped meaning anything: typing in the box filtered nothing, the
   filters filtered nothing, and the list looked broken rather than busy. This
   rule has to beat whatever a future layout sets, because every one of them
   will want to say how a line is laid out and none of them is thinking about
   this. */
.sup[hidden] { display: none !important; }
/* The one that is open, filled rather than ringed: at two lines a ring around
   a line is a box drawn round a sentence, and the figures here are read down
   the column rather than against each other. */
.sup.on { background: var(--accent-soft); }
.sup.on .sup-title { color: var(--accent); }

.sup-title {
  font-size: .85rem; font-weight: 600; letter-spacing: -.2px; color: var(--ink);
  overflow-wrap: anywhere;
}
.sup-owed {
  font-size: .85rem; font-weight: 600; text-align: right; white-space: nowrap;
  font-variant-numeric: tabular-nums; color: var(--muted);
}
.sup .meta {
  font-size: .75rem; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sup-state { font-size: .75rem; text-align: right; white-space: nowrap; color: var(--muted); }
/* How they stand, said in the figure's own colour as well as in the word under
   it, so the column can be scanned for late money without reading it. */
.sup-owed.warn, .sup-state.warn { color: var(--warn-ink); }
.sup-owed.bad, .sup-state.bad { color: var(--bad); }
.sup-owed.ok, .sup-state.ok { color: var(--ok); }
/* The contact detail a search found them by, which the line does not show. */
.sup-hit {
  grid-column: 1 / -1; font-size: .75rem; color: var(--accent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The status, with the dot that lets it be told apart at a glance without
   reading it - the list is scanned far more often than it is read. */
.state {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  font-size: 10.5px; font-weight: 600; padding: 3px 8px; border-radius: 6px;
  white-space: nowrap; text-transform: uppercase; letter-spacing: .05em;
}
.state i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.state.ok { background: var(--ok-bg); color: var(--ok); }
.state.warn { background: var(--warn-bg); color: var(--warn-ink); }
.state.bad { background: var(--bad-bg); color: var(--bad); }
.state.mute { background: var(--bg); color: var(--muted); }

.loose {
  flex: none; display: flex; align-items: center; gap: 10px;
  margin: 0 6px 6px; padding: 10px; border-radius: 8px; text-decoration: none;
  background: var(--warn-bg); color: var(--warn-ink);
  border-top: 1px solid transparent;
}
.loose:hover { filter: brightness(.97); }
.loose:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.loose.on { box-shadow: inset 0 0 0 1px var(--warn); }
.loose-n {
  flex: none; display: grid; place-items: center; min-width: 26px; height: 26px;
  padding: 0 6px; border-radius: 7px; background: var(--warn); color: var(--warn-bg);
  font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.loose-say { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.loose-say b { font-size: .82rem; font-weight: 600; }
.loose-say span { font-size: .72rem; opacity: .85; }
.loose > svg { flex: none; opacity: .7; }

/* --- the account beside it --- */
.suppane { min-width: 0; }
/* The name has first claim on the width; when it cannot have 240px beside the
   buttons, the buttons drop to a line of their own rather than squeezing the
   name into a column one word wide. */
.sup-head { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .9rem; margin-bottom: .8rem; }
.sup-head .sup-name { min-width: 0; flex: 1 1 240px; }
.sup-head h1 { margin-bottom: .2rem; }
.sup-head .sub { margin: 0; font-size: .85rem; }
.sup-head .sub .mono { margin-left: 1.1rem; }
.sup-head .headacts { padding-top: .2rem; }

/* Four names on one line, always: on a phone the strip scrolls sideways
   rather than breaking "Stock ledger" over two lines. */
.tabs { display: flex; gap: .2rem; border-bottom: 1px solid var(--line-strong); margin-bottom: var(--gap);
  overflow-x: auto; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tab {
  padding: .5rem .8rem; text-decoration: none; color: var(--muted); flex: none; white-space: nowrap;
  font-size: .9rem; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tab:hover { color: var(--ink); }
.tab.on { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }

/* With no column of cards beside it the open tab has the page to itself, and
   the pane that is left over (the screen with no supplier chosen) is one
   column of panels. */
.supbody { display: flex; flex-direction: column; gap: 0; }
.suptab { min-width: 0; }
.suptab .count { text-transform: none; }
/* The tabs are the head of the card the open one is drawn in: a little room
   over them, so they do not stand against its edge, and the rule under them
   and under each tab's line of figures runs to the card's edges. The year
   stands at their end, level with them. */
.panel.suptab { padding-top: .6rem; }
.suptab > .tabrow {
  align-items: center; flex-wrap: nowrap; border-bottom-color: var(--line);
  margin: 0 calc(var(--gutter) * -1) 1rem; padding: 0 var(--gutter);
}
.suptab > .tabrow .tab { padding: .8rem .8rem; }
.suptab > .tabrow .pickmenu { flex: none; margin: 0 0 0 auto; }
.suptab > .panel-head {
  flex-wrap: wrap; gap: .6rem 1.6rem; border-bottom: 1px solid var(--line);
  margin: 0 calc(var(--gutter) * -1) .4rem; padding: 0 var(--gutter) 1rem;
}
/* The search takes what the figures leave of the line. */
.suptab > .panel-head .tablefind { max-width: none; }
.suptab > .panel-head .hint-text { left: var(--gutter); right: var(--gutter); }
/* Something to do, and the button that does it at the banner's end. */
.banner.act { display: flex; align-items: center; gap: .8rem; margin-bottom: .9rem; }
.banner.act::before { content: ""; flex: none; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.banner.act > span { flex: 1 1 auto; min-width: 0; }
.banner.act > .minibtn { flex: none; }

/* A tab's figures, one line at the head of it: each figure beside its label,
   the pairs set apart by more than the gap inside one. Too narrow for the
   line, a whole pair drops under, never half. A nil amount is said quietly. */
.supfigs { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem 1.6rem; min-height: 34px; align-content: center; }
.supfigs .fig { display: inline-flex; align-items: baseline; gap: .4rem; white-space: nowrap; }
.supfigs .lbl { font-size: .88rem; font-weight: 400; color: var(--muted); letter-spacing: 0; text-transform: none; }
.supfigs .num { font-size: .88rem; font-weight: 650; font-variant-numeric: tabular-nums; }
.supfigs .nil .num { font-weight: 400; color: var(--muted); }
.supfigs .hint { margin-left: 0; align-self: center; }
.supfigs .owed .num { color: var(--bad); }
.supfigs .owed.clear .num { color: var(--ok); }

.supnotes { margin: 0 0 1.1rem; display: flex; flex-direction: column; gap: .3rem; }
.supnotes .note { margin: 0; }
.supnotes form { display: inline-flex; margin-left: .4rem; vertical-align: middle; }

.kvlist { margin: 0; display: flex; flex-direction: column; gap: .5rem; }
.kvlist > div { display: flex; gap: .6rem; align-items: baseline; justify-content: space-between; }
.kvlist dt { flex: none; font-size: .78rem; color: var(--muted); }
.kvlist dd { margin: 0; text-align: right; min-width: 0; overflow-wrap: anywhere; font-size: .88rem; }
/* A figure never breaks across two lines: 5,191.97 wrapped after the comma
   reads as two numbers. The label beside it gives way instead. */
.kvlist.figs dd { white-space: nowrap; }
.kvlist.figs dt { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.kvlist.figs dd.num { font-variant-numeric: tabular-nums; font-weight: 600; }

/* The tabs and the year on one rule: the strip keeps its sideways scroll, the
   year keeps to the right, and on a phone it drops to its own line beneath. */
.tabrow {
  display: flex; align-items: flex-end; justify-content: space-between; flex-wrap: wrap;
  gap: .3rem var(--gap); border-bottom: 1px solid var(--line-strong); margin-bottom: var(--gap);
}
.tabrow .tabs { flex: 1 1 auto; min-width: 0; border-bottom: 0; margin-bottom: 0; }
.tabrow .yearpick { flex: none; margin: 0 0 .4rem auto; }
/* A tab's count in red: the register's Review, while a bill waits on it. The
   plain .bad does not reach it - .count sets its own colour, later. */
.tab .count.bad { color: var(--bad); }
/* The register's row keeps to one line: what stands at its end is one short
   link, and wrapped under six tabs it read as a seventh that had fallen off.
   The tabs give way instead, and scroll sideways as a strip of them does. */
.tabrow.oneline { flex-wrap: nowrap; }
/* Facts read in a sheet: a label and its value on one line, the value
   starting where the labels end rather than pushed to the far edge of a
   680px sheet, where a phone number sat half a screen from its name. */
dialog.sheet .sheetfacts { margin: .9rem 0 1.2rem; gap: .55rem; }
dialog.sheet .sheetfacts > div { justify-content: flex-start; gap: 1rem; }
dialog.sheet .sheetfacts dt { flex: 0 0 7.5rem; }
dialog.sheet .sheetfacts dd { text-align: left; }
/* A detail read off a bill rather than from the record: named as such, and
   the bill a click away, so nobody mistakes the parser's reading for a fact
   the pharmacy wrote down. */
/* A supplier's addresses: one a line, each with the way to take it off. */
.sup-emails { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .15rem; }
.sup-emails li { display: flex; align-items: center; gap: .35rem; min-width: 0; }
.sup-emails li a { overflow-wrap: anywhere; }
.sup-emails form { display: flex; margin: 0; }
.sup-emails .minibtn { padding: 0 .4rem; font-size: .95rem; line-height: 1.4; color: var(--muted); }
.sup-emails .minibtn:hover { color: var(--bad); background: var(--bad-bg); }
.sup-attach { margin: -.4rem 0 1.1rem; padding-top: .9rem; border-top: 1px solid var(--line); }
.sup-attach .banner { margin: 0 0 .7rem; }
.sup-attach-form { display: flex; align-items: flex-end; gap: .6rem; flex-wrap: wrap; margin: 0; }
.sup-attach-form label.grow { flex: 1 1 16rem; min-width: 0; }
.sup-suggest { display: flex; align-items: center; justify-content: space-between; gap: .6rem;
               flex-wrap: wrap; margin: .55rem 0 0; }
.sup-suggest .note { margin: 0; }
.frombill { margin-left: .5rem; font-size: .74rem; color: var(--muted); text-decoration: none;
  border: 1px solid var(--line-strong); border-radius: 999px; padding: .05rem .45rem; white-space: nowrap; }
.frombill:hover { color: var(--accent); border-color: var(--accent); }
/* An i in a ring beside a heading, holding the sentence that used to sit
   under it. Shown on hover, and on focus so a keyboard or a tap reaches it.
   Anchored to the panel's head rather than the icon, so it spans the panel
   and never runs off a narrow screen. */
.panel-head.hinted { position: relative; }
.hint { display: inline-flex; vertical-align: middle; margin-left: .3rem; }
.hint-i {
  width: 18px; height: 18px; padding: 0; border-radius: 50%;
  border: 1px solid var(--line-strong); background: var(--panel); color: var(--muted);
  font: 600 11px/1 'IBM Plex Sans', -apple-system, sans-serif; cursor: default;
  display: inline-flex; align-items: center; justify-content: center;
}
.hint-i:hover, .hint-i:focus-visible { color: var(--accent); border-color: var(--accent); }
.hint-text {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 30;
  padding: .55rem .75rem; border-radius: 8px; background: var(--ink); color: var(--panel);
  font-size: 12.5px; font-weight: 400; line-height: 1.5; letter-spacing: 0; text-transform: none;
  box-shadow: var(--shadow); opacity: 0; visibility: hidden;
  transition: opacity .12s ease, visibility .12s;
}
.hint:hover .hint-text, .hint:focus-within .hint-text { opacity: 1; visibility: visible; }
.yearpick { display: flex; align-items: center; gap: .4rem; }
.yearpick select {
  font: inherit; font-size: .84rem; padding: .25rem .45rem;
  border: 1px solid var(--field-border); border-radius: 7px;
  background: var(--field); color: var(--ink);
}
/* Hidden only once the change handler is attached: with no JavaScript
   the button is the only way to change the year. */
.yearpick[data-wired] button { display: none; }
/* The register a page at a time: where you are on the left, the pages on the
   right. Inset by the rows' own gutter, so the count lines up under the bill
   numbers and Next under the Actions buttons, and clear of the drop zone's
   dashed edge below. */
.pager {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 8px 12px; padding: 12px var(--gutter);
}
.pager-count { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem 1rem; }
.pager-pages { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; }
.pager-gap { color: var(--muted); padding: 0 .15rem; }
.pager .pager-on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); cursor: default; }
.perpage label { white-space: nowrap; }
.pill.info { background: var(--accent-soft); color: var(--accent); }

/* A whole row opens the bill it stands for. Deliberately not `.reg-row`, which
   is the purchase register's own grid and would take these cells out of the
   table they belong to. */
.rowlink[data-open] { cursor: pointer; }
.suptab td.nowrap, .suptab th.nowrap { white-space: nowrap; }
/* Two controls now, so the cell stops paying for a left gutter as well: the
   glyphs carry their own spacing, and the width saved goes back to the bill
   number, which is the column somebody is reading down. */
.suptab td.go { width: 1%; text-align: right; white-space: nowrap; padding-left: 0; }
.suptab td.go .arrow {
  display: inline-block; padding: 0 .15rem 0 .3rem; font-size: 1.1rem; line-height: 1;
  color: var(--muted); text-decoration: none;
}
.rowlink:hover td.go .arrow { color: var(--accent); }
/* Correcting a bill is offered on the row it is wrong on, in the row's own
   quiet ink. Not a bordered button and not the word: either one, repeated down
   the column, took the width the figures need and read louder than them. */
.suptab td.go .rowedit {
  display: inline-block; padding: 0 .1rem; font-size: .9rem; line-height: 1;
  color: var(--muted); text-decoration: none;
}
.rowlink:hover td.go .rowedit { color: var(--ink-soft); }
.suptab td.go .rowedit:hover, .suptab td.go .rowedit:focus-visible { color: var(--accent); }
/* Reading a bill again sits beside correcting it: a form, so a button, dressed
   as the pencil next to it. */
.suptab td.go form { display: inline; margin: 0; }
.suptab td.go button.rowedit { border: 0; background: none; font: inherit; font-size: .9rem; cursor: pointer; }
.suptab td.go button.rowdel:hover, .suptab td.go button.rowdel:focus-visible { color: var(--bad); }
/* Reading again and deleting, a line under the pencil and the arrow, so the
   column is no wider than those two need. */
.suptab td.go .rowmore { display: block; margin-top: .3rem; }
/* Said to a screen reader and to nobody else: the column has an arrow in it,
   and a header cell with nothing in it is a column with no name. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}


/* Stacked, the directory becomes a rail across the top: the cards run
   sideways under the filter, and the account opens directly beneath them.
   Stacked as a column it stood a screen tall, so a supplier tapped on a
   tablet opened out of sight, below the list that was just scrolled. The
   open card is brought into view by the script, so the rail shows where you
   are; a filter still hides the cards it rules out. */
/* The rail keeps to what choosing a supplier needs: the search and the filters
   on one row, and the two lines of each supplier - who, what they owe, how
   they stand. The directory's own totals are read off the account beneath, so
   the rail does not repeat them. The same rules are written twice below, once
   per way of arriving at the stacked layout. */
@media (max-width: 900px) {
  .supscreen { grid-template-columns: 1fr; }
  .supdir { position: static; max-height: none; min-width: 0; }
  .supdir-fold { display: none; }
  .suppane { scroll-margin-top: 72px; }
  .supdir-head { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px 10px; padding: 12px 14px 10px; }
  .supdir-top { flex: 1 0 100%; }
  .supfind { flex: 1 1 200px; }
  .supdir-list {
    flex-direction: row; align-items: stretch; overflow-x: auto; overflow-y: hidden;
    padding: 10px 12px 12px; scroll-snap-type: x proximity; scroll-padding-inline: 12px; }
  .supdir-list .sup { flex: 0 0 auto; width: min(248px, 78vw); scroll-snap-align: start; }
  .supdir-list .sup:not([hidden]) ~ .sup:not([hidden]) {
    border-top: 0; border-left: 1px solid var(--line-strong); }
  .supdir-list .empty { flex: 0 0 100%; }
}
/* The same stacking sooner while the sidebar is open: it takes 246px of the
   window, and between 900 and 1150 wide what was left beside the directory
   was an account pane under 300px. Folded, the rail gives that width back
   and the two columns hold. */
@media (max-width: 1150px) {
  :root:not([data-sidenav="collapsed"]) .supscreen { grid-template-columns: 1fr; }
  :root:not([data-sidenav="collapsed"]) .supdir { position: static; max-height: none; min-width: 0; }
  :root:not([data-sidenav="collapsed"]) .supdir-fold { display: none; }
  :root:not([data-sidenav="collapsed"]) .supdir-head {
    flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px 10px; padding: 12px 14px 10px; }
  :root:not([data-sidenav="collapsed"]) .supdir-top { flex: 1 0 100%; }
  :root:not([data-sidenav="collapsed"]) .supfind { flex: 1 1 200px; }
  :root:not([data-sidenav="collapsed"]) .supdir-list {
    flex-direction: row; align-items: stretch; overflow-x: auto; overflow-y: hidden;
    padding: 10px 12px 12px; scroll-snap-type: x proximity; scroll-padding-inline: 12px; }
  :root:not([data-sidenav="collapsed"]) .supdir-list .sup { flex: 0 0 auto; width: min(248px, 78vw); scroll-snap-align: start; }
  :root:not([data-sidenav="collapsed"]) .supdir-list .sup:not([hidden]) ~ .sup:not([hidden]) {
    border-top: 0; border-left: 1px solid var(--line-strong); }
  :root:not([data-sidenav="collapsed"]) .supdir-list .empty { flex: 0 0 100%; }
}

/* ---- a dialog that is a form -------------------------------------------
   A sheet rather than a page: adding a supplier or recording a payment is done
   while looking at the list or the account, and losing that context to a form
   on its own screen is how the wrong supplier gets paid. */
dialog.sheet {
  width: min(680px, calc(100vw / var(--z, 1) - 2rem)); padding: 1.4rem var(--gutter) 1.2rem;
  border: 1px solid var(--line-strong); border-radius: 14px;
  background: var(--panel); color: var(--ink); box-shadow: var(--shadow);
}
dialog.sheet::backdrop { background: rgba(16, 22, 30, .45); }
/* Two things are called a sheet: this dialog, and the printed bill and credit
   note above. Everything here is scoped to dialog.sheet because the collision
   ran both ways. The bill's rule handed every dialog Arial at 13px and a
   margin of 0 auto, pinning a modal to the top of the window - answered by the
   font and margin below. And these rules, written for .sheet alone, gave the
   bill a dialog's 680px width, padding, rounded corners and shadow, so its
   eleven columns ran past its own frame on screen and on paper. */
dialog.sheet { font: inherit; margin: auto; }
dialog.sheet h2 { margin-bottom: .2rem; font-size: 1.25rem; }
dialog.sheet .note { margin-bottom: 1rem; }
dialog.sheet .vform { margin-bottom: .4rem; }
dialog.sheet .banner { margin: .8rem 0; }
.sheet-x { position: absolute; top: .7rem; right: .8rem; }
.sheet-x button {
  font: inherit; font-size: 1.3rem; line-height: 1; cursor: pointer;
  border: 0; background: none; color: var(--muted); padding: .2rem .4rem; border-radius: 7px;
}
.sheet-x button:hover { background: var(--bg); color: var(--ink); }
/* A dialog's foot - its commit and what it will do. The bill has a .sheet-foot
   of its own, its totals, which this used to turn into a row with a grey rule. */
dialog.sheet .sheet-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  flex-wrap: wrap; margin-top: 1.1rem; padding-top: .9rem; border-top: 1px solid var(--line);
}
dialog.sheet .sheet-foot .note { margin: 0; flex: 1 1 240px; }
.sheet-danger {
  display: flex; flex-direction: column; gap: .5rem;
  margin-top: 1rem; padding-top: .9rem; border-top: 1px solid var(--line);
}
.sheet-danger form { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; }
.sheet-danger .note { margin: 0; }
dialog.sheet select, dialog.sheet input[type=date] {
  font: inherit; font-size: .9rem; text-transform: none; letter-spacing: 0;
  padding: .35rem .55rem; border: 1px solid var(--field-border); border-radius: 7px;
  background: var(--field); color: var(--ink); min-width: 160px;
}

/* ---- the statement ------------------------------------------------------
   Read down the page against the one the distributor sends, so it prints. */
.statement { max-width: 60rem; }
.stmt-head { display: flex; justify-content: space-between; gap: var(--gap); flex-wrap: wrap; margin-bottom: 1.2rem; }
.stmt-parties { display: flex; gap: 2rem; }
.stmt-parties b { display: block; margin-top: .2rem; }
.stmt-pay td { color: var(--ok); }
.stmt-pay .note, .stmt-pay .mute { color: var(--muted); }
.stmt-foot {
  margin-top: 1.2rem; padding-top: 1rem; border-top: 2px solid var(--line);
  max-width: 26rem; margin-left: auto;
}
@media print {
  .statement { max-width: none; }
  .stmt-foot { break-inside: avoid; }
}

/* ---- the fields inside a sheet -----------------------------------------
   Not `.vform`, which is the inline correction row used on a page: this is a
   form somebody is filling in from a bill in their hand, so it is two plain
   columns with the name and the address across both, and labels written the
   way the field is spoken rather than as uppercase micro-type. */
.sheetgrid { display: grid; grid-template-columns: 1fr 1fr; gap: .9rem 1rem; }
.sheetgrid > label, .sup-attach-form > label {
  display: flex; flex-direction: column; gap: .35rem;
  font-size: .92rem; color: var(--ink); min-width: 0;
}
.sheetgrid > label.wide, .sheetgrid > .wide { grid-column: 1 / -1; }
.sheetgrid input, .sup-attach-form input {
  font: inherit; font-size: .95rem;
  padding: .55rem .7rem; border: 1px solid var(--field-border); border-radius: 9px;
  background: var(--field); color: var(--ink); width: 100%;
}
.sheetgrid input:focus, .sup-attach-form input:focus {
  border-color: var(--accent); outline: 2px solid var(--accent-soft); outline-offset: 0;
}
.sheetgrid input::placeholder, .sup-attach-form input::placeholder { color: var(--muted); opacity: .65; }
@media (max-width: 560px) { .sheetgrid { grid-template-columns: 1fr; } }
/* Which fields a form cannot be saved without, marked where the eye already is:
   the star beside the label. What is left empty after a refusal - or after a
   bill was read and did not print it - is outlined, so it is found at a glance. */
/* One line: the label is a column, so without this the star took a row of its own. */
.fname { display: block; }
.must { color: var(--bad); font-weight: 600; margin-left: .1rem; }
.fieldopt { color: var(--muted); font-size: .8rem; margin-left: .25rem; }
.sheetgrid input.missing { border-color: var(--bad); background: var(--bad-soft, var(--field)); }

/* The directory, at the top right of the bill's own details: it fills the two
   supplier boxes and is no field of the bill, so it sits apart from them.
   Layout only - the dropdown itself is the app's, styled with every other
   select in "form controls" below, and a second set of borders and radii here
   is how two dropdowns on one desk stop looking alike. */
/* The same width as the form's card and the same gutter inside it, so the
   dropdown's right edge lands on the right edge of the boxes it fills rather
   than hanging past the card into the page. */
.copyfrom { display: flex; gap: .5rem; align-items: center; justify-content: flex-end;
            font-size: .88rem; color: var(--muted); flex-wrap: wrap;
            margin: 1.3rem 0 .7rem; max-width: 900px;
            /* The card's gutter and its 1px border: without the border the row
               came to rest one pixel to the right of the fields. */
            padding-right: calc(var(--gutter) + 1px); }
.copyfrom select { max-width: 22rem; }
/* On a phone the row is the width of the page, and a label hard against the
   control it names reads as part of it. */
@media (max-width: 560px) {
  .copyfrom { justify-content: space-between; }
  .copyfrom select { flex: 1 1 12rem; }
}

/* ---- a form on a page of its own --------------------------------------
   Returning goods and correcting a bill each open a page rather than a sheet
   over the account: the list of what is going back is the whole of the job,
   and a page has the room for it and a back link to where it came from. The
   foot is a sheet's foot - what saving does, and the two buttons. */
/* A page that is one form: its heading, the notice over it and the card all
   stop at the same place, so the page reads as one column rather than a narrow
   card under full-width text. The card's own gutter insets the fields inside
   it, and the dropdown above it lines up with those fields, not with the card
   - it belongs to what it fills. */
.formpage > * { max-width: 900px; }
.pageform { max-width: 900px; }
.pageform .sheetgrid { margin-top: 1rem; }
.pagefoot {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  flex-wrap: wrap; margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid var(--line);
}
.pagefoot .note { margin: 0; flex: 1 1 280px; }

/* Goods going back: the bill's products down a table, a box for each, and
   what it is and why under it. The box is as wide as a count of packs, and a
   line with nothing left on the shelf is drawn back so the ones that can go
   stand out. */
.returns input.qty { width: 6rem; text-align: right; padding: .45rem .6rem; }
.returns td .note { margin: 0; }
.returns tr.spent td { color: var(--muted); }
/* On a phone the product, what is on hand and the box are what the return is
   made from; what came in and what it cost wait for a wider screen. */
@media (max-width: 560px) { .returns .opt { display: none; } }
/* On a bill's row: goods from it sent back, in the credit notes' colour, since
   a credit note is what answers it. */
.note .returned { color: var(--free); }
.note .returned .mono { white-space: nowrap; }
.note a.returned { text-decoration: none; }
.note a.returned:hover { text-decoration: underline; }
/* A credit note stored twice: listed, so it can be found and deleted, but
   drawn back - it is counted nowhere. */
.suptab tr.is-copy td { color: var(--muted); }
.suptab tr.is-copy td.menucol { color: inherit; }

/* How the year's bills share the table's width. Set as proportions rather than
   left to automatic layout, which gives the slack to whichever column has the
   longest run of text - here the bill's, whose second line is a product count
   and a time - and bunches the figures at the far end. */
#txtable th:nth-child(1) { width: 11%; }
#txtable th:nth-child(2) { width: 20%; }
#txtable th:nth-child(3) { width: 7%; }
#txtable th:nth-child(4),
#txtable th:nth-child(5),
#txtable th:nth-child(6) { width: 14%; }
#txtable th:nth-child(7) { width: 15%; }
#txtable th:nth-child(7), #txtable td:nth-child(7) { padding-left: 1.6rem; }

/* ---- what can be done to a bill, on its row ----------------------------
   One icon at the end of the row opens a menu of everything that can be done
   to the bill; sending goods back is the one action kept out of it, as a button
   on a line under the bill. The icon's column stays in view when the table
   scrolls sideways, so the menu can never be scrolled out of reach. */
.suptab th.menucol, .suptab td.menucol {
  width: 1%; padding-left: .25rem; text-align: right;
  position: sticky; right: 0; background: var(--panel);
}
/* The row's one worthwhile action sits in the same pinned cell as the icon,
   ahead of it, rather than in a pinned column of its own: a second sticky
   column has to know the first one's width in pixels to sit beside it, and
   that width is a button, its border and the table's own gutter. */
.suptab td.menucol { white-space: nowrap; }
.suptab td.menucol > .rowicon { margin-right: .3rem; vertical-align: middle; }
.rowicon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.1rem; height: 2.1rem; padding: 0; border-radius: 8px;
  border: 1px solid var(--field-border); background: var(--panel); color: var(--ink-soft);
  text-decoration: none;
}
.rowicon:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.rowicon:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.rowicon.amber {
  background: var(--warn-bg); border-color: var(--warn); color: var(--warn-ink);
}
.rowicon.amber:hover { background: var(--warn); border-color: var(--warn); color: var(--warn-bg); }
.suptab td.menucol > .rowmenu { display: inline-block; vertical-align: middle; }
/* Each sticky cell stacks on its own, and the rows below paint over an earlier
   one - so the cell whose menu is open is lifted over the icons under it. */
.suptab td.menucol:has(> details[open]) { z-index: 5; }
/* Everything a supplier's page can do but the three it is opened for, behind
   one control that reads as the buttons beside it - same height, same border,
   same radius - so the row is four controls and not three and a something. */
.headmenu > summary {
  padding: .5rem .95rem; border-radius: 8px; border-color: var(--line);
  background: var(--panel); color: var(--ink); font-size: .9rem; line-height: 1.5;
}
.headmenu > summary:hover, .headmenu[open] > summary { color: var(--accent); border-color: var(--accent); }
.headmenu > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.headmenu .menu { min-width: 13rem; }
.headmenu .menu a, .headmenu .menu span { padding: .5rem .6rem; font-size: .86rem; }

.rowmenu > summary.menubtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.1rem; height: 2.1rem; padding: 0; border-radius: 8px;
  border: 1px solid var(--field-border); background: var(--panel); color: var(--ink-soft);
}
.rowmenu > summary.menubtn::after { content: none; }
.rowmenu > summary.menubtn:hover, .rowmenu[open] > summary.menubtn {
  background: var(--accent-soft); border-color: var(--accent); color: var(--accent);
}
.rowmenu > summary.menubtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Drawn over the page, beside the icon (placed by app.js): inside the table's
   sideways-scrolling box it was cut off at the last row.

   `position: fixed` alone was not enough. A fixed box is still held by any
   ancestor that makes itself a containing block - and the reading aids put
   `zoom` on <body>, which does exactly that - so the menu was being measured
   against the scrolling box after all and cut off at its edge again, with the
   last items behind the note under the table. Opened as a popover it is drawn
   in the top layer, which no ancestor can clip or paint over, whatever is on
   the page above it. The DOM does not move, so a click inside is still a click
   inside the <details> and closes it as it always did.

   The popover's own defaults would centre it in the window, so they are undone
   here and app.js sets where it goes. */
.rowmenu.float .menu { position: fixed; top: auto; right: auto; z-index: 60; min-width: 190px; }
.rowmenu.float .menu:popover-open {
  margin: 0; inset: auto; width: auto; height: auto; overflow: visible;
  border: 1px solid var(--line-strong); padding: 4px; color: var(--ink);
}
.rowmenu.float .menu a, .rowmenu.float .menu button, .rowmenu.float .menu span {
  font-size: .86rem; padding: .55rem .75rem;
}
/* Return items, a link under the bill's number and products in its own cell.
   Padded to a finger's height without looking like a button. */
.suptab a.rowact { display: inline-block; margin-top: .2rem; padding: .3rem 0;
  font-size: .82rem; font-weight: 600; color: var(--accent); text-decoration: none; }
.suptab a.rowact:hover, .suptab a.rowact:focus-visible { text-decoration: underline; }
.suptab a.billno { font-weight: 700; color: var(--ink); text-decoration: none;
  white-space: nowrap; }
.suptab a.billno:hover, .suptab a.billno:focus-visible { color: var(--accent); text-decoration: underline; }

/* Three states of one thing, drawn as one control. Radios rather than a
   dropdown: there are three of them, all three are worth reading, and which
   one is chosen changes what the form asks for next. */
.seg { border: 0; padding: 0; margin: 0; min-width: 0; }
.seg legend { padding: 0 0 .35rem; font-size: .92rem; color: var(--ink); }
.seg { display: grid; grid-template-columns: repeat(3, 1fr); gap: .6rem; }
.seg legend { grid-column: 1 / -1; }
.seg label { display: block; min-width: 0; }
.seg input { position: absolute; opacity: 0; width: 0; height: 0; }
.seg span {
  display: block; text-align: center; cursor: pointer;
  padding: .6rem .5rem; border: 1px solid var(--field-border); border-radius: 9px;
  font-size: .95rem; color: var(--ink); background: var(--field);
}
.seg label:hover span { border-color: var(--accent); color: var(--accent); }
.seg input:checked + span {
  border-color: var(--accent); color: var(--accent); font-weight: 600;
  background: var(--accent-soft);
}
.seg input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
.seg.two { grid-template-columns: repeat(2, 1fr); }

/* The reading aids page: each choice a row of its own, the ticks with their
   reason beside them rather than under a heading. */
.reading-form .seg { margin-bottom: 1.1rem; }
.reading-form .seg:last-child { margin-bottom: 0; }
/* The form's buttons stand between its last card and the sample under it, at
   the right-hand end as a page form's are, and as far off the sample as one
   card is from the next - with no room of their own they sat on its edge. */
.reading-form .reading-end { justify-content: flex-end; margin-bottom: 1.1rem; }
.check { display: flex; gap: .7rem; align-items: flex-start; margin: 0 0 .9rem; cursor: pointer; }
.check:last-child { margin-bottom: 0; }
.check input { margin-top: .3rem; width: 1.05rem; height: 1.05rem; flex: none; }
.check span { color: var(--muted); }
.check b { color: var(--ink); font-weight: 600; }
.reading-sample td, .reading-sample th { padding: .6rem .7rem; }

/* The way in that is not the way in most of the time: reading a supplier off
   a bill is the better path, and it is still one line until it is wanted. */
.readoff { margin: 0 0 1rem; }
.readoff summary {
  cursor: pointer; font-size: .88rem; color: var(--accent);
  padding: .2rem 0; width: fit-content;
}
.readoff[open] summary { margin-bottom: .6rem; }
.readoff .note:last-child { margin-bottom: 0; }

/* --- supplier formats ---------------------------------------------------- */
/* A panel that opens: the rarely needed parts of the formats page stay one
   click away rather than a screen further down. */
details.panel > summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: .5rem; }
details.panel > summary::-webkit-details-marker { display: none; }
details.panel > summary::before { content: '▸'; color: var(--muted); }
details.panel[open] > summary::before { content: '▾'; }
details.panel > summary h2 { margin: 0; }
details.panel[open] > summary { margin-bottom: .75rem; }
.fmtfacts { margin-bottom: 1rem; }
.changes td.was { color: var(--muted); text-decoration: line-through; }
.impact tr.differs td { background: var(--warn-bg); }
.difftable td:first-child { width: 1%; white-space: nowrap; }
.difftable input[type="checkbox"], .carebox input[type="checkbox"] { width: auto; margin: 0; }
.carebox { display: flex; gap: .55rem; align-items: flex-start; margin-bottom: .8rem; font-size: .9rem; }
.bindform { display: flex; gap: .5rem; align-items: flex-end; flex-wrap: wrap;
            margin-top: 1rem; padding-top: .9rem; border-top: 1px solid var(--line, rgba(0,0,0,.08)); }
.bindform .grow { flex: 1 1 16rem; min-width: 0; }

/* The app's own confirmation, drawn by askFirst() in app.js. */
dialog.asksheet { max-width: 30rem; }
.asksheet .askmsg { white-space: pre-line; margin: .2rem 0 1.1rem; line-height: 1.5; }

/* A bill's history, under its sign-off. */
.billhistory { margin-top: 1rem; border-top: 1px solid var(--line); padding-top: .8rem; }
.billhistory summary { cursor: pointer; font-weight: 600; width: fit-content; }
.billhistory table { margin-top: .6rem; }
.billhistory tr.h-signed td, .billhistory tr.h-withdrawn td { background: var(--bg); }
.reconfirm { margin: 0 0 .4rem; }
.billhistory td.nowrap { white-space: nowrap; }

/* The NRx mark ------------------------------------------------------------
   A narcotic or psychotropic is marked the way its carton is: NRx in bold
   red, so it reads as the same warning a pharmacist already knows from the
   strip. Used wherever NRx marks a medicine, never as decoration. */
/* Its own spelling too: NRx, never NRX - table heads and printed titles are
   set in capitals, and the symbol is not a word to capitalise. */
.nrxmark { color: var(--nrx); font-weight: 700; letter-spacing: .01em; white-space: nowrap;
  text-transform: none; }
td .nrxmark, .tab .nrxmark { margin-left: 0; }


/* ---- a full-HD screen gets the same page, set one step larger -----------
   The scale was drawn on a laptop: 14px of body text, and a root of whatever
   the browser gives (16px), which is what the 577 rem values in this sheet
   hang off. On a 1920-wide monitor at arm's length that reads small, and the
   page answers by leaving a third of the window empty rather than by being
   easier to read.

   One step at 1600px, which no laptop reports, so nothing moves on the screens
   this was drawn for. Raising the root carries the whole rem scale with it -
   table text, panel padding, the lot - and the handful of sizes written in
   pixels that carry reading rather than labelling are stepped with it. The
   small print stays where it is: a tag, a note and a column heading are quiet
   on purpose, and growing them with the rest only makes the page louder. */
@media (min-width: 1600px) {
  html { font-size: 17px; }
  body { font-size: 15px; }
  h1 { font-size: 25px; }
  .topbar nav a { font-size: 15.5px; }
  .brandtext b { font-size: 16px; }
  .reg-row, .reg-group, .register > .reg-empty { font-size: 14px; }
  .card table, .card td.r.mono { font-size: 14px; }
  .sup-title, .sup-owed { font-size: .88rem; }
  .supfigs .num { font-size: .92rem; }
}
