/* Construction Arbitrage palette.

   The rule is about MEANING, not about what kind of thing the number is. A
   figure is not green because it is money. It is green because something went
   your way. A price you are still typing has no verdict attached to it yet, so
   it stays the same colour as the rest of the text.

   --em    the brand, and anything you can act on. Buttons, links, focus rings,
           kickers, the wordmark, a selected card. Nothing else.
   --good  something came back in your favour. A quote accepted, a job done, a
           client kept, cash you actually hold.
   --warn  caution. Nothing has gone wrong yet, but watch it.
   --clay  it went wrong. A loss, a client gone, money you will not see.
   --paper a number that is just a number.

   One colour, one meaning. If two of these ever end up the same value the
   signals stop working, which is exactly what happened when the accent moved
   to amber and the warning colour was still gold. */
:root{
  --ink:#0b0b0f; --ink2:#121218; --ink3:#1b1b23;
  --line:rgba(255,255,255,.10); --line2:rgba(255,255,255,.18);
  --paper:#f4f4f6; --muted:#9a9aa7; --dim:#8a8a97;
  --em:#f5b301; --em-soft:rgba(245,179,1,.12); --em-line:rgba(245,179,1,.34);
  --good:#2FB984; --good-soft:rgba(47,185,132,.12); --good-line:rgba(47,185,132,.34);
  --warn:#E8843C; --warn-soft:rgba(232,132,60,.12); --warn-line:rgba(232,132,60,.4);
  --clay:#E4795F; --clay-soft:rgba(228,121,95,.12);
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,Roboto,"Helvetica Neue",Arial,sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
}
*{box-sizing:border-box}
/* The page does not scroll. The game is exactly one screen and nothing lives
   underneath it any more - the write-up moved to its own page, because a player
   who flicked the wheel on a short screen used to land in the middle of an
   article. .stage keeps its own scroll for long screens, with
   overscroll-behavior:contain so reaching the end of one never drags the page. */
html,body{height:100%}
body{margin:0;background:var(--ink);color:var(--paper);font-family:var(--sans);
  font-size:16px;line-height:1.55;-webkit-font-smoothing:antialiased;
  -webkit-tap-highlight-color:transparent;overflow:hidden}
button{font:inherit}
b{font-weight:750}

/* ---------- frame ---------- */
.wrap{height:100%;height:100dvh;display:flex;flex-direction:column;max-width:780px;margin:0 auto;position:relative;
  padding:0 clamp(18px,4vw,36px)}
/* The top inset matters more here than it looks. In a browser this row sits
   below Safari's own chrome, so nothing collides. Full screen - an installed
   PWA, or a wrapped app - it is the first thing under the notch, and the day
   counter and cash figure end up behind the status bar. */
.top{display:flex;align-items:center;gap:14px;padding:calc(14px + env(safe-area-inset-top,0px)) 0 10px;flex:none}
.brand{display:flex;align-items:center;gap:9px;font-weight:800;letter-spacing:-.02em;font-size:15px}
.brand .mark{width:24px;height:24px;display:block;flex:none}
.stat{margin-left:auto;display:flex;gap:8px}
.stat > span{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted);border:1px solid var(--line);border-radius:100px;padding:6px 11px}
.stat > span b{color:var(--good);font-family:var(--sans);letter-spacing:0;font-weight:800}
.stat > span b.neg{color:var(--clay)}

/* Advanced mode is a straight game - no hints anywhere. */
body.fast .hintline{display:none}
body.fast .narr{display:none}

/* ---------- narration ---------- */
.narr{flex:none;background:linear-gradient(90deg,var(--em-soft),transparent 72%);
  border:0;border-left:3px solid var(--em);border-radius:0 8px 8px 0;padding:8px 34px 9px 12px;
  font-size:13.5px;line-height:1.45;color:var(--paper);margin-bottom:10px}
.narr::before{content:none}
.narr b{color:var(--em)}
.narr.pop{animation:pop .34s cubic-bezier(.2,.7,.3,1)}
@keyframes pop{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:none}}

/* ---------- stage ---------- */
.stage > *{flex:none}
.stage{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;
  display:flex;flex-direction:column;padding-bottom:26px;
  padding-right:14px;scrollbar-gutter:stable;
  scrollbar-width:thin;scrollbar-color:rgba(198,214,236,.2) transparent;overscroll-behavior:contain;
  opacity:0;transform:translateY(9px);transition:opacity .3s ease,transform .3s ease;
  -webkit-overflow-scrolling:touch}
.stage::-webkit-scrollbar{width:5px}
.stage::-webkit-scrollbar-track{background:transparent}
.stage::-webkit-scrollbar-thumb{background:rgba(198,214,236,.2);border-radius:100px}
.stage::-webkit-scrollbar-thumb:hover{background:rgba(198,214,236,.35)}
.stage.in{opacity:1;transform:none}
.kicker{display:block;font-family:var(--mono);font-size:10.5px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--em);margin-bottom:12px;font-weight:600}
.q{font-size:clamp(21px,3.4vw,30px);font-weight:800;letter-spacing:-.03em;line-height:1.12;margin:0 0 8px}
.q.big{font-size:clamp(28px,5vw,44px)}
.sub{color:var(--muted);font-size:15px;line-height:1.55;margin:0 0 14px;max-width:60ch}
.lab{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.15em;
  text-transform:uppercase;color:var(--muted);margin-bottom:8px;font-weight:600}
.hintline{margin:0;font-size:13px;color:var(--muted);line-height:1.45;margin-top:14px}
.hintline b{color:var(--paper)}
.lesson{margin:14px 0 0;font-size:14px;line-height:1.55;color:var(--muted);
  border-left:2px solid var(--em);padding-left:14px}
.lesson b{color:var(--paper)}

/* ---------- cards ---------- */
.cards{display:flex;flex-direction:column;gap:10px}
.card{display:block;width:100%;text-align:left;background:var(--ink2);border:1px solid var(--line);
  border-radius:14px;padding:16px 17px;color:var(--paper);cursor:pointer;
  transition:border-color .15s ease,transform .12s ease}
.card:hover{border-color:var(--em-line)}
.card:active{transform:scale(.995)}
.card .tag{display:inline-block;font-family:var(--mono);font-size:9.5px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--em);border:1px solid var(--em-line);background:var(--em-soft);
  border-radius:100px;padding:4px 9px;margin-bottom:9px}
.card b{display:block;font-size:17px;letter-spacing:-.02em;margin-bottom:4px}
.card .meta{display:block;font-size:12.5px;color:var(--dim);margin-bottom:8px}
.card .brief{display:block;font-size:13.5px;color:var(--muted);line-height:1.5}
.card .cta{display:block;margin-top:10px;font-size:13.5px;font-weight:700;color:var(--em)}
/* Boxed content inside a card must stand off the text above it. */
.card .warnline,.card .msg,.card .teach,.card .panel{margin-top:12px}
.card .warnline{margin-bottom:0;padding:12px 14px;line-height:1.5}
.card .brief + .warnline{margin-top:13px}
.card{padding:16px 16px 15px}
.card .tag{margin-bottom:11px}
.card > b{margin-bottom:6px}
.card .meta{margin-bottom:10px}
.card .cta{margin-top:14px}
.cards{gap:12px}
.card .msg{display:block;margin-top:7px;font-size:13px;color:var(--muted);line-height:1.5;
  background:var(--ink3);border-radius:9px;padding:10px 12px}
.card .pills{display:flex;gap:6px;flex-wrap:wrap;margin-top:9px}
.card .pills i{font-style:normal;font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);border:1px solid var(--line2);border-radius:100px;padding:5px 9px}

/* ---------- two roads ---------- */
.roads{display:grid;gap:11px}
.road{text-align:left;background:var(--ink2);border:1px solid var(--line);border-radius:15px;
  padding:17px 18px;cursor:pointer;color:var(--paper);transition:border-color .15s ease}
.road:hover{border-color:var(--em-line);background:linear-gradient(180deg,var(--em-soft),transparent 70%),var(--ink2)}
.road .rk{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--em);margin-bottom:7px}
.road > b{display:block;font-size:18px;letter-spacing:-.02em;margin-bottom:6px}
.road span b{display:inline;font-size:inherit;letter-spacing:inherit;margin:0;color:var(--paper)}
.road span{display:block;color:var(--muted);font-size:13.5px;line-height:1.5}

/* ---------- panels, dials ---------- */
.panel{background:var(--ink2);border:1px solid var(--line);border-radius:14px;padding:16px 18px;
  display:flex;flex-direction:column;gap:11px;margin-bottom:11px}
.panel.soft{background:transparent}
.dialrow{display:flex;align-items:baseline;gap:11px;flex-wrap:wrap}
.dialv{font-size:clamp(32px,4.6vw,46px);font-weight:800;letter-spacing:-.045em;color:var(--paper);
  line-height:1;font-variant-numeric:tabular-nums}
.dialk{font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
input[type=range]{-webkit-appearance:none;appearance:none;width:100%;height:6px;border-radius:100px;
  background:linear-gradient(90deg,var(--em) var(--fill,40%),rgba(198,214,236,.18) var(--fill,40%));
  outline:0;cursor:pointer;margin:2px 0}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:30px;height:30px;border-radius:50%;
  background:#fff;border:3px solid var(--em);box-shadow:0 4px 14px rgba(0,0,0,.5)}
input[type=range]::-moz-range-thumb{width:30px;height:30px;border-radius:50%;background:#fff;border:3px solid var(--em)}
textarea{width:100%;background:var(--ink3);border:1px solid var(--line);border-radius:11px;
  color:var(--paper);font:inherit;font-size:14px;padding:12px 13px;resize:vertical;line-height:1.5}
textarea:focus{outline:2px solid var(--em-line);border-color:transparent}
.kv{display:flex;justify-content:space-between;gap:14px;font-size:14px;color:var(--muted)}
.kv b{color:var(--paper);font-variant-numeric:tabular-nums}
.kv.hi{border-top:1px solid var(--line2);padding-top:10px;margin-top:2px}
.kv.hi b{color:var(--paper);font-size:17px}

/* ---------- buttons ---------- */
.acts{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}
.btn{border:0;cursor:pointer;font-weight:750;background:var(--em);color:#231a00;border-radius:12px;
  padding:15px 24px;font-size:15.5px;letter-spacing:-.01em;text-decoration:none;display:inline-block;
  transition:filter .12s ease,transform .1s ease}
.btn:hover{filter:brightness(1.08)}
.btn:active{transform:translateY(1px)}
.btn:disabled{opacity:.35;cursor:default}
.btn.ghost{background:transparent;color:var(--paper);border:1px solid var(--line2)}
.btn.ghost:hover{border-color:var(--em-line);color:var(--em)}
.btn.sm{padding:10px 15px;font-size:13.5px;align-self:flex-start}
.btn:focus-visible{outline:2px solid var(--em);outline-offset:3px}

/* ---------- verdicts ---------- */
.verdict{border-radius:15px;padding:18px 20px;margin-bottom:13px;border:1px solid var(--line)}
.verdict.good{border-color:var(--good-line);background:linear-gradient(180deg,var(--good-soft),transparent 70%),var(--ink2)}
.verdict.bad{border-color:rgba(228,121,95,.4);background:linear-gradient(180deg,var(--clay-soft),transparent 70%),var(--ink2)}
.verdict.warn{border-color:var(--warn-line);background:linear-gradient(180deg,var(--warn-soft),transparent 70%),var(--ink2)}
.vk{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--muted);margin-bottom:9px}
.verdict .q{margin-bottom:6px}

/* ---------- bids ---------- */
.bidlist{display:flex;flex-direction:column;gap:10px}
.bidcard{text-align:left;background:var(--ink2);border:1px solid var(--line);border-radius:14px;
  padding:15px 16px;color:var(--paper);cursor:pointer;opacity:0;transform:translateY(9px);
  transition:opacity .45s ease var(--d,0ms),transform .45s ease var(--d,0ms),border-color .15s ease}
.bidcard.in{opacity:1;transform:none}
.bidcard:hover{border-color:var(--em-line)}
.bidtop{display:flex;justify-content:space-between;gap:12px;align-items:baseline}
.bidtop b{font-size:16.5px;letter-spacing:-.02em}
.amt{font-size:19px;font-weight:800;font-variant-numeric:tabular-nums;color:var(--paper)}
.revs{display:flex;gap:6px;flex-wrap:wrap;margin:9px 0 8px}
.revs i{font-style:normal;font-family:var(--mono);font-size:9.5px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--muted);border:1px solid var(--line2);border-radius:100px;padding:5px 9px}
.bidfoot{display:flex;gap:6px;flex-wrap:wrap;font-size:11.5px;align-items:center}
.bidfoot span{border-radius:100px;padding:4px 10px;font-weight:700;line-height:1.2}
.bidfoot .ok{color:var(--good);background:var(--good-soft);border:1px solid var(--good-line)}
.bidfoot .no{color:var(--clay);background:rgba(228,121,95,.12);border:1px solid rgba(228,121,95,.38)}
.bidfoot .cis{color:var(--warn);background:var(--warn-soft);border:1px solid var(--warn-line);font-weight:700}
.bidfoot .terms{color:var(--muted);background:rgba(198,214,236,.06);border:1px solid var(--line)}
.bidfoot .note{color:var(--dim);flex-basis:100%;font-weight:400;padding:2px 0 0;border:0;background:none;border-radius:0}

/* ---------- run feed ---------- */
.feed{display:flex;flex-direction:column;gap:9px}
.ev{background:var(--ink2);border:1px solid var(--line);border-left:2px solid var(--line2);
  border-radius:11px;padding:12px 14px;display:flex;gap:12px;font-size:14px}
.ev b{flex:none;width:78px;color:var(--dim);font-family:var(--mono);font-size:10.5px;
  letter-spacing:.1em;text-transform:uppercase;padding-top:2px}
.ev span{color:var(--muted);line-height:1.45}
.ev.ok{border-left-color:var(--good)}
.ev.ok span{color:var(--paper)}
.ev.bad{border-left-color:var(--clay)}
.ev.bad span{color:var(--paper)}

/* ---------- proof ---------- */
.slots{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:12px}
.slot{border:1px dashed var(--line2);border-radius:13px;background:var(--ink2);aspect-ratio:4/3;
  display:grid;place-items:center;color:var(--dim);font-size:12.5px;font-weight:650;cursor:pointer;
  position:relative;transition:border-color .15s ease}
.slot:hover{border-color:var(--em-line);color:var(--muted)}
.slot.done{border-style:solid;border-color:transparent;color:transparent;
  background:linear-gradient(135deg,#DDE3EC,#EEF1F6 45%,#D6DDE8)}
.slot.done::after{content:"\2713";position:absolute;inset:auto auto 8px 10px;color:var(--good);
  font-size:15px;font-weight:800}

/* ---------- ledger ---------- */
.ledger{display:flex;flex-direction:column;gap:1px;background:var(--line);border:1px solid var(--line);
  border-radius:13px;overflow:hidden}
.ledger div{background:var(--ink2);padding:12px 16px;display:flex;justify-content:space-between;
  gap:14px;font-size:14.5px;color:var(--muted)}
.ledger div b{color:var(--paper);font-weight:750;font-variant-numeric:tabular-nums}
/* The label is a flex item with a long value beside it. Without these it was
   squeezed to a few pixels and its text painted across the number. */
.ledger div > span{flex:1 1 auto;min-width:0;overflow-wrap:anywhere}
.ledger div > b{flex:0 0 auto;text-align:right;white-space:nowrap}
@media (max-width:430px){
  /* Two long strings do not share one line on a phone. Stack them. */
  .ledger div{flex-wrap:wrap;gap:2px 14px}
  .ledger div > b{margin-left:auto}
}
.ledger div b.neg{color:var(--clay)}
.ledger div.hi{background:linear-gradient(90deg,var(--good-soft),transparent 70%),var(--ink2)}
.ledger div.hi b{color:var(--good)}
.ledger div.bad b{color:var(--clay)}

@media (max-width:600px){
  .q{font-size:clamp(19px,5.6vw,24px)}
  .q.big{font-size:clamp(25px,7.6vw,32px)}
  .sub{font-size:14px}
  .narr{font-size:13px;padding:9px 34px 10px 12px}
  .card{padding:14px 15px}
  .road{padding:14px 15px}
  .panel{padding:14px 15px}
  .btn{padding:14px 20px;font-size:15px;width:100%;text-align:center}
  .acts{flex-direction:column}
  .btn.sm{width:auto}
  .ev b{width:64px}
  .ledger div{padding:11px 13px;font-size:13.5px}
}
@media (min-width:601px){ .roads{grid-template-columns:1fr 1fr} }

/* bits the engine grew after the first pass */
.quitbtn{background:transparent;border:1px solid var(--line2);color:var(--muted);
  border-radius:100px;padding:6px 12px;font-family:var(--mono);font-size:10px;
  letter-spacing:.12em;text-transform:uppercase;cursor:pointer}
.quitbtn:hover{color:var(--clay);border-color:var(--clay)}
.card.row b{margin-bottom:6px}
.reporttext{margin:12px 0 0;font-size:14px;line-height:1.6;color:var(--paper);
  background:var(--ink3);border-radius:11px;padding:13px 14px}
.sh{margin:20px 0 10px;font-size:13px;font-family:var(--mono);letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted);font-weight:600}
.feed .ev b:empty{display:none}
.feed .ev b:empty + span{margin-left:0}
.slot{font-size:11.5px}
@media (max-width:600px){ .quitbtn{padding:5px 9px;font-size:9px} .stat{gap:6px} }

.quitbtn.live{color:var(--em);border-color:var(--em-line)}

/* ---------- motion ---------- */
@keyframes rise{from{opacity:0;transform:translateY(9px)}to{opacity:1;transform:none}}
.stage [data-count]{font-variant-numeric:tabular-nums}

/* a win deserves a moment */
.spark{position:fixed;inset:0;pointer-events:none;z-index:90;overflow:hidden}
.spark i{position:absolute;top:38%;width:9px;height:9px;border-radius:2px;
  background:var(--em);opacity:0;animation:pop-spark 1.5s cubic-bezier(.2,.7,.3,1) forwards}
.spark i:nth-child(3n){background:var(--warn)}
.spark i:nth-child(4n){background:#fff;width:6px;height:6px;border-radius:50%}
@keyframes pop-spark{
  0%{opacity:0;transform:translate3d(0,0,0) scale(.4) rotate(0)}
  12%{opacity:1}
  100%{opacity:0;transform:translate3d(var(--x,0),-46vh,0) scale(1) rotate(220deg)}
}
@media (prefers-reduced-motion:reduce){
  .spark{display:none}
  .stage{transition:none}
  .feed .ev,.cards .card,.ledger div{animation:none!important}
}

/* ---------- header at phone width ----------
   Four pills across 358px wrapped onto two lines and pushed the game down the
   screen. Labels shorten, the sound control becomes its glyph. */
.top{flex-wrap:nowrap}
.stat{flex-wrap:nowrap;align-items:center}
.quitbtn{white-space:nowrap;line-height:1}
@media (max-width:600px){
  .top{padding:11px 0 8px;gap:8px}
  .brand{font-size:13.5px;gap:7px}
  .brand i{width:19px;height:19px;border-radius:6px}
  .stat{gap:5px}
  .stat > span{padding:5px 8px;font-size:9px;letter-spacing:.08em}
  .quitbtn{padding:5px 8px;font-size:9px;letter-spacing:.08em}
  
}

.quitbtn .short{display:none}
@media (max-width:600px){ .quitbtn .long{display:none} .quitbtn .short{display:inline} }

/* ---------- the two roads, explained ---------- */
.explain{display:grid;gap:10px;margin-bottom:6px}
.explain>div{background:var(--ink2);border:1px solid var(--line);border-radius:13px;padding:14px 16px}
.explain b{display:block;font-size:15.5px;letter-spacing:-.02em;margin-bottom:5px;color:var(--em)}
.explain span{display:block;color:var(--muted);font-size:13.5px;line-height:1.5}
.warnline{margin:0 0 12px;font-size:13.5px;line-height:1.5;color:var(--warn);
  background:var(--warn-soft);border:1px solid rgba(232,132,60,.3);border-radius:11px;padding:11px 13px}
.warnline b{color:var(--warn)}
.tag.hot{background:var(--warn-soft);border-color:var(--warn-line);color:var(--warn);margin-left:6px}
.tag.best{background:var(--good-soft);border-color:var(--good-line);color:var(--good);font-weight:700}
.tag.plain{color:var(--muted);border-color:var(--line)}
.revs i.warn,.pills i.warn{color:var(--warn);border-color:var(--warn-line)}
.btn.big{padding:17px 26px;font-size:16.5px}

/* ---------- the diary ---------- */
.cal{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:8px}
.day{background:var(--ink2);border:1px solid var(--line);border-radius:11px;padding:11px 8px;
  color:var(--paper);cursor:pointer;text-align:center;transition:border-color .15s ease}
.day b{display:block;font-size:13.5px;font-weight:750}
.day i{display:block;font-style:normal;font-size:10px;color:var(--muted);margin-top:3px;
  font-family:var(--mono);letter-spacing:.06em;text-transform:uppercase}
.day:hover:not(:disabled){border-color:var(--em-line)}
.day.busy{opacity:.32;cursor:default}
.day.gone{opacity:.32;text-decoration:line-through;filter:saturate(.2)}
.sw.gone{background:rgba(198,214,236,.18)}
.day.late i{color:var(--warn)}
.day.late{border-color:rgba(232,132,60,.2);opacity:.62}

/* ---------- report photos ---------- */
.shots{display:grid;grid-template-columns:repeat(3,1fr);gap:9px;margin-bottom:12px}
.shots figure{margin:0;border-radius:12px;overflow:hidden;background:var(--ink3);border:1px solid var(--line)}
/* Photos fade in only once decoded - see the reveal handler in engine.js. The
   figure shimmers behind them so an empty slot reads as "loading", never as
   "broken", and a part-painted JPEG is never on screen. */
.shots img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block;
  opacity:0;transition:opacity .28s ease}
.shots img.ready,.jb-pic.ready,.jb-thumb.ready{opacity:1}
.jb-pic,.jb-thumb{opacity:0;transition:opacity .28s ease}
.shots figure,#jobbar .jb-pic,#jobbar .jb-thumb{
  background:linear-gradient(100deg,var(--ink3) 30%,rgba(198,214,236,.07) 50%,var(--ink3) 70%);
  background-size:220% 100%;animation:shimmer 1.5s linear infinite}
.shots figure:has(img.ready),#jobbar .jb-pic.ready,#jobbar .jb-thumb.ready{animation:none;background:var(--ink3)}
@keyframes shimmer{from{background-position:120% 0}to{background-position:-120% 0}}
@media (prefers-reduced-motion:reduce){
  .shots figure,#jobbar .jb-pic,#jobbar .jb-thumb{animation:none}
  .shots img,.jb-pic,.jb-thumb{transition:none}
}
.shots .ph.none{aspect-ratio:4/3;display:grid;place-items:center;color:var(--clay);
  font-size:11px;font-weight:650;background:rgba(228,121,95,.07)}
.shots figcaption{font-family:var(--mono);font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted);font-weight:700;padding:7px 9px}
.shots figure.missing{border-color:rgba(228,121,95,.35)}

@media (max-width:600px){
  .cal{grid-template-columns:repeat(3,1fr);gap:6px}
  .day{padding:9px 5px}
  .day b{font-size:12.5px}
  .day i{font-size:9px}
  .explain>div{padding:12px 13px}
  .shots{gap:6px}
}

@media (max-width:600px){ .brand .mark{width:21px;height:21px} }

/* ---------- money owed: the board's cashflow panel ---------- */
.owedpanel{margin:14px 0 4px;border:1px solid var(--em-line);border-radius:14px;
  padding:14px 16px 13px;background:linear-gradient(180deg,rgba(245,179,1,.08),transparent 70%),var(--ink2)}
.owedpanel .ow-row{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
.owedpanel .ow-k{font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted)}
.owedpanel .ow-v{font-size:clamp(21px,5vw,26px);font-weight:800;letter-spacing:-.03em;color:var(--good);font-variant-numeric:tabular-nums}
.owedpanel .ow-next{display:flex;align-items:center;justify-content:space-between;gap:10px;
  margin-top:8px;padding-top:9px;border-top:1px solid rgba(198,214,236,.1);font-size:13.5px;color:var(--paper)}
.owedpanel .ow-next b{color:var(--good);font-variant-numeric:tabular-nums}
.owedpanel .ow-in{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);border:1px solid rgba(198,214,236,.16);border-radius:100px;padding:3px 9px;white-space:nowrap}
.owedpanel .ow-in.soon{color:var(--em);border-color:var(--em-line)}
.owedpanel .ow-rest{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.owedpanel .ow-rest i{font-style:normal;font-family:var(--mono);font-size:10px;letter-spacing:.06em;
  color:var(--dim);border:1px solid rgba(198,214,236,.12);border-radius:100px;padding:3px 8px;font-variant-numeric:tabular-nums}
.owedpanel .ow-note{margin:10px 0 0;font-size:12.5px;line-height:1.55;color:var(--muted)}
.owedpanel .ow-note b{color:var(--paper)}

/* ---------- phases ---------- */
.phases{display:grid;gap:9px;margin-bottom:12px}
.phases>div{background:var(--ink2);border:1px solid var(--line);border-left:2px solid var(--em);
  border-radius:12px;padding:13px 15px}
.phases b{display:block;font-size:14.5px;letter-spacing:-.015em;margin-bottom:3px}
.phases .who{display:block;font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--em);margin-bottom:6px}
.phases span:last-child{display:block;color:var(--muted);font-size:13px;line-height:1.5}
.kv.ph span{display:flex;align-items:center;gap:9px}
.kv.ph i{font-style:normal;width:19px;height:19px;border-radius:50%;background:var(--em-soft);
  border:1px solid var(--em-line);color:var(--em);font-size:10px;font-weight:800;
  display:grid;place-items:center;flex:none}

#snd{font-size:13px;line-height:1;padding:6px 10px;color:var(--dim)}
#snd.live{color:var(--em);border-color:var(--em-line)}
@media (max-width:600px){ #snd{padding:5px 9px} }

/* ---------- a real second option, not a faint one ---------- */
.orline{display:flex;align-items:center;gap:12px;margin:18px 0 12px;color:var(--dim)}
.orline::before,.orline::after{content:"";flex:1;height:1px;background:var(--line)}
.orline span{font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase}
.card.pick{border-color:var(--em-line);background:linear-gradient(180deg,var(--em-soft),transparent 60%),var(--ink2)}
.card.pick b{color:var(--em)}
.card.pick i{font-style:italic;color:var(--paper)}
.road .cta{display:block;margin-top:10px;font-size:13.5px;font-weight:750;color:var(--em)}

/* ---- first-time teaching card ---------------------------------------- */
.teach{background:linear-gradient(90deg,var(--em-soft),transparent 40%),var(--ink2);
  border:1px solid var(--em-line);border-left:4px solid var(--em);
  border-radius:6px 14px 14px 6px;padding:15px 17px;margin:0 0 16px}
.teach .tk{display:inline-block;font-family:var(--mono);font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink);background:var(--em);border-radius:5px;
  padding:3px 8px;margin-bottom:10px;font-weight:700}
.teach p{margin:0 0 10px;font-size:14px;line-height:1.6;color:var(--paper)}
.teach p:last-child{margin-bottom:0}
.teach p b{color:#fff}
.teach .tp{color:var(--muted);font-size:13.5px;border-top:1px solid var(--line);padding-top:10px}

/* ---- the diary ------------------------------------------------------- */
.day.mine{background:var(--good-soft);border-color:var(--good-line);opacity:1}
.day.mine b{color:var(--em)}
.day.mine i{color:var(--em);opacity:.85}
.day.nofit{opacity:.28}
.callegend{display:flex;flex-wrap:wrap;gap:10px 14px;margin:13px 0 6px;
  font-size:11.5px;color:var(--muted);font-family:var(--mono);letter-spacing:.03em}
.callegend span{display:inline-flex;align-items:center;gap:6px}
.callegend .sw{width:11px;height:11px;border-radius:4px;border:1px solid var(--line2);flex:none}
.callegend .sw.free{background:var(--ink3)}
.callegend .sw.mine{background:var(--good-line);border-color:var(--good)}
.callegend .sw.busy{background:rgba(198,214,236,.1)}
.callegend .sw.late{background:rgba(232,132,60,.3);border-color:var(--warn-line)}

/* ---- the header's running position ----------------------------------- */
.afterbills b.neg,#bank.neg{color:var(--clay)}

/* ---- the closing step ------------------------------------------------ */
.nextstep{margin-top:20px;border:1px solid var(--em-line);border-radius:16px;padding:20px;
  background:linear-gradient(180deg,var(--em-soft),transparent 75%),var(--ink2)}
.nextstep h3{margin:4px 0 9px;font-size:20px;line-height:1.25;letter-spacing:-.02em}
.nextstep p{margin:0 0 15px;font-size:14px;line-height:1.6;color:var(--muted)}
.nextstep .btn{width:100%;text-align:center}

/* The way into the next month is the one click this whole screen exists to
   earn, so it sits at the top and looks like the point. The copy of it lower
   down stays for anyone who read the whole breakdown first. */
.nextstep.hero{margin:16px 0 4px;border-color:var(--em);
  box-shadow:0 0 0 1px var(--em-line),0 14px 34px -20px rgba(245,179,1,.55)}
.nextstep.hero .kicker{color:var(--em)}
.nextstep.hero .nx-note{display:block;margin-top:10px;text-align:center;
  font-size:12px;color:var(--muted)}
.nextstep.quiet{border-style:dashed;background:none;padding:16px}

/* Rank reads forwards: the title you earned, then how many people you are
   ahead of. A raw percentile told a player who had just passed month one that
   they were "top 79%", which is a true number and a demoralising sentence. */
.rankcard .rk-bar{height:6px;border-radius:100px;background:rgba(198,214,236,.12);
  margin:12px 0 2px;overflow:hidden}
.rankcard .rk-bar i{display:block;height:100%;border-radius:100px;
  background:linear-gradient(90deg,var(--em),#ffc62b);
  transition:width .9s cubic-bezier(.2,.7,.3,1)}
.rankcard .rk-title{margin:0 0 10px}
.rankcard .rk-main b{font-size:clamp(22px,5.4vw,30px);display:block}
@media (prefers-reduced-motion:reduce){.rankcard .rk-bar i{transition:none}}
.card .meta .dl{color:var(--paper);font-weight:600;font-size:inherit;display:inline;margin:0}

/* ---------- the note under a ledger label ----------
   This was an <i> sitting straight after the label with no separator, so it
   rendered "Office and yardRent, rates, electric". Two adjacent inline nodes
   with no whitespace between them will always run together - the fix is to
   stop it being inline, not to sprinkle in a space and hope. */
.ledger div span i{
  display:block;font-style:normal;margin-top:3px;
  font-size:11.5px;line-height:1.4;color:var(--dim);letter-spacing:0;
}
.ledger div span{min-width:0}

/* ---------- the way in ----------
   Autoplay is blocked until the player touches the screen, so a game that
   narrates itself has to ask first. It is also the right first impression:
   one card, one decision, and the thing starts talking. */
/* The body does not scroll, so this must. On a short screen, in landscape, or
   at a large accessibility text size the Play button would otherwise be pushed
   off the bottom with no way to reach it. */
#gate{position:fixed;inset:0;z-index:200;overflow-y:auto;background:radial-gradient(900px 600px at 50% -10%,rgba(245,179,1,.16),transparent 60%),var(--ink);
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:26px;text-align:center;animation:gatein .5s ease both;
  padding:calc(32px + env(safe-area-inset-top,0px)) 24px calc(32px + env(safe-area-inset-bottom,0px))}
#gate.out{animation:gateout .42s ease both;pointer-events:none}
@keyframes gatein{from{opacity:0}to{opacity:1}}
@keyframes gateout{to{opacity:0;transform:scale(1.02)}}
#gate .mark{display:flex;align-items:center;gap:11px;font-weight:800;letter-spacing:-.025em;font-size:19px}
#gate h1{margin:0;font-size:clamp(30px,8vw,54px);font-weight:800;letter-spacing:-.04em;line-height:1.04;max-width:15ch}
#gate h1 em{font-style:normal;color:var(--em)}
#gate p{margin:0;color:var(--muted);font-size:clamp(15px,4vw,18px);line-height:1.55;max-width:38ch}
#gate .gacts{display:flex;flex-direction:column;gap:11px;width:100%;max-width:340px;margin-top:4px}
#gate .btn{width:100%}
#gate .soundnote{font-family:var(--mono);font-size:10.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--dim)}
.eq{display:inline-flex;align-items:flex-end;gap:3px;height:15px;margin-right:9px;vertical-align:-2px}
.eq b{width:3px;background:#231a00;border-radius:2px;animation:eq .9s ease-in-out infinite}
.eq b:nth-child(1){height:6px;animation-delay:0s}
.eq b:nth-child(2){height:13px;animation-delay:.15s}
.eq b:nth-child(3){height:9px;animation-delay:.3s}
@keyframes eq{0%,100%{transform:scaleY(.5)}50%{transform:scaleY(1)}}
@media (prefers-reduced-motion:reduce){.eq b{animation:none}#gate,#gate.out{animation:none}}

/* A negative figure was breaking between its minus and its number, so the
   header read "After bills -" on one line and "£3,050" on the next. Any money
   figure is one word as far as wrapping is concerned. */
.stat b,.kv b,.ledger div b,.amt,.dialv,[data-count]{white-space:nowrap}

/* ---------- the job, always one tap away ----------
   Six screens after you priced it you are looking at four quotes and you have
   forgotten what the job even was. This keeps it there, folded away, and it
   holds its own open/closed state because the shell never re-renders it. */
#jobbar{flex:none;margin-bottom:12px;border:1px solid var(--line);border-radius:13px;
  background:var(--ink2);overflow:hidden}
#jobbar .jb-head{display:flex;align-items:center;gap:10px;width:100%;background:none;border:0;
  color:var(--paper);padding:11px 13px;cursor:pointer;text-align:left;font:inherit}
#jobbar .jb-thumb{width:38px;height:38px;border-radius:9px;object-fit:cover;flex:none;
  background:var(--ink3)}
#jobbar .jb-t{flex:1;min-width:0}
#jobbar .jb-t > b{display:block;font-size:13.5px;font-weight:700;letter-spacing:-.015em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#jobbar .jb-t > span{display:block;font-size:11.5px;color:var(--muted);margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#jobbar .jb-chev{flex:none;color:var(--muted);font-size:12px;transition:transform .25s ease}
#jobbar.open .jb-chev{transform:rotate(180deg)}
#jobbar .jb-body{display:none;padding:0 13px 13px;border-top:1px solid var(--line)}
#jobbar.open .jb-body{display:block;animation:rise .3s ease both}
#jobbar .jb-body p{margin:11px 0 0;font-size:13.5px;line-height:1.55;color:var(--muted)}
#jobbar .jb-facts{display:flex;flex-wrap:wrap;gap:6px;margin-top:11px}
#jobbar .jb-facts i{font-style:normal;font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);border:1px solid var(--line2);border-radius:100px;padding:5px 9px}
#jobbar .jb-pic{width:100%;border-radius:10px;margin-top:11px;display:block;aspect-ratio:4/3;object-fit:cover}
@media (max-width:600px){
  /* Collapsed, the drawer is one slim line: no photo, small type, small
     chevron. The photo and the brief live inside, where they were anyway. */
  #jobbar{margin-bottom:9px}
  #jobbar .jb-head{padding:6px 11px;gap:8px}
  #jobbar .jb-thumb{display:none}
  #jobbar.open .jb-thumb{display:block;width:32px;height:32px}
  #jobbar .jb-t > b{font-size:12px}
  #jobbar .jb-t > span{font-size:10px;margin-top:1px}
  #jobbar .jb-chev{font-size:9px}
  #jobbar .jb-t b{font-size:12.5px}
}

/* ---- the two bags -------------------------------------------------------
   Kept visually apart from the profit ledger on purpose. The whole lesson is
   that this money is NOT the same kind of money, so it must never read as
   another row of the same table. */
/* .stat span b sets the green, and it outranks a bare .bagchip b. */
.stat > span.bagchip{color:var(--warn);border-color:var(--warn-line)}
.stat > span.bagchip b{color:var(--warn)}
/* ---------- the month's headline number ----------
   One figure, big enough that nobody has to hunt the ledger for it, with the
   split stated underneath in words. --good because the month went your way;
   --clay when it did not. Never --em: this is a verdict, not a button. */
.made{margin:0 0 14px;padding:18px 20px;border-radius:15px;border:1px solid var(--good-line);
  background:linear-gradient(180deg,var(--good-soft),transparent 72%),var(--ink2)}
.made.neg{border-color:rgba(228,121,95,.4);
  background:linear-gradient(180deg,var(--clay-soft),transparent 72%),var(--ink2)}
.made .mk{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--good);margin-bottom:4px}
.made.neg .mk{color:var(--clay)}
.made .mnum{display:block;font-size:clamp(40px,10vw,66px);font-weight:800;letter-spacing:-.045em;
  line-height:1.02;color:var(--good);font-variant-numeric:tabular-nums;margin:2px 0 6px}
.made.neg .mnum{color:var(--clay)}
.made .msub{display:block;font-size:14px;line-height:1.5;color:var(--muted)}
.made .mnote{margin:12px 0 0;padding-top:12px;border-top:1px solid var(--line);
  font-size:14px;line-height:1.55;color:#98A4B7}
.made .mnote b{color:var(--paper)}

.bags{margin:16px 0 16px;border:1px dashed var(--warn-line);border-radius:14px;
  padding:13px 15px;background:rgba(232,132,60,.06)}
.bags .bk{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:10px;letter-spacing:.15em;text-transform:uppercase;color:var(--warn);margin-bottom:9px}
.bags div{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;
  padding:7px 0;border-top:1px solid rgba(232,132,60,.16);font-size:14px}
.bags div:first-of-type{border-top:0}
.bags div span{color:#98A4B7}
.bags div span i{display:block;font-style:normal;font-size:12.5px;color:#7C8798;margin-top:3px}
.bags div b{white-space:nowrap;font-variant-numeric:tabular-nums;color:var(--paper)}
.bags .hi span,.bags .hi b{color:var(--warn);font-weight:800}
.bagline{margin:11px 0 0;padding-top:11px;border-top:1px solid rgba(232,132,60,.16);
  font-size:13.5px;line-height:1.5;color:#98A4B7}
.bagline b{color:var(--paper)}
/* The same panel as the tax bags, in the calm colour. Money on its way to you
   is not a warning - the whole point of the screen is that nothing is wrong. */
.bags.due{border-color:var(--good-line);background:rgba(47,185,132,.05)}
.bags.due .bk{color:var(--good)}
.bags.due div{border-top-color:rgba(47,185,132,.16)}
.bags.due .hi span,.bags.due .hi b{color:var(--good)}
.bags.due .bagline{border-top-color:rgba(47,185,132,.16)}



/* Four counters plus two buttons will not fit a 390px phone. The wordmark is
   the cheapest thing to lose - the mark still brands it - and the two longest
   labels get a short form, same trick the End day button already uses. */
.stat > span .short{display:none}
@media (max-width:600px){
  .brand .bname{display:none}
  .stat span .long{display:none}
  .stat span .short{display:inline}
}
/* Below ~420px the four counters plus both buttons cannot sit on one line.
   Wrapping costs a little height and loses nothing, which beats dropping a
   number the player is supposed to be watching. */
@media (max-width:420px){
  .top{align-items:flex-start}
  .stat{flex-wrap:wrap;justify-content:flex-end;row-gap:5px}
}
/* Bad news must not be bolded in the colour used for getting paid. */
.narr.bad{background:linear-gradient(90deg,rgba(228,121,95,.1),transparent 72%);
  border-left-color:var(--clay)}
.narr.bad::before{color:var(--clay)}
.narr.bad b{color:var(--clay)}
/* The job card leads with the work rather than with the mechanics, so the
   photographs sit in the flow of it rather than after the fact. */
.jobshots{margin:14px 0 18px}
.daychip b{color:var(--paper)}
.stat span.daychip{border-color:rgba(198,214,236,.2)}

/* Jobs that died with a lost client stay on the board as a scar. */
.deadjob{opacity:.5;filter:grayscale(.7);pointer-events:none;border-style:dashed;
  border-color:rgba(228,121,95,.35)}
.tag.lost{background:rgba(228,121,95,.14);border:1px solid rgba(228,121,95,.4);color:var(--clay)}

/* ---- the clock ---------------------------------------------------------
   Sticky at the bottom, running only on the job board. The thin track on
   top fills over three seconds - one working day - so the pace is visible
   without a single number changing. */
#tickbar{position:fixed;left:0;right:0;bottom:0;z-index:40;
  background:rgba(11,17,26,.92);backdrop-filter:blur(10px);
  border-top:1px solid var(--line)}
#tickbar .tb-track{height:2px;background:rgba(198,214,236,.08)}
#tickbar .tb-track i{display:block;height:100%;background:var(--em);opacity:.7}
#tickbar .tb-row{display:flex;gap:7px;row-gap:5px;flex-wrap:wrap;align-items:center;
  padding:8px 14px calc(8px + env(safe-area-inset-bottom,0px));
  overflow-x:auto;scrollbar-width:none;max-width:780px;margin:0 auto;
  box-sizing:border-box;width:100%}
#tickbar .tb{flex:none;font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);border:1px solid var(--line);
  border-radius:100px;padding:5px 10px;white-space:nowrap;line-height:1.4}
#tickbar .tb b{color:var(--paper);font-family:var(--sans);letter-spacing:0}
#tickbar .tb.tday b{color:var(--em)}
#tickbar .tb.amber{color:var(--warn);border-color:var(--warn-line)}
#tickbar .tb.amber b{color:var(--warn)}
#tickbar .tb.good{color:var(--good);border-color:var(--good-line)}
#toast{position:fixed;left:50%;bottom:56px;transform:translate(-50%,12px);z-index:41;
  background:var(--ink2);border:1px solid var(--line);border-radius:100px;
  padding:9px 16px;font-size:13px;color:var(--paper);opacity:0;pointer-events:none;
  transition:all .3s cubic-bezier(.2,.7,.3,1);white-space:nowrap;max-width:92vw;
  overflow:hidden;text-overflow:ellipsis}
#toast.show{opacity:1;transform:translate(-50%,0)}
#toast.good{border-color:var(--good-line);color:var(--good)}
#toast.bad{border-color:rgba(228,121,95,.35);color:var(--clay)}
.wrap{padding-bottom:calc(72px + env(safe-area-inset-bottom,0px))}

/* ---- GAME OVER ----------------------------------------------------------
   The busted moment. A full-screen slam that cannot be mistaken for another
   screen of the game, then it hands over to the verdict. */
#goslam{position:fixed;inset:0;z-index:90;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:14px;
  background:radial-gradient(120% 120% at 50% 45%, rgba(120,20,14,.55), rgba(5,7,11,.97) 70%);
  opacity:0;transition:opacity .25s ease}
#goslam.in{opacity:1}
#goslam.out{opacity:0;transition:opacity .7s ease}
#goslam .go-stamp{font-weight:900;font-size:clamp(64px,20vw,150px);line-height:.9;
  letter-spacing:-.05em;text-align:center;color:var(--clay);
  text-shadow:0 0 60px rgba(228,121,95,.5);
  border:6px solid var(--clay);border-radius:18px;padding:18px 30px;
  transform:scale(2.4) rotate(-14deg);opacity:0;
  transition:transform .32s cubic-bezier(.2,1.4,.3,1), opacity .18s ease}
#goslam.in .go-stamp{transform:scale(1) rotate(-7deg);opacity:1}
#goslam .go-sub{font-family:var(--mono);font-size:13px;letter-spacing:.3em;
  text-transform:uppercase;color:#C7D0DE;opacity:0;transition:opacity .4s ease .5s}
#goslam.in .go-sub{opacity:1}
#goslam.hold .go-stamp{animation:goshake .5s ease 1}
@keyframes goshake{0%,100%{transform:scale(1) rotate(-7deg)}
  25%{transform:scale(1.02) rotate(-6.2deg)}75%{transform:scale(.99) rotate(-7.6deg)}}
@media (prefers-reduced-motion:reduce){#goslam .go-stamp{transition:opacity .2s ease;transform:scale(1) rotate(-7deg)}}

/* The intro overheads table sat flush against the line under it. */
#stage .ledger{margin:16px 0 18px}

/* ---- the counters -------------------------------------------------------
   Four counters, two buttons and a wordmark do not fit one line on a phone,
   and shortening the labels to "Bills" reads as broken rather than as tidy.
   The buttons keep line one, the counters get a full line of their own with
   their real names back. */
.topbtns{display:flex;align-items:center;gap:6px;margin-left:auto;flex:none}
@media (max-width:640px){
  .top{flex-wrap:wrap;align-items:center;row-gap:9px}
  .brand{order:1}
  .topbtns{order:2;margin-left:auto}
  .stat{order:3;width:100%;margin-left:0;justify-content:space-between;gap:5px;
    flex-wrap:nowrap;align-items:stretch}
  /* Equal height cards rather than pills: "After bills" wraps to two lines and
     a two-line pill pushed its own value out through the bottom border. */
  .stat > span{flex:1 1 0;min-width:0;text-align:center;padding:4px 6px;font-size:8.5px;
    display:flex;flex-direction:row;align-items:baseline;justify-content:center;gap:6px;
    letter-spacing:.05em;border-radius:100px;min-height:0;line-height:1.15}
  .stat > span b{font-size:12.5px;line-height:1}
  .brand .bname{display:inline}      /* the wordmark fits again */
  .stat span .long{display:inline}
  .stat > span .short{display:none}
}
@media (min-width:641px){
  .stat{margin-left:auto}
}

/* The clock readout sweeps 08:00 to 18:00 across each three-second day, and
   the HMRC chip starts pulsing when the bill is four days out. */
#tickbar .tclock{font-variant-numeric:tabular-nums;min-width:44px;display:inline-block}
#tickbar .tb.amber.urgent{animation:tbpulse 1s ease infinite}
@keyframes tbpulse{0%,100%{border-color:var(--warn-line)}50%{border-color:var(--warn);color:#f0a86a}}
@media (prefers-reduced-motion:reduce){#tickbar .tb.amber.urgent{animation:none}}

/* Breathing room in the chrome: chips were touching, and on desktop the row
   ran to the edge and clipped. Wrap with real gaps - desktop only, the phone
   row has its own layout. */
@media (min-width:641px){
  .top{flex-wrap:wrap;row-gap:10px;padding-right:4px}
  .stat{flex-wrap:wrap;row-gap:8px;gap:8px;justify-content:flex-end}
}

/* ---- client lives -------------------------------------------------------
   Three dots per client. Full = safe, one = last chance, empty = gone. */
.lives{display:inline-flex;gap:2px;vertical-align:baseline;margin:0 2px}
.lives i{font-style:normal;font-size:10px;line-height:1;color:rgba(198,214,236,.3)}
.lives i.on{color:var(--good)}
.lives.lastlife i.on{color:var(--clay);animation:lifepulse 1.2s ease infinite}
@keyframes lifepulse{0%,100%{opacity:1}50%{opacity:.45}}
@media (prefers-reduced-motion:reduce){.lives.lastlife i.on{animation:none}}

/* The narration banner can be put away on a phone. */
.narr{position:relative;padding-right:38px;transition:opacity .5s ease,margin .4s ease,
  max-height .4s ease,padding .4s ease;max-height:400px;overflow:hidden}
.narr.dismissed{opacity:0;max-height:0;padding-top:0;padding-bottom:0;margin-bottom:0;
  border-width:0;pointer-events:none}
.narr.faded{opacity:0;max-height:0;padding-top:0;padding-bottom:0;margin-bottom:0;border-width:0;pointer-events:none}
.narr-x{position:absolute;top:8px;right:6px;width:26px;height:26px;border-radius:50%;
  border:0;background:transparent;color:var(--dim);font-size:16px;
  line-height:1;cursor:pointer;display:grid;place-items:center}
.narr-x:hover{color:var(--paper);border-color:rgba(198,214,236,.35)}

/* The countdown row centres its chips on a phone. */
@media (max-width:640px){
  #tickbar .tb-row{justify-content:center}
}
/* Month-one training: everything below the first job waits its turn. */
.lockedcard{opacity:.45;filter:saturate(.4)}
.lockedcard .cta{color:var(--dim)}

/* Tap a photo, see the photo. Tap anywhere, it goes away. */
#lightbox{position:fixed;inset:0;z-index:95;background:rgba(5,8,12,.93);
  display:grid;place-items:center;cursor:zoom-out;
  padding:calc(14px + env(safe-area-inset-top,0px)) 14px calc(14px + env(safe-area-inset-bottom,0px))}
#lightbox img{max-width:100%;max-height:92vh;border-radius:12px;
  box-shadow:0 30px 80px rgba(0,0,0,.7)}
.shots img,.jb-pic{cursor:zoom-in}

/* The expanded job drawer must never swallow the screen - that was the
   all-black stage on a phone: the drawer took the height and the empty
   stage painted ink behind it.

   But this was written for a phone and left unscoped, so on a desktop the
   photograph was being cropped to a 170px letterbox and most of the job was
   simply not there. The cap belongs on the BODY, which scrolls, never on the
   picture, which cannot. */
#jobbar .jb-body{max-height:42vh;overflow-y:auto}
/* Whole picture, never a slice of one, at every width. contain guarantees it
   even if an asset ever ships at something other than four by three. */
#jobbar .jb-pic{width:100%;max-height:none;aspect-ratio:4/3;object-fit:contain;
  background:var(--ink3);border-radius:10px}
@media (min-width:641px){ #jobbar .jb-body{max-height:52vh} }

/* ---- the watched game --------------------------------------------------- */

/* ---- the flickering end -------------------------------------------------- */
.go-stamp.flicker{animation:goflicker 1.6s steps(1) infinite}
@keyframes goflicker{
  0%,100%{opacity:1}8%{opacity:.35}10%{opacity:1}34%{opacity:1}
  36%{opacity:.2}38%{opacity:1}39%{opacity:.6}41%{opacity:1}
  72%{opacity:1}74%{opacity:.4}76%{opacity:1}}
@media (prefers-reduced-motion:reduce){.go-stamp.flicker{animation:none}}

/* ---- the footer back to a slim slide ------------------------------------ */
#tickbar .tb-row{padding:5px 12px calc(5px + env(safe-area-inset-bottom,0px));gap:6px}
#tickbar .tb{font-size:8.5px;padding:4px 9px}
#tickbar .tb-track{height:2px}
.wrap{padding-bottom:calc(46px + env(safe-area-inset-bottom,0px))}

/* ---- report photos swipe on a phone -------------------------------------- */
@media (max-width:640px){
  .shots{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;gap:10px;
    -webkit-overflow-scrolling:touch;scrollbar-width:none;padding:0 12% 4px;scroll-padding:0 12%}
  .shots figure{flex:0 0 76%;scroll-snap-align:center;margin:0}
  .shots figure img,.shots .ph{width:100%}
}

/* ---- the rank card ------------------------------------------------------ */
.rankcard{margin:16px 0;border:1px solid var(--em-line);border-radius:14px;padding:16px 18px;
  background:linear-gradient(135deg,rgba(245,179,1,.1),transparent 60%),var(--ink2);text-align:center}
.rankcard .rk-kick{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--muted);margin-bottom:8px}
.rankcard .rk-main b{font-size:clamp(30px,8vw,44px);font-weight:800;letter-spacing:-.04em;color:var(--em)}
.rankcard .rk-main i{display:block;font-style:normal;font-size:13px;color:var(--muted);margin-top:2px}
.rankcard .rk-title{display:inline-block;margin-top:10px;font-family:var(--mono);font-size:11px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--em);border:1px solid var(--em-line);
  border-radius:100px;padding:5px 13px}
.rankcard .rk-note{margin:10px 0 0;font-size:13px;color:var(--muted)}
.rankcard .rk-note b{color:var(--em)}
.optcards{display:grid;gap:12px;margin:14px 0 4px}
.optcard{display:grid;gap:7px;padding:15px 16px;border:1px solid rgba(198,214,236,.16);
  border-radius:14px;background:rgba(198,214,236,.04);cursor:pointer;text-align:left;
  transition:border-color .15s,background .15s}
.optcard:hover,.optcard:focus-visible{border-color:rgba(198,214,236,.38);background:rgba(198,214,236,.07);outline:none}
.optcard:active{transform:scale(.99)}
.optcard b{font-size:15.5px}
.optcard span{font-size:13px;line-height:1.5;color:var(--muted)}
.optcard em{font-style:normal;font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);opacity:.65}

/* ---- declining a quote: bin + swipe ---- */
.bidcard{position:relative;touch-action:pan-y}
.bidcard .bin{position:absolute;right:9px;bottom:9px;width:26px;height:26px;border-radius:8px;
  display:grid;place-items:center;color:var(--dim);opacity:.55;cursor:pointer}
.bidcard .bin:hover{opacity:1;color:var(--clay)}
.bidcard.binned{transition:transform .3s ease,opacity .3s ease,max-height .32s ease .06s,margin .32s ease .06s,padding .32s ease .06s;
  transform:translateX(-112%);opacity:0;max-height:0;margin:0;padding-top:0;padding-bottom:0;overflow:hidden;border-width:0}
/* ---- the best contractor in London does not need a label ---- */
.bidcard.gold{border-color:rgba(232,132,60,.36)}
.bidcard.gold::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:linear-gradient(115deg,transparent 42%,rgba(232,132,60,.09) 50%,transparent 58%);
  background-size:260% 100%;animation:goldsheen 3.4s ease-in-out infinite}
@keyframes goldsheen{0%{background-position:120% 0}55%{background-position:-60% 0}100%{background-position:-60% 0}}
/* ---- the run verdict lands after the feed has told the story ---- */
.lateReveal{animation:vslide .5s ease 1.05s both}
@keyframes vslide{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion: reduce){.bidcard.gold::after{animation:none}.lateReveal{animation-delay:0s}}
.bidfoot .mats{color:var(--warn);border-color:rgba(232,132,60,.3)}

/* The gate lockup: the old rules were written for a <div class="mark"><i>
   markup that no longer exists, so the inline SVG had no size at all. */
#gate .gmark{display:flex;align-items:center;justify-content:center;gap:11px;
  font-weight:800;font-size:19px;letter-spacing:-.02em;margin-bottom:18px}
#gate .gmark .mark{width:28px;height:28px;flex:none}
/* A job you cannot fund yet reads as such, instead of looking live and
   stopping the player three screens later. */
.card.skintcard{opacity:.62;border-style:dashed}
.card.skintcard .cta{color:var(--warn)}

/* ---- the action bar follows you ----------------------------------------
   Long screens put their buttons a thousand pixels down an inner scroller,
   and nothing on a phone said so: the player saw a wall of dark text with
   no way forward and read it as a frozen screen. The actions now sit on
   the bottom edge of the stage wherever you are in it. */
.acts{position:sticky;bottom:-26px;z-index:6;
  margin-left:-6px;margin-right:-6px;
  padding:10px 6px calc(8px + env(safe-area-inset-bottom,0px));
  /* Solid under the buttons, fading only in the last few pixels, so text
     scrolling past never reads as sitting on top of the call to action. */
  background:linear-gradient(to top,var(--ink) 0,var(--ink) 74%,rgba(11,15,20,.92) 88%,rgba(11,15,20,0) 100%);
  backdrop-filter:blur(3px)}
/* Anything that explains the buttons has to sit above them, never under. */
.acts + .hintline,.acts + .lesson{margin-top:10px}
#stage .acts:has(~ .acts){position:static;background:none;backdrop-filter:none;padding-bottom:4px}
#stage .acts{margin-top:auto}
#stage .acts ~ *{flex:none}
.stage{padding-bottom:8px}
@media (min-width:641px){.stage{padding-bottom:10px}}
/* On a short screen the sticky action bar plus the fixed countdown bar eat a
   fifth of the viewport, and on 320x568 they physically covered the price and
   profit-share sliders - two testers found both controls dead, not merely
   cramped. Below this height the buttons sit in the flow where they cannot
   cover anything, and the scroll cue already tells the player they are there. */
@media (max-height:640px){
  .top{padding:7px 0 5px}
  .narr{font-size:12.5px;line-height:1.4;padding:7px 30px 8px 11px;margin-bottom:8px}
  .stat > span{padding:3px 6px}
  .acts{position:static;background:none;backdrop-filter:none;padding:12px 6px 4px}
  .stage{padding-bottom:26px}
}
/* A range input on a touch screen needs a real target, not a 4px line. */
#stage input[type=range]{box-sizing:content-box;padding:14px 0;background-clip:content-box}
#stage input[type=range]:focus-visible{outline:3px solid var(--em);outline-offset:4px;border-radius:6px}
.bin:focus-visible,[role=button]:focus-visible,button:focus-visible,a:focus-visible{
  outline:3px solid var(--em);outline-offset:2px;border-radius:8px}
.lives .livesnum{font-style:normal;font-family:var(--mono);font-size:9.5px;
  letter-spacing:.06em;color:var(--muted);margin-right:4px;vertical-align:1px}
#stage .panel:has(input[type=range]){scroll-margin-bottom:130px}
@media (max-width:640px){
  .acts{padding-top:10px}
  .acts .btn{flex:1 1 auto;min-height:46px}
}

/* ---- "there is more below" ---------------------------------------------
   Shown only when the first thing you can actually press starts below the
   fold. Tapping it takes you there; scrolling dismisses it. */
#scrollcue{position:absolute;left:50%;transform:translateX(-50%);bottom:64px;z-index:60;
  display:inline-flex;align-items:center;gap:7px;
  border:1px solid var(--em-line);border-radius:999px;
  background:var(--ink2);color:var(--em);
  font:inherit;font-size:12.5px;font-weight:700;letter-spacing:.01em;
  padding:9px 15px;cursor:pointer;box-shadow:0 6px 20px rgba(0,0,0,.45);
  animation:cuein .32s ease both, cuebob 2.4s ease-in-out 1.2s infinite}
#scrollcue:hover{color:var(--paper)}
@keyframes cuein{from{opacity:0;transform:translate(-50%,8px)}to{opacity:1;transform:translate(-50%,0)}}
@keyframes cuebob{0%,100%{transform:translate(-50%,0)}50%{transform:translate(-50%,4px)}}
@media (prefers-reduced-motion:reduce){#scrollcue{animation:cuein .2s ease both}}

/* The brand wordmark: "Construction" in paper, "Arbitrage" in amber, set solid
   the way the site's own mark sets it. */
.wm-a{color:var(--em)}
/* The one way out of the gate that is not "Play". It goes to the write-up,
   which is a real page a person can read - the prose used to sit under the
   game itself and players fell into it mid-run. */
.gwhat{margin-top:2px;font-size:12.5px;color:var(--dim);text-decoration:none;border-bottom:1px solid transparent}
.gwhat:hover{color:var(--paper);border-bottom-color:var(--line2)}
