/* =====================================================================
   Pinpoint Vending — operator app
   Brand: taken from pinpoint-vending.com. Dark graphite surfaces, Archivo
   display type over IBM Plex Sans, one red accent, mono labels.
   Token NAMES are inherited from the light build this grew out of, so --ink
   is the foreground and --bg the background; both are simply dark-theme
   values now. Anything that paints --ink on --bg still reads correctly.
   ===================================================================== */
:root {
  --bg: #131215;                /* ground */
  --bg-2: #1A191D;              /* surface */
  --bg-3: #211F25;              /* surface, one step up */
  --paper: #1A191D;             /* cards sit just above the ground */
  --paper-2: #2A282F;           /* raised: hovered rows, the focused tile */
  --graphite: #0D0C0F;          /* below the ground: the staff bar */
  --ink: #F3F1ED;
  --ink-2: #B7B2AB;
  --ink-3: #8F8981;
  --line: #302E35;
  --line-2: #403D46;
  --brand: #F84A2C;             /* the pin, primary buttons, highlights */
  --brand-deep: #EE2F13;        /* pressed and hovered red */
  --brand-ink: #FF6045;         /* red TEXT on dark surfaces (contrast-safe) */
  --brand-soft: #F84A2C26;
  --on-brand: #FFFFFF;
  --ok: #35C177;
  --ok-soft: #35C17724;
  --warn: #E9B03A;
  --warn-soft: #E9B03A29;
  --info: #6F9BFF;              /* the brand has no blue; this is the quietest
                                   thing that still reads as information */
  --info-soft: #6F9BFF22;
  --shadow: 0 1px 2px #0006, 0 12px 34px -14px #0009;
  --image-outline: oklch(1 0 0 / 0.1);   /* dark surface: white, not a tinted grey */

  /* Radii. Nested surfaces derive theirs from the parent minus its padding, so
     a concentric pair stays concentric when either value is edited. Where that
     subtraction goes negative the inset is too deep to be concentric at all,
     and those layers keep their own token — which is the documented exception,
     not an oversight. */
  --r-xs: 7px;
  --r-sm: 10px;
  --r-md: 12px;
  --r-lg: 14px;
  --r-xl: 18px;
  --r-2xl: 24px;
  /* How wide the page runs. 1180px left ~370px empty either side of a 1920
     screen; this fills a desktop out while still stopping a 4K monitor from
     stretching a table into something nobody can read across. */
  --page: min(96vw, 1760px);
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--bg); }
body {
  color: var(--ink);
  font-family: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  font-size: 14px; line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
::selection { background: var(--ink); color: var(--bg); }
a { color: inherit; }
[hidden] { display: none !important; }
h1, h2, h3 { margin: 0; letter-spacing: -0.035em; font-weight: 800;
              font-family: 'Archivo', 'IBM Plex Sans', sans-serif; }
h1 { font-size: clamp(28px, 4vw, 42px); line-height: 1.02; }
h2 { font-size: 20px; }
h3 { font-size: 16px; letter-spacing: -0.025em; font-weight: 700; }
.em { color: var(--brand); }
.mono, .eyebrow { font-family: 'IBM Plex Mono', ui-monospace, monospace; }
.eyebrow {
  font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 500;
}
.muted { color: var(--ink-3); }
.num { font-variant-numeric: tabular-nums; }

/* ---- brand mark ----
   The lockup from pinpoint-vending.com: the pin stands in for the second "o"
   of Pinpoint, with VENDING set beneath it. Everything scales off font-size,
   so one component serves the nav, the sign-in card and the legal headers. */
.wordmark {
  flex: none; display: inline-grid; justify-items: start; line-height: 1;
  font-size: 17px;                       /* the one knob */
  text-decoration: none; color: var(--ink);
}
.wordmark .pp {
  display: inline-flex; align-items: center;
  font-family: 'Archivo', sans-serif; font-weight: 800;
  font-size: 1em; letter-spacing: -0.045em;
}
/* The pin is squashed into the o's box exactly as the site does it, so the
   wordmark keeps its rhythm instead of opening a tall gap mid-word. */
.wordmark .pin {
  width: .72em; height: .96em; margin: 0 .03em -.04em;
  flex: none; display: block;
}
.wordmark .sub {
  font-family: 'IBM Plex Mono', monospace; font-weight: 500;
  font-size: .345em; letter-spacing: .28em; text-transform: uppercase;
  color: var(--ink-3); margin-top: .34em; padding-left: .08em;
}
.wordmark.lg { font-size: 31px; }
.wordmark.lg .sub { color: var(--ink-2); }

.brand { display: flex; align-items: center; gap: 11px; }

/* ---- buttons ---- */
.btn {
  font: 500 14px/1 'IBM Plex Sans', sans-serif; padding: 12px 18px; border-radius: 999px;
  border: 1px solid transparent; cursor: pointer; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  transition: background .15s ease, border-color .15s ease, opacity .15s ease;
}
.btn:disabled { opacity: .5; cursor: default; }
/* The button draws its own press state; the platform's grey flash doubles it. */
.btn, .tab, .seg .pill, .machine, .screen-total-row, .day-row { -webkit-tap-highlight-color: transparent; }
.btn.primary { background: var(--brand); color: var(--on-brand); }
@media (hover: hover) { .btn.primary:hover:not(:disabled) { background: var(--brand-deep); } }
.btn.ghost { background: transparent; border-color: var(--line-2); color: var(--ink); }
@media (hover: hover) { .btn.ghost:hover:not(:disabled) { background: var(--paper-2); border-color: var(--line-2); } }
.btn.google { background: var(--paper); border-color: var(--line-2); color: var(--ink); }
@media (hover: hover) { .btn.google:hover:not(:disabled) { background: var(--paper-2); border-color: var(--line-2); } }
.btn.google svg { width: 17px; height: 17px; flex: none; }
#googleHost { display: flex; justify-content: center; min-height: 44px; }
.btn.small { padding: 8px 14px; font-size: 13px; }
/* A leading icon reads as further from the edge than a letter at the same
   measurement, so the icon side gives back 2px. Judged by eye, not derived. */
.btn:has(> svg:first-child) { padding-left: calc(18px - 2px); }
.btn.small:has(> svg:first-child) { padding-left: calc(14px - 2px); }
.btn.danger { background: var(--brand-deep); color: var(--on-brand); }
.btn.block { width: 100%; }
/* The browser's own file picker, drawn as a ghost button. Its "No file chosen" stays, quieter. */
input[type=file] {
  width: 100%; font: 400 13px/1.4 'IBM Plex Sans', sans-serif; color: var(--ink-3); cursor: pointer;
}
input[type=file]::file-selector-button {
  font: 500 14px/1 'IBM Plex Sans', sans-serif; padding: 12px 18px; margin-right: 12px;
  border-radius: 999px; border: 1px solid var(--line-2); background: transparent; color: var(--ink);
  cursor: pointer; transition: background .15s ease, border-color .15s ease;
}
@media (hover: hover) { input[type=file]:hover::file-selector-button { background: var(--paper-2); border-color: var(--ink-3); } }
input[type=file]:focus-visible { outline: none; }
input[type=file]:focus-visible::file-selector-button { outline: 2px solid var(--brand); outline-offset: 2px; }
.linkish {
  background: none; border: 0; padding: 0; color: var(--ink); cursor: pointer;
  font: inherit; text-decoration: underline; text-underline-offset: 2px;
}

/* ---- forms ---- */
.field { margin-bottom: 14px; }
.field > label { display: block; margin-bottom: 6px; }
.field input[type=text], .field input[type=email], .field input[type=password],
.field input[type=number], .field input[type=search], .field input[type=tel], .field input[type=date],
.field select, .field textarea {
  width: 100%; padding: 12px 14px; border-radius: 12px;
  border: 1px solid var(--line-2); background: var(--paper); color: var(--ink);
  font: 400 15px/1.3 'IBM Plex Sans', sans-serif; outline: none;
}
.field textarea { min-height: 110px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--line-2); box-shadow: 0 0 0 3px var(--brand-soft);
}
.field .hint { font-size: 12px; color: var(--ink-3); margin-top: 6px; }

/* ---- grow page ---- */
.ticks { list-style: none; margin: 0 0 18px; padding: 0; display: grid; gap: 9px; }
.ticks li {
  position: relative; padding-left: 24px; font-size: 13.5px; color: var(--ink-2); line-height: 1.5;
}
.ticks li::before {
  content: ''; position: absolute; left: 2px; top: .45em;
  width: 11px; height: 6px; border-left: 2px solid var(--brand); border-bottom: 2px solid var(--brand);
  transform: rotate(-45deg);
}
.maker {
  background: var(--bg); border: 1px solid var(--line);
  border-radius: calc(var(--r-md) + 10px);     /* concentric around .machine */
  padding: 10px; margin-bottom: 12px; min-width: 0;
}
.maker > strong { display: block; font-size: 15px; letter-spacing: -0.025em; margin-bottom: 4px; }
.maker > span { display: block; font-size: 13px; color: var(--ink-3); line-height: 1.55; }

/* The machine line-up.
   Tiles are one fixed size and the row is centred, so a maker with three
   machines and a maker with four both look deliberate. Stretching tiles to
   fill the row instead made each machine draw at a different width — a wide
   one like ARTI filled its tile while a narrow fridge floated in the middle
   of its own.

   The column count is the number of machines (set inline) and steps straight
   down to 2 when the card is too narrow for them all, so four machines break
   2 + 2 rather than stranding one on a line of its own. Plain wrapping took
   as many as would fit and gave 3 + 1. Narrow screens get a scrolling row
   (see the media query), because four tiles squeezed onto a phone are four
   tiles nobody can see. */
.maker { container-type: inline-size; }
.machine-wrap { position: relative; min-width: 0; }
.machine-row {
  display: grid;
  /* Tiles grow with the card up to a cap, so a wide screen doesn't leave the
     line-up marooned in the middle of an empty panel — but they stay equal to
     each other, which is what keeps the machines reading at one scale. */
  grid-template-columns: repeat(var(--cols, 4), minmax(104px, 170px));
  justify-content: center; gap: 10px; margin-top: 14px; min-width: 0;
}
/* Measured against the card, not the window: it is the card's width that
   decides whether the line-up fits. 110px tiles plus 10px gaps need 470px. */
@container (max-width: 480px) {
  .machine-row { grid-template-columns: repeat(2, minmax(104px, 170px)); }
}
.machine {
  min-width: 0; margin: 0;
  background: var(--paper-2); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 12px 8px 9px; text-align: center;
}
.machine img {
  display: block; width: 100%; height: auto; aspect-ratio: 3 / 4;
  object-fit: contain; object-position: center bottom;
}
.machine figcaption {
  margin-top: 9px; font-size: 11.5px; color: var(--ink-2); line-height: 1.3;
  font-weight: 500;
}

/* ---- address picker ---- */
.ac { position: relative; }
.ac-list {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 20;
  margin: 0; padding: 4px; list-style: none; max-height: 232px;
  overflow-y: auto; overscroll-behavior: contain;
  background: var(--paper-2); border: 1px solid var(--line-2);
  border-radius: var(--r-md); box-shadow: 0 10px 30px #000000A6;
}
.ac-item {
  padding: 9px 11px; cursor: pointer;
  border-radius: calc(var(--r-md) - 4px);      /* concentric inside .ac-list's 4px */
  display: grid; gap: 2px; line-height: 1.35;
}
.ac-item.on { background: var(--brand-soft); }
@media (hover: hover) { .ac-item:hover { background: var(--brand-soft); } }
.ac-item .l1 { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.ac-item .l2 { font-size: 12px; color: var(--ink-3); }
/* `.field > label` sets display:block and outranks a bare `.check`, which made
   the wrapped second line of a checkbox label start under the box instead of
   under the text. Match the same specificity so the flex row wins. */
.check, .field > label.check {
  display: flex; gap: 10px; align-items: flex-start; font-size: 13px; color: var(--ink-2);
}
.check input { margin-top: 2px; width: 16px; height: 16px; flex: none; accent-color: var(--brand); }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }
/* A grid item defaults to min-width:auto, so anything inside that is wider than
   the column pushes the whole page sideways instead of scrolling itself. */
.grid-2 > * { min-width: 0; }
.msg { font-size: 13px; min-height: 20px; margin: 10px 0 0; }
.msg.err { color: var(--brand-ink); }
.msg.ok { color: var(--ok); }

/* ---- auth ---- */
#auth { min-height: 100vh; display: grid; place-items: center; padding: 32px 16px; }
.auth-card {
  width: 100%; max-width: 430px; background: var(--paper);
  border: 1px solid var(--line); border-radius: 24px; padding: 32px; box-shadow: var(--shadow);
}
.auth-card h1 { font-size: 31px; margin: 26px 0 10px; letter-spacing: -0.045em; }
.auth-card .lede { color: var(--ink-2); margin: 0 0 22px; font-size: 15px; }
.or {
  display: flex; align-items: center; gap: 12px; margin: 18px 0 14px;
  font-family: 'IBM Plex Mono', monospace; font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-3);
}
.or::before, .or::after { content: ''; flex: 1; height: 1px; background: var(--line); }
.switcher { margin-top: 18px; font-size: 13px; color: var(--ink-2); text-align: center; }

/* ---- app shell ---- */
/* The nav, the admin bar and the tab strip stick together as one block.
   They used to stick individually with hardcoded offsets (top: 52px), which
   assumed a nav height that was actually 57px on a phone — so the tab strip
   sat slightly over the heading below it. One sticky parent needs no offsets
   and can't drift when the nav's height changes. */
.topbar { position: sticky; top: 0; z-index: 60; }
.nav {
  background: #131215E6;
  backdrop-filter: saturate(180%) blur(16px); -webkit-backdrop-filter: saturate(180%) blur(16px);
  border-bottom: 1px solid var(--line);
}
.nav-inner { max-width: var(--page); margin: 0 auto; padding: 13px 24px; display: flex; align-items: center; gap: 14px; }
.nav .tag { padding: 3px 8px; border-radius: 6px; background: var(--bg-3); color: var(--ink-2); }
.nav-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.who { font-size: 13px; color: var(--ink-2); max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tabs { border-bottom: 1px solid var(--line); background: var(--bg); }
.tabs-inner { max-width: var(--page); margin: 0 auto; padding: 0 24px; display: flex; gap: 2px; overflow-x: auto; }
.tab {
  background: none; border: 0; cursor: pointer;
  padding: 13px 14px 15px; font: 500 14px/1 'IBM Plex Sans', sans-serif; color: var(--ink-3);
  white-space: nowrap; display: inline-flex; align-items: center; gap: 7px;
  position: relative;
}
@media (hover: hover) { .tab:hover { color: var(--ink); } }
.tab.active { color: var(--ink); font-weight: 600; }
.tab .t-short { display: none; }
.tab .pip {
  font: 600 10px/1 'IBM Plex Mono', monospace; padding: 3px 5px; border-radius: 5px;
  background: var(--brand-soft); color: var(--brand-ink);
}
.wrap { max-width: var(--page); margin: 0 auto; padding: 26px 24px 80px; }
.page-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 18px; flex-wrap: wrap; margin-bottom: 22px; }
.page-head .lede { color: var(--ink-2); font-size: 15px; margin: 8px 0 0; }

/* ---- cards & stats ---- */
.card { background: var(--paper); border: 1px solid var(--line); border-radius: 18px; padding: 20px; }
.card + .card { margin-top: 16px; }
/* ...but side by side they are columns, not a stack: that margin dropped the
   second card 16px below the first. */
.grid-2 > .card + .card { margin-top: 0; }
.card-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; }
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 18px; }
.stats.three { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.stat { background: var(--paper); border: 1px solid var(--line); border-radius: 18px; padding: 18px 20px; }
.stat .value { font-size: 30px; font-weight: 800; letter-spacing: -0.04em; line-height: 1; margin-top: 10px; font-variant-numeric: tabular-nums; }
.stat .value small { font-size: 13px; color: var(--ink-3); font-weight: 500; letter-spacing: -0.01em; margin-left: 4px; }
.stat .sub { font-size: 12px; color: var(--ink-3); margin-top: 10px; min-height: 18px; }
.stat.hero { background: var(--brand-deep); color: var(--on-brand); border-color: var(--brand-deep); }
.stat.hero .eyebrow, .stat.hero .sub { color: #FFFFFFB8; }

/* ---- range selector (earnings breakdown) ---- */
.seg { display: inline-flex; gap: 2px; padding: 3px; background: var(--bg);
       border: 1px solid var(--line); border-radius: var(--r-sm); }
.seg .pill {
  background: none; border: 0; cursor: pointer;
  border-radius: calc(var(--r-sm) - 3px);      /* concentric inside .seg's 3px */
  padding: 6px 11px; font: 500 12.5px/1 'IBM Plex Sans', sans-serif; color: var(--ink-3);
  white-space: nowrap;
}
@media (hover: hover) { .seg .pill:hover { color: var(--ink); } }
.seg .pill.on { background: var(--paper-2); color: var(--ink); font-weight: 600;
                box-shadow: 0 1px 2px #0006; }
.seg .pill .t-short { display: none; }

/* Totals for the selected window, above the chart. */
.range-sum {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); gap: 12px;
  padding: 14px 0 16px; border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line); margin-bottom: 14px;
}
.range-sum .k {
  display: block; font: 600 10px/1 'IBM Plex Mono', monospace; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 7px;
}
.range-sum .v { font-size: 19px; font-weight: 700; letter-spacing: -0.03em;
                font-variant-numeric: tabular-nums; }
.range-sum .v.em { color: var(--brand-ink); }

/* ---- status pills ---- */
.status {
  display: inline-flex; align-items: center; gap: 6px; width: max-content;
  font-family: 'IBM Plex Mono', monospace; font-size: 10px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; padding: 4px 10px; border-radius: 99px;
}
.status .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.status.live           { background: var(--ok-soft);    color: var(--ok); }
.status.calibrating    { background: var(--info-soft);  color: var(--info); }
.status.enabling       { background: var(--warn-soft);  color: var(--warn); }
.status.pending_review { background: var(--bg-3);       color: var(--ink-2); }
.status.paused         { background: var(--warn-soft);  color: var(--warn); }
.status.rejected       { background: var(--brand-soft); color: var(--brand-ink); }
.status.pending        { background: var(--warn-soft);  color: var(--warn); }
.status.approved       { background: var(--ok-soft);    color: var(--ok); }
.status.open           { background: var(--info-soft);  color: var(--info); }
.status.in_progress    { background: var(--warn-soft);  color: var(--warn); }
.status.resolved       { background: var(--bg-3);       color: var(--ink-2); }
.status.requested      { background: var(--warn-soft);  color: var(--warn); }
.status.processing     { background: var(--info-soft);  color: var(--info); }
.status.paid           { background: var(--ok-soft);    color: var(--ok); }
.status.failed         { background: var(--brand-soft); color: var(--brand-ink); }
.status.canceled       { background: var(--bg-3);       color: var(--ink-2); }
.status.archived       { background: var(--bg-3);       color: var(--ink-2); }
.status.live .dot { animation: pulse 1.6s infinite; }
@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: .35 } }

/* ---- screens list ---- */
.screens { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 16px; }
.screen-card { background: var(--paper); border: 1px solid var(--line); border-radius: 18px; overflow: hidden; display: flex; flex-direction: column; }
.screen-card .photo { aspect-ratio: 16/9; background: var(--bg-3); position: relative; }
.screen-card .photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* A hairline inside the edge, so a photo never bleeds into the surface behind
   it. Pure white at 10%: a tinted neutral shows as a coloured fringe on the
   image edge. outline, not border, so it cannot change the image's size. */
.screen-card .photo img, .thumb {
  outline: 1px solid var(--image-outline); outline-offset: -1px;
}
.screen-card .photo .ph { position: absolute; inset: 0; display: grid; place-items: center; color: var(--ink-3); font-size: 12px; }
/* Ad Units: the machine's own (original) photo beside its name. */
.unit-id { display: flex; align-items: center; gap: 12px; min-width: 0; }
.unit-photo { flex: none; width: 64px; height: 64px; border-radius: 12px; overflow: hidden; background: var(--bg-3); }
.unit-photo img { width: 100%; height: 100%; object-fit: cover; display: block;
  outline: 1px solid var(--image-outline); outline-offset: -1px; }
/* Admin: each machine's original photo beside Unique Media's AI version. */
.photo-pairs { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 14px; }
.photo-pair { margin: 0; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.photo-pair .pair { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.photo-pair .slot { position: relative; aspect-ratio: 4/3; background: var(--bg-3); border-radius: 10px; overflow: hidden; }
.photo-pair .slot img { width: 100%; height: 100%; object-fit: cover; display: block;
  outline: 1px solid var(--image-outline); outline-offset: -1px; }
.photo-pair .slot .ph { position: absolute; inset: 0; display: grid; place-items: center; color: var(--ink-3); font-size: 12px; text-align: center; padding: 8px; }
.photo-pair .tag { position: absolute; left: 6px; top: 6px; z-index: 1; padding: 2px 7px; border-radius: 999px;
  background: rgba(0,0,0,.6); color: #fff; font-size: 11px; }
.screen-card .body { padding: 16px 18px 18px; display: flex; flex-direction: column; gap: 10px; flex: 1; }
.screen-card .title { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.screen-card .title strong { font-size: 16px; font-weight: 700; letter-spacing: -0.02em; }
.screen-card .meta { font-size: 12px; color: var(--ink-3); }
.screen-card .foot { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: auto; padding-top: 4px; }
.screen-card .earn { font-size: 19px; font-weight: 800; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }

/* ---- progress ---- */
.prog { margin-top: 2px; }
.prog .bar { height: 6px; border-radius: 99px; background: var(--bg-3); overflow: hidden; }
.prog .bar > div { height: 100%; border-radius: 99px; background: var(--ink); transition: width .4s ease; }
.prog.enabling .bar > div    { background: var(--warn); }
.prog .labels { display: flex; justify-content: space-between; gap: 10px; margin-top: 7px; font-size: 12px; color: var(--ink-2); }

/* ---- stepper (Ad Units tab) ---- */
.steps { display: flex; gap: 0; margin: 18px 0 6px; }
.step { flex: 1; position: relative; padding-top: 22px; font-size: 12px; color: var(--ink-3); }
.step::before {
  content: ''; position: absolute; top: 5px; left: 0; right: 0; height: 2px; background: var(--line);
}
.step:first-child::before { left: 50%; }
.step:last-child::before { right: 50%; }
.step::after {
  content: ''; position: absolute; top: 0; left: calc(50% - 6px);
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--bg); border: 2px solid var(--line-2);
}
.step.done::before, .step.active::before { background: var(--ink-2); }
.step.done::after { background: var(--ink-2); border-color: var(--ink-2); }
.step.active::after { background: var(--brand); border-color: var(--brand); }
.step.done, .step.active { color: var(--ink); font-weight: 600; }
.step span { display: block; text-align: center; padding: 0 4px; }

/* ---- tables ---- */
.table { width: 100%; border-collapse: collapse; }
.table th, .table td { text-align: left; padding: 11px 12px; border-bottom: 1px solid var(--line); }
.table th {
  font-family: 'IBM Plex Mono', monospace; font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-3); font-weight: 600; white-space: nowrap;
}
.table td { font-size: 14px; }
.table tr:last-child td { border-bottom: 0; }
.table td.num, .table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.table .money { font-weight: 600; letter-spacing: -0.015em; }
.scroll-x { overflow-x: auto; }

/* ---- weekly chart ---- */
.chart { position: relative; }
.chart svg { display: block; width: 100%; overflow: visible; }
.chart .grid line { stroke: var(--line); stroke-width: 1; }
.chart .axis text { fill: var(--ink-3); font: 500 10px 'IBM Plex Mono', monospace; }
.chart .bar { fill: var(--ink); }
.chart .bar.withheld { fill: var(--line-2); }
.chart .col.focus .bar { fill: var(--brand); }
@media (hover: hover) { .chart .col:hover .bar { fill: var(--brand); } }
.chart .hit { fill: transparent; }
/* A sideways swipe reads the bars, and an up-and-down one still scrolls the page. */
.chart svg, .daybars .plot {
  touch-action: pan-y; user-select: none; -webkit-user-select: none;
  -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent;
}
.tip {
  position: absolute; pointer-events: none; z-index: 5; background: var(--ink); color: var(--bg);
  border-radius: 10px; padding: 8px 10px; font-size: 12px; line-height: 1.35; white-space: nowrap;
  transform: translate(-50%, -100%); box-shadow: 0 8px 24px #0009;
}
.tip .d { font-family: 'IBM Plex Mono', monospace; font-size: 10px; letter-spacing: .06em; opacity: .65; text-transform: uppercase; }
.tip b { font-size: 14px; font-weight: 700; }

/* ---- empty states ---- */
.empty { padding: 50px 24px; text-align: center; color: var(--ink-2); border: 1px dashed var(--line-2); border-radius: 18px; background: var(--paper); }
.empty h3 { font-size: 22px; font-weight: 800; letter-spacing: -0.03em; margin-bottom: 8px; }
.empty p { margin: 0 auto 16px; max-width: 420px; }

/* ---- modal ---- */
.veil { position: fixed; inset: 0; background: #0D0C0FB3; backdrop-filter: blur(4px); z-index: 100; }
.modal {
  position: fixed; z-index: 101; inset: auto 0 0 0; margin: 0 auto; width: min(560px, 100%);
  max-height: 92vh; max-height: 92dvh; overflow-y: auto; overscroll-behavior: contain; background: var(--paper);
  border-radius: 22px 22px 0 0; padding: 24px; box-shadow: 0 -20px 60px #000000A6;
  animation: rise .26s cubic-bezier(.32,.72,0,1);
}
@media (min-width: 720px) {
  .modal { inset: 50% auto auto 50%; transform: translate(-50%,-50%); border-radius: 22px; animation: pop .22s ease; }
  @keyframes pop { from { opacity: 0; transform: translate(-50%,-48%) } }
}
@keyframes rise { from { transform: translateY(100%) } }
.modal-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 14px; margin-bottom: 16px; }
.modal-head h2 { font-size: 22px; }
.close {
  width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--line);
  background: var(--paper-2); color: var(--ink-2); cursor: pointer; display: grid; place-items: center; flex: none;
}
@media (hover: hover) { .close:hover { border-color: var(--line-2); color: var(--ink); } }
.modal-foot { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; flex-wrap: wrap; }
/* Stays in reach while the sheet scrolls, and clears the phone's home bar at the bottom. dvh above
   is what keeps its top below Safari's toolbar: vh measures as if the toolbar were hidden. */
.modal-head {
  position: sticky; top: 0; z-index: 2; background: var(--paper);
  margin: 0 -24px 16px; padding: 24px 24px 12px;
}
.modal { padding-top: 0; padding-bottom: max(24px, calc(16px + env(safe-area-inset-bottom))); }
@media (pointer: coarse) { .close { width: 44px; height: 44px; } }

/* ---- QR scanner ---- */
.scanner { position: relative; border-radius: 16px; overflow: hidden; background: #000; aspect-ratio: 4/3; }
.scanner video { width: 100%; height: 100%; object-fit: cover; display: block; }
.scanner .reticle {
  position: absolute; inset: 50% auto auto 50%; transform: translate(-50%,-50%);
  width: 58%; aspect-ratio: 1; border: 3px solid rgba(255,255,255,.9); border-radius: 18px;
  box-shadow: 0 0 0 2000px rgba(0,0,0,.35);
}
.scanner .hintbar {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 10px 14px;
  background: rgba(0,0,0,.6); color: #fff; font-size: 12px; text-align: center;
}

/* ---- toggles (ad categories) ---- */
.toggle-row {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 12px 0; border-bottom: 1px solid var(--line);
}
.toggle-row:last-child { border-bottom: 0; }
.toggle-row .t-label strong { display: block; font-size: 14px; font-weight: 600; }
.toggle-row .t-label span { font-size: 12px; color: var(--ink-3); }
.toggle-row .t-label .t-note { display: block; color: var(--ink-2); }
.toggle-row .t-label .t-note:empty { display: none; }
.cat-status { font-size: 13px; margin: 6px 0 0; padding: 10px 12px; border-radius: 8px; background: var(--paper-2); }
.cat-status[data-state="applied"] { color: var(--ok); }
.cat-status[data-state="failed"]  { color: var(--warn); }
.switch { position: relative; width: 44px; height: 26px; flex: none; }
.switch input { opacity: 0; width: 0; height: 0; position: absolute; }
.switch .slider {
  position: absolute; inset: 0; border-radius: 99px; background: var(--line-2);
  cursor: pointer; transition: background .18s ease;
}
.switch .slider::before {
  content: ''; position: absolute; width: 20px; height: 20px; left: 3px; top: 3px;
  border-radius: 50%; background: #fff; transition: transform .18s ease;
  box-shadow: 0 1px 3px rgba(0,0,0,.25);
}
.switch input:checked + .slider { background: var(--ok); }
.switch input:checked + .slider::before { transform: translateX(18px); }
.switch input:focus-visible + .slider { box-shadow: 0 0 0 3px var(--brand-soft); }

/* ---- banners / notices ---- */
.notice { border-radius: 14px; padding: 13px 16px; font-size: 13px; margin-bottom: 16px; }
.notice.info { background: var(--info-soft); color: #AFC6FF; }
.notice.warn { background: var(--warn-soft); color: var(--warn); }
.notice.err  { background: var(--brand-soft); color: var(--brand-ink); }
.notice.demo {
  background: var(--graphite); color: var(--ink-2); display: flex; gap: 10px; align-items: center;
  justify-content: center; border-radius: 0; margin: 0; font-size: 12.5px;
}
.notice.demo b { font-weight: 700; color: var(--ink); }

/* ---- toast ---- */
#toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%) translateY(20px);
  background: var(--ink); color: var(--bg); padding: 12px 18px; border-radius: 999px;
  font-size: 13px; z-index: 200; opacity: 0; pointer-events: none;
  transition-property: opacity, transform; transition-duration: .22s; transition-timing-function: ease;
  box-shadow: 0 10px 30px #000000A6; max-width: calc(100vw - 32px); text-align: center;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---- misc ---- */
.loading { padding: 70px 0; text-align: center; color: var(--ink-3); }
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.spacer { flex: 1; }
.thumb { width: 54px; height: 40px; border-radius: 7px; object-fit: cover; background: var(--bg-3); display: block; }
.kv { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.kv > div { background: var(--paper-2); padding: 12px 14px; }
.kv strong { display: block; font-size: 17px; font-weight: 700; letter-spacing: -0.025em; font-variant-numeric: tabular-nums; }
.kv span { display: block; margin-top: 4px; }
.thread { display: flex; flex-direction: column; gap: 10px; margin: 14px 0; }
.bubble { padding: 11px 14px; border-radius: 14px; background: var(--paper-2); font-size: 13.5px; max-width: 86%; align-self: flex-start; }
/* Your own messages sit on the right, whichever side of the thread you're on. */
.bubble.mine { background: var(--brand-soft); color: var(--ink); align-self: flex-end; }
.bubble .by { font-size: 11px; opacity: .6; margin-bottom: 3px; }

@media (max-width: 900px) {
  .stats { grid-template-columns: repeat(2, 1fr); }
  .grid-2 { grid-template-columns: 1fr; }
}
@media (max-width: 600px) {
  .nav-inner, .tabs-inner, .wrap { padding-left: 16px; padding-right: 16px; }
  .nav .tag, .who { display: none; }
  /* Six tabs don't fit a phone. Tighten them so more are reachable, keep the
     row scrollable, and fade the right edge so it's visible that there's more
     than what fits — an off-screen tab with no hint is an unusable tab. */
  /* Short labels on a phone so every tab is reachable without scrolling. */
  .tab { padding: 13px 8px; font-size: 13.5px; }
  /* On a phone the earnings column was pushed off the right edge behind two
     columns nobody opens the app to read. Drop those instead of making the
     most important number the one you have to scroll for. */
  .table.weeks .col-screens, .table.weeks .col-ads,
  .table.days .col-days, .table .col-paid { display: none; }
  .table th, .table td { padding: 10px 8px; }   /* fit without a sideways nudge */
  .tab .t-full { display: none; }
  .tab .t-short { display: inline; }
  .tabs { position: relative; }
  .tabs::after {
    content: ''; position: absolute; top: 0; right: 0; bottom: 1px; width: 26px;
    background: linear-gradient(to left, var(--bg), #13121500);
    pointer-events: none;
  }
  .tabs-inner { scrollbar-width: none; }
  .tabs-inner::-webkit-scrollbar { display: none; }
  .auth-card { padding: 24px; }
  .screens { grid-template-columns: 1fr; }
  .steps { font-size: 11px; }
  /* Five ranges and a heading don't fit a phone row: abbreviate the ranges and
     let the group sit under the heading at full width. */
  /* A scrolling row on a phone. Tiles are wide enough to see the machine and
     narrow enough that the next one peeks, which is the clearest signal that
     the row moves. Snapping is proximity, not mandatory: mandatory fights a
     slow drag and yanks the row back to a tile you were scrolling away from. */
  .machine-row {
    display: flex;                    /* a scrolling row, not the grid above */
    flex-wrap: nowrap; justify-content: flex-start; gap: 12px; padding-bottom: 14px;
    overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x proximity; scroll-padding-left: 2px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin; scrollbar-color: var(--line-2) var(--bg-3);
  }
  .machine-row:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 12px; }
  /* A visible track, because an overlay scrollbar fades out and leaves no sign
     there is anything to scroll. */
  .machine-row::-webkit-scrollbar { height: 10px; }
  .machine-row::-webkit-scrollbar-track { background: var(--bg-3); border-radius: 99px; }
  .machine-row::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 99px; }
  .machine { flex: 0 0 41%; scroll-snap-align: start; padding: 12px 10px 10px; }
  /* A phone tile is narrow, so deriving the height from its width would shrink
     the machine to a thumbnail. Size the picture directly here. */
  .machine img { aspect-ratio: auto; height: 150px; }
  .machine figcaption { font-size: 12px; }

  /* Fades over whichever edge still has machines past it. Which ones show is
     set from the row's real scroll position, so the hint is never a lie. */
  .machine-wrap::before, .machine-wrap::after {
    content: ''; position: absolute; top: 0; bottom: 14px; width: 26px;
    pointer-events: none; opacity: 0; transition: opacity .18s ease; z-index: 1;
  }
  .machine-wrap::before { left: 0;  background: linear-gradient(to right, var(--bg), transparent); }
  .machine-wrap::after  { right: 0; background: linear-gradient(to left,  var(--bg), transparent); }
  .machine-wrap[data-scroll="start"]::after,
  .machine-wrap[data-scroll="middle"]::after,
  .machine-wrap[data-scroll="middle"]::before,
  .machine-wrap[data-scroll="end"]::before { opacity: 1; }
  .seg { width: 100%; justify-content: space-between; }
  .seg .pill { padding: 7px 9px; font-size: 12px; flex: 1; }
  .seg .pill .t-full { display: none; }
  .seg .pill .t-short { display: inline; }
  .range-sum { grid-template-columns: repeat(2, 1fr); gap: 14px 12px; }
}

/* ---- admin account switcher ---- */
.admin-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 24px; background: var(--graphite); color: var(--ink);
  font-size: 13px;
}
.admin-bar .eyebrow { color: var(--ink-3); }
.admin-bar .muted { color: var(--ink-3); font-size: 12px; }
.admin-bar select {
  padding: 6px 10px; border-radius: 8px; border: 1px solid var(--line-2);
  background: var(--bg-3); color: var(--ink); font: 500 13px 'IBM Plex Sans', sans-serif;
}
.admin-bar select option { background: var(--bg-3); color: var(--ink); }
@media (max-width: 600px) { .admin-bar { padding-left: 16px; padding-right: 16px; } }

/* ---- mobile: stop iOS zooming when a field gets focus ----
   Safari zooms the page whenever a focused control's text is under 16px, which
   throws the layout off and needs a pinch to undo. Only the inputs change;
   the rest of the type scale stays as designed. */
@media (max-width: 820px) {
  .field input[type=text], .field input[type=email], .field input[type=password],
  .field input[type=number], .field input[type=search], .field input[type=tel], .field input[type=date],
  .field select, .field textarea, #pickSearch, .admin-bar select {
    font-size: 16px;
  }
}

/* ---- daily bars on a screen card ---- */
.daybars { margin: 12px 0 2px; }
.daybars-head { display: flex; justify-content: space-between; gap: 10px; font-size: 12px; color: var(--ink-2); margin-bottom: 6px; }
.daybars-head b { color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.daybars .plot { display: flex; align-items: stretch; gap: 2px; height: 48px; border-bottom: 1px solid var(--line-2); touch-action: pan-y; }
.daybars .col { flex: 1 1 0; min-width: 0; display: flex; align-items: flex-end; cursor: default; }
.daybars .bar { display: block; width: 100%; min-height: 2px; border-radius: 3px 3px 0 0; background: var(--ink-2); }
.daybars .stub { display: block; width: 100%; height: 5px; border-radius: 2px 2px 0 0; background: var(--line-2); }
.daybars .col.focus .bar { background: var(--brand); }
.daybars .col.focus .stub { background: var(--ink-3); }
@media (hover: hover) { .daybars .col:hover .bar { background: var(--brand); } }
.daybars-foot { display: flex; justify-content: space-between; gap: 8px; margin-top: 5px;
  font: 500 10px 'IBM Plex Mono', monospace; color: var(--ink-3); }
.daybars-note, .day-none { font-size: 12px; margin: 6px 0 0; }

/* ---- the insight note ---- */
.insight {
  display: flex; gap: 12px; align-items: flex-start;
  background: linear-gradient(180deg, var(--paper), var(--bg-2));
  border: 1px solid var(--line); border-left: 2px solid var(--brand);
  border-radius: 14px; padding: 14px 18px; margin-bottom: 16px;
}
.insight .ico { color: var(--brand); flex: none; margin-top: 1px; }
.insight .lines { display: grid; gap: 5px; }
.insight p { margin: 0; font-size: 13.5px; color: var(--ink-2); line-height: 1.5; }
.insight b { color: var(--ink); font-weight: 600; }

/* ---- cash-out confetti ---- */
.confetti { position: fixed; inset: 0; pointer-events: none; z-index: 300; overflow: hidden; }
.confetti i {
  position: absolute; top: -14px; left: var(--x);
  width: var(--w); height: calc(var(--w) * 1.6); border-radius: 1px;
  animation: fall var(--d) cubic-bezier(.3, .6, .6, 1) forwards;
}
@keyframes fall {
  to { transform: translateY(102vh) rotate(var(--r)); opacity: .2; }
}

/* =====================================================================
   MOTION
   Everything here is short, and everything moves in the direction the
   content is already going. The point is to make a change legible —
   where a thing came from, that a number is new, that a button took the
   press — not to make the page a performance. Someone reading their
   earnings every morning should stop noticing it by the third visit.

   All of it is switched off under prefers-reduced-motion, at the bottom.
   ===================================================================== */
:root {
  --ease-out: cubic-bezier(.22, 1, .36, 1);
  --ease-spring: cubic-bezier(.34, 1.4, .64, 1);
}

/* ---- entrances ---- */
/* The hidden state lives in the `from` keyframe, so anything whose animation
   never runs stays visible rather than stuck at zero opacity. */
@keyframes fade-in { from { opacity: 0; } }
@keyframes rise-in {
  from { opacity: 0; transform: translateY(12px); filter: blur(4px); }
}

/* A view's blocks come in together but not in lockstep: the stagger reads
   as one movement, and it is capped so a long list never leaves the reader
   waiting on the last card. */
/* A view arrives as semantic chunks — heading, figures, the rest — 100ms
   apart, each over 300ms. Capped after the third so the tail of a long page
   never leaves a reader waiting on it. Under reduced motion the name swaps to
   the opacity-only keyframe, below. */
#view.enter > * { animation: fade-in 300ms ease-out both; }
#view.enter > *:nth-child(2) { animation-delay: 100ms; }
#view.enter > *:nth-child(3) { animation-delay: 200ms; }
#view.enter > *:nth-child(n+4) { animation-delay: 300ms; }

#view.enter .stats .stat { animation: fade-in 300ms ease-out both; }
#view.enter .stats .stat:nth-child(2) { animation-delay: 60ms; }
#view.enter .stats .stat:nth-child(3) { animation-delay: 120ms; }
#view.enter .stats .stat:nth-child(4) { animation-delay: 180ms; }

#view.enter .screens .screen-card { animation: fade-in 300ms ease-out both; }
#view.enter .screens .screen-card:nth-child(2) { animation-delay: 60ms; }
#view.enter .screens .screen-card:nth-child(3) { animation-delay: 120ms; }
#view.enter .screens .screen-card:nth-child(n+4) { animation-delay: 180ms; }

@media (prefers-reduced-motion: no-preference) {
  #view.enter > *,
  #view.enter .stats .stat,
  #view.enter .screens .screen-card { animation-name: rise-in; }
}

/* Rows opened under a screen slide out of the row that produced them. */
@keyframes row-in { from { opacity: 0; } }
.table .day-row { animation: row-in 150ms ease-out both; }
@media (prefers-reduced-motion: no-preference) {
  @keyframes row-slide-in { from { opacity: 0; transform: translateY(-4px); } }
  .table .day-row { animation-name: row-slide-in; }
}

/* ---- the things you touch ---- */
.btn { transition: background .15s ease, border-color .15s ease, opacity .15s ease,
                   transform .12s var(--ease-out), box-shadow .15s ease; }
@media (hover: hover) { .btn:hover:not(:disabled) { transform: translateY(-1px); } }
.btn:not(:disabled):active { transform: translateY(0) scale(.96); transition-duration: 150ms;
                             transition-timing-function: ease-out; }
@media (hover: hover) { .btn.primary:hover:not(:disabled) { box-shadow: 0 6px 18px -6px var(--brand); } }

.card, .screen-card, .stat, .machine {
  transition: transform .2s var(--ease-out), border-color .2s ease, box-shadow .2s ease;
}
@media (hover: hover) {
  .screen-card:hover, .machine:hover {
    transform: translateY(-2px); border-color: var(--line-2);
    box-shadow: 0 10px 26px -14px #000;
  }
  .machine:hover img { transform: translateY(-2px) scale(1.03); }
}
.machine img { transition: transform .25s var(--ease-spring); }

.tab { transition: color .15s ease; }
.tab::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--brand); border-radius: 2px;
  transform: scaleX(0); transform-origin: center;
  transition: transform .22s var(--ease-out);
}
.tab.active::after { transform: scaleX(1); }

.seg .pill { transition: color 150ms ease, background-color 150ms ease; }
.day-row, .screen-total-row { transition: background .15s ease; }
@media (hover: hover) { .screen-total-row:hover, .day-row:hover { background: var(--paper-2); } }
/* One glyph that turns. Swapping ▸ for ▾ gave the transition nothing to
   interpolate, so the rule below was dead until the class carried the state. */
.chev { display: inline-block; transition: rotate 150ms ease-out; }
.chev.open { rotate: 90deg; }

/* ---- the chart ---- */
/* Bars grow from the axis, left to right, so the eye follows the series the
   way it reads it. The delay is per-bar but capped, so ninety days still
   finish inside half a second. */
@keyframes bar-grow { from { transform: scaleY(0); } }
#dayChart .bar {
  transform-origin: center bottom;
  animation: bar-grow .45s var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 8ms);
}
/* Redrawn with the same numbers: show it, don't perform it again. */
#dayChart.no-anim .bar, #dayChart.no-anim .axis, #dayChart.no-anim .grid { animation: none; }
#dayChart .axis, #dayChart .grid { animation: fade-in .5s ease both; animation-delay: .15s; }

/* ---- a figure that has just changed ---- */
@keyframes value-in { from { opacity: 0; } }
@media (prefers-reduced-motion: no-preference) {
  @keyframes value-rise-in { from { opacity: 0; transform: translateY(6px); } }
  #view.enter .stat .value, #view.enter .range-sum .v { animation-name: value-rise-in; }
}
#view.enter .stat .value,
#view.enter .range-sum .v { animation: value-in .4s var(--ease-out) both; animation-delay: .1s; }

/* ---- address suggestions ---- */
@keyframes list-in { from { opacity: 0; transform: translateY(-6px); } }
.ac-list { animation: list-in 160ms ease-out both; }
/* Exits run shorter and move less than the enter: 150ms, and a fixed -12px
   rather than the element's own height. */
@keyframes list-out {
  to { opacity: 0; transform: translateY(-12px); filter: blur(4px); }
}
.ac-list.closing { animation: list-out 150ms ease-out both; }
@keyframes modal-out {
  to { opacity: 0; transform: translate(-50%, calc(-50% - 12px)); filter: blur(4px); }
}
@keyframes veil-out { to { opacity: 0; } }
.modal.closing { animation: modal-out 150ms ease-out both; }
.veil.closing { animation: veil-out 150ms ease-out both; }
@media (max-width: 600px) {
  /* The sheet slides to its own edge, because on a phone the destination is
     off the bottom and that is where it means to go. */
  @keyframes modal-out { to { opacity: 0; transform: translateY(16px); } }
}
@media (prefers-reduced-motion: reduce) {
  .modal.closing, .veil.closing, .ac-list.closing { animation: none; }
}
.ac-item { transition: background .12s ease; }

/* ---- setup progress ---- */
.prog .bar > div { transition: width .9s var(--ease-out); }
.step::after { transition: background .3s ease, border-color .3s ease, transform .3s var(--ease-spring); }
.step.active::after { transform: scale(1.25); }

@media (prefers-reduced-motion: reduce) {
  /* Not "less motion" — none. Someone who asks for this may be asking
     because movement makes them ill. */
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .btn:hover:not(:disabled), .screen-card:hover, .machine:hover,
  .machine:hover img, .step.active::after { transform: none; }
}

/* ---- admin tab ----
   Staff work this queue from a phone as often as a desk, so every admin table
   shares one rule: a normal table where there's room, and at phone width each
   row becomes a stacked card — the lead cell as its title, the others as
   labelled lines (from data-label), the actions along the bottom. .col-min
   marks detail that is dropped on a phone altogether, as .col-days does on
   the dashboard. The card layout is `.table.stack`, not admin-specific, so
   any table too wide for a phone (My Ads, for one) can opt in. */
.small { font-size: 12px; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.table.adm .sub { font-size: 12px; margin-top: 2px; }
.table.adm td.acts .row { justify-content: flex-end; flex-wrap: nowrap; gap: 6px; }
.table.adm th.sel, .table.adm td.sel { width: 1%; padding-right: 0; }
.table.adm tr.picked td { background: var(--brand-soft); }
.table.adm tr.bad td { background: var(--brand-soft); }
.table.adm .clip { max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* A checkbox with a finger-sized target, not a 13px square. */
.tick { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; min-height: 32px; font-size: 13px; color: var(--ink-2); }
.tick input { width: 18px; height: 18px; margin: 0; accent-color: var(--brand); cursor: pointer; }

.adm-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: -4px 0 10px; }
/* A toolbar whose only child is a hidden bulk bar takes no space. */
.adm-tools:not(:has(> :not([hidden]))) { display: none; }
.adm-search, .adm-filter {
  padding: 8px 12px; border-radius: var(--r-sm); border: 1px solid var(--line-2);
  background: var(--bg); color: var(--ink); font: inherit; font-size: 13px;
}
.adm-search { flex: 1 1 220px; min-width: 0; }
.adm-search:focus, .adm-filter:focus { outline: none; border-color: var(--ink-3); }
.bulk {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-left: auto;
  padding: 4px 4px 4px 12px; border-radius: var(--r-md); background: var(--paper-2); font-size: 13px;
}

/* How long something has waited: quiet when fine, louder as it slips. */
.age { font-variant-numeric: tabular-nums; white-space: nowrap; }
.age.slow { color: var(--warn); font-weight: 600; }
.age.late { color: var(--brand-ink); font-weight: 700; }

/* Stat tiles double as jump links to their section. */
button.stat { font: inherit; color: inherit; text-align: left; cursor: pointer; width: 100%; }
@media (hover: hover) { button.stat:not(.hero):hover { border-color: var(--line-2); } }

/* Support queue */
.sup-toggle { all: unset; cursor: pointer; display: block; }
.sup-toggle:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 4px; }
@media (hover: hover) { .sup-row:hover td { background: var(--paper-2); } }
.sup-row.open td { border-bottom-color: transparent; }
.sup-thread > td { background: var(--bg-2); padding-top: 4px; }
.sup-thread .thread { margin-top: 4px; }
.staff-reply textarea {
  width: 100%; min-height: 64px; padding: 10px 12px; border-radius: var(--r-sm); resize: vertical;
  border: 1px solid var(--line-2); background: var(--bg); color: var(--ink); font: inherit; margin-bottom: 8px;
}
.staff-reply textarea:focus { outline: none; border-color: var(--ink-3); }

/* Revenue import preview */
.rev-preview { margin: 4px 0 14px; }
.rev-preview .notice { margin-bottom: 10px; }
.rev-preview .notice .mono { word-break: break-all; }
.skips { margin: 6px 0 0; padding-left: 18px; }
.rev-sum {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 1px; margin-bottom: 10px;
  background: var(--line); border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden;
}
.rev-sum > div { background: var(--paper-2); padding: 10px 12px; }
.rev-sum strong { display: block; font-size: 17px; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.rev-sum .eyebrow { display: block; margin-top: 2px; }

@media (max-width: 600px) {
  .table.stack thead { display: none; }
  .table.stack, .table.stack tbody, .table.stack tr, .table.stack td { display: block; width: auto; }
  .table.stack tr { position: relative; padding: 12px 0; border-bottom: 1px solid var(--line); }
  .table.stack tr:last-child { border-bottom: 0; }
  .table.stack td { border: 0; padding: 2px 0; text-align: left; }
  .table.stack td.num { text-align: left; }
  .table.stack td.lead { padding-bottom: 6px; }
  /* A hanging indent rather than flex: a cell can hold several inline pieces,
     and a permanent id (cck- plus 32 hex) has to be free to wrap. */
  .table.stack td[data-label] { position: relative; padding-left: 92px; font-size: 13.5px; overflow-wrap: anywhere; }
  .table.stack td[data-label]::before {
    content: attr(data-label); position: absolute; left: 0; top: 5px;
    font: 600 10px 'IBM Plex Mono', monospace; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3);
  }
  .table.stack td.lead { overflow-wrap: anywhere; }
  .table.stack td.acts { padding-top: 10px; }
  .table.stack td.acts .row { justify-content: flex-start; flex-wrap: wrap; }
  .table.stack td.acts .btn { flex: 1 1 auto; }
  /* The tick rides in the card's top-right corner, clear of the title. */
  .table.stack tr:has(td.sel) { padding-right: 40px; }
  .table.stack td.sel { position: absolute; top: 8px; right: 0; padding: 0; width: auto; }
  .table.stack td.sel .tick { min-height: 40px; min-width: 40px; justify-content: center; }
  .table.stack tr.picked td { background: none; }
  .table.stack tr.picked { background: var(--brand-soft); box-shadow: 0 0 0 8px var(--brand-soft); border-radius: 2px; }
  .table.stack td[colspan] { padding: 8px 0; }
  .table.stack .col-min { display: none; }
  .table.stack td.acts:empty { display: none; }
  .table.stack[data-tbl="support"] td.acts { display: none; }
  .adm-search { flex-basis: 100%; }
  .adm-filter { flex: 1 1 0; }
  .bulk { margin-left: 0; width: 100%; }
  .bulk .btn.primary { margin-left: auto; }
  .rev-sum { grid-template-columns: repeat(2, 1fr); }
  .rev-sum > div:last-child { grid-column: 1 / -1; }
  #adm-support .card-head .seg { width: 100%; }
  /* My Ads: the thumbnail rides top-right, like the admin tick, so the title
     still leads the card. */
  .table.stack.my-ads tr { padding-right: 66px; }
  .table.stack.my-ads td.thumb-cell { position: absolute; top: 12px; right: 0; padding: 0; }
  .table.stack.my-ads td.acts { text-align: left; }
}
/* Same reason as the rule above: under 16px, iOS zooms the page on focus. */
@media (max-width: 820px) {
  .adm-search, .adm-filter, .staff-reply textarea { font-size: 16px; }
}
.sup-row { cursor: pointer; }
.photo-preview { display: block; width: 100%; max-height: 220px; object-fit: cover; border-radius: 10px; margin-bottom: 10px; }
.photo-preview[hidden] { display: none; }
