@import url('https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500&display=swap');

:root {
  --bg: #000;
  --panel: #0a0a0a;
  --rail: #050505;
  --line: #1f1f1f;
  --line-strong: #333;
  --text: #fff;
  --muted: #888;
  --dim: #555;
  --accent: #5865F2;
  --accent-ink: #fff;
  --ok: #22c55e;
  --bad: #ef4444;
  --radius: 6px;
  --sans: 'Geist', system-ui, -apple-system, sans-serif;
  --mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.mono { font-family: var(--mono); }
.muted { color: var(--muted); }
.dim { color: var(--dim); }
.ok { color: var(--ok); }
.bad { color: var(--bad); }

/* ---------------------------------------------------------------- shell */

.shell { display: grid; grid-template-columns: 72px 220px 1fr; min-height: 100vh; }

.rail {
  background: var(--rail);
  border-right: 1px solid var(--line);
  padding: 12px 0;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
.rail a {
  width: 48px; height: 48px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--panel); border: 1px solid var(--line);
  font-weight: 600; font-size: 15px; color: var(--muted);
  transition: border-radius .15s, border-color .15s, color .15s;
}
.rail a:hover { border-radius: 16px; color: var(--text); border-color: var(--line-strong); }
.rail a.on { border-radius: 16px; color: var(--text); border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.rail .sep { width: 32px; height: 1px; background: var(--line); margin: 4px 0; }
.rail a img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
.wsimg { width: 28px; height: 28px; border-radius: 8px; object-fit: cover; }

.side {
  background: var(--panel);
  border-right: 1px solid var(--line);
  display: flex; flex-direction: column;
}
.side .ws {
  padding: 18px 16px 14px;
  border-bottom: 1px solid var(--line);
}
.side .ws h2 { margin: 0; font-size: 15px; font-weight: 600; letter-spacing: -.01em; }
.side .ws .role { color: var(--muted); font-size: 12px; margin-top: 2px; }
.side nav { padding: 8px; display: flex; flex-direction: column; gap: 2px; }
.side nav a {
  padding: 8px 10px; border-radius: var(--radius);
  color: var(--muted); display: flex; justify-content: space-between; align-items: center;
}
.side nav a:hover { background: #111; color: var(--text); }
.side nav a.on { background: #151515; color: var(--text); }
.side .credits {
  margin-top: auto; padding: 14px 16px; border-top: 1px solid var(--line);
}
.side .credits .n { font-family: var(--mono); font-size: 20px; }
.side .credits .l { color: var(--muted); font-size: 12px; }
.side .foot { padding: 10px 16px 16px; display: flex; gap: 12px; font-size: 12px; color: var(--muted); }
.side .foot a:hover { color: var(--text); }

.main { padding: 40px 48px 80px; max-width: 980px; }
.main h1 { font-size: 26px; font-weight: 600; letter-spacing: -.02em; margin: 0 0 6px; }
.main .lead { color: var(--muted); margin: 0 0 28px; max-width: 60ch; }
.main .lead strong { color: var(--text); font-weight: 500; }

/* ---------------------------------------------------------------- primitives */

.card { border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); }
/* Spacing on the card itself, not on the pair: a dialog or any other
   element between two cards would break an adjacent-sibling rule. */
.card { margin-bottom: 12px; }
.card:last-child { margin-bottom: 0; }
.card .hd { padding: 14px 18px; border-bottom: 1px solid var(--line); display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.card .hd h3 { margin: 0; font-size: 14px; font-weight: 600; }
.card .bd { padding: 18px; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 36px; padding: 0 14px; border-radius: var(--radius);
  border: 1px solid var(--line-strong); background: transparent; color: var(--text);
  cursor: pointer; white-space: nowrap; font-weight: 500;
  transition: background .12s, border-color .12s, color .12s;
}
.btn:hover { background: #111; border-color: #444; }
.btn.primary { background: var(--text); color: var(--bg); border-color: var(--text); }
.btn.primary:hover { background: #e5e5e5; }
.btn.accent { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.btn.accent:hover { background: #4752c4; }
.btn.danger { color: var(--bad); border-color: #3a1a1a; }
.btn.danger:hover { background: #1a0a0a; }
.btn.sm { height: 30px; padding: 0 10px; font-size: 13px; }
.btn:disabled { opacity: .4; cursor: not-allowed; }
.btn.block { width: 100%; }

.field { display: flex; flex-direction: column; gap: 6px; }
/* The label carries the circle when a field has an optional note, and the
   circle's sentence is measured off it (see .tip.wide below), so it is the
   positioning context rather than the circle itself. */
.field label { font-size: 13px; color: var(--muted); position: relative; }
/* "(optional)" on the same line as a field's name: the name is muted, this
   half is lit, so it reads as a note about the field and not as part of it. */
.field label .opt { color: #cfcfcf; }
.input {
  height: 38px; padding: 0 12px; border-radius: var(--radius);
  border: 1px solid var(--line-strong); background: var(--bg); color: var(--text);
  width: 100%;
}
.input:focus { border-color: #666; outline: none; }
.input.mono { font-family: var(--mono); letter-spacing: .06em; }
.input::placeholder { color: var(--dim); }
textarea.input { height: auto; padding: 10px 12px; resize: vertical; }

.row { display: flex; gap: 10px; align-items: center; }
.row.between { justify-content: space-between; }
.stack { display: flex; flex-direction: column; gap: 10px; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 12px 18px; border-top: 1px solid var(--line); vertical-align: top; }
th { color: var(--muted); font-weight: 500; font-size: 13px; border-top: 0; }
td .mono { font-size: 13px; }

.pill { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 12px; border: 1px solid var(--line-strong); color: var(--muted); }
.pill.ok { color: var(--ok); border-color: #14532d; }
.pill.bad { color: var(--bad); border-color: #7f1d1d; }
.pill.run { color: var(--accent); border-color: #2b2f7a; }

.note { padding: 12px 14px; border: 1px solid var(--line-strong); border-radius: var(--radius); color: var(--muted); }
.note.ok { border-color: #14532d; color: var(--ok); }
.note.bad { border-color: #7f1d1d; color: var(--bad); }

.empty { padding: 48px 18px; text-align: center; color: var(--muted); }

/* ---------------------------------------------------------------- auth pages */

.auth { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.auth .box { width: 100%; max-width: 400px; }
.auth h1 { font-size: 22px; font-weight: 600; letter-spacing: -.02em; margin: 0 0 4px; }
.auth .brand { color: var(--muted); font-size: 13px; margin-bottom: 36px; }
.auth .or { display: flex; align-items: center; gap: 12px; color: var(--dim); font-size: 12px; margin: 22px 0; }
.auth .or::before, .auth .or::after { content: ''; flex: 1; height: 1px; background: var(--line); }

.number {
  font-family: var(--mono); font-size: 30px; letter-spacing: .08em;
  padding: 22px 18px; border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--panel); text-align: center; user-select: all;
}

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

.funnel { border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); }
.funnel + .funnel { margin-top: 12px; }
.funnel .hd { padding: 12px 18px; border-bottom: 1px solid var(--line); display: flex; justify-content: space-between; align-items: center; }
.funnel .hd b { font-weight: 600; }
.funnel .hd .sub { font-family: var(--mono); color: var(--muted); font-size: 13px; }
.funnel .bd { padding: 18px; display: grid; gap: 18px; }

.markets { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.markets label {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: var(--radius); cursor: pointer;
  color: var(--muted); user-select: none;
}
.markets label:hover { border-color: var(--line-strong); color: var(--text); }
.markets label.on { border-color: var(--accent); color: var(--text); background: #0d0f2a; }
.markets input { display: none; }

.seg { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--radius); overflow: hidden; }
.seg label { padding: 8px 14px; cursor: pointer; color: var(--muted); border-right: 1px solid var(--line-strong); }
.seg label:last-child { border-right: 0; }
.seg label.on { background: #151515; color: var(--text); }
.seg input { display: none; }

.names { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.names .field label { display: flex; gap: 6px; align-items: center; }

.total { position: sticky; bottom: 0; background: var(--bg); border-top: 1px solid var(--line); padding: 16px 0 0; margin-top: 24px; }
.total .card .bd { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.total #err { font-size: 13px; width: 100%; order: -1; }
.total #err:empty { display: none; }
.total .sum { margin-left: auto; }
.total #actions { margin-left: 8px; }
.total .sum { display: flex; gap: 28px; }
.total .sum div { font-size: 13px; color: var(--muted); }
.total .sum div b { display: block; font-family: var(--mono); font-size: 20px; color: var(--text); font-weight: 500; }
/* A lifetime discount, wherever it is shown: lit, so it reads as money the
   workspace is not being charged. */
.lifetime { color: var(--ok); font-size: 13px; }
.total .lifetime b { font-size: 15px; }

/* ---------------------------------------------------------------- responsive */

/* ---------------------------------------------------------------- mobile

   Under 900px this becomes the Discord phone layout: a header with a
   hamburger, and one drawer holding the workspaces and the navigation
   together. The rail and sidebar are the same markup, just moved.        */

.topbar { display: none; }
.scrim { display: none; }
.drawerws { display: none; }

@media (max-width: 900px) {
  .topbar {
    display: flex; align-items: center; gap: 12px;
    position: sticky; top: 0; z-index: 40;
    height: 52px; padding: 0 12px;
    background: var(--panel); border-bottom: 1px solid var(--line);
  }
  .topbar .title { font-weight: 600; font-size: 15px; }
  .topbar .bal {
    margin-left: auto; font-family: var(--mono); font-size: 13px;
    color: var(--muted); border: 1px solid var(--line); border-radius: 999px;
    padding: 4px 12px;
  }
  .burger {
    width: 36px; height: 36px; display: grid; align-content: center; gap: 4px;
    background: none; border: 0; cursor: pointer; padding: 0 8px;
  }
  .burger span { display: block; height: 2px; background: var(--text); border-radius: 2px; }

  .shell { display: block; }
  .rail { display: none; }

  .side {
    position: fixed; top: 0; left: 0; bottom: 0; width: 280px; z-index: 50;
    transform: translateX(-100%); transition: transform .18s ease;
    border-right: 1px solid var(--line);
  }
  body.drawer .side { transform: none; }
  body.drawer { overflow: hidden; }

  .scrim { display: block; position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 45; }
  .scrim[hidden] { display: none; }

  .drawerws {
    display: flex; gap: 8px; padding: 12px 14px; overflow-x: auto;
    border-bottom: 1px solid var(--line);
  }
  .drawerws a {
    flex: 0 0 auto; width: 42px; height: 42px; border-radius: 14px;
    display: grid; place-items: center; overflow: hidden;
    background: var(--bg); border: 1px solid var(--line);
    color: var(--muted); font-weight: 600; font-size: 13px;
  }
  .drawerws a.on { color: var(--text); border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
  .drawerws a img { width: 100%; height: 100%; object-fit: cover; }

  .side nav a { padding: 12px 12px; }

  .main { padding: 20px 16px 120px; }
  .main h1 { font-size: 21px; }
  .main .lead { margin-bottom: 20px; }

  .markets { grid-template-columns: 1fr 1fr; gap: 8px; }
  .markets label { padding: 11px 10px; }
  .names, .grid2 { grid-template-columns: 1fr; }
  .seg { display: grid; grid-template-columns: 1fr; width: 100%; }
  .seg label { border-right: 0; border-bottom: 1px solid var(--line-strong); text-align: center; }
  .seg label:last-child { border-bottom: 0; }

  .card .bd, .funnel .bd, .card .hd, .funnel .hd { padding: 14px; }

  /* Totals pinned to the bottom of the screen, not the page. */
  .total { position: fixed; left: 0; right: 0; bottom: 0; margin: 0; padding: 0;
           background: var(--bg); border-top: 1px solid var(--line); z-index: 30; }
  .total .card { border: 0; border-radius: 0; }
  .total .card .bd { flex-direction: column; align-items: stretch; gap: 12px; padding: 12px 16px; }
  .total .sum { margin: 0; justify-content: space-between; gap: 12px; width: 100%; }
  .total .sum div b { font-size: 17px; }
  .total #actions { margin: 0; display: grid; gap: 8px; grid-auto-flow: row; }
  .total #actions .btn { width: 100%; }
  .total #add { order: 99; }

  /* Tables don't fit a phone, so each row becomes a card. */
  table, thead, tbody, tr, th, td { display: block; }
  thead { display: none; }
  tr { border-top: 1px solid var(--line); padding: 12px 14px; }
  td { border: 0; padding: 3px 0; }
  td:empty { display: none; }
  td[data-l]::before { content: attr(data-l) " "; color: var(--muted); font-size: 12px; }
}

@media (max-width: 420px) {
  .number { font-size: 22px; letter-spacing: .05em; padding: 18px 10px; }
  .auth { padding: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

.gear { background: none; border: 0; color: var(--dim); cursor: pointer; font-size: 15px; padding: 2px 4px; border-radius: 4px; }
.gear:hover { color: var(--text); background: #151515; }
.dlg { background: var(--panel); color: var(--text); border: 1px solid var(--line-strong); border-radius: var(--radius); padding: 22px; width: 380px; max-width: 92vw; }
.dlg::backdrop { background: rgba(0,0,0,.7); }

.linkbox { display: flex; gap: 12px; align-items: flex-start; padding: 12px; border: 1px solid var(--line); border-radius: var(--radius); cursor: pointer; }
.linkbox:hover { border-color: var(--line-strong); }
.linkbox input { margin-top: 3px; accent-color: var(--accent); }
.linkbox .t { display: block; color: var(--text); }
.linkbox .t b { color: var(--accent); font-weight: 600; }
.linkbox .s { display: block; color: var(--muted); font-size: 12px; margin-top: 2px; }
.why { font-size: 13px; }
.why ul { margin: 6px 0 0; padding-left: 18px; color: var(--muted); }
.why li { margin: 3px 0; }
.why li::marker { color: var(--dim); }

.tip { position: relative; display: inline-grid; place-items: center; width: 15px; height: 15px; margin-left: 4px; border: 1px solid var(--line-strong); border-radius: 50%; font-size: 10px; color: var(--dim); cursor: help; vertical-align: middle; }
.tip:hover, .tip:focus { color: var(--text); border-color: #666; }
.tipbox { display: none; position: absolute; left: 50%; bottom: 22px; transform: translateX(-50%); white-space: nowrap; background: #151515; border: 1px solid var(--line-strong); border-radius: var(--radius); padding: 6px 10px; font-size: 12px; color: var(--text); font-family: var(--mono); }
.tip:hover .tipbox, .tip:focus .tipbox { display: block; }
/* The same circle when its text is a sentence or two: it wraps, and it is laid
   out from the field's own left edge, never wider than the field, so it stays
   inside the box the field is in however far along the line the circle sits
   ("Checkout links (optional)" puts it near the start of a full-width field,
   which the circle-anchored version pushed off the side). */
.field label .tip { position: static; }
.tip.wide .tipbox {
  white-space: normal; width: min(220px, 100%); left: 0; right: auto;
  bottom: 100%; transform: none; text-align: left; line-height: 1.5;
}

.payover { position: fixed; inset: 0; background: rgba(0,0,0,.75); display: grid; place-items: center; z-index: 60; padding: 20px; }
.payover .card { width: 320px; max-width: 100%; }
.payover .t { color: var(--text); font-size: 14px; text-align: center; }
.payover .s { font-size: 12px; text-align: center; }
.spinner { width: 26px; height: 26px; border: 2px solid var(--line-strong); border-top-color: var(--text); border-radius: 50%; animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------------------------------------------------------------- filter bar */

.filterbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.filterbar .count { margin-right: auto; color: var(--muted); font-size: 13px; }
.filterbar .count b { font-family: var(--mono); font-size: 20px; color: var(--text); font-weight: 500; }

.drop { position: relative; }
.dropbtn {
  display: inline-flex; align-items: center; gap: 8px;
  height: 34px; padding: 0 12px; border-radius: var(--radius);
  border: 1px solid var(--line-strong); background: transparent; color: var(--text);
  cursor: pointer; white-space: nowrap;
}
.dropbtn:hover { background: #111; border-color: #444; }
.drop.open .dropbtn { background: #151515; border-color: #555; }
.dropbtn .chev { color: var(--dim); font-size: 11px; line-height: 1; }

.dropmenu {
  display: none; position: absolute; top: calc(100% + 6px); right: 0; z-index: 20;
  min-width: 210px; max-height: 320px; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--line-strong);
  border-radius: var(--radius); padding: 6px;
  box-shadow: 0 8px 24px rgba(0,0,0,.6);
}
.drop.open .dropmenu { display: block; }
.dropmenu label {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 9px; border-radius: 4px; cursor: pointer; color: var(--muted);
  white-space: nowrap; font-size: 13px;
}
.dropmenu label:hover { background: #151515; color: var(--text); }
.dropmenu input { accent-color: var(--accent); margin: 0; }
.dropactions { display: flex; gap: 6px; padding: 4px 6px 8px; border-bottom: 1px solid var(--line); margin-bottom: 4px; }
.dropactions button {
  flex: 1; height: 26px; border-radius: 4px; cursor: pointer; font-size: 12px;
  background: transparent; border: 1px solid var(--line-strong); color: var(--muted);
}
.dropactions button:hover { color: var(--text); border-color: #555; }

.dropapply { padding: 8px 6px 4px; border-top: 1px solid var(--line); margin-top: 4px;
             position: sticky; bottom: 0; background: var(--panel); }
.dropapply button {
  width: 100%; height: 30px; border-radius: 4px; cursor: pointer; font-size: 13px;
  background: transparent; border: 1px solid var(--line-strong); color: var(--muted);
}
.dropapply button.ready { background: var(--text); color: var(--bg); border-color: var(--text); }

@media (max-width: 900px) {
  .filterbar { gap: 8px; }
  .filterbar .count { width: 100%; margin: 0 0 4px; }
  .dropmenu { right: auto; left: 0; }
}

.input.bad-input { border-color: var(--bad); }
.input.bad-input:focus { border-color: var(--bad); }

/* ---------------------------------------------------------------- wipe dialog */

.dlg.wipe { width: 480px; }
.dlg.wipe h3 { margin: 0 0 4px; font-size: 16px; }
.dlg.wipe p { margin: 0 0 16px; font-size: 13px; }
.dlg.wipe .field { margin-bottom: 14px; }
.wipecount {
  padding: 10px 12px; border: 1px solid var(--line-strong); border-radius: var(--radius);
  font-size: 13px; color: var(--muted); margin-bottom: 14px;
}
.wipecount b { color: var(--text); font-family: var(--mono); }
.wipewarn {
  border: 1px solid #7f1d1d; border-radius: var(--radius);
  padding: 12px 14px; margin-bottom: 16px; font-size: 13px; color: var(--muted);
}
.wipewarn > b { color: var(--bad); display: block; margin-bottom: 8px; }
.wipewarn ul { margin: 0; padding-left: 18px; }
.wipewarn li { margin: 6px 0; }
.wipewarn li b { color: var(--text); }
.wipewarn li::marker { color: #7f1d1d; }

/* ---------------------------------------------------------------- event log */

.log { display: flex; flex-direction: column; }
.logrow {
  display: grid; grid-template-columns: 150px 1fr 140px;
  gap: 16px; padding: 12px 18px; border-top: 1px solid var(--line);
  align-items: baseline;
}
.logrow:first-child { border-top: 0; }
.logrow .when { color: var(--muted); font-size: 13px; white-space: nowrap; }
.logrow .what b { font-weight: 500; }
.logrow .what .muted { margin-left: 8px; font-size: 13px; }
.logrow .who { color: var(--muted); font-size: 13px; text-align: right; }
.dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 9px; vertical-align: middle; }
.dot.g-work   { background: var(--accent); }
.dot.g-money  { background: var(--ok); }
.dot.g-people { background: #a855f7; }
.dot.g-admin  { background: var(--muted); }
.dot.g-other  { background: var(--dim); }

@media (max-width: 900px) {
  .logrow { grid-template-columns: 1fr; gap: 4px; }
  .logrow .who { text-align: left; }
  .logrow .what .muted { margin-left: 0; display: block; }
}

/* ---------------------------------------------------------------- top up */

.amounts { display: flex; gap: 8px; flex-wrap: wrap; }
.amt {
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  min-width: 120px; padding: 12px 14px; cursor: pointer;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: transparent; color: var(--text); text-align: left;
}
.amt:hover { background: #111; border-color: #555; }
.amt.on { border-color: var(--accent); background: #0d0f2a; }
.amt b { font-size: 16px; font-weight: 600; }
.amt span { font-size: 12px; color: var(--muted); font-family: var(--mono); }
.amt.custom b { display: flex; align-items: baseline; gap: 2px; }
.amt.custom input {
  width: 74px; background: none; border: 0; color: var(--text);
  font: inherit; font-size: 16px; font-weight: 600; padding: 0;
}
.amt.custom input:focus { outline: none; }
.amt.custom input::placeholder { color: var(--dim); font-weight: 400; }

.confirmbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  padding: 14px 16px; border: 1px solid var(--line-strong);
  border-radius: var(--radius); background: #0d0d0d;
}
.confirmbar .sum { font-size: 14px; color: var(--muted); }
.confirmbar .sum b { color: var(--text); font-family: var(--mono); }

@media (max-width: 900px) {
  .amounts { display: grid; grid-template-columns: 1fr 1fr; }
  .amt { min-width: 0; }
  .confirmbar { flex-direction: column; align-items: stretch; }
  .confirmbar .btn { width: 100%; }
}

/* ---------------------------------------------------------------- personalize

   The Personalize page (one box per tool, one row per setting the workspace's
   owner can pin for everybody). A row is the app's .linkbox, so the tick sits
   in front of the setting the way it sits in front of every other choice in
   the platform. The setting's name and its choice are beside the tick, and the
   choice is a sibling of the label rather than inside it: opening the dropdown
   must not tick the box on the way past. */

.persrow { align-items: flex-start; }
.persmain { display: flex; flex-direction: column; gap: 8px; min-width: 0; flex: 1 1 auto; }
.persline { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.persline label { cursor: pointer; }
.persline select.input { width: auto; min-width: 210px; height: 34px; padding: 0 8px; }
.persdays { flex: 0 0 auto; max-width: 140px; }
/* A setting that is off is not being run: the name and the choice beside it
   stay readable, plainly as something that is not in force. */
.persrow:not(.on) .persline label { color: var(--muted); }
.persrow:not(.on) select.input { border-color: var(--line); color: var(--muted); }
.perserr { margin: 0; }

@media (max-width: 900px) {
  /* One setting per line on a phone: the name, then the choice under it, then
     the days the choice needs. */
  .persline { display: grid; grid-template-columns: 1fr; gap: 8px; width: 100%; }
  .persline select.input, .persdays { min-width: 0; max-width: none; width: 100%; }
}

/* ---------------------------------------------------------------- tool chooser */

.tools { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.tool {
  display: block; padding: 18px; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--panel); color: inherit;
  transition: border-color .12s, background .12s;
}
a.tool:hover { border-color: #444; background: #101010; }
.tool b { font-size: 15px; font-weight: 600; }
.tool p { margin: 10px 0 0; color: var(--muted); font-size: 13px; }
.tool .foot { margin-top: 14px; color: var(--muted); font-size: 12px; }
.tool .foot .mono { color: var(--text); }
.tool.off { opacity: .55; }
.tool.off b { color: var(--muted); }

.status { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; white-space: nowrap; color: var(--muted); }
.status i { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.status.s-ok { color: var(--ok); }
.status.s-warn { color: #f59e0b; }
.status.s-bad { color: var(--bad); }
.status.s-muted { color: var(--dim); }

.backlink { display: inline-block; color: var(--muted); font-size: 13px; margin-bottom: 10px; }
.backlink:hover { color: var(--text); }

@media (max-width: 900px) { .tools { grid-template-columns: 1fr; } }

/* ---------------------------------------------------------------- pay methods */

.paysum {
  display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap;
  padding: 13px 16px; margin-bottom: 12px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: #0d0d0d; color: var(--muted); font-size: 14px;
}
.paysum b { color: var(--text); font-size: 17px; font-weight: 600; }

.methods { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.method {
  display: flex; flex-direction: column;
  padding: 16px; border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--panel);
}
.method .mhead { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.method .mhead b { font-size: 14px; font-weight: 600; }
.method .tag {
  font-size: 11px; color: var(--muted); white-space: nowrap;
  border: 1px solid var(--line); border-radius: 999px; padding: 2px 9px;
}
.method p { margin: 0 0 14px; font-size: 12.5px; color: var(--muted); line-height: 1.45; flex: 1; }
.method .btn { margin-top: auto; }
.method.off { opacity: .5; }

.payerr { font-size: 13px; }
.payerr:empty { display: none; }

@media (max-width: 900px) {
  .methods { grid-template-columns: 1fr; }
  .paysum { justify-content: center; }
}

ul.plain { margin: 0; padding-left: 18px; color: var(--muted); font-size: 13.5px; }
ul.plain li { margin: 9px 0; line-height: 1.5; }
ul.plain li b { color: var(--text); font-weight: 500; }
ul.plain li::marker { color: var(--dim); }

.refstats { display: flex; gap: 36px; flex-wrap: wrap; padding-top: 14px; border-top: 1px solid var(--line); }
.refstats div { display: flex; flex-direction: column; gap: 2px; }
.refstats span { font-size: 12px; color: var(--muted); }
.refstats b { font-size: 22px; font-weight: 500; }

/* ------------------------------------------------- how each market is built

   The funnel settings box on the request page (funnel-stack.js). Every rule is
   a .stack- name of its own, except .seg.wrap, which only lets the app's own
   segmented control wrap between its choices. It sits inside a funnel card, so
   it inherits .field, .input, .seg and .markets from above. */

/* One outline around the whole box: the line that says what it is for, the
   preset row, the offer count and one collapsible per market. The funnel card
   has no header for this, so that line is the heading and the outline is what
   gathers everything it describes into one box. */
.stack-box {
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  padding: 16px; display: grid; gap: 16px;
}
/* The heading is text inside the box, not a second outline of its own. */
.stack-box > .note { border: 0; padding: 0; }
.stack-box > .note b { color: var(--text); font-weight: 500; }
.stack-box > .note a { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }

.stack-hint { color: var(--dim); font-size: 12.5px; line-height: 1.45; }

/* One market per row, that market's configuration folded inside it. Closed, a
   market is a single line saying what has been filled in. */
.stack-mkt { border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); }
.stack-mkt + .stack-mkt { margin-top: 8px; }
.stack-mkt.on { border-color: var(--line-strong); }
/* Told they are not filled in, and left that way until they are. */
.stack-mkt.unfilled { border-color: var(--bad); }
.stack-mkhd {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 11px 14px;
  background: transparent; border: 0; border-radius: var(--radius);
  color: var(--text); font-size: 13px; cursor: pointer; text-align: left;
}
.stack-mkhd.flat { cursor: default; }
.stack-mkhd:not(.flat):hover { background: #101010; }
.stack-mkhd .flag { font-size: 15px; }
.stack-mkhd b { font-weight: 600; }
.stack-mkhd .sub {
  margin-left: auto; color: var(--dim); font-size: 12.5px; font-family: var(--mono);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.stack-mkhd .miss { color: var(--bad); font-size: 12.5px; white-space: nowrap; }
.stack-mkhd .caret { color: var(--dim); transition: transform .12s; }
.stack-mkt.on .caret { transform: rotate(180deg); }
.stack-mkbd { padding: 14px; border-top: 1px solid var(--line); display: grid; gap: 16px; }

/* The structure choice: one line per choice, its own width, wrapping between
   choices when the screen is narrow. */
.seg.wrap { flex-wrap: wrap; }
.seg.wrap label { white-space: nowrap; }

/* The apply-to-the-other-markets switch: a small rounded box inside the
   structure field and the guarantee field of the first market only. Colour and
   size sit on the span, not the label, so .field label cannot outrank them. */
.stack-apply {
  display: inline-flex; align-items: center; gap: 8px; width: fit-content;
  border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px;
  cursor: pointer; user-select: none; margin-top: 2px;
}
.stack-apply:hover { border-color: var(--line-strong); }
.stack-apply input { accent-color: var(--accent); margin: 0; }
.stack-apply span { font-size: 12.5px; color: var(--muted); }
.stack-apply input:checked + span { color: var(--text); }

/* A numbered row: the number on the side, the fields beside it. What each
   field is for lives in its placeholder, so no heading costs a line. */
.stack-rows { display: flex; flex-direction: column; gap: 10px; }
/* A row is one offer, so it is one line: the fields of an offer may not be
   split over two lines while it is beside its number, which is what a wrapped
   row does. The quantity is the width of a number, and it gives that much to
   the two prices rather than take from them; where the row is at its
   narrowest (the preset editor with its sidebar up) it is still wide enough to
   say Quantity in full. On a phone the rule below turns the row into a block
   and each field takes a line of its own. */
.stack-row { display: flex; align-items: center; gap: 10px; }
.stack-row .n { flex: 0 0 22px; text-align: center; font-family: var(--mono); font-size: 12px; color: var(--dim); }
.stack-row .input { flex: 1 1 190px; min-width: 0; }
.stack-row .input.qty { flex: 1 1 100px; min-width: 108px; }
/* The discount type is a part of the pricing field, not a field of its own: it
   sits above the first offer with its name and its choice on ONE line, the
   same way the box's preset row reads, so the field's own name stays the only
   heading above the offers. The choice is the height of that row (34px, like
   the preset one) and only as wide as the longest thing in it. */
.stack-disc { flex-direction: row; align-items: center; gap: 8px; }
.stack-disc label { flex: 0 0 auto; }
.stack-disc select.input {
  width: auto; min-width: 170px; height: 34px; padding: 0 8px; flex: 0 0 auto;
}
/* Free quantity: what they pay for and what comes free with it, as one line on
   the offer's row -- 2 + 1 free. Two boxes the width of a two-figure number
   with the plus and the word between them, not two form fields of their own. */
.stack-row .stack-qq { display: flex; align-items: center; gap: 3px; flex: 0 0 auto; }
.stack-row .stack-qq .input {
  flex: 0 0 32px; min-width: 0; width: 32px; padding: 0 6px; text-align: center;
}
.stack-qq .qq-plus { font-family: var(--mono); font-size: 13px; color: var(--muted); }
.stack-qq .qq-free { font-size: 12.5px; color: var(--muted); }
.stack-save { flex: 0 0 96px; text-align: right; font-family: var(--mono); font-size: 12px; color: var(--dim); }
.stack-save.on { color: var(--ok); }
.stack-save.bad { color: var(--bad); }

/* The preset row: the one line at the top of the box. */
.stack-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding-bottom: 18px; border-bottom: 1px solid var(--line);
}
.stack-bar .lbl { color: var(--muted); font-size: 13px; }
.stack-bar select.input { width: auto; min-width: 180px; height: 34px; padding: 0 8px; }
.stack-bar .push { margin-left: auto; }

/* Configure presets: a sidebar of names, the one being changed beside it. */
.dlg.wide { width: 840px; }
.cfgrid { display: grid; grid-template-columns: 190px 1fr; gap: 16px; margin-top: 14px; min-height: 200px; }
.cfside { display: flex; flex-direction: column; gap: 10px; border-right: 1px solid var(--line); padding-right: 16px; }
.cflist { display: flex; flex-direction: column; gap: 2px; max-height: 240px; overflow-y: auto; }
.cfitem {
  text-align: left; background: transparent; border: 0; border-radius: 6px; color: var(--muted);
  font: inherit; font-size: 13px; padding: 7px 9px; cursor: pointer; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.cfitem:hover { background: #101010; color: var(--text); }
.cfitem.on { background: #161616; color: var(--text); }
.cfmain { display: flex; flex-direction: column; gap: 12px; max-height: 64vh; overflow-y: auto; }
.cfmkts { display: flex; flex-direction: column; gap: 8px; }
/* Nothing saved yet: there is no list of names to put in a sidebar, so the box
   drops it and keeps its size -- the min-height on .cfgrid still holds it
   open -- with one line and one button in the middle. */
.cfgrid.solo { grid-template-columns: 1fr; }
.cfblank { margin: auto; display: flex; flex-direction: column; align-items: center; gap: 14px; text-align: center; }

/* One request on the admin page: one row in the list, and everything it was
   filled in with in a dialog of its own. The list is paged, so how long the
   page is does not depend on how many orders have been placed. */
.ordfunnel { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px; }
.ordfunnel table { margin-top: 8px; }

/* An order's delivered links: one closed row per funnel requested, and one
   line per market once that funnel is opened. Ten funnels is ten rows to scan
   rather than every market's buttons at once, and the link for a market is
   still only ever found under the funnel it was ordered for. */
.resgroup { margin-top: 8px; border: 1px solid var(--line); border-radius: var(--radius); }
.resgroup + .resgroup { margin-top: 6px; }
.resgroup[open] { padding-bottom: 8px; }
/* The browser's own triangle is off and one is added after the count, so the
   row reads left to right: which funnel, what was asked for, how many links. */
.ressum {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  padding: 9px 12px; cursor: pointer; font-size: 12px; color: var(--muted);
  list-style: none;
}
.ressum::-webkit-details-marker { display: none; }
.ressum:hover { color: var(--text); }
.ressum .resn { color: var(--text); font-size: 13px; font-weight: 600; }
.ressum .rescount { margin-left: auto; font-family: var(--mono); color: var(--dim); }
.ressum::after { content: "▾"; color: var(--dim); }
.resgroup[open] > .ressum { border-bottom: 1px solid var(--line); }
.resgroup[open] > .ressum::after { content: "▴"; }
.reslink { display: flex; align-items: center; gap: 8px; margin-top: 6px; padding: 0 12px; flex-wrap: wrap; }
.resmarket { min-width: 84px; color: var(--muted); font-size: 13px; }
/* The link field in the request dialog, as wide as it needs to be. */
.reqdlg .reqlink { min-width: 240px; }

/* First page, next page: the pager sits in the card's own body, under the
   last row, so it belongs to the Requests box rather than the page. */
.pager { border-top: 1px solid var(--line); display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }

/* The detail dialog: what the order says, then the markets themselves. */
.reqdlg { max-height: 88vh; overflow-y: auto; }
.reqmeta { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 18px; }
.reqmeta > div { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.reqmeta .k { color: var(--muted); font-size: 12px; flex: 0 0 76px; }
/* A funnel link, an email or a checkout URL has no space in it to wrap at, so
   it is broken wherever it runs out of room instead of pushing the box wider
   than the page, which is what a long one used to do. */
.requrl { font-size: 12px; word-break: break-all; }
.tablewrap { overflow-x: auto; }
.reqdlg .tablewrap table { min-width: 640px; }
.reqdlg td { overflow-wrap: anywhere; }
.reqjson summary { color: var(--muted); font-size: 13px; cursor: pointer; }
.reqjson pre { margin: 8px 0 0; color: var(--muted); font-size: 12px; white-space: pre-wrap; word-break: break-word; max-height: 260px; overflow-y: auto; }
.cfempty { color: var(--dim); font-size: 13px; line-height: 1.55; }

@media (max-width: 900px) {
  .dlg.wide { width: 92vw; }
  /* A phone has no room for two columns of order detail. */
  .reqmeta { grid-template-columns: 1fr; }
  /* A phone has no room for two columns, so the names stack on top. */
  .cfgrid { grid-template-columns: 1fr; min-height: 0; }
  .cfside { border-right: 0; border-bottom: 1px solid var(--line); padding: 0 0 12px; }
  .cflist { max-height: 160px; }
  /* The placeholder is the only name a field has, so on a phone every field
     takes the full width and the number moves into the left margin instead of
     squeezing the boxes until the text is cut off. */
  .stack-row { display: block; position: relative; padding-left: 30px; }
  .stack-row .n { position: absolute; left: 0; top: 11px; }
  .stack-row .input { width: 100%; }
  .stack-row .input + .input { margin-top: 8px; }
  .stack-save { display: block; margin-top: 6px; text-align: left; }
  /* A free-quantity line is one field of the offer, so its two boxes stay the
     pair they are on a phone rather than each taking a line of its own. */
  .stack-row .stack-qq .input { width: 32px; margin: 0; }
  /* The name and the choice stay on one line on a phone too, with the choice
     taking what is left of that line rather than a line of its own. */
  .stack-disc { flex-wrap: wrap; }
  .stack-disc select.input { min-width: 0; flex: 1 1 150px; }

  /* The closed-market line: the market and the arrow stay on the first line,
     the summary gets the second one, instead of squeezing the name or pushing
     the arrow onto a line of its own. */
  .stack-mkhd { display: grid; grid-template-columns: auto 1fr auto; gap: 4px 10px; }
  /* Placed by hand: the arrow comes after the summary in the markup, so left to
     the browser it would land on a line of its own. */
  .stack-mkhd .flag { grid-area: 1 / 1; }
  .stack-mkhd b { grid-area: 1 / 2; }
  .stack-mkhd .caret { grid-area: 1 / 3; }
  /* Why it is marked gets its own line rather than squeezing the name. */
  .stack-mkhd .miss { grid-area: 3 / 1 / 4 / -1; }
  .stack-mkhd .sub { grid-area: 2 / 1 / 3 / -1; margin-left: 0; width: auto; white-space: normal; }

  .stack-bar select.input { min-width: 0; width: 100%; order: 1; }
  .stack-bar .lbl { order: 0; }
  .stack-bar .push { margin-left: 0; }

  /* A closed funnel row on a phone: the name and the link count stay on the
     first line, the arrow lands at its right end, and the funnel link gets a
     line of its own because it has nowhere in it to wrap. */
  .ressum .requrl { order: 1; flex: 1 0 100%; }
  .ressum::after { order: 2; margin-left: auto; }
}

@media (max-width: 420px) {
  .stack-box { padding: 12px; }
  .stack-mkbd { padding: 12px; }
}
