/* Tab — one stylesheet, no build step.
 *
 * Every colour pair below was measured before it shipped, not eyeballed. The
 * house target is 6:1 and the floor is 4.5:1; the muted greys and the teal
 * fills were both moved to clear 6:1 (#655C53, not #6B6259; #075A57 fills, not
 * #21837C, which only reaches 4.56 under white text).
 */

/* Figtree, her pick. Self-hosted rather than linked from Google Fonts: Tab is a
 * PWA that consultants open on phones, and a webfont that arrives late (or not
 * at all on a bad connection) reflows the whole screen. One 20KB variable file
 * covers every weight from 400 to 800. */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('./fonts/figtree-latin.woff2') format('woff2');
}

:root {
  --bg: #FBF8F3;
  --surface: #FFFFFF;
  --ink: #171310;            /* 17.43 on bg */
  --muted: #655C53;          /* 6.18 on bg, 6.54 on surface */
  --line: #E7E0D5;
  --accent: #075A57;         /* 7.60 on bg; 8.05 under white text */
  --accent-soft: #E6F1F0;
  --danger: #8C2F1E;         /* 7.80 on bg */
  --on-accent: #FFFFFF;

  /* Brand colors from the mark. ⚠️ LOGO ONLY — never type, never a button fill.
   * Measured on the light ground: clay reaches 4.21 and sun 2.04, so both fail
   * the 4.5 text floor outright. They clear the 3:1 WCAG minimum for GRAPHICS,
   * which is the rule a logo is actually judged by. Reach for --accent when you
   * need something colored that a person has to read. */
  --brand-clay: #C4553A;
  --brand-sun: #E8A33D;
  --brand-amber: #BE7A16;    /* the deeper amber the "total" bar needs: 3.33 on
                                the slip, where #E8A33D managed only 2.05 */
  --brand-slip: #FFF8F0;
  --brand-cream: #FFF6E6;

  --draft-fg: #6B4E00;   --draft-bg: #FDF3D6;
  --sub-fg:   #0B4F6C;   --sub-bg:   #DCEEF6;
  --appr-fg:  #0D5A2E;   --appr-bg:  #DDF2E4;
  --paid-fg:  #4A3A75;   --paid-bg:  #E8E3F6;

  --radius: 12px;
  --font: 'Figtree', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #151210;
    --surface: #1E1A17;
    --ink: #F3F0EA;          /* 16.40 on bg */
    --muted: #A89E93;        /* 7.09 on bg, 6.56 on surface */
    --line: #38312A;
    /* The mark keeps its own colors in both themes: a logo that recolors is a
     * different logo. The clay tile reads on either ground (4.21 light,
     * 4.18 dark), which is why it was chosen. */
    --brand-cream: #2A2318;
    --accent: #4FC3BB;       /* 8.68 on bg, 8.02 on surface */
    --accent-soft: #12302E;
    --danger: #F09A88;       /* 8.53 on bg */
    --on-accent: #0A2E2C;    /* 6.84 under the accent fill */

    --draft-fg: #F0D48A;   --draft-bg: #3A2F12;
    --sub-fg:   #9BD4EC;   --sub-bg:   #12303D;
    --appr-fg:  #8FDCA9;   --appr-bg:  #123322;
    --paid-fg:  #C4B8F0;   --paid-bg:  #241F3A;
  }
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg); color: var(--ink);
  font-family: var(--font); font-size: 16px; line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}
.hide { display: none !important; }
.muted { color: var(--muted); }
.warn { color: var(--danger); }

/* ---- gates (sign in) ---------------------------------------------------- */
.gate { min-height: 100dvh; display: grid; place-items: center; padding: 24px; }
.gate-inner { width: min(420px, 100%); }
.gate h1 { font-size: 2rem; margin: 0 0 4px; }
.fields { display: grid; gap: 10px; margin-top: 20px; }
details { margin-top: 8px; }
details summary { color: var(--muted); cursor: pointer; padding: 6px 0; }
details input { margin-bottom: 8px; }

/* ---- form controls ------------------------------------------------------ */
input, select, textarea {
  width: 100%; padding: 11px 12px; font: inherit; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 10px; appearance: none;
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
}
label { display: block; margin-bottom: 14px; font-weight: 600; font-size: 0.9rem; }
label input, label select, label textarea { margin-top: 6px; font-weight: 400; }
.hint { display: block; margin-top: 5px; font-weight: 400; font-size: 0.82rem; color: var(--muted); }
.two { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

.btn {
  padding: 12px 18px; font: inherit; font-weight: 600; cursor: pointer;
  background: var(--accent); color: var(--on-accent);
  border: 1px solid var(--accent); border-radius: 10px;
}
.btn:hover { filter: brightness(1.08); }
.btn:disabled { opacity: 0.55; cursor: default; }
.btn.ghost { background: transparent; color: var(--accent); }
.btn.small { padding: 7px 12px; font-size: 0.85rem; }
.btn--danger { background: var(--danger); border-color: var(--danger); color: #FFFFFF; }
.link {
  background: none; border: 0; padding: 4px 2px; font: inherit; font-size: 0.87rem;
  color: var(--accent); cursor: pointer; text-decoration: underline;
}
.link--danger { color: var(--danger); }
.actions { display: flex; gap: 10px; align-items: center; }

/* ---- app shell ---------------------------------------------------------- */
/* The shell is wide; the PANELS decide their own measure.
 *
 * This was 780px on #app, which capped everything — including the nine-column
 * Review table — at 780px no matter how big the screen was. The table then
 * overflowed and I went and built scroll affordances for it, which was solving
 * the symptom. Her catch: "doesn't even take up my full laptop screen."
 *
 * Forms and lists still want a narrow measure, because long lines are hard to
 * read and a full-width text input looks broken. A data table wants room. So
 * the cap moved down to .panel, where it can differ per screen. */
#app { max-width: 1220px; margin: 0 auto; padding: 16px 16px 96px; }

/* Reading and data-entry panels keep the comfortable measure. */
.panel { max-width: 780px; margin-left: auto; margin-right: auto; }

/* The wide tables get the whole shell. At 1220px the Review table's ~890px of
   columns fits outright, so the scroll affordances below become the fallback
   for narrow windows and phones rather than the everyday experience. */
#panel-review, #panel-team, #panel-rates { max-width: 100%; }
.top {
  display: flex; justify-content: space-between; align-items: flex-start;
  padding-bottom: 14px; margin-bottom: 16px; border-bottom: 1px solid var(--line);
}
.brand-block { display: flex; align-items: center; gap: 11px; }
.brand-mark { width: 38px; height: 38px; flex-shrink: 0; display: block; }
.brand { font-size: 1.4rem; font-weight: 800; letter-spacing: -0.03em; line-height: 1.1; }
.who { font-size: 0.87rem; }

/* Sign-in wordmark. The slip keeps its literal colors; only the word takes a
   theme token, so it stays legible on either ground. */
.wordmark { width: min(232px, 78%); height: auto; display: block; margin-bottom: 10px; }
.wordmark-text {
  font-family: 'Figtree', system-ui, sans-serif;
  font-size: 44px; font-weight: 800; letter-spacing: -1.9px; fill: var(--ink);
}

h2 { font-size: 1.1rem; margin: 22px 0 10px; }
.card-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.card-head h2 { margin: 22px 0 10px; }

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px; margin-top: 8px;
}
.note {
  background: var(--accent-soft); border-left: 3px solid var(--accent);
  padding: 10px 12px; border-radius: 0 8px 8px 0; font-size: 0.87rem; margin: 12px 0;
}
/* A note that is stopping you, rather than telling you something. Same shape,
   danger colouring, so "you cannot do this" never looks like a tip. */
.note--stop {
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  border-left-color: var(--danger);
}
.empty { color: var(--muted); padding: 20px 4px; }
.preview { font-size: 0.88rem; color: var(--muted); min-height: 1.3em; margin: 4px 0 12px; }

/* ---- pay periods -------------------------------------------------------- */
/* The cadence, stated where people stand. Kaneisha could not recall the current
   arrangement when asked, which is what this bar exists to fix. */
.period-bar {
  display: flex; justify-content: space-between; align-items: baseline;
  flex-wrap: wrap; gap: 6px 16px;
  background: var(--accent-soft); border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 11px 14px; margin: 10px 0 4px;
}
.period-bar--closed { border-left-color: var(--muted); background: var(--surface); }
.period-name  { font-weight: 700; }
/* --muted is measured against --bg and --surface, not against this teal ground:
   on --accent-soft it drops to 5.67 light and 5.36 dark. Both clear the 4.5
   floor and both miss the house 6:1 target, so the bar carries its own muted,
   measured on the ground it actually sits on. #59514A reads 6.74 on the light
   accent-soft; #B8AEA2 reads 6.46 on the dark one. */
.period-bar { --muted-here: #59514A; }
@media (prefers-color-scheme: dark) { .period-bar { --muted-here: #B8AEA2; } }
.period-state {
  margin-left: 8px; font-size: 0.78rem; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--muted-here, var(--muted));
}
.period-dates {
  display: flex; gap: 14px; flex-wrap: wrap;
  font-size: 0.83rem; color: var(--muted-here, var(--muted));
  font-variant-numeric: tabular-nums;
}
/* The closed bar sits on --surface, where the standard --muted already reads
   6.54 light and 6.56 dark. Hand it back rather than carrying the teal value
   onto a ground it was not measured against. */
.period-bar--closed { --muted-here: var(--muted); }
.day-locked { font-size: 0.83rem; color: var(--muted); margin: 8px 0 0; }

/* Submitting a period. The summary sits in the dialog; this is the doorway. */
.submit-block { display: flex; justify-content: space-between; align-items: center; gap: 14px; flex-wrap: wrap; }
.submit-block p { margin: 0; font-size: 0.9rem; }

/* Editing someone else's pay. Loud on purpose: the form is otherwise identical
   to the one a consultant uses on their own entries. */
.owner-warning {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  border-left: 3px solid var(--danger);
  color: var(--ink);
  padding: 10px 12px; border-radius: 0 8px 8px 0;
  font-size: 0.88rem; font-weight: 600; margin: 4px 0 14px;
}

/* ---- the Review bar ----------------------------------------------------- */
.review-bar {
  display: flex; gap: 14px; align-items: flex-end; flex-wrap: wrap;
  margin: 14px 0 4px;
}
.review-bar label { flex: 1 1 150px; margin: 0; }
.review-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* ---- the two note fields ------------------------------------------------ */
/* Whichever field a person types into, they should already know who reads it.
   The client-visible one carries the accent border, the icon and the badge; the
   private one is deliberately quieter, because quiet is the safe default. */
.note-field {
  display: block; padding: 12px 14px; margin-bottom: 14px;
  border: 1px solid var(--line); border-radius: var(--radius);
}
.note-field--private { background: transparent; }
/* Even border all the way round. The heavy left edge was one signal too many
   next to the icon, the badge and the tint — her call. */
.note-field--client {
  background: var(--accent-soft);
  border-color: var(--accent);
}
.note-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-weight: 700; font-size: 0.9rem; margin-bottom: 8px;
}
.note-icon {
  /* 20px with a 2.1 stroke in a 24-unit box ≈ 1.75 real pixels. The first pass
     used 18px and 1.8, which came out at 1.35px — thin enough that the lens and
     the pupil merged into a blob. A stroked icon this small needs weight. */
  width: 20px; height: 20px; flex-shrink: 0;
  fill: none; stroke: currentColor; stroke-width: 2.1;
  stroke-linecap: round; stroke-linejoin: round;
}
.note-field--client .note-head { color: var(--accent); }
.note-field--private .note-head { color: var(--muted); }
.note-badge {
  font-size: 0.72rem; font-weight: 700; padding: 3px 9px; border-radius: 20px;
  letter-spacing: 0.01em; white-space: nowrap;
}
.note-badge--client  { background: var(--accent); color: var(--on-accent); }
.note-badge--private { background: transparent; color: var(--muted); border: 1px solid var(--line); }
.note-field textarea { margin-top: 0; }
.note-field--client textarea { border-color: var(--accent); background: var(--surface); }

/* ---- stats -------------------------------------------------------------- */
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.stat {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px; text-align: center;
}
.stat-label { display: block; font-size: 0.75rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.stat-value { display: block; font-size: 1.25rem; font-weight: 700; margin-top: 3px; }

/* ---- entries ------------------------------------------------------------ */
.day { margin-top: 18px; }
.day h3 {
  display: flex; justify-content: space-between; font-size: 0.82rem;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted);
  margin: 0 0 8px; padding-bottom: 5px; border-bottom: 1px solid var(--line);
}
.day-total { font-variant-numeric: tabular-nums; }
.entry {
  display: flex; justify-content: space-between; gap: 14px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px 14px; margin-bottom: 8px;
}
.entry--flag { border-left: 3px solid var(--danger); }
.entry-title { font-weight: 600; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.entry-meta { display: flex; gap: 10px; flex-wrap: wrap; font-size: 0.83rem; color: var(--muted); margin-top: 3px; }
.entry-meta .billed { color: var(--accent); font-weight: 600; }
.entry-note { font-size: 0.86rem; color: var(--muted); margin: 6px 0 0; }
.entry-note--client {
  display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap;
  color: var(--ink);
}
.entry-flag { font-size: 0.83rem; color: var(--danger); margin: 6px 0 0; }
.entry-side { text-align: right; flex-shrink: 0; }
.entry-amount { font-weight: 700; font-variant-numeric: tabular-nums; }
.entry-actions { display: flex; gap: 8px; justify-content: flex-end; }
.chip {
  font-size: 0.72rem; font-weight: 600; padding: 2px 8px; border-radius: 20px;
  background: var(--accent-soft); color: var(--accent);
}
.chip--internal { background: transparent; color: var(--muted); border: 1px solid var(--line); }

.pill {
  display: inline-block; font-size: 0.72rem; font-weight: 700; padding: 2px 9px;
  border-radius: 20px; text-transform: uppercase; letter-spacing: 0.03em; margin: 4px 0;
}
.pill--draft     { background: var(--draft-bg); color: var(--draft-fg); }
.pill--submitted { background: var(--sub-bg);   color: var(--sub-fg); }
.pill--approved  { background: var(--appr-bg);  color: var(--appr-fg); }
.pill--paid      { background: var(--paid-bg);  color: var(--paid-fg); }

/* ---- admin rows --------------------------------------------------------- */
.row {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px 14px; margin-bottom: 8px;
}
.row--off { opacity: 0.55; }
.row-title { font-weight: 600; display: flex; align-items: center; gap: 8px; }
.row-meta { font-size: 0.83rem; color: var(--muted); margin-top: 2px; }
.row-side { text-align: right; flex-shrink: 0; }
.rate-value { display: block; font-weight: 700; font-variant-numeric: tabular-nums; }
.rate-value--unset { color: var(--muted); font-weight: 400; font-style: italic; }

/* A client row holds a second list inside it: the people who may see that
   client's hours. The base .row is a single flex line, so the stacked variant
   turns the axis and keeps the head looking exactly like every other row. */
.row--stack { display: block; }
.row-head {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
}
.row--stack .row-side { display: flex; gap: 14px; }

.viewer-block { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line); }
.viewer-head {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); margin-bottom: 6px;
}
.viewer {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  padding: 6px 0;
}
.viewer + .viewer { border-top: 1px dashed var(--line); }
.viewer--off { opacity: 0.55; }
.viewer-name { font-weight: 600; font-size: 0.9rem; margin-right: 6px; }
.viewer .row-meta { display: inline; margin: 0; }
.empty--tight { padding: 4px 0; font-size: 0.86rem; }

/* ---- table -------------------------------------------------------------- */
/* Wide content scrolls inside its own box; the page body never scrolls sideways. */
/* The Review table is 9 columns and will not fit a laptop window, so it
   scrolls. The problem was that macOS hides overlay scrollbars until you
   actually scroll, so a scrollable table read as a BROKEN one: the last header
   sliced in half by a rounded corner, with nothing saying "there is more this
   way." Her report, twice.

   Two affordances, because either alone is missable: a scrollbar that is always
   visible, and a soft fade at the right edge. */
/* The fade lives on the NON-scrolling parent. Painting it on the scroll
   container itself made it scroll away with the table, which is why the first
   attempt showed nothing. */
.table-wrap { position: relative; }
.table-wrap::after {
  content: '';
  position: absolute; top: 1px; right: 1px; bottom: 1px; width: 40px;
  pointer-events: none;
  border-radius: 0 var(--radius) var(--radius) 0;
  background: linear-gradient(to right, transparent, var(--surface) 82%);
}
/* Once scrolled to the end there is nothing more to hint at, so JS drops this
   class and the fade goes away rather than lying about hidden content. */
.table-wrap.at-end::after { opacity: 0; transition: opacity .15s; }

.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}
/* ⚠️ Do NOT add `scrollbar-width` or `scrollbar-color` here. Setting either
   makes Chrome ignore every ::-webkit-scrollbar rule below, and macOS then goes
   back to hiding the scrollbar until it is already being used — which is the
   whole reason a scrollable table looked like a broken one. */
.table-scroll::-webkit-scrollbar { height: 10px; -webkit-appearance: none; }
.table-scroll::-webkit-scrollbar-track {
  background: var(--surface);
  border-radius: 0 0 var(--radius) var(--radius);
}
.table-scroll::-webkit-scrollbar-thumb {
  background: var(--line);
  border-radius: 10px;
  border: 2px solid var(--surface);
}
.table-scroll::-webkit-scrollbar-thumb:hover { background: var(--muted); }
.tbl { width: 100%; border-collapse: collapse; font-size: 0.86rem; min-width: 680px; }
.tbl th, .tbl td { padding: 9px 11px; text-align: left; border-bottom: 1px solid var(--line); white-space: nowrap; }
/* The first and last columns sit against the table's rounded corners, so the
   default 11px reads as "nearly touching the edge" — her catch on the Review
   table, where the drawdown dash was crowding the border. */
.tbl th:first-child, .tbl td:first-child { padding-left: 16px; }
.tbl th:last-child,  .tbl td:last-child  { padding-right: 18px; }
.tbl th { background: var(--accent-soft); color: var(--accent); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; }
.tbl .num { text-align: right; font-variant-numeric: tabular-nums; }
.tbl tr:last-child td { border-bottom: 0; }
.tbl tr.flag td { background: color-mix(in srgb, var(--danger) 8%, transparent); }

/* Rows that open an entry. The cursor and the hover state are what make a row
   look like it does something; the focusable button inside the date cell is
   what makes it reachable without a mouse. Both, not one. */
.tbl--rows tbody tr { cursor: pointer; }
.tbl--rows tbody tr:hover td { background: var(--accent-soft); }
.tbl--rows tbody tr.flag:hover td { background: color-mix(in srgb, var(--danger) 14%, transparent); }
.tbl--rows .decide { white-space: nowrap; }
.tbl--rows .decide .link + .link { margin-left: 10px; }
.tbl--rows td .link { font-weight: 600; }
/* The row is clickable, so the focus ring has to be obvious about which control
   inside it actually has focus. */
.tbl--rows td .link:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px;
}

/* ---- nav ---------------------------------------------------------------- */
.nav {
  position: fixed; left: 0; right: 0; bottom: 0; display: flex;
  background: var(--surface); border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
}
.nav button {
  flex: 1; padding: 13px 4px; font: inherit; font-size: 0.8rem; font-weight: 600;
  background: none; border: 0; color: var(--muted); cursor: pointer;
  border-top: 2px solid transparent;
}
.nav button.on { color: var(--accent); border-top-color: var(--accent); }

/* ---- dialog + toast ----------------------------------------------------- */
.ask {
  border: 1px solid var(--line); border-radius: var(--radius); padding: 20px;
  background: var(--surface); color: var(--ink); width: min(420px, calc(100vw - 32px));
}
.ask::backdrop { background: rgba(0, 0, 0, 0.45); }
.ask h3 { margin: 0 0 8px; }
.ask p { margin: 0 0 12px; font-size: 0.88rem; white-space: pre-line; }
.ask input, .ask textarea { margin-bottom: 14px; }

/* The several-fields shape. Labels already carry their own spacing from the
   base form rules, so this only has to stop the last field crowding the
   buttons. */
.ask-fields { margin-bottom: 6px; }
.ask-fields label { display: block; margin-bottom: 10px; font-size: 0.88rem; font-weight: 600; }
.ask-fields input, .ask-fields select { margin-bottom: 0; }
/* A reason is prose. The box has to look like it expects a sentence or three,
   because the size of the field is the instruction people actually read. */
.ask textarea {
  width: 100%; min-height: 96px; resize: vertical;
  font-family: inherit; font-size: 1rem; line-height: 1.45;
}

.toast {
  position: fixed; left: 50%; bottom: 76px; transform: translateX(-50%);
  max-width: min(520px, calc(100vw - 32px));
  background: var(--ink); color: var(--bg);
  padding: 11px 16px; border-radius: 10px; font-size: 0.88rem; z-index: 50;
}
.toast.err { background: var(--danger); color: #FFFFFF; }

@media (max-width: 520px) {
  .stats { grid-template-columns: 1fr; }
  .two { grid-template-columns: 1fr; }
  .entry { flex-direction: column; }
  .entry-side { text-align: left; }
  .entry-actions { justify-content: flex-start; }
}

/* ---- documents: my invoices and timesheets ------------------------------- */
/* Every colour here is a token that was already measured. Nothing new is
 * introduced, so nothing new needs re-measuring: the contrast work is done in
 * :root and these rules only arrange it. */

.doc {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); margin-bottom: 12px; overflow: hidden;
}
.doc--open { border-color: var(--accent); }

.doc-head {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 14px 16px; background: none; border: 0; cursor: pointer;
  font: inherit; color: var(--ink); text-align: left;
}
.doc-head:hover { background: var(--accent-soft); }
.doc-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.doc-when { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.doc-period { font-weight: 700; }
.doc-number { font-size: 0.8rem; color: var(--muted); }

.doc-figures { display: flex; flex-direction: column; align-items: flex-end; }
.doc-total { font-weight: 700; font-variant-numeric: tabular-nums; }
.doc-hours { font-size: 0.8rem; color: var(--muted); font-variant-numeric: tabular-nums; }

.doc-stands { margin: 0; padding: 0 16px 12px; color: var(--muted); font-size: 0.88rem; }
.doc-body:not(:empty) { padding: 0 16px 16px; border-top: 1px solid var(--line); }

/* An adjustment is a line no time entry produced, so it reads as its own kind
 * of thing. The guaranteed-minimum top-up is the one that matters: a visible
 * line is the difference between a number she can check and one she has to
 * trust. */
.tbl tr.adj td { background: var(--accent-soft); }
.tbl tr.total-row td { border-top: 2px solid var(--ink); font-size: 1rem; }

/* ---- payday -------------------------------------------------------------- */

.batch { margin-bottom: 16px; }
.batch-figures {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  color: var(--muted); font-size: 0.85rem;
}
/* Each subtotal carries its own currency symbol, and they never merge into one
 * figure. An unlabeled number in a two-currency list is a mistake waiting to be
 * made at the exact moment money moves. */
.batch-subtotal {
  font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums;
  background: var(--accent-soft); border-radius: 20px; padding: 2px 10px;
}
.batch-rows { margin: 4px 0 12px; }
.ref-box {
  width: 150px; padding: 7px 9px; font: inherit; font-size: 0.85rem;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg); color: var(--ink);
}
.ref-box:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Seven tabs at the admin's width, three at a consultant's. The smaller size is
 * what keeps "My time" on one line on a 375px phone rather than wrapping into
 * the bar above it. */
.nav button { font-size: 0.72rem; padding: 13px 2px; }

/* ==========================================================================
   THE CLIENT'S OWN SCREEN
   Its own shell, deliberately, not the staff app with tabs hidden. There is no
   nav here, so the bottom padding that clears the nav bar is not needed either.
   ========================================================================== */
#clientapp { max-width: 900px; margin: 0 auto; padding: 16px 16px 48px; }

/* Wider than the 780px reading measure the staff panels take: this panel is a
   five-column table, not a form. Narrow enough that a note still reads well. */
.panel--client { max-width: 100%; }

.client-block + .client-block {
  margin-top: 34px; padding-top: 26px; border-top: 1px solid var(--line);
}
.client-name { margin: 0 0 12px; }

/* Hours LEFT is the headline, and hours used is the supporting figure. The
   spec's framing note, and it is not decoration: a live meter counting up makes
   a client feel watched by a taxi meter, so the emphasis goes on what they
   still have. */
.stats--client { grid-template-columns: repeat(3, 1fr); margin-bottom: 14px; }
.stat--headline {
  background: var(--accent-soft); border-color: var(--accent);
}
.stat--headline .stat-value { font-size: 1.9rem; color: var(--accent); }

/* --muted is measured against --bg and --surface, not against this teal ground,
   where it drops to 5.67 light and 5.36 dark. Both still clear the 4.5 floor,
   but the label is small text, so it takes the darker pair used on the period
   bar for the same reason. */
.stat--headline { --muted-here: #59514A; }
@media (prefers-color-scheme: dark) { .stat--headline { --muted-here: #B8AEA2; } }
.stat--headline .stat-label { color: var(--muted-here); }

.stats--client .stat-value { font-variant-numeric: tabular-nums; }

/* The end date is a date, not a number, so it does not want the big numeral
   size that would make it shout over the balance. */
.stats--client .stat:last-child .stat-value { font-size: 1rem; line-height: 1.35; }

.tbl--client { min-width: 620px; }

/* The base .tbl sets white-space: nowrap, which is right for a money column and
   wrong for a sentence: a consultant's note would run the table off the screen
   rather than wrapping inside its own cell. */
.tbl--client .note-cell {
  color: var(--muted); font-size: 0.88rem;
  white-space: normal; max-width: 34ch;
}
/* Two short lines rather than one long one, for the rows that used no hours. */
.tbl--client .num .muted { display: inline-block; line-height: 1.3; }

@media (max-width: 620px) {
  .stats--client { grid-template-columns: 1fr; }

  /* 🔴 ON A PHONE THE TABLE BECOMES A STACK OF CARDS, and this is the fix that
     matters most on this screen. A parent checks her daughter's hours on a
     phone. As a scrolling table the hours column, which is the entire reason
     she opened the page, sat off the right edge behind a sideways swipe she
     had no reason to know was there. The staff tables keep the scroll: they
     are dense grids read at a desk, and a dense grid does not become clearer
     as forty stacked cards. */
  .tbl--client { min-width: 0; display: block; }
  .tbl--client thead { display: none; }
  .tbl--client tbody, .tbl--client tr, .tbl--client td { display: block; }

  .tbl--client tr {
    border: 1px solid var(--line); border-radius: var(--radius);
    padding: 10px 12px; margin-bottom: 8px;
  }
  .tbl--client td {
    border-bottom: 0; padding: 3px 0; white-space: normal;
    display: flex; justify-content: space-between; gap: 12px; text-align: right;
  }
  .tbl--client td::before {
    content: attr(data-label);
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--muted); text-align: left; flex-shrink: 0;
  }
  /* An empty note would otherwise render as a stray "NOTES" label with nothing
     beside it. */
  .tbl--client td.note-cell:empty { display: none; }
  .tbl--client td.note-cell { max-width: none; }
  .tbl--client td:first-child { padding-left: 0; }
  .tbl--client td:last-child { padding-right: 0; }

  /* The fade and the scrollbar are for content that scrolls. This no longer
     does, and a fade over a stack of cards is a hint pointing at nothing. */
  .panel--client .table-wrap::after { display: none; }
  .panel--client .table-scroll { overflow-x: visible; }
}

/* ---- view as: the preview banner ---------------------------------------- */
/* It has to be impossible to mistake this screen for the client's own, because
   a screenshot of it will outlive the context it was taken in. */
.preview-bar {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  flex-wrap: wrap;
  background: var(--accent-soft); border: 1px solid var(--accent);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius); padding: 10px 14px; margin-bottom: 18px;
  font-size: 0.88rem;
}
.preview-tag {
  background: var(--accent); color: var(--on-accent);
  border-radius: 20px; padding: 2px 10px; margin-right: 8px;
  font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.05em;
}
/* --muted is measured against --bg and --surface, not this teal ground, where
   it drops to 5.67 light and 5.36 dark. The bar's own text uses --ink instead,
   which clears the floor comfortably on both. */
.preview-bar > div { color: var(--ink); }

/* The preview panel gets the client screen's measure, not the admin forms'. */
#panel-preview { max-width: 900px; }

.viewer-actions { display: flex; gap: 14px; flex-shrink: 0; }

/* An engagement with no end date on file shows two tiles, not three. */
.stats--two { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 620px) { .stats--two { grid-template-columns: 1fr; } }

/* ---- the greeting ------------------------------------------------------- */
/* Option B, her pick: its own line under the header rather than squeezed onto
   it. The header goes back to being identity and the sign-out. */
.greeting { margin: 0 0 18px; }
.pagehead {
  font-size: 1.55rem; font-weight: 800; letter-spacing: -0.02em;
  margin: 0 0 2px; line-height: 1.2;
}
.greeting-date { margin: 0; font-size: 0.88rem; }

/* The staff shell is 1220px wide while its reading panels are 780px, so an
   unconstrained greeting would start far to the left of the screen it belongs
   to. It takes the same measure as whatever panel is showing. */
#app .greeting { max-width: 780px; margin-left: auto; margin-right: auto; }
#app:has(#panel-review:not(.hide)) .greeting,
#app:has(#panel-team:not(.hide)) .greeting,
#app:has(#panel-rates:not(.hide)) .greeting { max-width: 100%; }
#app:has(#panel-preview:not(.hide)) .greeting { max-width: 900px; }

@media (max-width: 620px) { .pagehead { font-size: 1.35rem; } }
