/* Deliberately small and hand-written: the dashboard is tables, filters and one chart,
   and a CSS framework would be a build step this project has no other need for.

   The palette is the Pricechecker product's own, sampled from the signed-in dashboard and
   the sign-in screen rather than guessed. Two things about it are load-bearing:

   - **Chrome is charcoal, not red.** Brand red and "this listing undercuts" red are within
     a few degrees of each other, so a red header would make every severity figure read as
     decoration. The product already solves this: red appears in data, never in furniture.
     Brand red is confined to the sign-in page and the logo mark.
   - **Light only.** The product has no dark mode, and a dashboard that renders differently
     from the client's other tab invites "is this the same data?" at exactly the wrong
     moment.
*/

:root {
  /* Brand */
  --brand: #c81721;
  --brand-ink: #fff;
  --chrome: #444;
  --chrome-ink: #f2f2f2;

  /* Surfaces */
  --canvas: #f9f9f9;
  --surface: #fff;
  --rule: #e3e3e3;
  --rule-strong: #d2d2d2;

  /* Text */
  --ink: #1f1f1f;
  --muted: #6b6b6b;

  /* Data. Never used for furniture — see the note above. */
  --bad: #e82f44;
  --bad-bg: #fdecee;
  --good: #3fbb18;
  --neutral: #c6c8db;
  --accent: #74a4c7;

  --warn-bg: #fdf3e3;
  --warn-ink: #7a4a00;

  --radius: 4px;
  --radius-lg: 6px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 15px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--ink);
  background: var(--canvas);
}

/* ----------------------------------------------------------------- chrome ---- */

.site-header {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  padding: 0 1.25rem;
  height: 52px;
  background: var(--chrome);
  color: var(--chrome-ink);
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 1.05rem;
  font-weight: 300;
  letter-spacing: 0.01em;
  text-decoration: none;
  color: inherit;
  white-space: nowrap;
}

.brand b { font-weight: 700; }
.brand .mark { width: 22px; height: 22px; flex: none; }

.site-nav { display: flex; gap: 0.25rem; align-items: center; }

.site-nav a,
.nav-pending {
  display: inline-block;
  padding: 0.3rem 0.85rem;
  border-radius: var(--radius);
  font-size: 0.9rem;
  color: var(--chrome-ink);
  text-decoration: none;
}

.site-nav a:hover { background: rgb(255 255 255 / 12%); }

/* The current section, as a filled pill — the product's own active state. */
.site-nav a[aria-current="page"],
.site-nav a[aria-current="page"]:hover {
  background: #fff;
  color: #1f1f1f;
  font-weight: 600;
}

/* Views not built yet read as disabled rather than being absent, so the shape of the
   dashboard is legible before all of it exists. */
.nav-pending { color: rgb(255 255 255 / 45%); cursor: default; }

.site-user {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.9rem;
}

.site-user form { margin: 0; }

.avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
}

.btn-ghost {
  font: inherit;
  font-size: 0.82rem;
  padding: 0.25rem 0.7rem;
  cursor: pointer;
  color: var(--chrome-ink);
  background: transparent;
  border: 1px solid rgb(255 255 255 / 35%);
  border-radius: var(--radius);
}

.btn-ghost:hover { background: rgb(255 255 255 / 12%); }

/* ------------------------------------------------------------- freshness ---- */

.freshness {
  margin: 0;
  padding: 0.5rem 1.25rem;
  font-size: 0.85rem;
  color: var(--muted);
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
}

.freshness-stale {
  background: var(--warn-bg);
  color: var(--warn-ink);
  border-bottom-color: #e8d3a8;
}

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

main { padding: 1.5rem 1.25rem 3rem; }
.wrap { max-width: 1200px; margin: 0 auto; }

h1 { font-size: 1.35rem; font-weight: 600; margin: 0 0 0.25rem; }
h2 { font-size: 1rem; font-weight: 600; margin: 0 0 0.5rem; }

.page-sub { color: var(--muted); margin: 0 0 1.25rem; font-size: 0.9rem; }

.note { color: var(--muted); font-size: 0.88rem; }
.error { color: var(--bad); }

.card {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  padding: 1rem 1.25rem;
  margin-bottom: 1.25rem;
}

.card.flush { padding: 0; overflow: hidden; }

/* ----------------------------------------------------------------- stats ---- */

.stats { display: flex; flex-wrap: wrap; gap: 0.75rem; margin: 0 0 1.25rem; }

.stat {
  flex: 1 1 160px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  padding: 0.85rem 1rem;
}

.stat b {
  display: block;
  font-size: 1.7rem;
  font-weight: 600;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

/* The tile's caption, and only the caption. Scoped to a direct child because the run tile
   now has spans nested inside its control, and this rule was shrinking the date itself. */
.stat > span {
  color: var(--muted);
  font-size: 0.78rem;
}

/* -------------------------------------------------------------- run pick ---- */

/* The tile's two controls. Exactly one is shown, decided by `js` on <html> before first
   paint — see the note in base.html. Neither is hidden with [hidden] or inline styles,
   because this markup is inside the htmx swap target and must be correct the instant it
   lands, with no script having run. */
.run-trigger { display: none; }
.js .run-form { display: none; }
.js .run-trigger { display: inline-flex; }

.run-form { display: flex; gap: 0.4rem; align-items: center; }

.run-form select {
  font: inherit;
  font-size: 0.95rem;
  padding: 0.3rem 0.4rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  max-width: 100%;
}

/* Sized to match `.stat b`, so the tile reads as a figure that happens to be clickable
   rather than as a form control dropped into a row of figures.

   It carries its border and its caret at rest, not on hover. The first version showed the
   affordance only once the pointer was already on it, which is the wrong way round: the
   reader has to suspect the date is a control before the page will confirm it. Two of the
   three tiles are plain numbers, so nothing here says "this one is different" unless this
   one says it. The caret sits in its own compartment behind a rule — the shape a select
   has — because a lone glyph beside a large number reads as punctuation. */
.run-trigger {
  align-items: center;
  gap: 0;
  font: inherit;
  font-size: 1.7rem;
  font-weight: 600;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  cursor: pointer;
  padding: 0;
  margin-left: -0.45rem;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
}

.run-trigger:hover { border-color: var(--chrome); }
.run-trigger:hover .caret { background: var(--rule); }
.run-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.run-trigger .run-date { padding: 0.1rem 0.5rem 0.1rem 0.45rem; }

.run-trigger .caret {
  display: flex;
  align-items: center;
  align-self: stretch;
  padding: 0 0.5rem;
  font-size: 1.15rem;
  color: var(--ink);
  background: var(--canvas);
  border-left: 1px solid var(--rule-strong);
}

.rp-panel {
  position: absolute;
  z-index: 40;
  width: 268px;
  padding: 0.6rem;
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-lg);
  box-shadow: 0 6px 20px rgb(0 0 0 / 14%);
}

.rp-head { display: flex; align-items: center; gap: 0.3rem; margin-bottom: 0.45rem; }

.rp-title { flex: 1; text-align: center; font-size: 0.85rem; font-weight: 600; }

.rp-nav {
  font: inherit;
  line-height: 1;
  width: 1.7rem;
  height: 1.7rem;
  cursor: pointer;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.rp-nav:hover:not(:disabled) { background: var(--canvas); }
.rp-nav:disabled { color: var(--rule-strong); cursor: default; }

.rp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }

.rp-dow {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  text-align: center;
  padding-bottom: 0.2rem;
}

.rp-day {
  font: inherit;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  aspect-ratio: 1;
  cursor: pointer;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.rp-day:hover:not(:disabled) { background: var(--canvas); border-color: var(--rule-strong); }

/* A day with no run. Flat and unbordered rather than greyed-out-but-boxed: it is not a
   choice that has been temporarily withdrawn, it is a day this service cannot answer for. */
.rp-day:disabled {
  color: #c3c3c3;
  background: transparent;
  border-color: transparent;
  cursor: default;
}

.rp-day.is-selected {
  color: #fff;
  background: #0c0c0c;
  border-color: #0c0c0c;
}

.rp-foot {
  margin: 0.55rem 0 0;
  font-size: 0.7rem;
  line-height: 1.35;
  color: var(--muted);
}

/* --------------------------------------------------------------- filters ---- */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  align-items: flex-end;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  padding: 0.85rem 1rem;
  margin: 0 0 1.25rem;
}

.fgroup { display: flex; flex-direction: column; gap: 0.3rem; }
.fgroup.grow { flex: 1 1 260px; }

.fgroup > span {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
}

.filters input,
.filters select {
  font: inherit;
  font-size: 0.85rem;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  max-width: 100%;
}

.filters input[type="search"] { width: 100%; }

.btn {
  font: inherit;
  font-size: 0.85rem;
  padding: 0.38rem 1rem;
  cursor: pointer;
  border: 1px solid #0c0c0c;
  border-radius: var(--radius);
  background: #0c0c0c;
  color: #fff;
}

.btn:hover { background: #262626; border-color: #262626; }

/* Filter and Reset are one pair. Left to wrap independently they land on separate rows and
   read as two unrelated buttons, so they wrap together or not at all. */
.fgroup.actions { flex-direction: row; flex-wrap: nowrap; gap: 0.5rem; align-items: stretch; }
.fgroup.actions .btn { white-space: nowrap; }

/* Category values run to three segments and, given the room Run left behind, the select
   grows to fit the longest of them and pushes the buttons onto a second row. Capped: the
   native dropdown opens at its own width, so nothing is lost by the closed control being
   narrower than its widest option. */
.filters select { max-width: 13rem; }

/* Reset sits beside Filter and must not compete with it: same size and baseline, no fill. */
.btn-quiet {
  display: inline-block;
  text-decoration: none;
  color: var(--ink);
  background: var(--surface);
  border-color: var(--rule-strong);
}

.btn-quiet:hover { background: #f2f2f2; }

.btn-sm { font-size: 0.8rem; padding: 0.3rem 0.7rem; }

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

.result-count {
  color: var(--muted);
  font-size: 0.85rem;
  margin: 0 0 0.75rem;
}

.tablewrap { overflow-x: auto; }

.rows { border-collapse: collapse; width: 100%; font-size: 0.88rem; }

.rows th,
.rows td {
  text-align: left;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--rule);
}

.rows thead th {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  background: #fbfbfb;
  white-space: nowrap;
}

.rows tbody tr:last-child td { border-bottom: 0; }
.rows tbody tr:hover { background: #fafcfd; }

.rows .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }

.rows .sku { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--muted); }

/* The product title links out to Google Shopping. Underlined only on hover: 400 underlined
   titles down a page is a wall of blue, and the column is read as data first. */
.rows td.product a { color: var(--ink); text-decoration: none; }
.rows td.product a:hover { text-decoration: underline; }
.rows td.product .ext { color: var(--muted); font-size: 0.75rem; }

/* The offence itself, and the only place red is allowed to carry meaning. */
.rows td.undercut { color: var(--bad); font-weight: 700; }

.tag {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.1rem 0.5rem;
  border-radius: 99px;
  white-space: nowrap;
  background: var(--bad-bg);
  color: var(--bad);
}

/* The benchmark moved after the run, so the figure recorded then cannot be restated.
   Muted rather than red: it is a caveat about our own data, not a fault of the member. */
.moved { color: var(--muted); font-style: italic; font-weight: 400; }

/* The raw listing-row count on the member table. It runs about a tenth above the pair
   count because the provider returns one offer under several tracking URLs, so it is
   deliberately quieter than the column beside it: present, never the headline. */
.rows td.muted-num { color: var(--muted); }
.rows thead th small { display: block; font-weight: 400; text-transform: none; letter-spacing: 0; }

/* The row expander on the product table, and the sub-table it reveals. The sub-table is
   quieter than its parent throughout: it is detail under a row, not a second dataset. */
.rows th.expander-head, .rows td.expander { width: 2rem; padding-right: 0; }

.expand {
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1;
  padding: 0.2rem 0.3rem;
  border-radius: var(--radius);
}
.expand:hover { color: var(--ink); background: var(--canvas); }
.expand[aria-expanded="true"] span[aria-hidden] { display: inline-block; transform: rotate(90deg); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* An opened row and its panel have to read as one object, or the table looks like rows of
   the same weight repeating. Three things do that: the parent row takes a tinted, bottom-
   border-less "open" state so it joins what follows; the panel is inset on the canvas
   ground with a left rule running down it; and the panel carries a caption, so the reader
   lands on a sentence rather than straight into a second grid. */
.rows tbody tr.is-open > td {
  background: #f4f7f9;
  border-bottom-color: transparent;
}
.rows tbody tr.is-open:hover > td { background: #f4f7f9; }
.rows tbody tr.is-open td.expander .expand { color: var(--ink); }

.rows tr.listings-row > td { padding: 0 0 0.75rem; background: #f4f7f9; }
.rows tr.listings-row:hover > td { background: #f4f7f9; }

.listings-panel {
  margin: 0 0.9rem 0 2.6rem;
  border-left: 2px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--surface);
  box-shadow: 0 1px 2px rgb(0 0 0 / 6%);
  overflow: hidden;
}

.sub-caption {
  margin: 0;
  padding: 0.55rem 0.75rem;
  font-size: 0.78rem;
  color: var(--muted);
  border-bottom: 1px solid var(--rule);
  background: #fbfcfd;
}
.sub-caption strong { color: var(--ink); }

.sub-rows { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
.sub-rows th {
  text-align: left;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  font-weight: 600;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--rule);
}
.sub-rows td { padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
.sub-rows tbody tr:last-child td { border-bottom: 0; }
.sub-rows .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.sub-rows td.undercut { color: var(--bad); font-weight: 700; }
.sub-rows .code { color: var(--muted); font-variant-numeric: tabular-nums; margin-left: 0.4rem; }

/* The member panel's two product columns, matching the main product table's treatment so
   the same product reads the same way on either page. The catalogue title is clamped for
   the same reason the advertised one is. */
.sub-rows .sku { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--muted); }

/* The member panel carries two long title columns where the product panel carries one, and
   left to themselves the pair set the width of the whole sub-table — which then widened the
   member table above it and pushed its last column into a horizontal scroll. 12rem each is
   what makes the panel fit inside the 1200px content width exactly; the full text is on each
   cell's tooltip either way, so nothing is lost but the sprawl. */
.sub-rows td.product {
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sub-rows-products td.advertised { max-width: 12rem; }

.sub-rows td.product a { color: var(--ink); text-decoration: none; }
.sub-rows td.product a:hover { text-decoration: underline; }
.sub-rows td.product .ext { color: var(--muted); font-size: 0.75rem; }

/* The shared-shopfront clause inside the panel's caption. Amber ink, no amber ground: the
   full-width row above is the disclosure, this is the reminder that it applies here too. */
.sub-caveat { color: var(--warn-ink); }

/* The member's own advertised title. Long and unpredictable, so it is clamped rather than
   allowed to set the width of the whole sub-table. */
.sub-rows td.advertised {
  max-width: 22rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* One offer returned under several tracking URLs. Stated, never silently folded away. */
.dupe {
  margin-left: 0.4rem;
  font-size: 0.7rem;
  color: var(--muted);
  border: 1px solid var(--rule-strong);
  border-radius: 99px;
  padding: 0.05rem 0.4rem;
  white-space: nowrap;
}

.sub-empty { padding: 0.75rem; margin: 0; color: var(--muted); font-size: 0.82rem; }

/* Shared shopfront. Two retailers trade under one website each, attribution anchors to
   the lowest customer code, and this row says so under the member it was anchored to.
   Amber rather than red: it is a caveat about whose figures these are, not an offence. */
.rows tr.disclosure td {
  background: var(--warn-bg);
  color: var(--warn-ink);
  font-size: 0.82rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}
.rows tr.disclosure:hover td { background: var(--warn-bg); }

.pager {
  display: flex;
  gap: 1rem;
  align-items: center;
  padding: 0.85rem 1rem;
  border-top: 1px solid var(--rule);
  font-size: 0.85rem;
}

.pager span { color: var(--muted); }

.empty {
  padding: 1.25rem;
  margin: 0;
  background: var(--surface);
  border: 1px dashed var(--rule-strong);
  border-radius: var(--radius-lg);
}

a { color: #1f6ea8; }

/* --------------------------------------------------------------- sign-in ---- */

/* The one page that wears the brand: full-bleed red, the mark, and nothing else to look
   at. It is also the first thing the client sees at UAT. */

body.signin-page {
  background: var(--brand);
  color: var(--brand-ink);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.signin-page main { flex: 1; display: grid; place-items: center; padding: 2rem 1.25rem; }

.signin { width: 100%; max-width: 20rem; }

.signin .brand {
  justify-content: center;
  font-size: 1.9rem;
  margin-bottom: 1.75rem;
  color: #fff;
}

.signin .brand .mark { width: 40px; height: 40px; }

.signin label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 0.3rem;
}

.signin input {
  width: 100%;
  font: inherit;
  padding: 0.5rem 0.6rem;
  border: 0;
  border-radius: var(--radius);
  background: #fff;
  color: var(--ink);
}

.signin p { margin: 0 0 1.15rem; }

.signin .btn {
  display: block;
  width: 60%;
  margin: 1.75rem auto 0;
  padding: 0.55rem 1rem;
}

.signin .note { margin-top: 2.5rem; text-align: center; }

.signin .helptext,
.signin .note { color: rgb(255 255 255 / 80%); font-size: 0.8rem; }

.signin .error {
  color: #fff;
  background: rgb(0 0 0 / 22%);
  padding: 0.5rem 0.7rem;
  border-radius: var(--radius);
}

.signin-foot {
  padding: 1rem 1.25rem;
  font-size: 0.78rem;
  color: rgb(255 255 255 / 75%);
}

/* The sign-in screen shows the mark and nothing else, as the product's does — but a page
   with no heading is a page a screen reader cannot announce. */
.visually-hidden-heading {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
