/* =====================================================================
   FiniTasks base
   Structure, layout, and component SHAPE. Carries no colour, no radius,
   and no elevation values of its own: every one of those is a token the
   active theme sets. That is what lets a theme change the entire feel of
   the platform without editing a single component rule.
   ===================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

/*
 * THE `hidden` ATTRIBUTE ACTUALLY HIDES THINGS.
 *
 * It is only a user-agent rule of display:none, so ANY author rule that sets
 * display beats it — and most components set display. Without this line, every
 * `<button class="btn" hidden>` and `<div class="stack" hidden>` on the site
 * rendered regardless of the attribute, and three separate places were shipping
 * that way: the "Add to home screen" button and the iOS instructions on the
 * install page showed on every device, and the rejection-reason panel opened on
 * every row of the account-check queue at once, which is what turned a
 * scannable list into a wall of controls.
 *
 * Each of those looked like a JavaScript failure and was not: the markup and
 * the scripts were correct, and a test asserting the attribute was present
 * passed, because the attribute WAS present. `!important` is the normal idiom
 * here for exactly this reason — it is what normalize.css does — and it means
 * no component can quietly opt out of being hideable.
 *
 * scripts/check-hidden-guards.mjs fails the build if this rule goes missing.
 */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
}

img, svg { max-width: 100%; }
img { height: auto; display: block; }

a { color: var(--link); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--link-hover); }

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

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--ink); color: var(--paper); padding: 12px 16px;
}
.skip-link:focus { left: 0; }

/* ---------------------------------------------------------------- Type */
h1, h2, h3, h4 { margin: 0; font-family: var(--font-display); }
p { margin: 0; }

.h1 { font-weight: var(--weight-display); font-size: var(--text-h1); line-height: 1.1;  letter-spacing: var(--tracking-h1); }
.h2 { font-weight: var(--weight-display); font-size: var(--text-h2); line-height: 1.18; letter-spacing: var(--tracking-h2); }
.h3 { font-weight: var(--weight-h3);      font-size: var(--text-h3); line-height: 1.3; }

.lede    { font-size: var(--text-lede); line-height: 1.55; max-width: 54ch; color: var(--ink-body); }
.body    { font-size: var(--text-sm); }
.caption { font-size: var(--text-xs); color: var(--ink-muted); }
.measure { max-width: 62ch; }
.muted   { color: var(--ink-muted); }

.label {
  font-size: var(--text-xs); font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-muted);
}

/* Money. Mono and tabular in every theme, without exception: figures have to
   line up in a column, and a worker has to be able to compare two of them at
   a glance. Gold is reserved for money and verification and means nothing
   else, in any theme. */
.money, .money-lg, .mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.money    { font-weight: 600; font-size: var(--text-money);    color: var(--gold-text); }
.money-lg { font-weight: 600; font-size: var(--text-money-lg); color: var(--gold-text); }
.mono     { font-size: var(--text-xs); }
.money--pending { color: var(--pending-text); font-weight: 500; }
.money--void    { color: var(--ink-muted);    font-weight: 500; }

/* -------------------------------------------------------------- Layout */
.shell { max-width: var(--shell); margin: 0 auto; padding-inline: var(--gutter); }
.section { border-bottom: var(--section-rule); }
.section--raised { background: var(--paper-raised); }
.section--sunk   { background: var(--paper-sunk); }
.section__inner  { padding-block: var(--section-pad); }

.stack { display: flex; flex-direction: column; }
.stack-6{gap:6px} .stack-8{gap:8px} .stack-12{gap:12px} .stack-16{gap:16px}
.stack-20{gap:20px} .stack-24{gap:24px} .stack-32{gap:32px}

.row { display: flex; flex-wrap: wrap; gap: 12px; }
.row--between  { justify-content: space-between; align-items: center; }
.row--baseline { align-items: baseline; justify-content: space-between; }
.row--tight    { flex-wrap: nowrap; }

.grid { display: grid; gap: var(--grid-gap); }
.grid--halves { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid--cards  { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid--tiles  { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }

.start { align-self: flex-start; }
.hr { border: 0; border-top: var(--rule-1); margin: 0; }

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

/* ------------------------------------------------------------- Buttons */
.btn {
  min-height: var(--tap);
  display: inline-flex; align-items: center; justify-content: center;
  padding-inline: var(--btn-pad);
  border: var(--btn-border); border-radius: var(--radius-btn);
  background: var(--btn-bg); color: var(--btn-fg);
  font-family: inherit; font-size: var(--text-base); font-weight: 600;
  text-decoration: none; cursor: pointer;
  box-shadow: var(--btn-shadow);
  transition: background var(--t-fast), color var(--t-fast),
              border-color var(--t-fast), box-shadow var(--t-fast),
              transform var(--t-fast);
}
.btn:hover  { background: var(--btn-bg-hover); color: var(--btn-fg); border-color: var(--btn-border-hover); }
.btn:active { transform: translateY(1px); }

.btn--primary {
  background: var(--accent); color: var(--accent-fg);
  border-color: var(--accent-border); box-shadow: var(--btn-shadow-primary);
}
.btn--primary:hover { background: var(--accent-hover); color: var(--accent-fg); border-color: var(--accent-hover); }

.btn--lg    { min-height: var(--tap-lg); padding-inline: calc(var(--btn-pad) + 6px); font-size: var(--text-lede); }
/* A row action, not a page action. Several of these have to sit side by side
   inside one table cell, so this drops below the 44px tap target the rest of
   the platform keeps to. A deliberate trade, confined to the admin console,
   which is work done sitting down with a pointer. */
.btn--sm    { min-height: 32px; padding-inline: 11px; font-size: var(--text-xs); }
.btn--block { width: 100%; }
.btn--quiet {
  border: 0; background: transparent; box-shadow: none; padding-inline: 8px;
  text-decoration: underline; text-underline-offset: 4px;
}
.btn--quiet:hover { background: transparent; color: var(--link-hover); }
.btn:disabled, .btn[aria-disabled="true"] {
  background: var(--paper-sunk); color: var(--ink-muted);
  border-color: var(--rule-color); box-shadow: none; cursor: not-allowed;
}

/* -------------------------------------------------------------- Inputs */
.field { display: flex; flex-direction: column; gap: 7px; }
.field__label { font-size: var(--text-sm); font-weight: 600; }
.field__hint  { font-size: var(--text-xs); color: var(--ink-muted); }
.field__error { font-size: var(--text-xs); color: var(--error); }

.input {
  min-height: var(--input-h);
  padding: 0 14px; width: 100%;
  border: var(--input-border); border-radius: var(--radius-input);
  background: var(--input-bg); color: var(--ink);
  font-family: inherit;
  /* Never below 16px. iOS zooms the whole page on focus otherwise, and a
     worker who has to pinch back out after every field will not finish. */
  font-size: 16px;
  box-shadow: var(--input-shadow);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.input::placeholder { color: var(--ink-faint); }
.input:focus { outline: none; border-color: var(--accent); box-shadow: var(--input-shadow-focus); }
.input--invalid { border-color: var(--error); }
.input--numeric { font-family: var(--font-mono); letter-spacing: .06em; }

/* Show / Hide on password boxes (js/password-toggle.js). */
.pw-wrap { position: relative; display: block; }
.pw-input { padding-right: 68px; }
.pw-toggle {
  position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
  min-height: 32px; padding: 0 10px;
  border: 0; border-radius: var(--radius-input); background: transparent;
  color: var(--accent); font: inherit; font-size: var(--text-sm); font-weight: 600;
  cursor: pointer;
}
.pw-toggle:hover { text-decoration: underline; }
.pw-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* --------------------------------------------------------------- Cards */
.card {
  border: var(--card-border); border-radius: var(--radius-card);
  background: var(--paper-raised); box-shadow: var(--card-shadow);
  display: flex; flex-direction: column; overflow: hidden;
}
.card--flat { background: var(--paper); }

.card__head {
  padding: 14px 18px; border-bottom: var(--rule-1);
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
}
.card__body  { padding: 18px; display: flex; flex-direction: column; gap: 12px; flex: 1; }
.card__foot  { padding: 16px 18px; border-top: var(--rule-1); }
.card__title { font-size: var(--text-title); font-weight: 600; line-height: 1.35; }
.card__price {
  display: flex; justify-content: space-between; align-items: baseline;
  border-top: var(--rule-dashed); padding-top: 14px; margin-top: auto;
}

.tile {
  border: var(--card-border); border-radius: var(--radius-card);
  background: var(--paper-raised); box-shadow: var(--card-shadow);
  padding: 20px; display: flex; flex-direction: column; gap: 10px;
  text-decoration: none; color: inherit;
  transition: box-shadow var(--t-med), transform var(--t-med), border-color var(--t-med);
}
a.tile:hover { color: inherit; border-color: var(--accent); box-shadow: var(--card-shadow-hover); }

/* --------------------------------------------------------------- Steps */
.steps {
  display: flex; flex-direction: column;
  border: var(--card-border); border-radius: var(--radius-card);
  background: var(--paper-raised); overflow: hidden;
}
.step { display: flex; gap: 18px; padding: 22px; border-bottom: var(--rule-1); }
.step:last-child { border-bottom: 0; }
.step--alt { background: var(--paper-sunk); }
.step__num {
  font-family: var(--font-mono); font-weight: 600; font-size: var(--text-h3);
  color: var(--accent); min-width: 38px;
}

/* -------------------------------------------------------------- Badges */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-xs); font-weight: 600; padding: 5px 11px;
  border: 1px solid currentColor; border-radius: var(--radius-pill);
}
.badge--verified { color: var(--green);        background: var(--green-wash); }
.badge--pending  { color: var(--pending-text); background: var(--pending-wash); border-style: dashed; }
.badge--rejected { color: var(--error);        background: var(--error-wash); }

.rejection {
  border: 1px solid var(--error); border-radius: var(--radius-card);
  background: var(--error-wash); padding: 14px;
  display: flex; flex-direction: column; gap: 6px;
}

/* --------------------------------------------------------- Ledger rows */
.ledger { display: flex; flex-direction: column; }
.ledger__row {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 18px; border-bottom: var(--rule-1); font-size: var(--text-sm);
}
.ledger__row:last-child { border-bottom: 0; }
.ledger__row--alt { background: var(--paper-sunk); }
.ledger__ref  { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--ink-muted); min-width: 74px; }
.ledger__what { flex: 1; min-width: 0; }

/* A live figure with no data behind it yet. Never a zero dressed as a number. */
.stat-empty { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--ink-muted); }

/* =====================================================================
   OPERATIONAL TABLE
   The console is scanned and acted on, not read. Where the rest of the
   platform spends space on calm, this spends it on density: rows you can
   compare down a column, and the action for a row sitting on that row.

   It scrolls inside its own box. A table with nine columns will not fit a
   phone at any font size worth reading, and a page that scrolls sideways
   as a whole loses the sidebar and the header with it.
   ===================================================================== */
.otable-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Below this the columns stop being readable and start being three words a
   line, so the box scrolls instead of squeezing. */
.otable { width: 100%; min-width: 1060px; border-collapse: collapse; font-size: var(--text-sm); }
.otable th {
  text-align: left; white-space: nowrap;
  font-size: var(--text-xs); font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-muted);
  padding: 10px 12px; border-bottom: var(--rule-1);
}
.otable td { padding: 11px 12px; border-bottom: var(--rule-1); vertical-align: top; }
.otable tbody tr:last-child td { border-bottom: 0; }
.otable tbody tr:nth-child(even) { background: var(--paper-sunk); }
/* Figures that get compared down a column line up, digit under digit. */
.otable__num { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* A link is evidence you clicked the right order, not something to read. One
   line, ellipsis, full value in the title attribute. */
.otable__link { display: block; max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.otable__acts { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
/* Column hints. Without them the auto table layout gives the greedy link
   column everything and breaks a three word service name over three lines. */
.otable__title { display: block; min-width: 150px; }

/* ------------------------------------------------------ Order rows
   The orders list was a nine column table pinned to a 1060px minimum, so on
   any phone it scrolled sideways and every cell carried a second caption line
   under it. Same data, laid out as rows that reflow: the identifying half on
   the left, the figures on the right, actions last. */
.orders { list-style: none; margin: 0; padding: 0; }
.orow {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 10px 22px; align-items: center;
  padding: 14px 18px; border-bottom: var(--rule-1);
}
.orow:last-child { border-bottom: 0; }
.orow:hover { background: var(--paper-sunk); }
.orow__main { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.orow__head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.orow__ref { font-family: var(--font-mono); font-weight: 600; text-decoration: none; }
.orow__title { font-weight: 600; }
.orow__meta { font-size: var(--text-xs); color: var(--ink-muted); }
/* A link here is evidence you are looking at the right order, not something
   to read. One line, ellipsis, full value in the title attribute. */
.orow__link {
  font-size: var(--text-xs); display: block; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.orow__figs {
  display: flex; flex-direction: column; gap: 3px; text-align: right;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.orow__acts { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

@media (max-width: 720px) {
  .orow { grid-template-columns: 1fr; }
  .orow__figs { flex-direction: row; gap: 14px; text-align: left; align-items: baseline; }
}

/* ------------------------------------------------------ Filter toolbar */
.toolbar {
  display: flex; flex-direction: column; gap: 12px;
  padding: 14px 18px; border-bottom: var(--rule-1); background: var(--paper-sunk);
}
.toolbar__search { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.toolbar__search .input { flex: 1 1 260px; min-width: 0; }

.tabs { display: flex; flex-wrap: wrap; gap: 6px; }
.tabs__tab {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 12px; border: var(--btn-border); border-radius: var(--radius-pill);
  background: var(--paper); color: var(--ink-body);
  font-size: var(--text-xs); font-weight: 600; text-decoration: none;
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast);
}
.tabs__tab:hover { border-color: var(--btn-border-hover); color: var(--ink); }
.tabs__tab[aria-current="page"] {
  background: var(--accent); color: var(--accent-fg); border-color: var(--accent-border);
}
/* The count is a fact about the queue, not a decoration, so it stays legible
   on both the plain and the selected pill. */
.tabs__count { font-variant-numeric: tabular-nums; opacity: .75; }

/* ------------------------------------------------------ Seal and stamp */
.seal  { display: block; }
.stamp { animation: stamp-land 420ms cubic-bezier(.2,.85,.15,1) both; transform-origin: center; }
@keyframes stamp-land {
  0%   { opacity: 0; transform: scale(2.15) rotate(-13deg); }
  55%  { opacity: 1; transform: scale(1.03) rotate(-6deg); }
  100% { opacity: 1; transform: scale(1) rotate(-6.5deg); }
}
.stamp-frame {
  border: var(--card-border); border-radius: var(--radius-card);
  background: var(--paper-sunk); padding: 14px; min-height: 108px;
  display: flex; align-items: center; justify-content: center;
  position: relative; overflow: hidden;
}

/* -------------------------------------------------------------- Reveal
   Desktop only, once, on scroll. Deliberately small: 12px and a fade.
   Expensive-feeling sites are quiet. Sliding and bouncing everything into
   view is what a cheap template does, so the budget here is tiny on purpose.

   Not applied at all below 900px. Scroll-driven animation on a mid-range
   Android on patchy data drops frames and makes the phone feel broken,
   which is the opposite of what we are going for. */
@media (min-width: 900px) {
  .reveal { opacity: 0; transform: translateY(12px); }
  .reveal.is-in {
    opacity: 1; transform: none;
    transition: opacity 520ms ease-out, transform 520ms cubic-bezier(.2,.7,.3,1);
  }
  .reveal-d1.is-in { transition-delay:  70ms; }
  .reveal-d2.is-in { transition-delay: 140ms; }
  .reveal-d3.is-in { transition-delay: 210ms; }
  a.tile:hover { transform: translateY(-2px); }
}

/* Motion off entirely. The stamp still appears, at its final angle, because
   it carries meaning rather than decoration. */
@media (prefers-reduced-motion: reduce) {
  .stamp  { animation: none; opacity: 1; transform: rotate(-6.5deg); }
  .reveal { opacity: 1 !important; transform: none !important; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
}

/* ------------------------------------------------------- Header/footer */
.site-header {
  border-bottom: var(--rule-1);
  background: var(--paper-raised);
  position: sticky; top: 0; z-index: 5;
}
/* The header is sticky, so an anchor that scrolls to the top of its target
   parks that target underneath it. Every in-page link landed on a heading you
   could not read. */
:target, [id] { scroll-margin-top: 86px; }

/* ------------------------------------------------------ Day bars
   A bar per day, all scaled against the busiest one so a quiet day reads as
   quiet. Scrolls inside itself rather than squeezing, because ninety bars in
   a phone's width is a grey smear. */
.bars { display: flex; align-items: flex-end; gap: 3px; height: 150px; overflow-x: auto; padding-bottom: 2px; }
.bars__col { display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 5px; flex: 1 0 14px; height: 100%; }
.bars__bar { width: 100%; min-height: 2px; border-radius: 3px 3px 0 0; background: var(--accent); opacity: .85; }
.bars__col:hover .bars__bar { opacity: 1; }
.bars__tick { font-size: 10px; color: var(--ink-muted); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------ Colour mode toggle
   Both icons ship in the markup and CSS picks the one that applies, so
   pressing it swaps instantly with no second paint and no icon that can lag a
   frame behind the theme it is describing. */
.modetog {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex: 0 0 auto;
  border: var(--btn-border); border-radius: var(--radius-btn);
  background: var(--btn-bg); color: var(--ink-muted);
  cursor: pointer; padding: 0;
  transition: color var(--t-fast), background var(--t-fast), border-color var(--t-fast);
}
.modetog:hover { color: var(--ink); background: var(--btn-bg-hover); border-color: var(--btn-border-hover); }
.modetog__sun { display: none; }
:root[data-mode="dark"] .modetog__moon { display: none; }
:root[data-mode="dark"] .modetog__sun  { display: block; }

/* The signed-in shell's top bar sits on the sidebar colour, so the control has
   to read against that rather than against page paper. */
.app__top .modetog { background: transparent; border-color: var(--rule-color); }
.app__top .modetog:hover { background: var(--paper-sunk); }

/* ------------------------------------------------------ Home hero
   The hero was a column of text with an empty right half, which read as
   unfinished. It is filled with atmosphere rather than a picture: the image
   below is blurred past recognition and sunk to a low opacity, so it gives the
   space depth without asking to be looked at. */
.hero { position: relative; overflow: hidden; isolation: isolate; }
.hero__copy { position: relative; z-index: 1; max-width: 840px; }

.hero__wash {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  display: flex; justify-content: flex-end; align-items: center;
}
.hero__seal {
  /* Bleeds off the right edge on purpose. A mark cropped by the frame reads as
     a watermark the page is sitting on; the same mark fully inside its box
     reads as a second logo somebody forgot to delete. */
  width: clamp(260px, 38vw, 500px); height: auto; flex: 0 0 auto;
  margin-right: -6%;
  opacity: .1;
}
/* Gold on a dark ground is a lighter step against its background than gold on
   white is, so the same alpha reads fainter there. */
:root[data-mode="dark"] .hero__seal { opacity: .14; }

@media (max-width: 900px) {
  /* The copy runs full width here, so the mark has nowhere to sit except
     behind it. Smaller, fainter, and pushed further off the edge, so it reads
     as a corner watermark rather than as texture under the words. */
  .hero__seal { width: 250px; opacity: .06; margin-right: -18%; }
  /* Needs restating: the dark rule above outranks the width query on its own. */
  :root[data-mode="dark"] .hero__seal { opacity: .09; }
}
.site-header__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding-block: 14px;
}
.brand { display: flex; align-items: center; gap: 11px; text-decoration: none; color: inherit; }
.brand:hover { color: inherit; }
.brand__name {
  font-family: var(--font-display); font-weight: var(--weight-display);
  font-size: 20px; letter-spacing: var(--tracking-h2);
}
.site-nav { display: flex; align-items: center; gap: 22px; font-size: var(--text-sm); }
.site-nav__link { text-decoration: none; font-weight: 500; }
@media (max-width: 720px) { .site-nav__link { display: none; } }

.site-footer { background: var(--paper-raised); border-top: var(--rule-1); }
.site-footer__grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 30px; padding-block: 48px;
}
.site-footer__grid a { text-decoration: none; }
.site-footer__grid a:hover { text-decoration: underline; }
.site-footer__legal { border-top: var(--rule-1); font-size: var(--text-xs); color: var(--ink-muted); }
.site-footer__legal .shell {
  display: flex; flex-wrap: wrap; gap: 12px;
  justify-content: space-between; padding-block: 18px;
}

/* --------------------------------------------------- Theme compare bar
   Temporary. Removed with the losing theme once the choice is made. */
.themebar {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 16px; z-index: 60;
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px;
  border: var(--card-border); border-radius: var(--radius-pill);
  background: var(--paper-raised); box-shadow: var(--card-shadow-hover);
  font-size: var(--text-xs);
}
.themebar__label { color: var(--ink-muted); font-weight: 600; }
.themebar a {
  text-decoration: none; padding: 7px 14px; border-radius: var(--radius-pill);
  color: var(--ink); font-weight: 600;
}
.themebar a[aria-current="true"] { background: var(--accent); color: var(--accent-fg); }
@media (max-width: 520px) { .themebar { font-size: 13px; gap: 6px; padding: 7px 9px; } }

/* ------------------------------------------------------------- Hero lanes
   The two-lane split: a visitor identifies which half is theirs in about
   two seconds, rather than reading a paragraph to find out. */
.lanes { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--grid-gap); }
.lane {
  border: var(--card-border); border-radius: var(--radius-card);
  background: var(--paper-raised); box-shadow: var(--card-shadow);
  padding: 26px; display: flex; flex-direction: column; gap: 16px;
  transition: box-shadow var(--t-med), transform var(--t-med), border-color var(--t-med);
}
.lane--accent { border-color: var(--accent); }
.lane__points { display: flex; flex-direction: column; gap: 11px; margin: 0; padding: 0; list-style: none; }
.lane__points li { display: flex; gap: 11px; align-items: flex-start; font-size: var(--text-sm); }
.lane__tick { flex: 0 0 auto; margin-top: 3px; color: var(--green); }
@media (min-width: 900px) { .lane:hover { box-shadow: var(--card-shadow-hover); transform: translateY(-2px); } }

/* Headline figures. Real numbers from real queries, never invented. */
.figures { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 2px; }
.figure {
  padding: 18px; background: var(--paper-raised);
  border: var(--card-border); display: flex; flex-direction: column; gap: 5px;
}
.figure:first-child { border-radius: var(--radius-card) 0 0 var(--radius-card); }
.figure:last-child  { border-radius: 0 var(--radius-card) var(--radius-card) 0; }
.figure__value { font-family: var(--font-mono); font-weight: 600; font-size: 26px; font-variant-numeric: tabular-nums; }
.figure__label { font-size: var(--text-xs); color: var(--ink-muted); }
@media (max-width: 620px) {
  .figure, .figure:first-child, .figure:last-child { border-radius: var(--radius-card); }
}

/* =====================================================================
   Application shell
   Everything behind the login. A fixed sidebar on desktop, a bottom bar on
   mobile, because a worker uses this one-handed in short bursts and the two
   questions they return with are "is there a task" and "did I get paid".
   Both have to be one thumb-reach away.
   ===================================================================== */

.app { display: flex; min-height: 100vh; background: var(--paper-sunk); }

.app__side {
  width: 252px; flex: 0 0 252px;
  background: var(--sidebar-bg); color: var(--sidebar-fg);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
}
.app__brand {
  display: flex; align-items: center; gap: 11px;
  padding: 20px 18px; text-decoration: none; color: var(--sidebar-fg);
  border-bottom: 1px solid var(--sidebar-rule);
}
.app__brand:hover { color: var(--sidebar-fg); }
.app__brand span { font-family: var(--font-display); font-weight: 700; font-size: 19px; }

.app__navgroup { padding: 18px 12px 6px; }
.app__navlabel {
  font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--sidebar-muted); padding-inline: 10px;
}
.app__nav { display: flex; flex-direction: column; gap: 2px; padding: 6px 12px; }
.app__navlink {
  display: flex; align-items: center; gap: 11px;
  min-height: 44px; padding: 0 12px; border-radius: var(--radius-btn);
  color: var(--sidebar-fg); text-decoration: none;
  font-size: 15px; font-weight: 500;
  transition: background var(--t-fast), color var(--t-fast);
}
.app__navlink:hover { background: var(--sidebar-hover); color: var(--sidebar-fg); }
.app__navlink[aria-current="page"] { background: var(--sidebar-active); color: #fff; font-weight: 600; }
.app__navlink svg { flex: 0 0 auto; opacity: .9; }
.app__navtag {
  margin-left: auto; font-size: 11px; font-weight: 700; padding: 2px 7px;
  border-radius: 999px; background: var(--accent); color: #fff;
}
/* A live count ("3 waiting") reads differently from a fixed label ("Earn"):
   red says something needs you, the sidebar's own accent colour never does
   since it is also the active-link colour used right next to it. */
.app__navtag--count { background: var(--error); }
.app__navlink[aria-current="page"] .app__navtag--count { background: rgba(255, 255, 255, .3); }

.app__main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.app__top {
  background: var(--paper-raised); border-bottom: var(--rule-1);
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 12px 22px; position: sticky; top: 0; z-index: 6;
}
.app__title { font-family: var(--font-display); font-weight: 700; font-size: 18px; letter-spacing: -.01em; }
/* The staff quick-jump. Takes the space between the title and the who-am-i
   block, wide enough to read an order number or an email as it is typed. */
.app__jump { flex: 1 1 320px; max-width: 420px; }
.app__jump .input { min-height: 38px; font-size: 14px; }
.app__who { display: flex; align-items: center; gap: 11px; }
.app__avatar {
  width: 38px; height: 38px; border-radius: 999px; flex: 0 0 38px;
  background: var(--accent); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 14px;
}
.app__whoname { display: flex; flex-direction: column; line-height: 1.25; }
.app__whoname b { font-size: 14px; font-weight: 600; }
.app__whoname span { font-size: 12px; color: var(--ink-muted); text-transform: capitalize; }

.app__body { padding: 26px 22px 96px; flex: 1; }
.app__inner { max-width: 1080px; margin: 0 auto; display: flex; flex-direction: column; gap: 22px; }

/* Stat cards. The balance is the first thing anyone looks for. */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 16px; }
.stat {
  background: var(--paper-raised); border: var(--card-border);
  border-radius: var(--radius-card); box-shadow: var(--card-shadow);
  padding: 20px; display: flex; flex-direction: column; gap: 7px;
}
.stat--hero { border-color: var(--accent); }
.stat__label { font-size: 13px; color: var(--ink-muted); font-weight: 600; }
.stat__value {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-weight: 600; font-size: 27px; line-height: 1.1;
}
.stat__value--money { color: var(--gold-text); }
.stat__note { font-size: 12px; color: var(--ink-muted); }

/* Delivery, on one line.
   An order page exists to answer "how much of this is done", and a bar plus
   "12 of 50" answers it in a glance, where four stat cards made somebody read
   four numbers and do the division themselves. Two layers, because claimed
   and approved are different promises: claimed is work somebody is holding
   right now, approved is work that is finished and paid for. */
.progress { display: flex; flex-direction: column; gap: 9px; }
.progress__head { display: flex; justify-content: space-between; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.progress__count {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-weight: 600; font-size: 24px; line-height: 1.1;
}
.progress__track {
  position: relative; height: 10px; border-radius: 999px;
  background: var(--paper-sunk); border: 1px solid var(--rule-color); overflow: hidden;
}
.progress__fill { position: absolute; top: 0; bottom: 0; left: 0; }
.progress__fill--claimed { background: var(--gold); opacity: .4; }
.progress__fill--done { background: var(--green); }
.progress__key { display: flex; gap: 16px; flex-wrap: wrap; font-size: 12px; color: var(--ink-muted); }
.progress__key i { display: inline-block; width: 9px; height: 9px; border-radius: 3px; margin-right: 5px; }

/* A stat card that is also a link to the queue it counts. Neutralised back to
   plain text so the number reads as a figure, not a hyperlink. */
a.stat { text-decoration: none; color: inherit; }
a.stat:hover { border-color: var(--btn-border-hover); box-shadow: var(--card-shadow-hover); }

/* An empty state that says what will appear and how to make it appear,
   rather than a blank panel that reads as broken. */
.empty {
  background: var(--paper-raised); border: 1px dashed var(--rule-color);
  border-radius: var(--radius-card); padding: 40px 24px;
  display: flex; flex-direction: column; align-items: center; gap: 12px; text-align: center;
}
.empty__title { font-weight: 600; font-size: 17px; }
.empty__body { font-size: 14px; color: var(--ink-muted); max-width: 46ch; }

.notice {
  display: flex; gap: 12px; align-items: flex-start;
  border: 1px solid var(--gold); background: var(--pending-wash);
  border-radius: var(--radius-card); padding: 15px 17px;
}
.notice__body { font-size: 14px; }

/* How-to videos: the drawn picture (_tut-art.eta), the compact card on
   Linked accounts (_howto.eta) and the Tutorials page (tutorials.eta). The
   picture keeps the same blue in both themes, like a video thumbnail does. */
.tutart {
  position: relative; flex: 0 0 auto; width: 84px; aspect-ratio: 9 / 14;
  border-radius: 12px; overflow: hidden; color: #fff;
  background: linear-gradient(165deg, #1B4F9C, #0B63CE 55%, #1E86F0) center / cover no-repeat;
  display: block;
}
.tutart--verify { background-image: linear-gradient(165deg, #14305A, #0B63CE 60%, #3A93F2); }
.tutart__pic { position: absolute; top: 12%; left: 11%; width: 78%; height: auto; }
.tutart__play {
  position: absolute; left: 50%; bottom: 5%; transform: translateX(-50%);
  width: 32px; height: 32px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(15, 27, 45, .72); backdrop-filter: blur(2px);
}
.tutart__len {
  position: absolute; top: 7px; right: 7px; font-size: 10.5px; font-weight: 600;
  padding: 2px 7px; border-radius: 99px; background: rgba(15, 27, 45, .6);
}
.howto {
  display: flex; align-items: center; gap: 14px;
  border: var(--card-border); background: var(--paper-raised);
  border-radius: var(--radius-card); padding: 12px 14px;
  color: var(--ink); text-decoration: none; box-shadow: var(--card-shadow);
}
.howto:hover, .tut:hover { border-color: var(--accent); }
.howto .tutart { width: 56px; border-radius: 9px; }
.howto .tutart__play { width: 20px; height: 20px; bottom: 3%; }
.howto .tutart__play svg { width: 11px; height: 11px; }
.howto .tutart__len { display: none; }
.howto__text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.howto__note { font-size: 13px; color: var(--ink-muted); }
.howto__go { flex: 0 0 auto; font-weight: 600; font-size: 14px; color: var(--accent); }
.tuts { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr)); }
.tuts__title { margin: 0; font-size: 24px; }
.tut {
  display: flex; gap: 16px; align-items: stretch; color: var(--ink); text-decoration: none;
  border: var(--card-border); background: var(--paper-raised);
  border-radius: var(--radius-card); padding: 14px; box-shadow: var(--card-shadow);
}
.tut .tutart { width: 112px; }
.tut__text { display: flex; flex-direction: column; gap: 6px; justify-content: center; min-width: 0; }
.tut__name { font-size: 17px; line-height: 1.3; }
.tut__steps { margin: 0; padding-left: 18px; font-size: 13.5px; color: var(--ink-muted); display: flex; flex-direction: column; gap: 3px; }
.tut__btn { align-self: flex-start; margin-top: 4px; }
@media (max-width: 420px) { .tut .tutart { width: 92px; } .tut { gap: 12px; } }

/* Policy pages: /terms, /privacy, /conduct, /refunds, /support.
   Long-form body copy needs a looser line height and a hard measure, and
   numbered clauses need to be findable when somebody is quoting one back at
   us. Nothing here is used outside those five pages. */
.prose {
  display: flex; flex-direction: column; gap: 12px;
  max-width: 68ch; font-size: var(--text-sm); line-height: 1.65;
}
.prose ul { display: flex; flex-direction: column; gap: 9px; margin: 0; padding-left: 20px; }
.prose strong { font-weight: 600; }

/* Request and response examples on /api.
   Scrolls inside itself. A long JSON line must never widen the page and set
   the whole document scrolling sideways on a phone. */
.codeblock {
  margin: 0; padding: 14px 16px;
  background: var(--paper-sunk); border: 1px solid var(--rule-color);
  border-radius: var(--radius-card);
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  font-family: var(--font-mono); font-size: 13px; line-height: 1.55;
  color: var(--ink); tab-size: 2;
}
.codeblock code { font: inherit; background: none; border: 0; padding: 0; white-space: pre; }

.clause { display: flex; flex-direction: column; gap: 10px; padding: 22px 0; border-top: var(--rule-1); }
.clause:first-child { border-top: 0; padding-top: 0; }
.clause__num {
  font-family: var(--font-mono); font-size: var(--text-xs);
  color: var(--ink-muted); letter-spacing: .08em;
}

/* =====================================================================
   MOBILE
   The bottom bar carries four destinations. Everything else, including
   sign out, lives in the drawer behind the menu button.

   The sidebar used to be display:none below 900px, which took sign out
   with it: on a phone there was no way to leave your own account, and no
   route to two thirds of the app. It is off canvas now rather than gone.
   ===================================================================== */

/* The checkbox that drives the drawer. Moved off screen rather than hidden,
   because a display:none input is not reliably reachable by its label. */
.navtoggle {
  position: absolute; width: 1px; height: 1px;
  opacity: 0; pointer-events: none; margin: -1px;
}
.app__scrim { display: none; }
.app__burger { display: none; }

/* Identity and sign out, at the foot of the drawer. */
.app__sidewho {
  margin-top: auto; display: flex; align-items: center; gap: 11px;
  padding: 16px 18px 10px; border-top: 1px solid var(--sidebar-rule);
}
.app__sidewhoname { display: flex; flex-direction: column; line-height: 1.3; min-width: 0; }
.app__sidewhoname b { font-size: 14px; font-weight: 600; color: var(--sidebar-fg); }
.app__sidewhoname span { font-size: 12px; color: var(--sidebar-muted); }
.app__signout { padding: 4px 12px 20px; }
.app__signoutbtn {
  display: flex; align-items: center; gap: 11px; width: 100%;
  min-height: 44px; padding: 0 12px; border: 0; border-radius: var(--radius-btn);
  background: none; color: var(--sidebar-muted);
  font: inherit; font-size: 15px; font-weight: 500; text-align: left; cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast);
}
.app__signoutbtn:hover { background: var(--sidebar-hover); color: var(--sidebar-fg); }

.app__bottom { display: none; }

@media (max-width: 900px) {
  .app { flex-direction: column; }
  .app__body { padding: 18px 16px calc(96px + env(safe-area-inset-bottom, 0px)); }
  .app__top { padding: 10px 14px; gap: 10px; }
  /* No room for it here, and the admin dashboard carries the same box at the
     top of its own body, reachable in one tap from the drawer. */
  .app__jump { display: none; }

  .app__burger {
    position: relative;
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; margin-left: -10px; flex: 0 0 44px;
    border-radius: var(--radius-btn); color: var(--ink); cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .app__burger:hover { background: var(--paper-sunk); }
  .app__burger:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
  .app__burgerdot {
    position: absolute; top: 6px; right: 6px; width: 9px; height: 9px;
    border-radius: 999px; background: var(--error); border: 2px solid var(--paper-raised);
  }

  /* The title takes what is left, and the account block never squeezes it
     onto two lines on a 360px screen. */
  .app__title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .app__whoname { display: none; }

  /* Off canvas, not absent. dvh as well as vh, so an iOS toolbar sliding away
     does not leave the sign out button below the fold. */
  .app__side {
    display: flex;
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 40;
    width: min(84vw, 320px); height: 100vh; height: 100dvh;
    transform: translateX(-100%);
    transition: transform var(--t-med, .22s) ease;
    box-shadow: 0 0 40px rgba(0, 0, 0, .35);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    overscroll-behavior: contain;
  }
  .navtoggle:checked ~ .app .app__side { transform: translateX(0); }

  .app__scrim {
    display: block; position: fixed; inset: 0; z-index: 30;
    background: rgba(15, 27, 45, .55);
    opacity: 0; pointer-events: none;
    transition: opacity var(--t-med, .22s) ease;
  }
  .navtoggle:checked ~ .app .app__scrim { opacity: 1; pointer-events: auto; }

  .app__bottom {
    display: grid; position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
    background: var(--paper-raised); border-top: var(--rule-1);
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  .app__bottomlink {
    min-height: 58px; display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 3px;
    text-decoration: none; color: var(--ink-muted); font-size: 12px; font-weight: 600;
    -webkit-tap-highlight-color: transparent;
  }
  .app__bottomicon { position: relative; display: inline-flex; }
  .app__bottomdot {
    position: absolute; top: -2px; right: -4px; width: 8px; height: 8px;
    border-radius: 999px; background: var(--error); border: 2px solid var(--paper-raised);
  }
  /* Labels always visible. Icon-only bars cost more in support tickets than
     they save in pixels. */
  .app__bottomlink[aria-current="page"] { color: var(--accent); }
  .app__bottomlink[aria-current="page"] svg { color: var(--accent); }
}

@media (prefers-reduced-motion: reduce) {
  .app__side, .app__scrim { transition: none; }
}

/* =====================================================================
   NARROW PHONES
   Below is about a 360px screen, where a row that merely wraps on a tablet
   starts overlapping instead.
   ===================================================================== */
@media (max-width: 560px) {
  /* A card head with a title and an action stacks rather than colliding. */
  .card__head { flex-wrap: wrap; row-gap: 6px; }

  /* A ledger row is a label and an amount. Below this width the amount goes
     under the label rather than squeezing it to two characters a line. */
  .ledger__row { flex-wrap: wrap; row-gap: 6px; padding: 12px 14px; }
  .ledger__what { flex: 1 1 100%; }

  /* Money stays legible. 27px numerals overflow a narrow stat card as soon as
     the figure passes six digits. */
  .stat { padding: 16px; }
  .stat__value { font-size: 23px; }

  /* A row told never to wrap still has to fit on the screen it is on. */
  .row--tight { flex-wrap: wrap; }

  /* Full width primary buttons. A 44px tall button floating at 120px wide in
     the middle of a card is the most missed control on a phone. */
  .btn--lg { width: 100%; justify-content: center; }
}

/* ------------------------------------------------------ Proof strip
   The rate range, the catalogue size and the two numbers that decide whether
   somebody can get their money out, directly under the hero. Visible pricing
   is the trust signal this market responds to most, and it is the one most
   competitors put behind a signup. */
.proof { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 20px 28px; }
.proof__item { display: flex; flex-direction: column; gap: 4px; }
.proof__num {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-weight: 600; font-size: 21px; line-height: 1.15; color: var(--gold-text);
}
.proof__label { font-size: var(--text-xs); color: var(--ink-muted); }

/* ------------------------------------------------------ Tick lists
   Benefits, scanned rather than read. The landing page used to make the same
   points in paragraphs, which meant a visitor had to read three sentences to
   learn one fact. A tick and five words carries it. */
.ticks { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.ticks--two { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px 26px; }
.ticks li { display: flex; align-items: flex-start; gap: 10px; font-size: var(--text-sm); }
.ticks svg { flex: 0 0 auto; margin-top: 3px; color: var(--green); }


/* ------------------------------------------------------ Activation bar
   The standing state of an unactivated worker's account, on every page they
   see. Gold rather than red: this is a step they have not taken yet, not an
   error they have made, and a red bar on every screen reads as something
   being broken. */
.actbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 13px 16px;
  border: 1px solid var(--gold);
  border-radius: var(--radius-card);
  background: var(--pending-wash);
  font-size: var(--text-sm);
}
.actbar__icon { flex: 0 0 auto; color: var(--gold); }
.actbar__text { flex: 1 1 260px; min-width: 0; }
.actbar__cta {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; min-height: 38px;
  padding-inline: 16px;
  border-radius: var(--radius-btn);
  background: var(--accent); color: var(--accent-fg);
  font-weight: 600; text-decoration: none;
}
.actbar__cta:hover { background: var(--accent-hover); color: var(--accent-fg); }

/* ==================================================================
   Dashboard charts
   ==================================================================
   Sparklines are rendered on the server as inline SVG (src/app/spark.ts).
   They inherit colour from here rather than carrying it in the markup, so a
   theme switch moves them with everything else instead of leaving one card
   painted for the other theme. */

.dash-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--gap, 16px);
  margin-bottom: var(--gap, 16px);
}

.spark-wrap {
  width: 100%;
  color: var(--brand, #0d7355);
  line-height: 0;
}
.spark-wrap--money { color: var(--gold-text, #b8860b); }

.spark { width: 100%; height: 52px; display: block; overflow: visible; }
.spark__line { stroke: currentColor; }
.spark__area { fill: currentColor; opacity: .13; }
.spark__tip  { fill: currentColor; }
.spark--empty { color: var(--ink-muted, #8b97a6); height: 52px; }

/* Direction is carried by colour AND by the glyph in the markup, so it still
   reads for anybody who cannot separate the two hues. */
.trend { font-variant-numeric: tabular-nums; white-space: nowrap; }
.trend--up   { color: var(--green, #0d7355); }
.trend--down { color: var(--error, #a92920); }
.trend--flat { color: var(--ink-muted, #8b97a6); }

/* Progress bar, used for delivery against slots. */
.meter {
  display: block;
  height: 6px;
  border-radius: 99px;
  background: rgba(127, 127, 127, .2);
  overflow: hidden;
  min-width: 60px;
}
.meter__fill {
  display: block;
  height: 100%;
  border-radius: 99px;
  background: var(--brand, #0d7355);
  transition: width .4s cubic-bezier(.2, .7, .3, 1);
}
@media (prefers-reduced-motion: reduce) {
  .meter__fill { transition: none; }
}

/* ==================================================================
   Hero cards (advertiser dashboard)
   ==================================================================
   Balance, and the three things you do with it, in one place. The actions
   live inside the card rather than in a toolbar because they are actions ON
   this number, and separating them makes you go looking. */

.hero-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--gap, 16px);
  margin-bottom: var(--gap, 16px);
}

.hero-card__body {
  align-items: center;
  text-align: center;
  gap: 6px;
  padding-top: 26px;
  padding-bottom: 26px;
}
.hero-card__icon { color: var(--ink-muted, #8b97a6); margin-bottom: 6px; }
.hero-card__value {
  font-size: clamp(28px, 5vw, 40px);
  font-weight: 600;
  letter-spacing: -.02em;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.hero-card__label { color: var(--ink-muted, #8b97a6); font-size: 14px; }
.hero-card--soft { background: var(--gold-wash, rgba(184, 134, 11, .07)); }

.quick { display: flex; gap: 22px; justify-content: center; margin-top: 18px; flex-wrap: wrap; }
.quick__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  text-decoration: none;
  color: inherit;
  min-width: 68px;
}
.quick__ring {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  transition: transform .14s ease, background-color .14s ease;
}
.quick__ring--go    { color: var(--brand, #0d7355); }
.quick__ring--fund  { color: var(--gold-text, #b8860b); }
.quick__ring--learn { color: var(--ink-muted, #8b97a6); }
.quick__ring--tut   { color: var(--accent, #0B63CE); }
.quick__item:hover .quick__ring { transform: translateY(-2px); background: rgba(127,127,127,.08); }
.quick__label { font-size: 12.5px; color: var(--ink-muted, #8b97a6); }

/* ==================================================================
   Task board
   ==================================================================
   Six columns on a desktop so pay and remaining slots can be compared down a
   column; a stacked card per row below 720px, because six columns at 360px is
   unreadable however it is styled. */

.taskboard { display: flex; flex-direction: column; }

.taskboard__row {
  display: grid;
  /* # · task · done/needed · pay · status · ref · button
     642px of fixed track plus gaps. The sidebar is 252px, so this only has the
     room it needs above about 1180px — which is exactly where the stacked
     layout below takes over. */
  grid-template-columns: 34px minmax(0, 1fr) 140px 92px 96px 88px 108px;
  gap: 14px;
  align-items: center;
  padding: 13px 18px;
  border-bottom: 1px solid var(--rule, #e6ecf2);
}
.taskboard__row:last-child { border-bottom: none; }
.taskboard__row--alt { background: rgba(127, 127, 127, .035); }
.taskboard__row--head {
  font-size: 10.5px;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--ink-muted, #8b97a6);
  padding-top: 10px;
  padding-bottom: 10px;
}
.taskboard__row--head .tb-pay,
.taskboard__row--head .tb-rate { text-align: left; }

.tb-n { color: var(--ink-muted, #8b97a6); font-size: 13px; font-variant-numeric: tabular-nums; }
.tb-what { display: flex; align-items: center; gap: 11px; min-width: 0; }
.tb-name { min-width: 0; line-height: 1.35; }
.tb-rate { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
.tb-pay { font-size: 15px; }
.tb-status { display: flex; align-items: center; }
.tb-ref { white-space: nowrap; }
.tb-go { text-align: right; }
.tb-go .btn { min-height: 36px; font-size: 13px; padding-left: 14px; padding-right: 14px; }

/* 1180, not 720.
   Seven columns need about 840px and the sidebar takes 252 of whatever the
   window has, so the table only fits on a wide desktop. It was set to 720
   while the table had six columns and did not really fit at 900 either: the
   name column collapsed and its wrapped text ran under the next column. A
   laptop gets the stacked cards, which is the better reading of this data at
   that width anyway. */
@media (max-width: 1180px) {
  .taskboard__row--head { display: none; }
  .taskboard__row {
    grid-template-columns: 1fr auto;
    row-gap: 10px;
    padding: 15px 16px;
  }
  /* Rows are explicit, not auto-placed.
     Auto-placement fills in DOM order, so a full-width reference row claimed
     the whole of row four and pushed the button down to a row of its own with
     nothing beside it. Naming the rows puts the button where it belongs:
     right-hand side, bottom-aligned, spanning the status and reference lines. */
  .tb-n { display: none; }
  .tb-what   { grid-column: 1 / -1; grid-row: 1; }
  .tb-rate   { grid-column: 1; grid-row: 2; }
  .tb-pay    { grid-column: 2; grid-row: 2; text-align: right; font-size: 17px; }
  .tb-status { grid-column: 1; grid-row: 3; }
  .tb-ref    { grid-column: 1; grid-row: 4; }
  .tb-go     { grid-column: 2; grid-row: 3 / span 2; align-self: end; }
}

/* Corrected ledger pairs: one line for the net, both halves inside. */
.ledger__pair { flex-basis: 100%; margin-top: 4px; }
.ledger__pair summary {
  cursor: pointer;
  font-size: 12px;
  opacity: .7;
  list-style: none;
  display: inline-block;
  padding: 2px 0;
}
.ledger__pair summary::-webkit-details-marker { display: none; }
.ledger__pair summary::before { content: "▸ "; }
.ledger__pair[open] summary::before { content: "▾ "; }
.ledger__pair summary:hover { opacity: 1; }
.ledger__pair-body {
  border-left: 2px solid var(--rule, #e6ecf2);
  margin: 6px 0 2px;
  padding-left: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.ledger__pair-row { display: flex; gap: 14px; align-items: baseline; flex-wrap: wrap; font-size: 13px; }

/* ==================================================================
   Account menu
   ==================================================================
   A <details> element, so it opens, closes and takes the keyboard without a
   line of JavaScript. The backdrop is a ::before on the open panel rather than
   a separate element, so a tap anywhere else closes it on a phone. */

.acct { position: relative; }

.acct__trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  list-style: none;
  padding: 4px 4px 4px 8px;
  border-radius: 10px;
  transition: background-color .14s ease;
}
.acct__trigger::-webkit-details-marker { display: none; }
.acct__trigger:hover { background: rgba(127, 127, 127, .1); }
.acct[open] .acct__trigger { background: rgba(127, 127, 127, .12); }
.acct__trigger:focus-visible { outline: 2px solid var(--brand, #0d7355); outline-offset: 2px; }

.acct__menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 60;
  min-width: 262px;
  background: var(--surface, #fff);
  border: 1px solid var(--rule, #e6ecf2);
  border-radius: 12px;
  box-shadow: 0 2px 6px rgba(16, 23, 32, .06), 0 16px 40px -14px rgba(16, 23, 32, .3);
  overflow: hidden;
  animation: acct-in .13s ease both;
}
@keyframes acct-in {
  from { opacity: 0; transform: translateY(-5px); }
  to   { opacity: 1; transform: none; }
}

/* Tapping outside closes it. Sits behind the panel, covers everything else. */
.acct[open] .acct__menu::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
}

.acct__head {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 13px 15px;
  border-bottom: 1px solid var(--rule, #e6ecf2);
}
.acct__hi { font-size: 14px; }
.acct__mail { font-size: 12px; color: var(--ink-muted, #8b97a6); word-break: break-all; }

.acct__group { padding: 5px 0; border-bottom: 1px solid var(--rule, #e6ecf2); }
.acct__group:last-child { border-bottom: none; }

.acct__item {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: 9px 15px;
  font-size: 14px;
  color: inherit;
  text-decoration: none;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  transition: background-color .12s ease;
}
.acct__item:hover { background: rgba(127, 127, 127, .09); }
.acct__item svg { flex: none; color: var(--ink-muted, #8b97a6); }
.acct__item:hover svg { color: inherit; }
.acct__value { margin-left: auto; font-variant-numeric: tabular-nums; }
.acct__item--money .acct__value { color: var(--gold-text, #b8860b); }
.acct__tag { margin-left: auto; font-size: 10px; }
.acct__item--out { color: var(--error, #a92920); }
.acct__item--out svg { color: currentColor; }

@media (max-width: 560px) {
  /* Anchored to the screen rather than the trigger, so a wide menu cannot hang
     off the right edge of a narrow phone. */
  .acct__menu { position: fixed; top: 62px; right: 10px; left: 10px; min-width: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .acct__menu { animation: none; }
  .acct__trigger, .acct__item { transition: none; }
}

/* ==================================================================
   Breadcrumbs
   ==================================================================
   The separator is a ::before on every item but the first, so it is CSS
   content rather than markup — a screen reader reads the trail as a list and
   never announces a slash. */

.crumbs {
  border-bottom: 1px solid var(--rule, #e6ecf2);
  background: var(--paper-sunk, #f1f5f9);
}
.crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0;
  list-style: none;
  margin: 0;
  padding: 10px 0;
  font-size: 13px;
}
.crumbs li { display: flex; align-items: center; }
.crumbs li + li::before {
  content: "/";
  margin: 0 9px;
  color: var(--ink-faint, #93a1b4);
}
.crumbs a { color: var(--ink-muted, #5b6b82); text-decoration: none; }
.crumbs a:hover { color: var(--gold-text, #8a6304); text-decoration: underline; }
.crumbs [aria-current="page"] { color: var(--ink, #0f1b2d); font-weight: 500; }

/* ==================================================================
   Public-page heading semantics
   ==================================================================
   These were styled spans. They are headings now, so a screen reader can
   navigate by them and a crawler can see the page structure — but they must
   still look exactly as they did, since the visual design was already right.
   The h1..h4 rule at the top of this file sets the display family; everything
   else is reset back to what the span carried. */

.faq-q {
  font-family: inherit;
  font-size: inherit;
  font-weight: 600;
  line-height: inherit;
  letter-spacing: normal;
}

.reject-title {
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  line-height: inherit;
  letter-spacing: normal;
  color: var(--error);
}

/* ==================================================================
   Accounts list: tier at a glance
   ==================================================================
   The row is a div rather than one big anchor, because a form cannot live
   inside an anchor and the tier toggle has to be reachable from the list. */

.acct-row { align-items: center; }
.acct-tier {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 190px;
  flex-wrap: wrap;
}
.acct-tier__form { margin: 0; }
.acct-tier__btn {
  min-height: 30px;
  font-size: 12px;
  padding: 0 10px;
}
@media (max-width: 720px) {
  .acct-tier { min-width: 0; width: 100%; }
}

/* ==================================================================
   Accounts list: bulk selection
   ==================================================================
   The bar sits above the rows and the checkboxes join it through the form=
   attribute, so nothing is nested inside anything. It wraps rather than
   scrolls, because a reason field pushed off the right edge of a phone is a
   required field nobody can see. */

.bulk {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 12px 18px;
  border-bottom: var(--rule-1);
  background: var(--paper-sunk);
}
.bulk__count {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-muted);
  /* Fixed enough that the controls beside it do not jump left and right as the
     count changes from "No accounts ticked" to "9 accounts ticked". */
  min-width: 148px;
}
.bulk__field { min-height: 34px; font-size: 13px; padding: 0 10px; width: 150px; }
.bulk__field--wide { flex: 1; min-width: 190px; }
.bulk__all { min-height: 34px; font-size: 12px; padding: 0 10px; }

/* The proof queue is hundreds of rows long, and the bar that acts on the ticked
   ones used to scroll away with the first screen. Pinned, so the count and the
   buttons are always in reach while somebody works down the list. */
.bulk--sticky {
  position: sticky;
  top: 0;
  z-index: 30;
  border: var(--card-border, 1px solid var(--rule-color));
  border-radius: var(--radius-card, 8px);
  margin-bottom: 12px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

/* A fixed-width cell whether or not the row has a box, so names stay in one
   column instead of stepping left on every worker row. */
.acct-pick { width: 16px; height: 16px; flex: 0 0 16px; margin: 0; accent-color: var(--accent); }
.acct-pick--none { display: inline-block; }

@media (max-width: 720px) {
  .bulk__count { min-width: 0; width: 100%; }
  .bulk__field { width: 100%; }
}

/* ==================================================================
   Account page: bank accounts and API keys
   ==================================================================
   Both are a list of records with controls on the right, so they share a
   shape. Retired and revoked rows stay on the page at reduced weight: the
   removed one is usually the one a dispute is about. */

.bank,
.apikey {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px 0;
  border-bottom: var(--rule-1);
}
.bank:last-of-type,
.apikey:last-of-type { border-bottom: 0; }

/* Not display:none. A retired account is evidence, and the opacity says
   "no longer used" without saying "never existed". */
.bank--retired,
.apikey--dead { opacity: 0.62; }

.bank .mono,
.apikey .mono { font-size: 13px; color: var(--ink-muted); }

@media (max-width: 720px) {
  .bank,
  .apikey { align-items: stretch; }
}

/* ==================================================================
   Platform marks
   ==================================================================
   Was defined twice, in two <style> blocks inside rates.eta and partners.eta,
   and not at all for tasks.eta — which used the class and got an unstyled
   inline span for it. One definition, here, used by all three.

   A real brand glyph where one is published, a tinted initial where none is.
   Both shapes are the same size so a list mixing them stays in one column. */

.pmark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 9px;
  flex: none;
  line-height: 1;
  /* The tile colour is set inline, per brand. The fallback keeps the shape
     visible if a mark is ever rendered without one. */
  background: var(--paper-sunk);
  /* A hairline, so the near-black tiles — X, TikTok, Threads — still read as a
     tile rather than dissolving into the page behind them. Inset rather than a
     border so it costs no layout.

     The light value is the DEFAULT rather than sitting behind
     [data-mode="light"], because light mode stamps no attribute at all: only
     dark is written onto the root element. A rule keyed on data-mode="light"
     would never match. */
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .06);
}
:root[data-mode="dark"] .pmark { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .14); }

.pmark svg {
  width: 17px;
  height: 17px;
  display: block;
  /* fill is set inline: white on most brands, a very dark version of the hue on
     the pale ones where white would be under 2:1. */
}

/* The lettered fallback.
   Its background and colour are set inline, and an inline style beats every
   selector above, so it keeps its brand hue in dark mode without needing a
   rule to say so. */
.pmark--initial {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: -.02em;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .18);
}

/* ==================================================================
   Account check queue
   ==================================================================
   One row per pending account. The current row carries the accent border
   so a reviewer working by keyboard can see where they are without a mouse,
   and an old row (a day or more waiting) is tinted so it is not missed. */

.qrow { outline: none; scroll-margin-top: 80px; }
.qrow--current { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
.qrow--old { border-left: 3px solid var(--gold); }
/* A row whose profile has been opened this sitting. Quiet on purpose: it marks
   what has been LOOKED at, which is not the same as decided, and it is what
   the "tick the N I opened" button acts on. */
.qrow--seen { border-left: 3px solid var(--accent); }

.qrow__body {
  display: grid;
  grid-template-columns: 22px minmax(0, 1.6fr) 150px minmax(0, 1fr) auto;
  gap: 18px;
  align-items: start;
}
.qrow__pick { display: flex; align-items: flex-start; padding-top: 3px; }
.qrow__pick input { width: 17px; height: 17px; margin: 0; accent-color: var(--accent); }
.qrow__who { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.qrow__link { font-family: var(--mono, ui-monospace, monospace); font-size: 15px; font-weight: 600; word-break: break-all; }
.qrow__code { display: flex; flex-direction: column; gap: 4px; }
.qrow__signals { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.qrow__acts { display: flex; flex-direction: column; gap: 8px; min-width: 150px; }
.qrow__reason, .qrow__note { min-height: 36px; font-size: 13px; padding: 0 10px; }

/* Keycap hints. */
.kbd {
  display: inline-block; min-width: 1.6em; padding: 1px 6px; border-radius: 5px;
  border: 1px solid var(--rule-1); border-bottom-width: 2px; background: var(--paper-sunk);
  font: 600 12px/1.5 var(--mono, ui-monospace, monospace); text-align: center;
}

@media (max-width: 1000px) {
  .qrow__body { grid-template-columns: 1fr; }
  .qrow__acts { min-width: 0; }
}

/* ------------------------------------------------------------------ */
/* Rejection reasons: a tick list, not a dropdown                      */
/* ------------------------------------------------------------------ */

/*
 * A <select> could only ever carry one reason, and accounts fail on several
 * counts at once. These are checkboxes.
 *
 * FULL WIDTH OF THE ROW, not a fifth column. The panel first shipped inside the
 * actions cell, and a fieldset of seven checkboxes forced that cell wide enough
 * to collapse every other column — the handle, the email and the verification
 * code crushed into narrow ribbons that overlapped each other. It is a grid
 * child of .qrow__body now and reaches its form by id.
 */
.reasons {
  grid-column: 1 / -1;
  border: var(--rule-1);
  border-radius: 10px;
  margin: 2px 0 0 0;
  padding: 10px 14px 12px;
  display: grid;
  gap: 8px 18px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  align-items: start;
  min-width: 0;
}

.reasons > legend {
  padding: 0 4px;
}
.reasons__opt {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-sm);
  line-height: 1.3;
  cursor: pointer;
  min-width: 0;
}
.reasons__opt input {
  margin: 0;
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  accent-color: var(--error);
}
.reasons__opt span {
  min-width: 0;
}
/* The note and the submit run the full width under the columns of ticks. */
.qrow__notewrap,
.reasons__go {
  grid-column: 1 / -1;
}
.reasons__go {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 2px;
}
/* In the bulk bar the fieldset sits on its own line rather than squeezing
   between the buttons. */
.reasons--bulk {
  flex-basis: 100%;
  margin-top: 4px;
}
@media (max-width: 720px) {
  .reasons {
    grid-template-columns: 1fr;
  }
}

/*
 * The note pane for "Other".
 *
 * A textarea, not the one-line input this used to be: it is the only place a
 * reviewer writes to a worker in their own words, and a single line invites a
 * fragment. The wrapper carries the field and its caption, so hiding one
 * element hides the whole pane — see the [hidden] guard above.
 */
.qrow__notewrap {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 2px;
}
/* The shared .qrow__note rule sets `padding: 0 10px`, which on a textarea puts
   the first line hard against the top edge. */
textarea.qrow__note {
  padding: 8px 10px;
  min-height: 52px;
  line-height: 1.45;
  resize: vertical;
  font-family: inherit;
}

/*
 * A destructive button.
 *
 * The reject buttons carried `style="border-color:var(--error);color:var(--error)"`
 * inline on every one of them. Three copies of the same two declarations is
 * three places to forget, and one of them had already drifted.
 */
.btn--danger {
  border-color: var(--error);
  color: var(--error);
}
.btn--danger:hover {
  background: color-mix(in srgb, var(--error) 8%, transparent);
}

/*
 * A card that is making an offer rather than reporting a fact.
 *
 * The gold edge is the same one the activation card carries, so "this is
 * something to act on" reads the same way in both places. Used by the
 * notification ask in install.js, which was previously an ordinary card and
 * scanned as one — a worker who never turns notifications on opens an empty
 * board, decides there is nothing here, and stops opening it at all.
 */
.card--offer {
  border-color: var(--gold);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold) 14%, transparent);
}

/* ------------------------------------------------------------------ */
/* Support tickets                                                     */
/* ------------------------------------------------------------------ */

.tickets__box {
  padding: 10px;
  line-height: 1.5;
  min-height: 96px;
  resize: vertical;
  font-family: inherit;
}

/* A row in either ticket list: reference, subject, state. */
.tkrow {
  display: flex;
  gap: 14px;
  align-items: center;
  flex-wrap: wrap;
}
.tkrow__ref { flex: 0 0 auto; }
.tkrow__main { flex: 1; min-width: 190px; }

.tkhead {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
}
.tkhead__subject { flex: 1; min-width: 180px; }

.tkfilters, .tkactions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
}

/*
 * A message bubble. Theirs left, ours right — the arrangement every messaging
 * app on the customer's phone already uses. The one on the admin side is the
 * mirror of it, which is the point: each side sees their own words where they
 * expect their own words to be.
 */
.tkmsg { display: flex; }
.tkmsg--mine { justify-content: flex-end; }
.tkmsg__bubble { max-width: min(640px, 92%); }
.tkmsg--mine .tkmsg__bubble { border-color: var(--accent); }
/* Newlines a customer typed are meaning, not noise. */
.tkmsg__text { white-space: pre-wrap; }

/* ==================================================================
   Advertisers page
   ==================================================================
   A real table on a wide screen, because these are columns somebody compares
   (who has money, who has spent, who came back). On a phone it turns into one
   card per advertiser, so nothing scrolls sideways. The actions menu is a
   <details>, the same trick as the account menu, and there is deliberately no
   overflow wrapper around the table: a wrapper that scrolls also clips the
   menu that hangs below a row. */

.advtable { width: 100%; border-collapse: collapse; font-size: 14px; }
.advtable th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-muted);
  padding: 12px 12px;
  border-bottom: var(--rule-1);
  white-space: nowrap;
}
.advtable td { padding: 13px 12px; border-bottom: var(--rule-1); vertical-align: top; }
.advtable tbody tr:last-child td { border-bottom: 0; }
.advtable tbody tr:hover td { background: var(--paper-sunk); }
.advtable .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.advtable__actions { text-align: right; white-space: nowrap; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

.rowmenu { position: relative; display: inline-block; text-align: left; }
.rowmenu > summary { list-style: none; cursor: pointer; }
.rowmenu > summary::-webkit-details-marker { display: none; }
.rowmenu__list {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  min-width: 220px;
  background: var(--paper-raised, #fff);
  border: var(--card-border);
  border-radius: 12px;
  box-shadow: 0 2px 6px rgba(16, 23, 32, .06), 0 16px 40px -14px rgba(16, 23, 32, .3);
  overflow: hidden;
}
.rowmenu__list a { display: block; padding: 10px 15px; color: var(--ink); text-decoration: none; font-size: 14px; }
.rowmenu__list a:hover { background: var(--paper-sunk); }
/* A tap anywhere else closes it. */
.rowmenu[open] .rowmenu__list::before { content: ""; position: fixed; inset: 0; z-index: -1; }

@media (max-width: 900px) {
  .advtable, .advtable tbody, .advtable tr, .advtable td { display: block; width: 100%; }
  .advtable thead { display: none; }
  .advtable tr { padding: 12px 14px; border-bottom: var(--rule-1); }
  .advtable td { border: 0; padding: 4px 0; display: flex; justify-content: space-between; gap: 12px; text-align: right; }
  .advtable td:first-child { display: block; text-align: left; padding-bottom: 8px; }
  .advtable td[data-label]:not(:first-child)::before {
    content: attr(data-label);
    color: var(--ink-muted);
    font-size: 12px;
    text-align: left;
  }
  .advtable td.advtable__actions { justify-content: flex-end; padding-top: 8px; }
  .advtable tbody tr:hover td { background: transparent; }
}

/* Push up: a small number box and the button, inline in an order row. */
.orow__push { display: inline-flex; align-items: center; gap: 6px; }
.orow__hours { width: 68px; min-height: 30px; padding: 0 8px; font-size: 13px; text-align: center; }

/* The cookie choice (public/js/consent.js). Fixed to the bottom, clear of a
   phone's home bar, and out of the way once answered because it is removed. */
.consent {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  z-index: 60;
  max-width: 640px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 12px 16px;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  background: var(--paper-raised);
  color: var(--ink);
  border: 1px solid var(--rule-color);
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(15, 27, 45, 0.18);
}
.consent__text { margin: 0; flex: 1 1 280px; font-size: 14px; line-height: 1.5; color: var(--ink-body); }
.consent__actions { display: flex; gap: 8px; flex: 0 0 auto; }
.consent__actions .btn { min-height: 40px; font-size: 14px; }

/* ------------------------------------------------------------------ */
/* Blog (1 Oct 2026): the list, an article, and its typography         */
/* ------------------------------------------------------------------ */
.post-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); gap: 22px; }
.post-card {
  display: flex; flex-direction: column; gap: 14px; padding: 18px;
  background: var(--paper-raised); border: var(--card-border); border-radius: var(--radius-card); box-shadow: var(--card-shadow);
}
.post-card__img { display: block; border-radius: var(--radius-sm); overflow: hidden; }
.post-card__img img { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.post-card h2 a, .post-card h3 a { color: var(--ink); text-decoration: none; }
.post-card h2 a:hover, .post-card h3 a:hover { color: var(--link-hover); text-decoration: underline; }
.post-cover { margin: 0; }
.post-cover img { display: block; width: 100%; height: auto; border-radius: var(--radius-card); border: var(--card-border); }
.post-toc { padding: 16px 20px; background: var(--paper-sunk); border-radius: var(--radius-card); display: flex; flex-direction: column; gap: 8px; }
.post-toc ol { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 4px; }
.post-cta { padding: 22px; background: var(--paper-sunk); border-radius: var(--radius-card); }
.pager { display: flex; justify-content: space-between; align-items: center; gap: 12px; }

.post-body { font-size: 18px; line-height: 1.7; color: var(--ink-body); max-width: 68ch; }
.post-body > * + * { margin-top: 1.1em; }
.post-body h2 { font-size: 26px; line-height: 1.25; margin-top: 2em; color: var(--ink); font-weight: var(--weight-display); scroll-margin-top: 80px; }
.post-body h3 { font-size: 21px; line-height: 1.3; margin-top: 1.6em; color: var(--ink); font-weight: var(--weight-h3); scroll-margin-top: 80px; }
.post-body a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; }
.post-body a:hover { color: var(--link-hover); }
.post-body ul, .post-body ol { padding-left: 1.4em; }
.post-body li + li { margin-top: .4em; }
.post-body blockquote { margin-left: 0; padding: 4px 0 4px 18px; border-left: 3px solid var(--gold); color: var(--ink); }
.post-body blockquote p { margin: 0; }
.post-body img { display: block; max-width: 100%; height: auto; border-radius: var(--radius-card); border: var(--card-border); }
.post-body code { font-family: var(--font-mono, ui-monospace, monospace); font-size: .92em; background: var(--paper-sunk); padding: 1px 6px; border-radius: 4px; }
.post-body hr { border: 0; border-top: 1px solid var(--rule-color); margin: 2em 0; }

/* The blog (2 Oct 2026): list, topic pages, article. Colours of a topic are
   set once in --tc; the drawn covers use the same colours. */
.bcover {
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  aspect-ratio: 3 / 2; border-radius: 12px; background: var(--tc, #0B63CE);
}
.bcover svg { width: 76%; height: auto; }
.bcover--photo { background: var(--paper-sunk); }
.bcover--photo img { display: block; width: 100%; height: 100%; object-fit: cover; }
.bcover--make-money, .btag--make-money { --tc: #0B63CE; }
.bcover--grow-your-page, .btag--grow-your-page { --tc: #0E7C66; }
.bcover--money-banking, .btag--money-banking { --tc: #8A5A00; }
.bcover--phone-data, .btag--phone-data { --tc: #4B3FB5; }
.bcover--jobs-skills, .btag--jobs-skills { --tc: #1F4D7A; }
.bcover--stay-safe, .btag--stay-safe { --tc: #B3261E; }
.bcover--make-money { background: linear-gradient(135deg, #0B63CE, #3A93F2); }
.bcover--grow-your-page { background: linear-gradient(135deg, #0E7C66, #2BB58F); }
.bcover--money-banking { background: linear-gradient(135deg, #7A4B00, #D9961A); }
.bcover--phone-data { background: linear-gradient(135deg, #4B3FB5, #8C7BF0); }
.bcover--jobs-skills { background: linear-gradient(135deg, #1F4D7A, #4C86B8); }
.bcover--stay-safe { background: linear-gradient(135deg, #B3261E, #E8645B); }
.btag {
  display: inline-block; align-self: flex-start; font-size: 12px; font-weight: 700; letter-spacing: .03em;
  padding: 3px 11px; border-radius: 99px; background: var(--tc, #0B63CE); color: #fff; text-decoration: none;
}
.bmeta { font-size: 13px; color: var(--ink-muted); }
.bhead__title { max-width: 22ch; }
.bsearch { display: flex; gap: 8px; max-width: 560px; }
.bsearch .input { flex: 1; min-width: 0; }
.bchips { display: flex; gap: 8px; flex-wrap: wrap; }
.bchip {
  padding: 7px 14px; border-radius: 99px; border: var(--card-border); background: var(--paper-raised);
  color: var(--ink); text-decoration: none; font-size: 14px; font-weight: 500;
}
.bchip i { font-style: normal; color: var(--ink-muted); margin-left: 6px; font-size: 12px; }
.bchip:hover { border-color: var(--accent); }
.bchip--on { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.bchip--on i { color: var(--paper); opacity: .7; }
.bgrid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
.bcard {
  display: flex; flex-direction: column; gap: 9px; text-decoration: none; color: var(--ink);
  border: var(--card-border); background: var(--paper-raised); border-radius: var(--radius-card);
  padding: 12px; box-shadow: var(--card-shadow);
}
.bcard:hover, .bfeat:hover { border-color: var(--accent); }
.bcard__title { font-size: 18px; line-height: 1.3; }
.bcard__sum { font-size: 14.5px; color: var(--ink-muted); line-height: 1.5; }
.bfeat {
  display: grid; grid-template-columns: 1.1fr 1fr; gap: 24px; align-items: center; text-decoration: none; color: var(--ink);
  border: var(--card-border); background: var(--paper-raised); border-radius: var(--radius-card); padding: 14px; box-shadow: var(--card-shadow);
}
.bfeat .bcover { aspect-ratio: 16 / 10; }
.bfeat__text { display: flex; flex-direction: column; gap: 10px; }
.bfeat__title { font-size: 28px; line-height: 1.2; }
.btrust {
  display: flex; flex-direction: column; gap: 10px; padding: 18px 20px; border-radius: var(--radius-card);
  background: var(--paper-sunk); border: 1px solid var(--accent);
}
.bpost__title { font-size: clamp(28px, 4.4vw, 40px); line-height: 1.15; }
.bshare { display: flex; gap: 8px; flex-wrap: wrap; }
.bshare__wa { background: #1FA855; color: #fff; border-color: #1FA855; }
.bpost { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 36px; align-items: start; }
.bpost__side { position: sticky; top: 84px; display: flex; flex-direction: column; gap: 16px; }
.bjoin { display: flex; flex-direction: column; gap: 8px; border: var(--card-border); border-radius: var(--radius-card); padding: 14px; background: var(--paper-raised); }
.btoc-m { display: none; border: var(--card-border); border-radius: 12px; padding: 10px 14px; background: var(--paper-raised); margin-bottom: 18px; }
.btoc-m summary { font-weight: 600; cursor: pointer; }
.btoc-m ol { margin: 8px 0 0; padding-left: 20px; }
.bcta {
  display: flex; gap: 16px; align-items: center; justify-content: space-between; flex-wrap: wrap; margin-top: 36px;
  padding: 20px; border-radius: var(--radius-card); background: var(--paper-sunk); border: 1px solid var(--accent);
}
.bcta__title { font-size: 20px; line-height: 1.3; }
.bcta .btn--primary, .btrust .btn--primary { color: var(--accent-fg); text-decoration: none; }
.callout {
  margin: 1.4em 0; padding: 14px 18px; border-radius: 0 12px 12px 0; border-left: 4px solid var(--accent); background: var(--paper-sunk);
  font-size: 17px; line-height: 1.6;
}
.callout > * + * { margin-top: .6em; }
.callout__label { display: block; font-size: 13px; letter-spacing: .06em; text-transform: uppercase; color: var(--ink); }
.callout ul, .callout ol { padding-left: 1.3em; display: flex; flex-direction: column; gap: 4px; }
.callout--tip { border-left-color: var(--gold); background: var(--pending-wash); }
.callout--warning { border-left-color: var(--error); background: var(--error-wash); }
.faq { margin: 1em 0; }
.faq__item { border-bottom: var(--card-border); padding: 12px 0; }
.faq__item summary { cursor: pointer; font-weight: 600; color: var(--ink); font-size: 18px; }
.faq__a { margin-top: 8px; color: var(--ink-body); }
.faq__a > * + * { margin-top: .6em; }
@media (max-width: 820px) {
  .bfeat { grid-template-columns: 1fr; gap: 14px; }
  .bpost { grid-template-columns: 1fr; }
  .bpost__side { display: none; }
  .btoc-m { display: block; }
  .bfeat__title { font-size: 23px; }
}
@media (max-width: 640px) { .post-body { font-size: 17px; } .post-body h2 { font-size: 23px; } }
.post-body .table-wrap { overflow-x: auto; }
.post-body table { border-collapse: collapse; width: 100%; font-size: 16px; line-height: 1.5; }
.post-body th, .post-body td { text-align: left; padding: 10px 12px; border-bottom: var(--card-border); vertical-align: top; }
.post-body th { font-size: 13px; letter-spacing: .05em; text-transform: uppercase; color: var(--ink); background: var(--paper-sunk); }

/* ---- The welcome slides (public/js/welcome.js): shown once to every worker ---- */
.welcome {
  position: fixed; inset: 0; z-index: 400;
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
  background: rgba(15, 27, 45, .6);
}
.welcome__card {
  width: 100%; max-width: 420px;
  background: var(--paper-raised); color: var(--ink);
  border: var(--rule-1); border-radius: 16px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .28);
  overflow: hidden;
  display: flex; flex-direction: column;
}
.welcome__top { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px 0; min-height: 44px; }
.welcome__brand { font-weight: 700; font-size: 15px; }
.welcome__skip {
  font: inherit; font-size: 14px; color: var(--ink-muted);
  background: none; border: 0; padding: 8px; margin: -8px; cursor: pointer; border-radius: 6px;
}
.welcome__skip:hover { color: var(--ink); }
.welcome__viewport { overflow: hidden; }
.welcome__track { display: flex; transition: transform .28s ease; }
.welcome__slide { flex: 0 0 100%; padding: 22px 24px 8px; box-sizing: border-box; }
.welcome__art {
  width: 96px; height: 96px; border-radius: 24px;
  display: grid; place-items: center;
  background: var(--paper-sunk); color: var(--accent);
  margin-bottom: 20px;
}
.welcome__art--green { background: var(--green-wash); color: var(--green); }
.welcome__art--warn { background: var(--error-wash); color: var(--error); }
.welcome__art svg { width: 48px; height: 48px; }
.welcome__title { font-size: 24px; line-height: 1.2; margin: 0 0 10px; text-wrap: balance; }
.welcome__text { font-size: 16px; line-height: 1.5; color: var(--ink-body); margin: 0; max-width: 34ch; }
.welcome__text + .welcome__text { margin-top: 10px; }
.welcome__bottom { padding: 14px 24px 22px; display: flex; flex-direction: column; gap: 16px; }
.welcome__dots { display: flex; gap: 8px; }
.welcome__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--rule-color); transition: width .2s ease, background .2s ease; }
.welcome__dot[aria-current="true"] { width: 24px; border-radius: 4px; background: var(--accent); }
.welcome__actions { display: flex; gap: 10px; }
.welcome__actions .btn { min-height: 48px; font-size: 16px; }
.welcome__actions .btn--primary { flex: 1; }
@media (prefers-reduced-motion: reduce) { .welcome__track, .welcome__dot { transition: none; } }

/* Nothing may be wider than the screen (owner, 15 Oct 2026: the reject reasons ran off the right edge of a phone).
   A flex child sized to its content takes the width of its longest unbroken line, so one long sentence inside it
   pushes the whole card past the viewport. Capping those children at their parent's width lets the text wrap
   instead, and long button labels wrap rather than run out of the button. */
[style*="flex:0 0 auto"], [style*="flex: 0 0 auto"] { max-width: 100%; }
.btn { max-width: 100%; }
