/* StockPro — the whole stylesheet.
 *
 * Hand-written rather than a framework: the app is a dozen screens made of the
 * same six components, and a CDN build with no configuration is what made the
 * old screens look like defaults arranged in a column.
 *
 * Everything below is built from the tokens. If a colour appears as a literal
 * anywhere past :root, that is a bug — it will not follow the dark palette.
 */

:root {
  --bg: #F4F4F3;
  --surface: #FFFFFF;
  --sidebar: #1C1F22;
  --sidebar-text: #A8AEB4;
  --sidebar-active: #2C3136;
  --ink: #16191C;
  --muted: #646B72;
  --faint: #949BA1;
  --hair: #E3E4E4;
  --brand: #A4551A;
  --brand-soft: #FAF0E5;
  --amber: #8A6D1F;
  --amber-soft: #F7F2DE;
  --danger: #B23B36;
  --danger-soft: #FBECEB;

  --radius: 6px;
  --shadow-sm: 0 1px 2px rgba(20, 33, 28, .07);
  --shadow: 0 2px 4px rgba(20, 33, 28, .06), 0 8px 20px rgba(20, 33, 28, .06);

  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, monospace;

  /* Nothing interactive smaller than a thumb. One interface serves a phone
     behind a counter and a laptop in the office. */
  --tap: 2.75rem;

  /* Chart series. Validated in both modes -- lightness band, chroma floor, CVD
     separation, normal-vision floor and contrast against the surface. The
     validator the old comment referred to was never committed; it is
     scripts/check_palette.py now, so this rule has teeth:

         uv run python scripts/check_palette.py

     Do not substitute by eye. Two series that look distinct to whoever picked
     them and identical to a colour-blind cashier is the failure being guarded
     against, and a chart is the one screen where colour carries meaning. */
  --chart-sales: #0F766E;
  --chart-margin: #6D28D9;
  --chart-cash: #0F766E;
  --chart-grid: var(--hair);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #131517;
    --surface: #1A1D20;
    --sidebar: #0F1113;
    --sidebar-text: #9BA2A8;
    --sidebar-active: #262A2E;
    --ink: #E8EAEC;
    --muted: #99A1A8;
    --faint: #6E767D;
    --hair: #2A2E32;
    --brand: #E08B45;
    --brand-soft: #2A1E12;
    --amber: #D0B45C;
    --amber-soft: #262010;
    --danger: #D97B75;
    --danger-soft: #2B1917;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow: 0 2px 4px rgba(0, 0, 0, .35), 0 8px 20px rgba(0, 0, 0, .3);

    /* Dark is its own set of steps, not the light pair brightened: the dark
       lightness band is L 0.48-0.67, and --brand at #E08B45 sits outside it. */
    --chart-sales: #26A69A;
    --chart-margin: #9575CD;
    --chart-cash: #26A69A;
  }
}

:root[data-theme="dark"] {
  --bg: #131517;
  --surface: #1A1D20;
  --sidebar: #0F1113;
  --sidebar-text: #9BA2A8;
  --sidebar-active: #262A2E;
  --ink: #E8EAEC;
  --muted: #99A1A8;
  --faint: #6E767D;
  --hair: #2A2E32;
  --brand: #E08B45;
  --brand-soft: #2A1E12;
  --amber: #D0B45C;
  --amber-soft: #262010;
  --danger: #D97B75;
  --danger-soft: #2B1917;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow: 0 2px 4px rgba(0, 0, 0, .35), 0 8px 20px rgba(0, 0, 0, .3);

  /* Dark is its own set of steps, not the light pair brightened: the dark
     lightness band is L 0.48-0.67, and --brand at #E08B45 sits outside it. */
  --chart-sales: #26A69A;
  --chart-margin: #9575CD;
  --chart-cash: #26A69A;
}

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

/* Any author `display` beats the UA stylesheet's [hidden], so a component
   given `display: grid` stays on screen while claiming to be hidden. This is
   what put an empty selected-item panel under the search box. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--brand); }

:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: 3px;
}

/* Figures. Money and quantities line up in columns everywhere, which is the
   difference between software that looks considered and software that does not. */
.num, td.num, .price, .amt {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}

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

.app { display: grid; grid-template-columns: 216px 1fr; min-height: 100vh; }

.side {
  background: var(--sidebar);
  padding: 1rem .75rem;
  display: flex;
  flex-direction: column;
}

.brandmark {
  display: flex; align-items: center; gap: .55rem;
  min-height: 40px;
  padding: .35rem .5rem 1.25rem;
  color: #fff; font-weight: 600; letter-spacing: -.01em;
  text-decoration: none;
}
.brandmark .dot {
  width: 22px; height: 22px; border-radius: 5px;
  background: var(--brand); flex: none;
}
/* Height-driven, not boxed. A 22px square cropped a shop's logo to whatever
   fitted inside it, so a wide wordmark -- the shape most shops upload --
   rendered a few pixels tall. Setting the height and letting the width follow
   gives every shape the same presence; max-width catches the very wide ones
   before they push the wordmark out of a 216px sidebar. */
.brandmark .logo {
  height: 40px; width: auto;
  max-width: 108px;
  border-radius: 5px;
  object-fit: contain; flex: none;
}

.side nav { display: flex; flex-direction: column; gap: 1px; }
.side nav a {
  display: flex; align-items: center; gap: .6rem;
  min-height: var(--tap);
  padding: .5rem .6rem;
  border-radius: var(--radius);
  color: var(--sidebar-text);
  text-decoration: none;
  font-size: .875rem;
}
.side nav a:hover { background: var(--sidebar-active); color: #fff; }
.side nav a[aria-current="page"] {
  background: var(--sidebar-active); color: #fff; font-weight: 500;
}
.side nav a svg { width: 16px; height: 16px; flex: none; opacity: .85; }

.top {
  background: var(--surface);
  border-bottom: 1px solid var(--hair);
  padding: .7rem 1.5rem;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  position: sticky; top: 0; z-index: 5;
}
.crumbs { color: var(--muted); font-size: .82rem; }
.crumbs b { color: var(--ink); font-weight: 600; }
.store {
  display: inline-flex; align-items: center; gap: .4rem;
  background: var(--brand-soft); color: var(--brand);
  padding: .2rem .55rem; border-radius: 100px;
  font-size: .78rem; font-weight: 500; white-space: nowrap;
}

.topright { display: flex; align-items: center; gap: .8rem; }
.top .whoami {
  display: flex; align-items: center; gap: .5rem;
  font-size: .8rem; color: var(--muted);
}
.top .whoami .name { color: var(--ink); font-weight: 500; }
.top .whoami form { margin: 0; }
.top .whoami button {
  background: none; border: 0; padding: 0;
  color: var(--brand); font: inherit; font-size: .8rem;
  cursor: pointer; text-decoration: underline;
}
.top .whoami button:hover { color: var(--ink); }

main { padding: 1.5rem; max-width: 1180px; }

/* The sidebar becomes a horizontal strip on a phone. The nav is five links,
   so it fits; a drawer would be more machinery for no gain. */
@media (max-width: 860px) {
  .app { grid-template-columns: 1fr; }
  .side { flex-direction: row; align-items: center; gap: .5rem; padding: .5rem .75rem; }
  .side .brandmark { padding: 0; min-height: 0; }
  .side .brandmark span:not(.dot) { display: none; }
  /* Smaller here than on desktop: the strip shares its width with a scrolling
     nav, so the logo takes the room it needs to be legible and no more. */
  .side .brandmark .logo { height: 30px; max-width: 76px; }
  .side nav { flex-direction: row; overflow-x: auto; flex: 1; }
  .side nav a { white-space: nowrap; }
  .top { padding: .6rem 1rem; }
  main { padding: 1rem; }
}

/* ----------------------------------------------------------- typography -- */

h1 { font-size: 1.35rem; font-weight: 600; letter-spacing: -.02em; margin: 0 0 .2rem; }
h2 { font-size: .9rem; font-weight: 600; margin: 0; }
.sub { color: var(--muted); margin: 0 0 1.25rem; font-size: .875rem; }
.spacer { height: 1.5rem; }

/* ---------------------------------------------------------------- panel -- */

.panel {
  background: var(--surface);
  border: 1px solid var(--hair);
  border-radius: 10px;
  box-shadow: var(--shadow-sm);
}
.panel > header {
  padding: .8rem 1rem;
  border-bottom: 1px solid var(--hair);
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}
.panel > .body { padding: 1rem; }

.panel .foot {
  display: flex; justify-content: space-between; align-items: center;
  padding: .7rem 1rem; color: var(--muted); font-size: .8rem;
  border-top: 1px solid var(--hair);
}
.panel .foot .tot {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  color: var(--ink); font-weight: 600; font-size: .95rem;
}

/* ---------------------------------------------------------------- table -- */

.scroller { overflow-x: auto; }

table { width: 100%; min-width: 560px; border-collapse: collapse; }
/* Narrow tables — three columns in a half-width panel — do not need the wide
   minimum, and inheriting it made them scroll and hide their own figures. */
table.compact { min-width: 0; }
th {
  text-align: left;
  font-size: .7rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
  padding: .55rem 1rem;
  border-bottom: 1px solid var(--hair);
  background: var(--bg);
  white-space: nowrap;
}
th.r, td.r { text-align: right; }
td { padding: .6rem 1rem; border-bottom: 1px solid var(--hair); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--brand-soft); }
td.who { color: var(--muted); font-size: .8rem; }
td.empty { color: var(--muted); text-align: center; padding: 1.5rem 1rem; }

/* ----------------------------------------------------------------- pill -- */

.pill {
  display: inline-block;
  font-size: .72rem; padding: .12rem .45rem; border-radius: 100px;
  /* Neutral on purpose. A pill reading "50 in stock" is information, not
     something to act on, and tinting it with the accent is what made the old
     palette read as one colour on top of itself. */
  background: var(--bg); color: var(--muted); border: 1px solid var(--hair);
  font-weight: 500; white-space: nowrap;
}
.pill.low { background: var(--amber-soft); color: var(--amber); border-color: transparent; }
.pill.none { background: var(--danger-soft); color: var(--danger); border-color: transparent; }

/* -------------------------------------------------------------- control -- */

.field { display: block; margin-bottom: .9rem; }
.field label,
label {
  display: block;
  font-size: .75rem; color: var(--muted);
  margin-bottom: .3rem; font-weight: 500;
}

.control {
  width: 100%;
  min-height: var(--tap);
  border: 1px solid var(--hair);
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius);
  padding: .5rem .6rem;
  font: inherit; font-size: 1rem;
}
.control:focus { outline: 0; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.control[readonly] { background: var(--bg); color: var(--muted); }
input.num, .control.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

select.control { appearance: none; padding-right: 2rem; }

.reveal { position: relative; display: block; }
/* Room for the eye, so a long password does not run underneath it. */
.reveal .control { padding-right: 2.9rem; }
.reveal-toggle {
  position: absolute; right: .3rem; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 2.2rem; height: 2.2rem;
  border: 0; background: none; color: var(--muted); cursor: pointer;
}
.reveal-toggle:hover { color: var(--ink); }

.btn.ghost.danger { color: var(--danger); border-color: var(--danger); }
.btn.ghost.danger:hover { background: var(--danger-soft); }

.resetpw { display: flex; gap: .4rem; align-items: center; margin-top: .35rem; }
.resetpw .reveal { flex: 1 1 auto; min-width: 9rem; }

.presets { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .9rem; }
.btn.ghost.on { border-color: var(--brand); color: var(--brand); background: var(--brand-soft); }
.rangeform {
  display: flex; flex-wrap: wrap; gap: .6rem; align-items: flex-end;
  padding-top: .3rem; border-top: 1px solid var(--hair);
}
.rangeform .field { margin: 0; }
/* A withheld figure, shown greyed with its reason rather than left out --
   a missing tile reads as a fault, an explained one reads as a rule. */
.tile.muted { opacity: .72; }
.tile.muted .value { color: var(--muted); }

.plancards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: .8rem;
}
.plancard {
  display: flex; flex-direction: column; gap: .35rem;
  padding: 1rem; border: 1px solid var(--hair); border-radius: var(--radius);
  background: var(--surface);
}
/* The plan they are on, marked by more than a word: somebody scanning three
   cards should see which is theirs before reading any of them. */
.plancard.on { border-color: var(--brand); background: var(--brand-soft); }
.plancard .label { color: var(--muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; }
.plancard .value { font-family: var(--mono); font-size: 1.4rem; font-weight: 600; color: var(--ink); }
.plancard .note { color: var(--muted); font-size: .8rem; }
.planfeatures { list-style: none; margin: .5rem 0; padding: 0; font-size: .85rem; color: var(--ink); }
.planfeatures li { padding: .15rem 0; }
.planfeatures .soon { color: var(--muted); }
.plancard form { margin-top: auto; padding-top: .6rem; }

.err { color: var(--danger); font-size: .8rem; margin: .25rem 0 0; }
.err ul { margin: 0; padding-left: 1.1rem; }

/* ------------------------------------------------------------------ btn -- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  min-height: var(--tap);
  border: 1px solid transparent; border-radius: var(--radius);
  background: var(--brand); color: #fff;
  font: inherit; font-weight: 600;
  padding: .5rem 1rem; cursor: pointer; text-decoration: none;
}
.btn:hover { filter: brightness(1.08); }
.btn:disabled { opacity: .45; cursor: not-allowed; filter: none; }
.btn.wide { width: 100%; }
.btn.ghost {
  background: var(--surface); color: var(--ink);
  border-color: var(--hair); font-weight: 500;
}
.btn.ghost:hover { background: var(--brand-soft); filter: none; }
.btn.quiet {
  background: none; border: 0; color: var(--muted);
  font-weight: 500; padding: .35rem .5rem; min-height: 0;
}
.btn.quiet:hover { color: var(--danger); filter: none; }

.toolbar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }

/* ----------------------------------------------------------- idle sign-out -- */

/* Bottom-right, not a full-screen overlay. It appears while somebody may be
   mid-keystroke in a stock count, and a modal that swallowed the screen would
   cost them their place to tell them they are about to lose it. */
.idle-warning {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 50;
  max-width: 20rem;
  display: flex; flex-direction: column; gap: .6rem;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--hair);
  border-left: 3px solid var(--amber);
  border-radius: var(--radius);
  padding: .9rem 1rem;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .18);
  font-size: .875rem;
}
.idle-warning p { margin: 0; }
.idle-warning b { color: var(--amber); }
/* [hidden] loses to .idle-warning's display:flex without this -- the panel
   would sit on every page from load, counting down in plain sight. */
.idle-warning[hidden] { display: none; }

/* Borrows the idle warning's shape -- bottom-right, out of the way,
   dismissable -- and shares its corner. On the rare page where both are up the
   idle warning wins, by one z-index: being signed out in sixty seconds is
   worth interrupting for, and something being low on the shelf will still be
   low after signing back in. */
.stocknote { z-index: 49; border-left-color: var(--brand); }
.stocknote b { color: var(--brand); }
.stocknote .row { display: flex; align-items: center; gap: .5rem; }

/* The sidebar count. Quiet on purpose: it says there is something to look at,
   not that something is wrong. */
/* A tile that is also a filter. Keeps the tile's own look -- these are still
   the figures, not buttons dressed as figures -- and marks the chosen one with
   the same brand edge the sidebar uses for the current page. */
a.tile { text-decoration: none; color: inherit; }
a.tile:hover { border-color: var(--brand); }
a.tile.picked { border-color: var(--brand); box-shadow: inset 3px 0 0 var(--brand); }
a.tile.lead.picked { box-shadow: inset 3px 0 0 var(--chart-sales); }

/* What a customer took, listed under the entry it came from. Quiet, because
   the row's own figures are the answer to "how much" -- this is the answer to
   "to what". */
.took { margin: .3rem 0 0; padding-left: 1.1rem; font-size: .8125rem; color: var(--muted); }
.took li { margin: .1rem 0; }

.navcount {
  margin-left: auto;
  background: var(--brand-soft); color: var(--brand);
  border-radius: 999px; padding: 0 .45rem;
  font-size: .75rem; font-weight: 600;
}

/* ----------------------------------------------------------- on account -- */

/* Set apart from the till's own controls on purpose. Record sale and this mean
   opposite things about whether money changed hands, so they are kept at
   opposite ends of the screen rather than stacked where a thumb could confuse
   them. The rule above it is the separation, not decoration. */
.onaccount {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px dashed var(--hair);
}
.onaccount .picker {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
}
.onaccount .picker label { color: var(--muted); font-size: .875rem; }
.onaccount .picker .control { width: auto; min-width: 12rem; }
/* [hidden] loses to display:flex without this, so the picker would sit open
   from page load with the sale one tap from a tab nobody chose. */
.onaccount .picker[hidden] { display: none; }

/* -------------------------------------------------------------- message -- */

.msg {
  border: 1px solid var(--hair); background: var(--surface);
  border-left: 3px solid var(--muted);
  border-radius: var(--radius);
  padding: .7rem 1rem; margin-bottom: 1rem; font-size: .875rem;
}
.msg.error { border-left-color: var(--danger); background: var(--danger-soft); }
.msg.success { border-left-color: var(--brand); background: var(--brand-soft); }

.banner {
  /* Suspended: the same danger colour as .msg.error, so a shop that can no
     longer record a sale reads as unmistakably different from one that is
     merely overdue -- see .banner.warn below, which stays amber on purpose. */
  background: var(--danger-soft); color: var(--ink);
  border-bottom: 1px solid var(--hair);
  padding: .6rem 1.5rem; font-size: .85rem;
}
.banner b { color: var(--danger); }
.banner.warn { background: var(--amber-soft); color: var(--amber); }
.banner.warn b { color: var(--amber); }

/* --------------------------------------------------------------- signin -- */

.signin {
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 1.5rem;
}
.signin .panel { width: 100%; max-width: 22rem; }
.signin .brandmark { color: var(--ink); justify-content: center; padding-bottom: 1rem; }

@media (prefers-reduced-motion: no-preference) {
  .btn, .side nav a, tbody tr { transition: background-color .12s ease, filter .12s ease; }
}

/* --------------------------------------------------------- record a sale -- */

.searchbox {
  display: flex; align-items: center; gap: .6rem;
  background: var(--surface);
  border: 1.5px solid var(--brand);
  border-radius: 10px;
  padding: .7rem 1rem;
  box-shadow: 0 0 0 3px var(--brand-soft);
}
.searchbox svg { width: 18px; height: 18px; color: var(--brand); flex: none; }
.searchbox input {
  border: 0; outline: 0; background: transparent; color: var(--ink);
  font: inherit; font-size: 1.05rem; width: 100%; min-height: 1.6rem;
}

.results:not(:empty) {
  margin-top: .4rem;
  background: var(--surface);
  border: 1px solid var(--hair);
  border-radius: 10px;
  box-shadow: var(--shadow);
  overflow: hidden;
}
.result {
  display: grid; grid-template-columns: 1fr auto auto;
  align-items: center; gap: 1rem;
  width: 100%; min-height: var(--tap);
  padding: .6rem 1rem;
  border: 0; border-bottom: 1px solid var(--hair);
  background: var(--surface); color: var(--ink);
  font: inherit; text-align: left; cursor: pointer;
}
.result:last-child { border-bottom: 0; }
.result:hover:not(:disabled) { background: var(--brand-soft); }
.result:disabled { opacity: .5; cursor: not-allowed; }
.result .nm { font-weight: 500; }
.result .price { font-size: 1rem; }
.noresults { padding: .8rem 1rem; margin: .4rem 0 0; color: var(--muted);
  background: var(--surface); border: 1px solid var(--hair); border-radius: 10px; }

.chosen { margin-top: 1rem; display: grid; grid-template-columns: 1fr 280px; gap: 1rem; }
.chosen .fields { display: grid; grid-template-columns: 1fr 1fr; gap: .9rem; }
@media (max-width: 780px) {
  .chosen { grid-template-columns: 1fr; }
}

.totalcard { display: flex; flex-direction: column; justify-content: space-between; }
.totalcard .lbl { color: var(--muted); font-size: .78rem; display: block; }
.totalcard .amt {
  display: block; font-size: 2rem; font-weight: 600;
  letter-spacing: -.02em; margin-top: .1rem;
}

/* -------------------------------------------------- screens: shared bits -- */

.pagehead {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem;
}
.pagehead .sub { margin: 0; }
.sectionhead {
  font-size: .8rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin: 0 0 .6rem;
}

.twocol { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; align-items: start; }
/* Grid children default to min-width:auto, so a table with a min-width pushes
   its column wide and squeezes the one beside it. This lets them shrink and
   the table scroll inside its own .scroller instead. */
.twocol > *, .cardgrid > * { min-width: 0; }
.cardgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; }
@media (max-width: 780px) {
  .twocol { grid-template-columns: 1fr; }
}
.panel.narrow { max-width: 26rem; }

/* Figure lists — the money day and the month summary. */
.figures { margin: 0; }
.figures > div {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; padding: .3rem 0;
}
.figures > div.rule {
  border-top: 1px solid var(--hair); margin-top: .35rem; padding-top: .6rem;
}
.figures > div.rule dt, .figures > div.rule dd { font-weight: 600; }
.figures dt { color: var(--muted); margin: 0; }
.figures dd { margin: 0; }
.figures .derived { color: var(--faint); font-size: .75rem; }

.foot.bottomline { background: var(--bg); }
.foot.bottomline span:first-child { color: var(--ink); font-weight: 600; font-size: .95rem; }
.foot .tot.big { font-size: 1.5rem; }

.amt {
  display: block; font-size: 1.75rem; font-weight: 600; letter-spacing: -.02em;
}
.who { color: var(--muted); font-size: .8rem; }

/* Inline undo forms inside table cells. */
.inline { display: flex; align-items: center; gap: .35rem; }
.control.tiny { width: 7rem; min-height: 2rem; padding: .25rem .4rem; font-size: .8rem; }

/* Search that lives in a panel header rather than on its own. */
.inline-search { max-width: 16rem; min-height: 2.25rem; font-size: .875rem; }

/* Delivery and count rows. lines.js keys off data-* attributes, not these
   classes — renaming a data attribute silently breaks it and no test notices. */
.linerows { display: flex; flex-direction: column; gap: .6rem; }
.linerow { display: grid; grid-template-columns: 1fr 8rem 8rem auto; gap: .5rem; align-items: center; }
.linerow.two { grid-template-columns: 1fr 12rem auto; }
@media (max-width: 700px) {
  .linerow, .linerow.two { grid-template-columns: 1fr 1fr; }
  .linerow .linepick { grid-column: 1 / -1; }
}

.linepick { position: relative; }
.lineresults {
  position: absolute; z-index: 20; left: 0; right: 0; top: calc(100% + 2px);
  max-height: 15rem; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--hair);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.lineresult {
  display: flex; align-items: baseline; justify-content: space-between; gap: .75rem;
  width: 100%; min-height: var(--tap); padding: .5rem .7rem;
  border: 0; border-bottom: 1px solid var(--hair);
  background: none; color: var(--ink); text-align: left; cursor: pointer;
  font: inherit;
}
.lineresult:last-child { border-bottom: 0; }
.lineresult:hover, .lineresult:focus { background: var(--brand-soft); }
.lineresult .hint { color: var(--muted); font-size: .8rem; white-space: nowrap; }
.noresults { margin: 0; padding: .6rem .7rem; color: var(--muted); font-size: .85rem; }

.checkline {
  display: flex; align-items: center; gap: .6rem;
  margin: .9rem 0 0; font-size: .875rem; color: var(--ink); font-weight: 400;
}
.checkline input { width: 1.1rem; height: 1.1rem; }

/* What needs attention. */
.attention { display: flex; flex-direction: column; gap: .9rem; }
.flag .what { margin: 0; font-weight: 500; display: flex; align-items: center; gap: .45rem; }
.flag .detail { margin: .2rem 0 0; color: var(--muted); font-size: .8rem; }

/* ------------------------------------------------------------ dashboard -- */

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1rem;
}
.tile {
  background: var(--surface);
  border: 1px solid var(--hair);
  border-radius: 10px;
  box-shadow: var(--shadow-sm);
  padding: 1rem;
  display: flex; flex-direction: column; gap: .15rem;
  min-width: 0;
}
.tile .label {
  font-size: .72rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
}
.tile .value {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 1.6rem; font-weight: 600; letter-spacing: -.02em;
  line-height: 1.15; overflow-wrap: anywhere;
}
.tile .note { font-size: .75rem; color: var(--muted); }
/* The headline tile. One number an owner opens the page for should not look
   like the five beside it. */
.tile.lead { background: var(--sidebar); border-color: var(--sidebar); }
.tile.lead .label { color: var(--sidebar-text); }
.tile.lead .value { color: #fff; font-size: 1.9rem; }
.tile.lead .note { color: var(--sidebar-text); }
.tile .value.down { color: var(--danger); }
.tile.lead .value.down { color: #FF9E97; }
.tile .delta { font-size: .8125rem; color: var(--muted); }
/* Direction is carried by the arrow glyph as well as the colour: a delta that
   only means something in colour means nothing to a colourblind reader, and
   nothing at all in print. */
.tile .delta.up { color: var(--brand); }
.tile .delta.down { color: var(--danger); }

/* The line chart: an inline SVG rather than a library, drawn from geometry
   computed server-side in reports/queries.py. preserveAspectRatio="none" on
   the <svg> lets it stretch to its panel width while the viewBox keeps the
   computed y values meaningful.

   The viewBox is fixed at 720x200 user units and the SVG scales to its
   container, so stroke-width and font-size below are NOT screen pixels —
   they shrink with the container. `vector-effect="non-scaling-stroke"` on
   the polylines, axis/zero lines and marker rings (set in
   _line_chart.html) keeps their stroke-width in true screen pixels at any
   width; text has no such escape hatch, so its font-size is picked for the
   narrow case rather than the wide one. */
/* The plot, with room down its left for the figures. Relative so the scale can
   be positioned into it by percentage -- the SVG keeps its aspect ratio, so a
   percentage of the box is a percentage of the 200-unit viewBox. */
.chartwrap { position: relative; padding-left: 4.75rem; }
.chartwrap .scale { position: absolute; left: 0; top: 0; bottom: 0; width: 4.25rem; }
/* translateY(-50%) puts the figure ON its line rather than hanging below it.
   The top mark would otherwise sit half outside the panel. */
.chartwrap .scale span {
  position: absolute; right: 0; transform: translateY(-50%);
  font-size: .6875rem; font-family: var(--mono); color: var(--faint);
  white-space: nowrap;
}

.chart { width: 100%; height: auto; display: block; }
/* Filled under a running balance. Faint, because the line is the reading and
   this is only saying the quantity exists all the way down. */
.chart .area { opacity: .13; stroke: none; }
.chart .area.chart-cash { fill: var(--chart-cash); }
.chart .grid { stroke: var(--chart-grid); stroke-width: 1; }
.chart .axis { stroke: var(--hair); stroke-width: 1; }
.chart .series { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart .series.chart-sales { stroke: var(--chart-sales); }
.chart .series.chart-margin { stroke: var(--chart-margin); }
.chart .series.chart-cash { stroke: var(--chart-cash); }
/* Takings are bars: a day's takings are a discrete amount, not a reading on a
   continuous quantity. A shop that recorded nothing until the 20th now shows
   twenty absent bars rather than a line crawling along the axis and leaping at
   the end, which read as a fault in the chart rather than a fact about the
   month. Filled, not stroked -- the chart stretches horizontally, and a
   stroked bar's outline would stretch with it. */
/* The base rule matters: an SVG rect with no fill declared is BLACK, not
   invisible and not inheriting. If the class ever fails to match -- a stale
   stylesheet against fresh markup, a renamed class -- the chart does not
   degrade, it turns into black slabs. This makes the worst case "the right
   colour" instead. */
.chart .bar { fill: var(--chart-sales); }
.chart .bar.chart-sales { fill: var(--chart-sales); }
.chart .bar.chart-margin { fill: var(--chart-margin); }
.chart .zero { stroke: var(--hair); stroke-width: 1; stroke-dasharray: 3 3; }
.chart .marker { r: 4; stroke: var(--surface); stroke-width: 2; }
.chart .marker.chart-sales { fill: var(--chart-sales); }
.chart .marker.chart-margin { fill: var(--chart-margin); }
.chart .marker.chart-cash { fill: var(--chart-cash); }
.chart text { fill: var(--faint); font-size: 18px; font-family: var(--mono); }

.chart-legend { display: flex; gap: 1rem; align-items: center; font-size: .8125rem; color: var(--muted); }
.chart-legend .key { display: inline-flex; align-items: center; gap: .375rem; }
.chart-legend .swatch { width: 10px; height: 10px; border-radius: 2px; flex: none; }
.chart-legend .swatch.chart-sales { background: var(--chart-sales); }
.chart-legend .swatch.chart-margin { background: var(--chart-margin); }

.bars { display: flex; flex-direction: column; gap: .55rem; }
.bars .row { display: grid; grid-template-columns: 8.5rem 1fr auto; gap: .6rem; align-items: center; }
/* display:block matters — these are spans, and an inline element ignores
   height, which drew the tracks empty. */
.bars .track {
  display: block; background: var(--bg); border-radius: 100px;
  height: .55rem; overflow: hidden;
}
.bars .fill { display: block; background: var(--brand); height: 100%; border-radius: 100px; }
.bars .cat { color: var(--muted); font-size: .8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bars .amt2 {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: .8rem; text-align: right;
}
@media (max-width: 560px) {
  .bars .row { grid-template-columns: 6rem 1fr auto; }
}

/* The generic bar partial (_bar_chart.html): cash's sibling charts, drawn as
   plain HTML rather than SVG so ranked, long product names stay selectable
   and un-clipped. Its own container class — `.barchart`, not `.bars` — so it
   cannot collide with the expenses panel's own `.bars .row/.cat/.track/.fill/
   .amt2` markup above. It once reused `.bars` and silently changed that
   panel's row gap, a panel the dashboard spec placed out of scope. */
.barchart { display: flex; flex-direction: column; gap: .5rem; }
.bar-row { display: grid; grid-template-columns: minmax(6rem, 12rem) 1fr auto; gap: .75rem; align-items: center; }
.bar-label { color: var(--ink); font-size: .875rem; display: flex; flex-direction: column; gap: .1rem; }
/* The units figure a manager needs to reorder — kept visible on every bar
   rather than dropped along with the table's owner-only "Made" column. */
.bar-note { color: var(--muted); font-size: .75rem; }
.bar-track { background: var(--bg); border-radius: 3px; height: 14px; }
/* 4px rounded ends, anchored at the baseline — the fill starts flush left and
   only its far end is rounded, so the bar cannot appear to float. */
.bar-fill { display: block; height: 100%; background: var(--chart-sales); border-radius: 0 4px 4px 0; }
.bar-value {
  color: var(--muted); font-size: .8125rem;
  display: flex; flex-direction: column; align-items: flex-end; gap: .1rem;
}
/* Per-product profit — an owner-only figure the old table gated behind
   may_see_owner_figures. Its presence in the row (not a role check here)
   is the gate: a manager's row never carries `profit` to begin with. */
.bar-made { color: var(--faint); font-size: .75rem; }
@media (max-width: 860px) { .bar-row { grid-template-columns: 1fr auto; } .bar-track { grid-column: 1 / -1; } }
