/* ==================================================================
   base.css — design tokens, reset, app frame, HUD.
   One warm paper stock, neumorphic light, ink letterpress. Numbers in
   Orbit. Soft dual shadows everywhere: light from the top-left, gentle
   ambient depth, pressed = the surface sinks into the stock.
   ================================================================== */
@font-face{
  font-family:"Orbit";
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url("../fonts/orbit-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
/* digits-only slice of the same file: rides first in the text stacks so
   every number in the game renders in Orbit while letters fall through */
@font-face{
  font-family:"Orbit Num";
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url("../fonts/orbit-latin.woff2") format("woff2");
  unicode-range:U+0030-0039,U+002B,U+0025,U+00D7;
}
:root{
  --paper:#E4DFD1; --stock:#F1EDE1; --raise:#EBE6D8;
  --ink:#15140E; --ink2:#46422F; --ink3:#8A8471; --line:#C6BFAB;
  --sh:#B9B2A0; --shs:rgba(160,150,126,.5); --hl:#FFFDF4;
  --grad:linear-gradient(145deg,#F4F0E4,#E6E1D2);      /* raised surface */
  --gradi:linear-gradient(145deg,#E0DBCB,#EFEADD);     /* sunken surface */
  --red:#8E2B1C; --grn:#2C563C; --gld:#8A6A2F; --blu:#2A4761;
  --zap:#EFC93F;                                        /* lightning: the cull ward's countdown */
  --cw:54px; --ch:76px;
  --nm:"Orbit Num",ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  /* mixed letter+digit tokens (chips, tags, the cloud key): one face for
     the whole token — Orbit digits beside mono letters read as two weights */
  --mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  --disp:"Orbit","Avenir Next","Segoe UI",system-ui,sans-serif;
  --ui:"Orbit Num",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,sans-serif;
  --sp:cubic-bezier(.2,1.02,.32,1.04);
}
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
/* form controls ignore inherited fonts unless told: without this, buttons
   render digits in the UA font and dodge the Orbit-numbers rule */
button,input,select,textarea{font-family:inherit}
html,body{height:100%;overflow:hidden;overscroll-behavior:none}
body{background:var(--paper);color:var(--ink);font-family:var(--ui);font-size:14px;
  user-select:none;-webkit-user-select:none;letter-spacing:-.005em}
body::after{content:"";position:fixed;inset:0;pointer-events:none;z-index:999;opacity:.5;
  mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='.34'/%3E%3C/svg%3E")}
/* hidden until boot's first layout is in (main.js adds .rdy): the raw
   HTML would otherwise paint empty HUD/felt for a frame and flash */
#app{display:flex;flex-direction:column;height:100dvh;max-width:520px;margin:0 auto;position:relative;overflow:hidden;
  padding-bottom:var(--bibH,0px);visibility:hidden}
#app.rdy{visibility:visible}
#app.shake{animation:shk .4s cubic-bezier(.36,.07,.19,.97)}
@keyframes shk{10%,90%{transform:translate(-2px,1px)}20%,80%{transform:translate(4px,-2px)}
30%,50%,70%{transform:translate(-7px,2px)}40%,60%{transform:translate(7px,-1px)}}


/* ---------- hud: the counters sit on raised plaques ---------- */
#hud{display:flex;align-items:center;gap:8px;padding:max(10px,env(safe-area-inset-top)) 12px 9px;flex:none}
.mny{line-height:1.1;border-radius:10px;padding:6px 12px 7px;background:var(--grad);
  box-shadow:3px 3px 8px var(--shs),-3px -3px 8px var(--hl)}
.mny .l{font-size:8px;letter-spacing:.22em;color:var(--ink3);font-weight:700}
.mny .v{font-family:var(--disp);font-size:19px;font-weight:800;letter-spacing:-.01em;color:var(--ink)}
.mny.s .v{color:var(--gld)}
/* one soft gold ring when a bank lands — a breath, not a fanfare. The
   plaque's lean-in rides its own track so it LANDS with the digits (the
   ticker's big sweep is 550ms): the ring snaps on the pay, the body
   springs past its lean as the roll arrives and settles under it */
.mny.pulse{animation:mp .7s ease,mpt .8s cubic-bezier(.34,1.56,.64,1)}
@keyframes mp{30%{box-shadow:3px 3px 8px var(--shs),-3px -3px 8px var(--hl),0 0 13px rgba(138,106,47,.42)}}
@keyframes mpt{68%{transform:scale(1.055)}}
#hud .gap{flex:1}
.ic{width:44px;height:44px;border-radius:11px;display:grid;place-items:center;color:var(--ink2);
  border:0;background:var(--grad);cursor:pointer;transition:box-shadow .12s;
  box-shadow:3px 3px 8px var(--shs),-3px -3px 8px var(--hl)}
.ic svg{width:21px;height:21px;display:block;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.ic svg .fl{fill:currentColor;stroke:none}
/* inline UI icons (js/icons.js): sized by the surrounding text, inked by it */
.gi{width:1em;height:1em;display:inline-block;vertical-align:-.125em;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.gi .fl{fill:currentColor;stroke:none}
.ic:active{background:var(--gradi);box-shadow:inset 3px 3px 6px rgba(160,150,126,.45),inset -2px -2px 5px var(--hl)}

/* ---------- scrollbars: one slim ink rail, floating on the stock.
   The transparent border keeps the thumb thin and off the edge;
   it darkens only when grabbed ---------- */
.view,#sh{scrollbar-width:thin;scrollbar-color:rgba(70,66,47,.28) transparent}
.view::-webkit-scrollbar,#sh::-webkit-scrollbar{width:11px}
.view::-webkit-scrollbar-track,#sh::-webkit-scrollbar-track{background:transparent}
.view::-webkit-scrollbar-thumb,#sh::-webkit-scrollbar-thumb{border-radius:7px;
  border:3px solid transparent;background-clip:padding-box;background-color:rgba(70,66,47,.26)}
.view::-webkit-scrollbar-thumb:hover,#sh::-webkit-scrollbar-thumb:hover{background-color:rgba(70,66,47,.44)}
.view::-webkit-scrollbar-thumb:active,#sh::-webkit-scrollbar-thumb:active{background-color:rgba(70,66,47,.6)}
.view::-webkit-scrollbar-corner,#sh::-webkit-scrollbar-corner{background:transparent}

@media (prefers-reduced-motion:reduce){*{animation-duration:.01ms!important;transition-duration:.06s!important}}

/* hidden tab: main.js stamps .hid on <html> and the whole animation
   floor freezes — no engine keeps a keyframe clock warm under a page
   that renders nothing. Mid-flight one-shots resume and finish on the
   return; transitions are time-based and need no help */
.hid *,.hid *::before,.hid *::after{animation-play-state:paused!important}

/* ---------- the title screen: SQUEEZER over the whole app on every
   load, again after an idle minute, gone at its click. The box is
   pointer-through everywhere: dismissal rides a document-capture tap
   (fx.js), so the click that hides it still plays ---------- */
#ttl{position:absolute;inset:0;z-index:940;display:none;flex-direction:column;
  align-items:center;justify-content:center;gap:12px;pointer-events:none;
  text-align:center;transition:opacity .55s var(--sp);
  translate:0 -12dvh}   /* the pair sits a touch above center */
#ttl.on{display:flex}
#ttl.gone{opacity:0}
/* the sticker drift: one layer in the felt, bound to the card table —
   under the text and under the cards. Each flyer is a real 3D slab —
   glyph front, vinyl backing, thin edge gap — driven as a velocity
   body: layered-sine facing, sine-breathing thrust, force-field walls
   and billiard collisions, banking in 3D as it turns */
#ttlF{position:absolute;inset:0;z-index:0;pointer-events:none;perspective:700px}
#ttlF .tfl{position:absolute;left:0;top:0;will-change:transform;transform-style:preserve-3d}
#ttlF .t3{position:absolute;inset:0;transform-style:preserve-3d}
#ttlF .tf,#ttlF .tb{position:absolute;inset:0;backface-visibility:hidden;
  -webkit-backface-visibility:hidden;border-radius:7px}
#ttlF .tf{transform:translateZ(1.2px)}
#ttlF .tb{transform:rotateY(180deg) translateZ(1.2px);
  background:linear-gradient(145deg,#DDD6C4,#CBC3AF);
  border:1px solid rgba(21,20,14,.28);box-shadow:inset 0 0 0 3px rgba(255,253,244,.55)}
#ttlT,#ttlS{position:relative;z-index:1}
#ttlT b,#ttlS{text-shadow:0 -1px 1px var(--shs),0 1px 1px var(--hl)}
/* the title type: bare fat glyphs on neumorphic rims that fade in at
   their position — no boxes, no travel. Each line rides a felt-colored
   radial drop so the type stays legible over busy rows. The pads live
   on --padT/--padS and only ever move with the title's own show/hide —
   never per-frame, which is what made drifters blink under them */
#ttlT{display:flex;gap:.2em;font-family:var(--disp);font-weight:700;
  font-size:clamp(32px,9.2vw,56px);color:var(--ink);
  padding:12px 10px 14px}
#ttlT::before{content:"";position:absolute;inset:-130px -180px -150px;z-index:-1;
  background:radial-gradient(ellipse closest-side,var(--raise) 44%,rgba(235,230,216,0) 72%);
  opacity:calc(.65*var(--padT,1));transition:opacity .9s ease}
#ttlS::before{content:"";position:absolute;inset:-16px -52px -18px;z-index:-1;
  background:radial-gradient(ellipse closest-side,var(--raise) 50%,rgba(235,230,216,0) 70%);
  opacity:calc(.8*var(--padS,1));transition:opacity .9s ease}
/* the hide rides the letter wave: each pad dissolves inside its own
   line's fade, the title across the whole L-to-R walk, the byline with
   its own letters. Durations must track fx.js's TTL_FDELAY/TTL_FDUR
   (8 × 110ms stagger + 500ms each, byline +80ms); the gif stage can't
   use these transitions (?t= scrubs frames) and sets the vars itself */
#ttl.out #ttlT::before{--padT:0;transition:opacity 1.27s ease}
#ttl.out #ttlS::before{--padS:0;transition:opacity .5s ease .96s}
#ttlT b{font-weight:inherit;will-change:transform;color:transparent;
  -webkit-text-stroke:2.5px rgba(138,132,113,.9);
  text-shadow:-2px -2px 0 rgba(255,253,244,.8),-4px -3px 0 rgba(255,253,244,.5),
    -3px 0 0 rgba(255,253,244,.65),0 -4px 0 rgba(255,253,244,.6),
    2px 2px 0 rgba(160,150,126,.55),4px 3px 0 rgba(160,150,126,.35),
    3px 0 0 rgba(160,150,126,.45),0 4px 0 rgba(160,150,126,.3);
  animation:ttlrise .5s ease both}
@keyframes ttlrise{from{opacity:0}}
/* screensaver: while the title holds the stage only the type and the
   sticker drift speak — the deck stack and its slot stay, every
   counter and label sinks */
#felt.ttl :is(#deckN,#chev,#tkO,#tkX,#tkC,#chTip,#wardR,#ring){opacity:0}
#ttlS{font-size:11.5px;letter-spacing:.24em;color:var(--ink3);
  animation:ttlin .45s var(--sp) both}
/* the byline lands one by one after the pads (per-letter delay set in
   ttlOn); the rise rides `translate`, a separate property, so the
   sway's inline transform is never touched */
@keyframes ttlin{from{opacity:0;translate:0 9px}}

/* ---------- onboarding coach: one bubble on the felt's top band, with
   the tutorial's progress as a gold wash sweeping left to right. It
   lives inside #felt, so it only speaks while the table is up ---------- */
#tut{position:absolute;left:10px;right:10px;top:10px;margin:0 auto;max-width:400px;
  z-index:120;pointer-events:none}
#tut.off{display:none}
/* the one-shot tips wear the same placard, freed from the felt: they
   fire in side views too, so this one sits at body level, top-center.
   No gold progress wash — a tip is one beat, not a climb */
#tip{position:fixed;left:50%;transform:translateX(-50%);top:14px;
  max-width:400px;width:calc(100% - 20px);z-index:160;pointer-events:none}
#tip.off{display:none}
#tutBox,#tipBox{pointer-events:none;padding:11px 13px 12px;border-radius:12px;transition:--p .7s var(--sp);
  background:linear-gradient(90deg,rgba(138,106,47,.16) var(--p,0%),transparent 0),var(--grad);
  box-shadow:4px 4px 11px var(--shs),-3px -3px 8px var(--hl)}
#tutBox.in,#tipBox.in{animation:tutin .32s var(--sp)}
@keyframes tutin{from{opacity:0;transform:translateY(-9px)}}
/* flow-root: the floated SKIP must count toward the text's height, or
   it hangs across the bubble's bottom edge when it drops a line */
#tutTx,#tipTx{display:flow-root;color:var(--ink2)}
#tutTx.swp,#tipTx.swp{animation:tutsw .45s ease}
@keyframes tutsw{from{opacity:.15}}
/* the poster: one loud line (optionally a gold second line), one tiny
   sub. All centered, all Orbit for the head — the bubble is a placard,
   not a paragraph, and it never illustrates: the felt is the picture */
#tutTx .ph,#tipTx .ph{font-family:var(--disp);font-weight:800;font-size:17px;
  letter-spacing:.05em;line-height:1.18;color:var(--ink);text-align:center;
  -webkit-text-stroke:.022em currentcolor}
#tutTx .ph.bad,#tipTx .ph.bad{color:var(--red)}
#tutTx .ph .p2,#tipTx .ph .p2{display:block;margin-top:3px;font-size:15px;color:var(--gld)}
/* the swipe coach: the action line leads a gold chevron that walks the
   gesture's direction (up before SWIPE UP, a flipped one before SWIPE
   DOWN). Slight by design: the words name the move, the arrow taps it
   out, fading at each end of its nudge */
#tutTx .sar{display:inline-block;width:12px;height:12px;margin-right:7px;
  vertical-align:-1px;color:var(--gld)}
#tutTx .sar.dn{transform:rotate(180deg)}
#tutTx .sar svg{display:block;width:100%;height:100%;fill:none;
  stroke:currentColor;stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round;
  animation:sarN 1.2s ease-in-out infinite}
@keyframes sarN{0%,100%{transform:translateY(2.5px);opacity:.3}
  45%{transform:translateY(-2.5px);opacity:1}}
#tutTx .pg,#tipTx .pg{display:flex;justify-content:center;align-items:center;gap:7px;margin:7px 0 3px}
/* the outro's verb chips: DRAW · BANK · BUY */
#tutTx .tv,#tipTx .tv{display:inline-block;padding:4px 9px;border:1.3px solid var(--line);
  border-radius:8px;background:var(--grad);font-family:var(--ui);font-weight:700;
  font-size:10px;letter-spacing:.14em;color:var(--ink)}
#tutTx b,#tipTx b{color:var(--ink)}
/* SKIP rides the text: inline at its end, floated right — it sits on the
   last line's right edge, dropping to its own right-aligned line only
   when there is no room */
#tutX,#tipX{pointer-events:auto;float:right;display:inline-block;margin:5px 0 3px 12px;border:0;
  background:none;padding:2px 3px;font-family:var(--nm);font-size:8.5px;
  letter-spacing:.2em;color:var(--ink3);font-weight:700;cursor:pointer}
#tutTx .tgo{pointer-events:auto;display:block;clear:both;width:100%;margin-top:9px;padding:11px;
  border:0;border-radius:8px;background:var(--grad);color:var(--grn);font-family:var(--ui);
  font-size:12px;letter-spacing:.12em;font-weight:700;cursor:pointer;
  box-shadow:3px 3px 8px var(--shs),-3px -3px 7px var(--hl)}
#tutTx .tgo:active{background:var(--gradi);
  box-shadow:inset 3px 3px 6px rgba(160,150,126,.45),inset -2px -2px 5px var(--hl)}
/* the coach's pointer: a comet of light lapping the target. The rings
   are fixed overlays tut.js measures each frame, so nothing clips
   them: not #rk's overflow, not the rail above #bank. A faint
   breathing gold band keeps the target marked between laps; the
   rotating conic sweep is the flash that says look here. .tutG stays
   on the targets as a bare DOM marker (no visuals of its own) */
.tutR{position:fixed;left:0;top:0;z-index:940;pointer-events:none;
  padding:2.5px;opacity:0;visibility:hidden;
  transition:opacity .25s,visibility 0s .25s;
  -webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor;mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
  mask-composite:exclude}
.tutR.on{opacity:1;visibility:visible;transition:opacity .25s}
.tutR b{position:absolute;inset:0;border-radius:inherit;background:var(--gld);
  animation:tutBr 1.6s ease-in-out infinite}
.tutR i{position:absolute;left:50%;top:50%;
  background:conic-gradient(from 0deg,rgba(138,106,47,0) 0 62%,
    rgba(138,106,47,.55) 80%,#C79A44 90%,#FFF3D0 97%,rgba(255,243,208,0) 100%);
  animation:tutLap 2.4s linear infinite}
@keyframes tutLap{to{transform:rotate(1turn)}}
@keyframes tutBr{0%,100%{opacity:.3}50%{opacity:.55}}

/* ---------- desktop: the phone-stage frame breathes out. 15% wider
   app; the felt cards ride table.js's matching 15% (deskK); text and
   controls take the human-size pass here ---------- */
@media (min-width:720px){
  #app{max-width:598px}
  #hud{padding:max(10px,env(safe-area-inset-top)) 14px 9px}
  body{font-size:15px}
  .mny{padding:7px 13px 8px}
  .mny .l{font-size:9px}
  .mny .v{font-size:21px}
  .ic{width:48px;height:48px;border-radius:12px}
  .ic svg{width:23px;height:23px}
  #ttlT{font-size:clamp(32px,9.2vw,62px)}
  #ttlS{font-size:13px}
  #tut{max-width:460px}
  #tutTx .ph{font-size:20px}
  #tutTx .ph .p2{font-size:17.5px}
  #tutX{font-size:9.5px}
  #tutTx .tgo{font-size:13.5px;padding:12px}
}

/* wide desktop (>=1024px; tabs.js owns the body.wide switch and births
   it only once a side tab exists): the phone column opens into a
   two-column stage. tabs.js moves the side views and the rail into a
   #side column; the grid here gives the table its own lane — hud and
   title belong to the table, the sidebar owns its full height. Stacks
   on the 720px pass above */
@media (min-width:1024px){
  body.wide #app{max-width:1280px;display:grid;
    grid-template-columns:minmax(0,1fr) minmax(360px,45%);
    grid-template-rows:auto minmax(0,1fr)}
  /* the money plaques read over the table, not over the panel; the
     12px sides match the felt's lane padding so the header, felt and
     panel all share one edge rhythm */
  body.wide #hud{grid-column:1;padding:max(12px,env(safe-area-inset-top)) 12px 9px}
  body.wide #views{grid-column:1;grid-row:2}
  /* the sidebar's content drops in level with the HUD plaques, not
     the app's very top edge */
  body.wide #side{grid-column:2;grid-row:1/3;display:flex;flex-direction:column;min-height:0;
    padding-top:max(12px,env(safe-area-inset-top))}
  /* the title/screensaver belongs to the table: its box ends where the
     sidebar begins, so the letters center over the felt */
  body.wide #ttl{right:calc(min(45%,640px) + 10px)}
}
