/* Bre-zy Cleaning Co. — phone-first stylesheet.
 *
 * Hand-written rather than Tailwind: plan §2 says "pick one, avoid a Node
 * toolchain in prod", and a purge-less Tailwind build would ship megabytes to
 * a phone on mobile data. If a richer design system is wanted later, add
 * django-tailwind as a *build-time* step and replace this file.
 *
 * Tap targets are 44px minimum throughout — this is used standing in a doorway.
 *
 * Palette is taken from the logo: forest green ink, gold flourishes, cream.
 * The green is the accent — 7.9:1 on the light surface, so it carries text
 * and controls as drawn. The gold (--gold-bright) is decorative only: 2.9:1
 * on white, exactly as the old logo's gold was. In the dark theme the green
 * is lifted along its own hue to 4.7:1 rather than swapped for something
 * else, so the brand survives the theme. Every text/ground pair below is at
 * or above 4.5:1 in both themes; the numbers were measured, not eyeballed.
 */

:root {
  color-scheme: light dark;
  --bg: #f4f1ec;
  --surface: #fdfcfa;
  --surface-2: #eeeae2;
  --border: #ded7cb;
  --text: #1f1d1b;
  --muted: #6b635a;
  --accent: #405637;          /* brand green: 7.86:1 on --surface; white on it 8.06:1 */
  --accent-text: #ffffff;
  --gold-bright: #ba8d3c;     /* the logo's gold — rules and flourishes only */
  --danger: #8f2622;
  --danger-bg: #fbeae8;
  --warn: #6f4e07;
  --warn-bg: #fbf2da;
  --ok: #1f6045;
  --ok-bg: #e6f1eb;
  --radius: 10px;
  --shadow: 0 1px 2px rgba(31, 29, 27, 0.07), 0 4px 14px rgba(31, 29, 27, 0.05);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #100f13;
    --surface: #16151a;
    --surface-2: #201e25;
    --border: #322f39;
    --text: #efeae2;
    --muted: #a79d90;
    --accent: #688c59;        /* the green lifted: 4.74:1 on --surface */
    --accent-text: #16151a;
    --gold-bright: #ba8d3c;   /* 6.02:1 on the dark surface as drawn */
    --danger: #ffa79f;
    --danger-bg: #3a1e1c;
    --warn: #f0c765;
    --warn-bg: #352c16;
    --ok: #74d0ab;
    --ok-bg: #143024;
    --shadow: none;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "Helvetica Neue", Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
}

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

.wrap {
  max-width: 34rem;
  margin: 0 auto;
  padding: 1rem 1rem 3rem;
}

/* Pages that are lists, tables, calendars or the dashboard take the screen on
 * anything bigger than a phone; see the comment in base.html for why that is
 * opt-in rather than the default. Below 48rem this rule does nothing at all,
 * so the phone layout is untouched — which is the layout that matters most.
 *
 * 90rem rather than `none`: a table row 1900px wide is not easier to read
 * than one 1400px wide, it is just longer to track across. */
@media (min-width: 48rem) {
  .wrap--wide { max-width: 90rem; padding-inline: 1.5rem; }
}

.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6rem 1rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.masthead .who { color: var(--muted); font-size: 0.85rem; }

/* Brand lockup. The logo image is used when one has been installed; the
 * wordmark below is the fallback and is what the emails and PDFs echo. */
.brand { display: flex; align-items: center; gap: 0.6rem; min-width: 0; }
.brand__link { display: block; line-height: 0; }
/* The compact mark is a wide strip of script, not a square icon — height is
 * what makes it legible, and 30px is where this face stops being readable. */
.brand img { display: block; height: 30px; width: auto; max-width: 62vw; }

.wordmark { margin: 0; min-width: 0; }
.wordmark__name {
  display: block;
  font-family: "Didot", "Bodoni MT", "Playfair Display", Georgia, "Times New Roman", serif;
  font-size: 1.15rem;
  font-weight: 600;
  font-style: italic;
  letter-spacing: 0.01em;
  line-height: 1.1;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.wordmark__sub {
  display: block;
  font-size: 0.6rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The sign-in pages give the brand room, the way the logo is drawn to be seen. */
.brand--hero {
  flex-direction: column;
  gap: 0.5rem;
  margin: 0.5rem 0 1.5rem;
  text-align: center;
}
.brand--hero img { height: auto; max-height: 150px; max-width: min(21rem, 82vw); }
.brand--hero .wordmark__name { font-size: 1.9rem; white-space: normal; }
.brand--hero .wordmark__sub { font-size: 0.65rem; }
.brand--hero .wordmark::after {
  content: "";
  display: block;
  width: 70%;
  max-width: 14rem;
  height: 1px;
  margin: 0.7rem auto 0;
  background: var(--gold-bright);
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.25rem;
  margin-bottom: 1rem;
}
.card > h2 { margin-top: 0; font-size: 1.2rem; }
.card > p:last-child { margin-bottom: 0; }

.lede { color: var(--muted); margin-top: -0.25rem; }

label { display: block; font-weight: 600; margin: 0.9rem 0 0.3rem; font-size: 0.9rem; }

.field,
input[type="text"], input[type="password"], input[type="email"], input[type="number"] {
  width: 100%;
  min-height: 44px;
  padding: 0.6rem 0.7rem;
  font-size: 1rem;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.field:focus-visible, input:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.field--code {
  font: 600 1.5rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.4em;
  text-align: center;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: 44px;
  width: 100%;
  margin-top: 1.1rem;
  padding: 0.6rem 1rem;
  font-size: 1rem;
  font-weight: 600;
  color: var(--accent-text);
  background: var(--accent);
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  text-decoration: none;
}
.btn--quiet {
  color: var(--text);
  background: var(--surface-2);
  border-color: var(--border);
}
.btn--link {
  width: auto; min-height: 0; margin: 0; padding: 0;
  background: none; border: none; color: var(--accent);
  font-weight: 500; text-decoration: underline;
}

.alt { margin-top: 1rem; font-size: 0.9rem; text-align: center; }

.msg { border-radius: 8px; padding: 0.7rem 0.9rem; margin-bottom: 0.75rem; font-size: 0.95rem; }
.msg--error, .msg--danger { background: var(--danger-bg); color: var(--danger); }
.msg--warning { background: var(--warn-bg); color: var(--warn); }
.msg--success, .msg--info { background: var(--ok-bg); color: var(--ok); }

.errorlist {
  list-style: none;
  margin: 0.4rem 0 0;
  padding: 0;
  color: var(--danger);
  font-size: 0.9rem;
}

.qr {
  display: block;
  width: min(15rem, 70vw);
  margin: 1rem auto;
  padding: 0.75rem;
  background: #fff;   /* a QR must be dark-on-light to scan in either theme */
  border-radius: 8px;
}
.qr svg { display: block; width: 100%; height: auto; }

.secret {
  display: block;
  padding: 0.6rem;
  font: 0.95rem/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  word-break: break-all;
  text-align: center;
  background: var(--surface-2);
  border-radius: 8px;
}

.codes {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5rem;
  margin: 1rem 0;
  padding: 0;
  list-style: none;
}
.codes li {
  padding: 0.5rem;
  font: 1rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  text-align: center;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
}

.tiles { display: grid; gap: 0.75rem; }
.tile {
  display: block;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--text);
}
.tile strong { display: block; font-size: 1.05rem; }
.tile span { color: var(--muted); font-size: 0.9rem; }

.footnote { color: var(--muted); font-size: 0.85rem; }
.centred { text-align: center; }

@media print {
  .no-print { display: none !important; }
  body { background: #fff; color: #000; }
}

/* ── Phase 1: clients, properties and the reveal ──────────────────────────── */

.pagehead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin: 1.25rem 0 0.5rem;
}
.pagehead h1, .pagehead h2, .pagehead h3 { margin: 0; }
.pagehead h1 { font-size: 1.4rem; }
.pagehead h2 { font-size: 1.15rem; }
.pagehead h3 { font-size: 1.05rem; }

/* A second button size: "Add client" beside a heading should not be the
 * full-width slab that a form's submit is. Still 44px tall to tap. */
.btn--compact { width: auto; margin-top: 0; padding: 0.5rem 0.85rem; font-size: 0.9rem; }

.crumb { margin: 0 0 0.25rem; font-size: 0.9rem; }
.crumb a { text-decoration: none; }

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

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

/* Filters */
.filters { margin-bottom: 1rem; }
.filters__row { display: flex; gap: 0.5rem; margin-top: 0.5rem; }
.filters__row .field { flex: 1 1 0; min-width: 0; }
select.field { appearance: none; background-image: none; }
.check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0.75rem 0 0;
  font-weight: 500;
  font-size: 0.95rem;
}
.check input { width: 22px; height: 22px; margin: 0; accent-color: var(--accent); }

/* Rows — the list shapes used by clients and by the access log. */
.rows { list-style: none; margin: 0; padding: 0; }
.row {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 0.5rem;
}
.row__link,
.row > .row__name {
  display: block;
  min-height: 44px;
  padding: 0.75rem 0.9rem;
  color: var(--text);
  text-decoration: none;
}
.row__link { padding-bottom: 0.5rem; }
.row__name { display: block; font-weight: 600; }
.row__meta {
  display: block;
  padding: 0 0.9rem 0.75rem;
  color: var(--muted);
  font-size: 0.87rem;
}
.row__link .row__meta { padding: 0.15rem 0 0; }
.row--empty { padding: 1rem 0.9rem; color: var(--muted); }
.row--refused { border-left: 3px solid var(--danger); }
.rows--log .row { margin-bottom: 0.4rem; }

.pill {
  display: inline-block;
  margin-left: 0.3rem;
  padding: 0.1rem 0.45rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: 0.1em;
  color: var(--muted);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
}
.pill--paused { color: var(--warn); background: var(--warn-bg); border-color: transparent; }
.pill--former { color: var(--danger); background: var(--danger-bg); border-color: transparent; }

/* Tabs. Most of them are Phase 2–5 and are deliberately inert: the shape of
 * the record is worth showing before the screens exist. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 1rem 0;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--border);
}
.tab {
  padding: 0.4rem 0.7rem;
  font-size: 0.9rem;
  border-radius: 999px;
  text-decoration: none;
  color: var(--accent);
}
.tab--current { color: var(--accent-text); background: var(--accent); font-weight: 600; }
.tab--pending { color: var(--muted); background: var(--surface-2); cursor: default; }

/* Definition lists of small facts. */
.facts { display: grid; grid-template-columns: auto 1fr; gap: 0.3rem 0.9rem; margin: 0.5rem 0; }
.facts dt { color: var(--muted); font-size: 0.85rem; }
.facts dd { margin: 0; }

.address { margin: 0.25rem 0 0.5rem; font-size: 1rem; }
.notes { white-space: pre-line; }
.card--property .pagehead { margin-top: 0; }
.card--inactive { opacity: 0.7; }

/* ── Reveal ───────────────────────────────────────────────────────────────
 * Every state of one encrypted field occupies the same slot, so the swap does
 * not make the card jump under a thumb. */
.reveals { display: grid; gap: 0.5rem; margin-top: 0.75rem; }
.reveal { min-height: 44px; }
.btn--reveal { width: 100%; margin-top: 0; }

.reveal--shown, .reveal--refused, .reveal--reauth {
  padding: 0.6rem 0.75rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.reveal--shown { border-color: var(--accent); }
.reveal__label {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  margin: 0 0 0.35rem;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}
.reveal__timer { font-weight: 400; letter-spacing: 0; text-transform: none; }
.reveal__value {
  display: block;
  font: 600 1.4rem/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
  word-break: break-word;
  white-space: pre-wrap;
  color: var(--text);
}
.reveal__hide { margin-top: 0.5rem; }
.reveal--shown .msg, .reveal--refused .msg, .reveal--reauth .msg { margin-bottom: 0.4rem; }
.reveal--reauth .btn { margin-top: 0.25rem; }

/* A revealed code has no business on paper. */
@media print { .reveal__value { display: none; } }

/* ── Phase 2: the schedule ────────────────────────────────────────────────── */

.daynav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.75rem 0 1rem;
}
.daynav .btn { flex: 1 1 auto; }

.dayhead {
  margin: 1.25rem 0 0.4rem;
  font-size: 1rem;
  color: var(--muted);
  font-weight: 600;
}
.dayhead a { color: inherit; text-decoration: none; }
.dayhead a:hover { color: var(--accent); }

.row--closed { opacity: 0.6; }
.row--closed .row__name { text-decoration: line-through; }

.plain { list-style: none; margin: 0.5rem 0; padding: 0; }
.plain li { padding: 0.2rem 0; }

/* The month grid. It scrolls sideways on a phone rather than reflowing: a
 * calendar that reflows into a list is the week view with extra steps. */
.calendar-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.calendar {
  width: 100%;
  min-width: 34rem;
  border-collapse: collapse;
  table-layout: fixed;
  font-size: 0.8rem;
}
.calendar th {
  padding: 0.4rem 0.2rem;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
}
.calendar td {
  height: 5rem;
  padding: 0.25rem;
  vertical-align: top;
  border: 1px solid var(--border);
  background: var(--surface);
}
/* Dimmed rather than blank: an empty cell would read as "nothing scheduled",
 * which for a day in the next month would be a lie. */
.cal--outside { background: var(--bg); opacity: 0.55; }
.cal--today { outline: 2px solid var(--accent); outline-offset: -2px; }
.cal__date {
  display: block;
  margin-bottom: 0.2rem;
  font-weight: 600;
  color: var(--muted);
  text-decoration: none;
}
.cal--today .cal__date { color: var(--accent); }
.cal__visit {
  display: block;
  margin-bottom: 0.15rem;
  padding: 0.15rem 0.3rem;
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--text);
  font-size: 0.72rem;
  text-decoration: none;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.cal__visit--closed { opacity: 0.55; text-decoration: line-through; }

/* ── Phase 3: timers and the hours review ────────────────────────────────── */

/* The timer face. Big, because it is read at arm's length in someone else's
 * hallway, and because it is the one number on the page that is changing. */
.timer-face { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; margin: 0.2rem 0 0.6rem; }
.timer-elapsed { font-size: 1.9rem; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.pill--live { color: var(--ok); background: var(--ok-bg); border-color: transparent; }

/* Start and Finished side by side: two taps that must not be confused with
 * each other, so they are the same size and never move between renders. */
.timer-buttons { display: flex; gap: 0.6rem; }
.timer-buttons .btn { margin-top: 0.6rem; }

.entries { list-style: none; margin: 0.9rem 0 0; padding: 0.7rem 0 0; border-top: 1px solid var(--border); }
.entry { display: grid; grid-template-columns: 1fr auto; gap: 0 0.7rem; padding: 0.35rem 0; }
.entry__when { font-variant-numeric: tabular-nums; }
.entry__length { font-weight: 600; font-variant-numeric: tabular-nums; }
.entry .row__meta { grid-column: 1 / -1; padding: 0; }

.card--danger { border-color: var(--danger); }
.card--danger > h2 { color: var(--danger); }

/* One hours row: the visit above, the decision below. Stacked rather than
 * side by side because the number field wants a thumb, not a cursor. */
.hours-action {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
  padding: 0.6rem 0.9rem 0.9rem;
  border-top: 1px solid var(--border);
}
.hours-action label { margin: 0; }
.hours-action .btn { width: auto; margin-top: 0; margin-left: auto; }
.hours-action .row__meta { padding: 0; }
/* `input.` and not just `.field--hours`: the base rule above matches
 * `input[type="number"]`, which is specificity (0,1,1) and beats a bare class
 * (0,1,0) — so `width: 100%` had always been winning and this box has never
 * actually been 5.5rem. It was merely less obvious in a 34rem column than in
 * a wide one. A field for "2.25" does not want the width of the page. */
input.field--hours {
  width: 5.5rem;
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.hours-unit { color: var(--muted); }
.hours-settled { font-size: 1.15rem; font-weight: 700; font-variant-numeric: tabular-nums; }

/* ── Phase 4: invoices ───────────────────────────────────────────────────── */

/* The on-screen copy of an invoice. Same shape as the PDF so the two can be
 * compared at a glance, but using the app's own type rather than print's. */
.lines { width: 100%; border-collapse: collapse; margin-top: 0.4rem; }
.lines th { text-align: left; font-size: 0.78rem; text-transform: uppercase;
            letter-spacing: 0.05em; color: var(--muted); font-weight: 600;
            padding-bottom: 0.4rem; border-bottom: 1px solid var(--border); }
.lines td { padding: 0.5rem 0.4rem 0.5rem 0; border-bottom: 1px solid var(--surface-2);
            vertical-align: top; }
.lines .num { text-align: right; white-space: nowrap;
              font-variant-numeric: tabular-nums; }
.lines tfoot td { border-bottom: none; padding-top: 0.5rem; }
.lines tfoot .label { color: var(--muted); }
.lines tfoot .total td { padding-top: 0.7rem; border-top: 1px solid var(--text);
                         font-size: 1.05rem; font-weight: 700; }
.lines .btn--link { font-size: 0.85rem; color: var(--danger); }

/* A row that carries its own buttons — "Draft it", "Send a reminder". */
.row__actions { display: flex; flex-wrap: wrap; gap: 0.5rem;
                padding: 0 0.9rem 0.8rem; }
.row__actions .btn { width: auto; margin-top: 0; }

.pill--ok { color: var(--ok); background: var(--ok-bg); border-color: transparent; }

/* ── Phase 6: the owner dashboard ─────────────────────────────────────────── */

/* Counts that link. One column on a phone, two once there is room for two.
 * No three-column rule: `.wrap` caps the page at 34rem however wide the
 * screen is, so a third column would only make the numbers smaller — and a
 * number you have to lean in to read is not a glance. */
.stats {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
  margin: 1rem 0;
}
@media (min-width: 30rem) { .stats { grid-template-columns: repeat(2, 1fr); } }
/* Only inside a wide page — in a 34rem column a third column would shrink the
 * numbers below the point of a glance, which is what the note above says. */
@media (min-width: 62rem) {
  .wrap--wide .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* Two charts beside each other rather than one enormous one. */
  /* `minmax(0, 1fr)` and not `1fr`: a grid item defaults to `min-width: auto`,
   * so it refuses to shrink below its content and the column silently grows
   * past the page instead — a horizontal scrollbar and a second column half
   * off the screen. Applies to every grid on this page. */
  .wrap--wide .chartpair {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 1rem;
  }
  .wrap--wide .chartpair > .card { margin-bottom: 0; }
  /* Tiles are navigation, and a full-width bar per destination reads as a
   * list of instructions rather than a set of choices. */
  .wrap--wide .tiles { grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
  .wrap--wide .tiles > .tile { min-width: 0; }

  /* The portal's lists are one column of rows in a page that is now wide, so
   * the rows get a readable cap and the page does not become a field of very
   * long, very short lines. */
  .wrap--wide .portal-cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 1rem;
    align-items: start;
  }
  .wrap--wide .portal-cols > .card { margin-bottom: 0; }

  /* A filter bar laid out for a phone becomes a stack of 1100px-wide boxes on
   * a desktop. Same controls, one row, each no wider than it needs to be. */
  .wrap--wide .filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
  }
  .wrap--wide .filters > .field { flex: 0 1 22rem; }
  .wrap--wide .filters__row { margin-top: 0; flex: 0 1 26rem; }
  .wrap--wide .filters .check { margin: 0; white-space: nowrap; }
  .wrap--wide .filters .btn { width: auto; margin-top: 0; }

  /* Giving a page the screen must not give everything *on* it the screen.
   * `.btn` and `.field` are `width: 100%` because that is right on a phone;
   * inherited by a 1200px card it produces a 1200px-wide "Email it" button
   * and a 1200px-wide box for an e-transfer reference. A control should be as
   * wide as what goes in it. */
  .wrap--wide .btn { width: auto; min-width: 11rem; }
  .wrap--wide .field { max-width: 26rem; }
  .wrap--wide textarea.field { max-width: 44rem; }

  /* Four short columns stretched across the page put the description at one
   * edge and the amount at the other, and reading a line means tracking a
   * thousand pixels of whitespace. */
  .wrap--wide .lines { max-width: 46rem; }

  /* `flex: 1 1 auto` shares a phone's width between three buttons; on a
   * desktop it shares 1400px between them, and "This week" becomes a 400px
   * slab. They only ever needed to be as wide as their words. */
  .wrap--wide .daynav .btn { flex: 0 0 auto; min-width: 9rem; }

  /* The week, as a week. Seven stacked full-width strips is a phone layout
   * being asked to do a desk's job — one column per day is what somebody
   * looking at a whole week actually wants to see. */
  .wrap--wide .weekgrid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0.75rem 1rem;
    align-items: start;
  }
  .wrap--wide .weekgrid__day { min-width: 0; }
  .wrap--wide .weekgrid .dayhead { margin-top: 0; font-size: 0.9rem; }
}

/* Exactly seven columns once they fit. `auto-fit` alone lands on six at this
 * width and wraps Sunday onto a line of its own, which stops looking like a
 * week — the one thing this view is for. */
@media (min-width: 78rem) {
  .wrap--wide .weekgrid { grid-template-columns: repeat(7, minmax(0, 1fr)); }
}

.stat {
  display: block;
  padding: 0.9rem 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--border);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--text);
  min-height: 44px;
}
.stat:hover, .stat:focus-visible { border-color: var(--accent); }
.stat-value { display: block; font-size: 1.9rem; line-height: 1.1; font-weight: 600; }
.stat strong { display: block; font-size: 0.95rem; }
.stat-detail { display: block; color: var(--muted); font-size: 0.85rem; }

/* Tone marks the edge, never the text: colour is the second signal here, and
 * the number and its caption say the same thing without it. */
.stat--warn { border-left-color: var(--warn); }
.stat--bad  { border-left-color: var(--danger); }

.card--running { border-left: 4px solid var(--ok); }
.card--running .footnote { margin-left: 0.4rem; }

/* Charts are SVG, not divs with an inline `style="height: …%"`. The CSP is
 * `style-src 'self'` with no 'unsafe-inline', so a style attribute is refused
 * by the browser and every bar draws flat — silently, since the page is still
 * a 200 with the right numbers in it. A geometry attribute on a <rect> is not
 * CSS and is not covered by style-src. Colour comes from here, so the chart
 * follows the palette in both themes. */
/* `height: auto` so the viewBox scales uniformly — the chart has text in
 * it, and a fixed height would stretch the glyphs at every width but one. */
/* Capped so a wide page does not turn a twelve-bar chart into a mural. The
 * cap is on WIDTH, not height: the chart carries text, and fixing its height
 * while the width still stretches is what squashes the glyphs. */
.chart {
  display: block;
  width: 100%;
  max-width: 34rem;
  height: auto;
  margin: 0.75rem 0 0.25rem;
}
.chart-track { fill: var(--surface-2); }
.chart-fill  { fill: var(--accent); }
.chart-label { fill: var(--muted); font-size: 7px; }

/* Hours by cleaner: horizontal, because names are long and there are few. */
.barlist li {
  display: grid;
  grid-template-columns: minmax(4.5rem, auto) 1fr auto;
  align-items: center;
  gap: 0.6rem;
  margin: 0.4rem 0;
}
.barlist-chart { display: block; width: 100%; height: 0.8rem; }
.barlist-value { font-variant-numeric: tabular-nums; font-size: 0.9rem; }

/* The access log's filter bar wraps rather than scrolls. A modifier, not a
 * redefinition of `.filters` — that class is already worn by the client,
 * hours and invoice filter bars, and quietly turning them all into flex rows
 * would be a change to three pages this phase never meant to touch. */
.filters--log { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.filters--log .field { flex: 1 1 8rem; min-width: 0; }
.filters--log .btn { width: auto; }
.filters--log__date {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  flex: 1 1 10rem;
  font-size: 0.85rem;
  color: var(--muted);
}
.filters--log__date .field { flex: 1 1 auto; }
