/* MAGNI PARK - style.css
   Chunky moulded-plastic UI. Everything is a bevelled block with peg dots. */

:root {
  --lime:      #B5D827;
  --lime-lt:   #D8EF6B;
  --lime-dk:   #7E9C16;
  --yellow:    #F6C42A;
  --yellow-lt: #FFE072;
  --yellow-dk: #C1900E;
  --red:       #E8543A;
  --red-dk:    #B03823;
  --purple:    #9B6BD8;
  --purple-dk: #6E45A2;
  --blue:      #3FA9E0;

  --plastic:    #EDEEE8;
  --plastic-dk: #CACCC4;
  --plastic-sh: #A6A89F;
  --ink:        #33372F;
  --ink-soft:   #6B6F63;

  --r: 16px;
  --shadow: 0 6px 0 rgba(0,0,0,.14), 0 12px 26px rgba(0,0,0,.20);
  --ui: 'Segoe UI Rounded', 'Nunito', 'Varela Round', 'Segoe UI Variable Display',
        'Segoe UI', system-ui, -apple-system, sans-serif;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; overflow: hidden; }
body {
  font-family: var(--ui);
  background: #1b1e22;
  color: var(--ink);
  user-select: none;
  -webkit-user-select: none;
  -webkit-font-smoothing: antialiased;
}
#app { position: fixed; inset: 0; }
#view { position: absolute; inset: 0; width: 100%; height: 100%; display: block; cursor: pointer; }
.hidden { display: none !important; }

/* ---------- generic plastic panel ---------- */
.panel {
  background: linear-gradient(#FFFFFF 0%, var(--plastic) 46%, var(--plastic-dk) 100%);
  border-radius: var(--r);
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.95),
    inset 0 -3px 0 rgba(0,0,0,.10),
    var(--shadow);
}

/* ---------- top hud ---------- */
.hud {
  position: absolute; top: 16px; left: 16px; z-index: 6;
  display: flex; flex-direction: column; gap: 10px; align-items: flex-start;
  pointer-events: none;
}
.coinbox {
  padding: 12px 22px 12px 18px; position: relative; pointer-events: auto;
  border-top: 3px solid rgba(255,255,255,.6);
}
.coinbox::before {
  content: ''; position: absolute; left: 10px; top: 10px; width: 7px; height: 7px;
  border-radius: 50%; background: rgba(0,0,0,.13);
  box-shadow: 0 62px 0 rgba(0,0,0,.13);
}
.coin-amount {
  font-size: 40px; font-weight: 900; letter-spacing: -1px; line-height: 1;
  color: var(--ink); text-shadow: 0 2px 0 #fff;
}
/* A drawn coin, not the section sign: '§' read as terms-and-conditions, not
   money. Pure CSS - a glossy disc with a rim and a milled inner ring. */
.coin-amount::before {
  content: ''; display: inline-block; width: 26px; height: 26px; border-radius: 50%;
  margin-right: 8px; vertical-align: 4px;
  background: radial-gradient(circle at 36% 30%, var(--yellow-lt) 0, var(--yellow) 48%, var(--yellow-dk) 100%);
  box-shadow: inset 0 0 0 3px rgba(255,255,255,.28), inset 0 -3px 4px rgba(0,0,0,.18),
              0 2px 0 var(--yellow-dk);
}
.coin-rate { font-size: 14px; font-weight: 700; color: var(--ink-soft); margin-top: 3px; }

.statstrip { display: flex; gap: 8px; pointer-events: auto; }
.stat {
  padding: 7px 13px; display: flex; flex-direction: column; align-items: center; min-width: 74px;
}
.stat b { font-size: 19px; font-weight: 900; line-height: 1.05; }
.stat i { font-size: 10px; font-style: normal; font-weight: 800; text-transform: uppercase;
          letter-spacing: .8px; color: var(--ink-soft); }
#coresStat b { color: var(--purple-dk); }

.buffs { display: flex; gap: 8px; flex-wrap: wrap; max-width: 380px; }
.buff {
  pointer-events: auto;
  background: linear-gradient(var(--yellow-lt), var(--yellow));
  border-radius: 12px; padding: 6px 12px; font-weight: 900; font-size: 13px;
  color: #5B4300; box-shadow: inset 0 2px 0 rgba(255,255,255,.8), 0 4px 0 var(--yellow-dk);
  animation: pop .25s ease-out;
}
.buff small { display: block; font-size: 10px; font-weight: 800; opacity: .75; }
@keyframes pop { from { transform: scale(.6); opacity: 0 } to { transform: scale(1); opacity: 1 } }

/* ---------- shop ---------- */
.shop {
  position: absolute; top: 16px; right: 16px; bottom: 16px; width: 372px; z-index: 7;
  display: flex; flex-direction: column;
  background: linear-gradient(#FFFFFF 0%, var(--plastic) 60px, var(--plastic) 100%);
  border-radius: 20px; box-shadow: var(--shadow);
  transition: transform .28s cubic-bezier(.2,.9,.3,1.2);
  overflow: hidden;
}
.shop.closed { transform: translateX(calc(100% - 46px)); }
.shop-head {
  display: flex; align-items: center; gap: 6px; padding: 10px 10px 8px;
  border-bottom: 3px solid rgba(0,0,0,.07);
}
.tabs { display: flex; gap: 6px; flex: 1; }
.tab {
  position: relative;
  flex: 1; border: 0; cursor: pointer; font-family: var(--ui); font-weight: 900; font-size: 13px;
  padding: 9px 4px; border-radius: 12px; color: var(--ink-soft);
  background: linear-gradient(#FFF, var(--plastic-dk));
  box-shadow: inset 0 2px 0 #fff, 0 3px 0 var(--plastic-sh);
  transition: transform .08s;
}
.tab:active { transform: translateY(2px); box-shadow: inset 0 2px 0 #fff, 0 1px 0 var(--plastic-sh); }
.tab.active {
  color: #3C4A00; background: linear-gradient(var(--lime-lt), var(--lime));
  box-shadow: inset 0 2px 0 rgba(255,255,255,.7), 0 3px 0 var(--lime-dk);
}
.dot {
  position: absolute; top: -3px; right: -3px; width: 12px; height: 12px; border-radius: 50%;
  background: var(--red); box-shadow: 0 0 0 2px #fff; animation: blip 1s infinite;
}
@keyframes blip { 50% { transform: scale(1.25) } }
.iconbtn {
  width: 32px; height: 32px; border: 0; border-radius: 10px; cursor: pointer;
  background: linear-gradient(#FFF, var(--plastic-dk)); font-weight: 900; color: var(--ink-soft);
  box-shadow: inset 0 2px 0 #fff, 0 3px 0 var(--plastic-sh);
}

.buyrow {
  display: flex; align-items: center; gap: 5px; padding: 8px 12px;
  font-size: 12px; font-weight: 900; color: var(--ink-soft);
  border-bottom: 3px solid rgba(0,0,0,.07);
}
.qty {
  border: 0; cursor: pointer; font-family: var(--ui); font-weight: 900; font-size: 12px;
  padding: 5px 11px; border-radius: 9px; color: var(--ink-soft);
  background: linear-gradient(#FFF, var(--plastic-dk));
  box-shadow: inset 0 1px 0 #fff, 0 2px 0 var(--plastic-sh);
}
.qty.active { color: #3C4A00; background: linear-gradient(var(--lime-lt), var(--lime));
              box-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 2px 0 var(--lime-dk); }

.shop-body { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 10px; }
.shop-body::-webkit-scrollbar { width: 10px; }
.shop-body::-webkit-scrollbar-thumb { background: var(--plastic-sh); border-radius: 6px; border: 3px solid transparent; background-clip: content-box; }
.tabpane { display: none; }
.tabpane.active { display: block; }

/* ---------- ride cards ---------- */
.card {
  display: flex; gap: 10px; align-items: center; width: 100%; text-align: left;
  border: 0; cursor: pointer; font-family: var(--ui);
  padding: 9px; margin-bottom: 8px; border-radius: 14px;
  background: linear-gradient(#FFF, #E4E6DE);
  box-shadow: inset 0 2px 0 #fff, 0 4px 0 var(--plastic-sh);
  transition: transform .07s, filter .12s;
}
.card:hover { filter: brightness(1.04); }
.card:active { transform: translateY(3px); box-shadow: inset 0 2px 0 #fff, 0 1px 0 var(--plastic-sh); }
.card.locked { opacity: .55; filter: grayscale(.65); cursor: not-allowed; }
.card.poor .c-cost { color: var(--red-dk); }
.card.afford { box-shadow: inset 0 2px 0 #fff, 0 4px 0 var(--lime-dk); background: linear-gradient(#FFF, #EDF6D2); }

.c-thumb {
  width: 54px; height: 54px; border-radius: 12px; flex: 0 0 54px;
  box-shadow: inset 0 -3px 0 rgba(0,0,0,.15), inset 0 2px 0 rgba(255,255,255,.6);
  position: relative; overflow: hidden;
}
.c-thumb canvas { width: 100%; height: 100%; display: block; }
.c-main { flex: 1; min-width: 0; }
.c-name { font-weight: 900; font-size: 14.5px; color: var(--ink); display: flex; align-items: baseline; gap: 6px; }
.c-name em { font-style: normal; font-size: 11px; color: var(--ink-soft); font-weight: 800; }
.c-cost { font-weight: 900; font-size: 13px; color: var(--lime-dk); margin-top: 1px; }
.c-sub { font-size: 11px; color: var(--ink-soft); font-weight: 700; margin-top: 2px;
         white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.c-count {
  font-size: 22px; font-weight: 900; color: var(--plastic-sh); min-width: 34px; text-align: right;
  text-shadow: 0 2px 0 #fff;
}
.c-count.has { color: var(--ink); }

/* upgrade cards are square chips in a grid */
.upgrid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 7px; margin-bottom: 12px; }
.chip {
  position: relative; aspect-ratio: 1; border: 0; cursor: pointer; border-radius: 12px; padding: 0;
  background: linear-gradient(#FFF, #E4E6DE);
  box-shadow: inset 0 2px 0 #fff, 0 3px 0 var(--plastic-sh);
  overflow: hidden; transition: transform .07s;
}
.chip canvas { width: 100%; height: 100%; display: block; }
.chip:active { transform: translateY(2px); }
.chip.afford { box-shadow: inset 0 2px 0 #fff, 0 3px 0 var(--lime-dk); }
.chip.poor { filter: grayscale(.8); opacity: .6; cursor: not-allowed; }

.section { margin-bottom: 16px; }
.section h3 { font-size: 13px; text-transform: uppercase; letter-spacing: 1px;
              color: var(--ink-soft); margin-bottom: 6px; }
.hint { font-size: 12px; color: var(--ink-soft); font-weight: 600; margin-bottom: 8px; line-height: 1.45; }
.statlist { font-size: 12.5px; font-weight: 700; color: var(--ink-soft); }
.statlist div { display: flex; justify-content: space-between; padding: 3px 0;
                border-bottom: 1px dashed rgba(0,0,0,.1); }
.statlist b { color: var(--ink); font-weight: 900; }
.btnrow { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }

.bigbtn {
  width: 100%; border: 0; cursor: pointer; font-family: var(--ui); font-weight: 900; font-size: 15px;
  padding: 12px; border-radius: 14px; color: #3C4A00;
  background: linear-gradient(var(--lime-lt), var(--lime));
  box-shadow: inset 0 2px 0 rgba(255,255,255,.7), 0 4px 0 var(--lime-dk);
}
.bigbtn:active { transform: translateY(3px); box-shadow: inset 0 2px 0 rgba(255,255,255,.7), 0 1px 0 var(--lime-dk); }
.bigbtn:disabled { filter: grayscale(1); opacity: .5; cursor: not-allowed; }
.bigbtn.purple { color: #FFF; background: linear-gradient(var(--purple), var(--purple-dk));
                 box-shadow: inset 0 2px 0 rgba(255,255,255,.35), 0 4px 0 #4A2C6E; }
.smallbtn {
  border: 0; cursor: pointer; font-family: var(--ui); font-weight: 900; font-size: 12px;
  padding: 8px 12px; border-radius: 10px; color: var(--ink-soft);
  background: linear-gradient(#FFF, var(--plastic-dk));
  box-shadow: inset 0 1px 0 #fff, 0 3px 0 var(--plastic-sh);
}
.smallbtn.danger { color: var(--red-dk); }
.smallbtn:active { transform: translateY(2px); }

/* ---------- room picker ---------- */
.roomlist { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.roomcard {
  border: 0; cursor: pointer; font-family: var(--ui); padding: 0; border-radius: 13px; overflow: hidden;
  background: linear-gradient(#FFF, #E4E6DE);
  box-shadow: inset 0 2px 0 #fff, 0 3px 0 var(--plastic-sh); text-align: left;
}
.roomcard canvas { width: 100%; height: 62px; display: block; }
.roomcard span { display: block; padding: 6px 8px; font-weight: 900; font-size: 12px; }
.roomcard.active { box-shadow: inset 0 2px 0 #fff, 0 3px 0 var(--lime-dk); background: linear-gradient(#FFF, #EDF6D2); }
.roomcard.locked { opacity: .5; filter: grayscale(1); cursor: not-allowed; }
.roomcard small { display: block; padding: 0 8px 6px; font-size: 10px; font-weight: 800; color: var(--red-dk); }

/* ---------- bottom dock + pegman ---------- */
.dock { position: absolute; left: 16px; bottom: 16px; z-index: 6; display: flex; gap: 8px; }
.dockbtn {
  border: 0; cursor: pointer; font-family: var(--ui); font-weight: 900; font-size: 13px;
  padding: 11px 16px; border-radius: 14px; color: var(--ink);
  display: flex; align-items: center; gap: 8px;
  background: linear-gradient(#FFF, var(--plastic-dk));
  box-shadow: inset 0 2px 0 #fff, 0 4px 0 var(--plastic-sh);
}
.dockbtn:active { transform: translateY(3px); box-shadow: inset 0 2px 0 #fff, 0 1px 0 var(--plastic-sh); }
.dockicon { font-size: 17px; }

.pegman {
  position: absolute; right: 404px; bottom: 24px; z-index: 8; width: 88px;
  cursor: grab; text-align: center; touch-action: none;
  filter: drop-shadow(0 8px 8px rgba(0,0,0,.35));
  transition: right .28s cubic-bezier(.2,.9,.3,1.2);
}
.pegman.wide { right: 78px; }
.pegman.dragging { cursor: grabbing; z-index: 30; }
.pegman canvas { display: block; }
.pegtip {
  font-size: 10px; font-weight: 900; color: #fff; text-shadow: 0 2px 4px rgba(0,0,0,.7);
  line-height: 1.25; margin-top: -6px; letter-spacing: .3px;
}
.pegman.dragging .pegtip { opacity: 0; }

/* ---------- visit overlay ---------- */
.visitui { position: absolute; inset: 0; z-index: 5; pointer-events: none; }
.crosshair {
  position: absolute; left: 50%; top: 50%; width: 8px; height: 8px; margin: -4px 0 0 -4px;
  border-radius: 50%; background: rgba(255,255,255,.85);
  box-shadow: 0 0 0 2px rgba(0,0,0,.35);
}
.prompt {
  position: absolute; left: 50%; top: 58%; transform: translateX(-50%);
  background: rgba(24,27,22,.82); color: #fff; padding: 10px 18px; border-radius: 14px;
  font-weight: 900; font-size: 15px; white-space: nowrap;
  box-shadow: 0 6px 20px rgba(0,0,0,.4); backdrop-filter: blur(4px);
}
.prompt b {
  display: inline-block; background: var(--lime); color: #2C3600; border-radius: 7px;
  padding: 1px 8px; margin-right: 7px; box-shadow: 0 2px 0 var(--lime-dk);
}
.controlhint {
  position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%);
  color: rgba(255,255,255,.85); font-size: 12px; font-weight: 700;
  text-shadow: 0 2px 5px rgba(0,0,0,.8); white-space: nowrap;
}
/* The visit overlay lets pointer events through to the canvas, so the few
   things in it that ARE controls opt back in. */
.visitui .prompt { pointer-events: auto; cursor: pointer; user-select: none; }
.visitui .prompt:active { transform: translateX(-50%) translateY(2px); }
/* A desktop key legend on a phone: hidden, and the Jump button stands in
   for the one key a thumb stick cannot press. */
.jumpBtn {
  display: none; position: absolute; right: 18px; bottom: 112px; z-index: 6;
  width: 74px; height: 74px; border-radius: 50%; border: 2px solid rgba(255,255,255,.55);
  background: rgba(255,255,255,.18); color: #fff; font-family: var(--ui);
  font-weight: 900; font-size: 15px; text-shadow: 0 2px 4px rgba(0,0,0,.6);
  pointer-events: auto; touch-action: none; user-select: none;
  box-shadow: 0 2px 14px rgba(0,0,0,.3);
}
.jumpBtn:active { background: rgba(255,255,255,.4); }
@media (pointer: coarse) {
  .controlhint { display: none; }
  body.visit .jumpBtn { display: block; }
}
/* Screen-edge compass towards the VIP (white) or golden pod (gold). Rotated
   by transform from player.js; 0deg points up. */
.findarrow {
  position: absolute; left: 0; top: 0; width: 0; height: 0; z-index: 6; pointer-events: none;
  border-left: 13px solid transparent; border-right: 13px solid transparent;
  border-bottom: 26px solid rgba(255,255,255,.92);
  filter: drop-shadow(0 2px 4px rgba(0,0,0,.6));
  animation: findpulse 1s ease-in-out infinite alternate;
}
.findarrow.gold { border-bottom-color: #FFD23A; }
.findarrow.hidden { display: none; }
@keyframes findpulse { from { opacity: .6; } to { opacity: 1; } }
.controlhint b {
  background: rgba(255,255,255,.9); color: #2A2E26; border-radius: 5px; padding: 1px 6px;
  font-weight: 900; font-size: 11px;
}

/* ---------- minigame ---------- */
.minigame { position: absolute; inset: 0; z-index: 20; display: grid; place-items: center;
            background: rgba(20,23,19,.55); backdrop-filter: blur(3px); }
.mg-card { width: min(520px, 92vw); padding: 22px; text-align: center; }
.mg-card h2 { font-size: 26px; font-weight: 900; margin-bottom: 4px; }
.mg-card p { font-size: 13px; font-weight: 700; color: var(--ink-soft); margin-bottom: 14px; }
.mg-stage { height: 150px; position: relative; margin-bottom: 14px; }
.mg-foot { display: flex; gap: 10px; align-items: center; }
.mg-foot .bigbtn { flex: 1; }
/* The rewarded offer sits UNDER the free swing and is styled quieter than it,
   on purpose: the thing that costs the player nothing is the thing the eye
   should land on first. */
.mg-extra { margin-top: 10px; display: flex; }

/* who is open right now */
.checkrow { display: flex; gap: 8px; align-items: flex-start; margin: 8px 0 2px;
  font-size: 11px; font-weight: 700; color: var(--ink-soft); cursor: pointer; }
.checkrow input { margin: 1px 0 0; flex: none; }
.openparks { margin-top: 12px; }
.openparks-head { display: flex; align-items: center; justify-content: space-between;
  font-size: 11px; font-weight: 900; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-soft); margin-bottom: 6px; }
.parklist { display: flex; flex-direction: column; gap: 6px; }
.parkrow { display: flex; align-items: center; gap: 8px; padding: 7px 9px;
  border-radius: 11px; background: linear-gradient(#FFFFFF, #F1F2EC);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.9), 0 2px 0 rgba(0,0,0,.07); }
.parkrow b { font-size: 12px; font-weight: 900; flex: 1; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.parkrow small { font-size: 10px; font-weight: 700; color: var(--ink-soft); }
.parkrow .smallbtn { flex: none; }
.mg-extra .btn { flex: 1; font-size: 13px; opacity: 0.92; }
.mg-score { font-weight: 900; font-size: 14px; color: var(--lime-dk); min-width: 90px; text-align: left; }

.mg-bar {
  position: absolute; left: 0; right: 0; top: 54px; height: 44px; border-radius: 12px;
  background: linear-gradient(#DDE0D6, #C2C5BA); overflow: hidden;
  box-shadow: inset 0 3px 8px rgba(0,0,0,.2);
}
.mg-zone { position: absolute; top: 0; bottom: 0; background: var(--lime); opacity: .9; }
.mg-zone.gold { background: var(--yellow); }
.mg-needle { position: absolute; top: -8px; bottom: -8px; width: 6px; border-radius: 3px;
             background: var(--red); box-shadow: 0 0 0 2px #fff, 0 3px 8px rgba(0,0,0,.4); }
.mg-mole { position: absolute; width: 62px; height: 62px; border-radius: 50%; border: 0; cursor: pointer;
           background: linear-gradient(var(--lime-lt), var(--lime-dk)); box-shadow: 0 5px 0 rgba(0,0,0,.25); }
.mg-mole.up { animation: pop .18s ease-out; }
.mg-hole { position: absolute; width: 62px; height: 62px; border-radius: 50%;
           background: rgba(0,0,0,.16); box-shadow: inset 0 4px 8px rgba(0,0,0,.3); }

/* ---------- toasts / modal ---------- */
/* In the park the centre of the screen is the view; toasts sit at the bottom
   above the control strip instead of over the walker's face. */
body.visit .toasts { top: auto; bottom: 64px; }
.toasts { position: absolute; left: 50%; top: 96px; transform: translateX(-50%); z-index: 12;
          display: flex; flex-direction: column; gap: 8px; align-items: center; pointer-events: none; }
.toast {
  background: rgba(24,27,22,.88); color: #fff; padding: 10px 18px; border-radius: 14px;
  font-weight: 900; font-size: 15px; box-shadow: 0 6px 20px rgba(0,0,0,.4);
  animation: toastin .3s cubic-bezier(.2,.9,.3,1.4);
  /* A toast is a one-line acknowledgement, so it is bounded like one. Placing a
     lot of rides at once produces a list of every name, and unbounded that
     covered an entire phone screen and hid the park behind it. */
  max-width: min(88vw, 460px);
  max-height: 4.6em;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.35;
}
.toast.good { background: linear-gradient(var(--lime), var(--lime-dk)); color: #29330A; }
.toast.gold { background: linear-gradient(var(--yellow-lt), var(--yellow-dk)); color: #4A3600; }
.toast.bad  { background: linear-gradient(var(--red), var(--red-dk)); }
@keyframes toastin { from { transform: translateY(-14px) scale(.9); opacity: 0 } }

.modal { position: absolute; inset: 0; z-index: 25; display: grid; place-items: center;
         background: rgba(20,23,19,.6); backdrop-filter: blur(4px); }
.modal-card { width: min(460px, 90vw); padding: 26px; text-align: center; }
.modal-card h2 { font-size: 26px; font-weight: 900; margin-bottom: 10px; }
.modal-card #modalBody { font-size: 14px; font-weight: 700; color: var(--ink-soft);
                          line-height: 1.6; margin-bottom: 18px; }
.modal-card #modalBody b { color: var(--lime-dk); font-size: 19px; }

/* ---------- boot ---------- */
.boot { position: absolute; inset: 0; z-index: 40; display: grid; place-items: center;
        background: radial-gradient(circle at 50% 40%, #3A4048, #14171A);
        transition: opacity .5s; }
.boot.gone { opacity: 0; pointer-events: none; }
.bootcard { text-align: center; }
.bootlogo { font-size: 62px; font-weight: 900; letter-spacing: -2px; color: var(--lime);
            text-shadow: 0 6px 0 var(--lime-dk), 0 14px 30px rgba(0,0,0,.6); }
.bootlogo span { color: var(--yellow); text-shadow: 0 6px 0 var(--yellow-dk), 0 14px 30px rgba(0,0,0,.6); }
.bootsub { color: rgba(255,255,255,.6); font-weight: 800; font-size: 14px; margin-top: 8px; }
.bootbar { width: 260px; height: 10px; border-radius: 6px; background: rgba(0,0,0,.4);
           margin: 22px auto 0; overflow: hidden; }
.bootbar i { display: block; height: 100%; width: 0; border-radius: 6px;
             background: linear-gradient(90deg, var(--lime), var(--yellow)); transition: width .3s; }

/* ---------- floating click numbers (DOM, cheap) ---------- */
.float {
  position: absolute; z-index: 4; pointer-events: none; font-weight: 900; font-size: 22px;
  color: #fff; text-shadow: 0 2px 0 rgba(0,0,0,.35), 0 4px 12px rgba(0,0,0,.5);
  /* a stroke, so the white number still reads on a bright lawn at phone size */
  -webkit-text-stroke: 1px rgba(0,0,0,.5); paint-order: stroke fill;
  animation: floatup 1.05s ease-out forwards;
}
.float.tip { color: var(--yellow-lt); font-size: 25px; }
@keyframes floatup {
  0%   { transform: translate(-50%, 0) scale(.7); opacity: 0; }
  18%  { transform: translate(-50%, -14px) scale(1.12); opacity: 1; }
  100% { transform: translate(-50%, -78px) scale(.95); opacity: 0; }
}

/* ---------- narrow screens ---------- */
@media (max-width: 900px) {
  .shop { width: 320px; }
  .pegman { right: 352px; }
  .coin-amount { font-size: 30px; }
}

/* ---------- park identity + lands ---------- */
.identity { display: flex; height: 16px; border-radius: 8px; overflow: hidden;
            box-shadow: inset 0 2px 4px rgba(0,0,0,.25); background: var(--plastic-dk); margin-bottom: 8px; }
.identity i { display: block; height: 100%; transition: width .4s; }
.identity .empty { flex: 1; }

.landlist { display: flex; flex-direction: column; gap: 6px; }
.landrow {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-radius: 11px;
  background: linear-gradient(#FFF, #E7E9E1); box-shadow: inset 0 2px 0 #fff, 0 3px 0 var(--plastic-sh);
  font-size: 12.5px; font-weight: 800; color: var(--ink);
}
.landrow b { width: 12px; height: 12px; border-radius: 4px; flex: 0 0 12px; }
.landrow span { flex: 1; }
.landrow em { font-style: normal; font-size: 11px; color: var(--ink-soft); }
.landrow small { font-size: 10.5px; font-weight: 900; color: var(--lime-dk); }
.landnone { font-size: 12px; font-weight: 700; color: var(--ink-soft); font-style: italic; }

.c-theme {
  font-size: 9.5px; font-weight: 900; text-transform: uppercase; letter-spacing: .6px;
  padding: 1px 6px; border-radius: 6px; color: #fff; margin-left: 4px; vertical-align: 1px;
}

/* ---------- sandbox / cheat panel ---------- */
.sandbox {
  position: absolute; left: 16px; top: 200px; z-index: 14; width: 340px;
  border-radius: 16px; overflow: hidden;
  background: linear-gradient(#FFFFFF 0%, var(--plastic) 44px, var(--plastic) 100%);
  box-shadow: var(--shadow);
}
.sb-head {
  display: flex; align-items: center; gap: 8px; padding: 9px 10px 9px 14px;
  background: linear-gradient(var(--purple), var(--purple-dk)); color: #fff;
}
.sb-head b { font-size: 15px; font-weight: 900; }
.sb-head em { flex: 1; font-style: normal; font-size: 10.5px; font-weight: 800; opacity: .8; }
.sb-head .iconbtn { width: 24px; height: 24px; font-size: 15px; line-height: 1; }
.sb-body { padding: 10px 12px 12px; }
.sb-row { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 9px; }
.sb-row > span {
  width: 46px; flex: 0 0 46px; padding-top: 5px;
  font-size: 10.5px; font-weight: 900; text-transform: uppercase;
  letter-spacing: .6px; color: var(--ink-soft);
}
.sb-btns { display: flex; flex-wrap: wrap; gap: 5px; flex: 1; }
.sb-btns button {
  border: 0; cursor: pointer; font-family: var(--ui); font-weight: 900; font-size: 11.5px;
  padding: 6px 10px; border-radius: 9px; color: var(--ink);
  background: linear-gradient(#FFF, var(--plastic-dk));
  box-shadow: inset 0 1px 0 #fff, 0 3px 0 var(--plastic-sh);
}
.sb-btns button:active { transform: translateY(2px); box-shadow: inset 0 1px 0 #fff, 0 1px 0 var(--plastic-sh); }
.sb-btns button.on {
  color: #3C4A00; background: linear-gradient(var(--lime-lt), var(--lime));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 3px 0 var(--lime-dk);
}
.sb-btns button.danger { color: var(--red-dk); }

/* ---------- plot panel ---------- */
.plotpanel {
  position: absolute; left: 16px; bottom: 74px; z-index: 12; width: 290px;
  border-radius: 16px; overflow: hidden;
  background: linear-gradient(#FFFFFF 0%, var(--plastic) 42px, var(--plastic) 100%);
  box-shadow: var(--shadow);
}
.pp-head {
  display: flex; align-items: center; gap: 8px; padding: 9px 10px 9px 14px;
  background: linear-gradient(var(--lime-lt), var(--lime)); color: #33400A;
}
.pp-head b { flex: 1; font-size: 15px; font-weight: 900; }
.pp-head .iconbtn { width: 24px; height: 24px; font-size: 15px; line-height: 1; }
.pp-body { padding: 10px 14px 13px; }
.pp-info { font-size: 12.5px; font-weight: 700; color: var(--ink-soft); line-height: 1.5; margin-bottom: 9px; }
.pp-info b { color: var(--ink); }
.pp-btns { display: flex; gap: 6px; }
.pp-btns .smallbtn { flex: 1; }
.pp-hint { font-size: 11.5px; font-weight: 800; color: var(--lime-dk); margin-top: 8px; min-height: 14px; }
.pp-hint.moving { color: var(--red-dk); }

/* ---------- speedometer (movement styles) ---------- */
.speedo {
  position: absolute; left: 50%; bottom: 58px; transform: translateX(-50%);
  display: flex; align-items: baseline; gap: 5px;
  background: rgba(24,27,22,.72); color: #fff; padding: 5px 14px; border-radius: 12px;
  font-weight: 900; box-shadow: 0 4px 14px rgba(0,0,0,.4); backdrop-filter: blur(3px);
}
.speedo b { font-size: 21px; font-variant-numeric: tabular-nums; }
.speedo i { font-style: normal; font-size: 10px; font-weight: 800; opacity: .7; }
.speedo.fast b { color: var(--lime); }
.speedo.blazing b { color: var(--yellow); }

/* ---------- avatar editor ---------- */
.avatar-card { width: min(720px, 94vw); padding: 22px; }
.avatar-card h2 { font-size: 24px; font-weight: 900; margin-bottom: 14px; text-align: center; }
.av-body { display: flex; gap: 18px; align-items: flex-start; }
.av-preview {
  flex: 0 0 200px; border-radius: 16px; padding: 8px;
  background: linear-gradient(#EAF3FF, #CFE0F2);
  box-shadow: inset 0 3px 8px rgba(0,0,0,.16);
}
.av-preview canvas { width: 100%; display: block; }
.av-controls { flex: 1; max-height: 340px; overflow-y: auto; padding-right: 4px; }
.av-row { margin-bottom: 12px; }
.av-row h4 {
  font-size: 11px; font-weight: 900; text-transform: uppercase; letter-spacing: .9px;
  color: var(--ink-soft); margin-bottom: 6px;
}
.av-swatches { display: flex; flex-wrap: wrap; gap: 6px; }
.av-sw {
  width: 30px; height: 30px; border-radius: 9px; border: 0; cursor: pointer;
  box-shadow: inset 0 2px 0 rgba(255,255,255,.45), 0 3px 0 var(--plastic-sh);
}
.av-sw.on { box-shadow: inset 0 2px 0 rgba(255,255,255,.45), 0 0 0 3px var(--lime), 0 3px 0 var(--lime-dk); }
.av-chip {
  border: 0; cursor: pointer; font-family: var(--ui); font-weight: 900; font-size: 11.5px;
  padding: 7px 11px; border-radius: 10px; color: var(--ink);
  background: linear-gradient(#FFF, var(--plastic-dk));
  box-shadow: inset 0 1px 0 #fff, 0 3px 0 var(--plastic-sh);
}
.av-chip.on { color: #3C4A00; background: linear-gradient(var(--lime-lt), var(--lime)); box-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 3px 0 var(--lime-dk); }
.av-chip.locked { opacity: .45; cursor: not-allowed; }
.av-chip small { display: block; font-size: 9.5px; font-weight: 800; opacity: .7; }
.av-foot { display: flex; gap: 8px; margin-top: 16px; align-items: center; }
.av-foot .bigbtn { flex: 1; }

/* ---------- visit-mode view switch ---------- */
.viewswitch {
  position: absolute; right: 20px; top: 20px; z-index: 9;
  display: flex; align-items: center; gap: 4px; padding: 5px;
  border-radius: 14px; background: rgba(24,27,22,.7); backdrop-filter: blur(4px);
  box-shadow: 0 5px 16px rgba(0,0,0,.4); pointer-events: auto;
}
.vs-btn {
  border: 0; cursor: pointer; font-family: var(--ui); font-weight: 900; font-size: 13px;
  padding: 8px 16px; border-radius: 10px; color: rgba(255,255,255,.72); background: transparent;
}
.vs-btn.on { color: #2C3600; background: linear-gradient(var(--lime-lt), var(--lime)); box-shadow: 0 2px 0 var(--lime-dk); }
.vs-key { font-size: 10px; font-weight: 900; color: rgba(255,255,255,.5);
  border: 1.5px solid rgba(255,255,255,.35); border-radius: 5px; padding: 1px 5px; margin-left: 3px; }
.lockhint {
  position: absolute; left: 50%; top: 92px; transform: translateX(-50%); z-index: 9;
  background: rgba(24,27,22,.85); color: #fff; padding: 9px 18px; border-radius: 13px;
  font-size: 13px; font-weight: 800; white-space: nowrap; pointer-events: none;
  box-shadow: 0 5px 18px rgba(0,0,0,.45);
}
.lockhint b { background: var(--lime); color: #2C3600; border-radius: 5px; padding: 1px 6px; font-weight: 900; margin: 0 2px; }

/* ================================================================== */
/* GOALS: rank, contracts, boosters, streak, badges                    */
/* ================================================================== */

/* ---------- the rank chip in the HUD ---------- */
.rankstat { position: relative; padding-bottom: 11px; }
.hudrankbar {
  position: absolute; left: 9px; right: 9px; bottom: 5px; height: 4px;
  border-radius: 3px; background: rgba(0,0,0,.14); overflow: hidden;
}
.hudrankbar i {
  display: block; height: 100%; width: 0;
  background: linear-gradient(90deg, var(--lime), var(--yellow));
  transition: width .3s ease-out;
}

/* ---------- the rank header on the panel ---------- */
.ranktop {
  background: linear-gradient(#FFFFFF, #F3F4EE);
  border-radius: 14px; padding: 13px 14px 11px; margin-bottom: 16px;
  box-shadow: inset 0 2px 0 rgba(255,255,255,.9), inset 0 -2px 0 rgba(0,0,0,.07),
              0 3px 0 rgba(0,0,0,.08);
}
.rankrow { display: flex; gap: 13px; align-items: center; }
.rankno {
  font-size: 30px; font-weight: 900; line-height: 1; color: #FFF;
  background: linear-gradient(var(--purple), var(--purple-dk));
  width: 54px; height: 54px; flex: 0 0 54px; border-radius: 15px;
  display: grid; place-items: center;
  box-shadow: inset 0 2px 0 rgba(255,255,255,.45), 0 3px 0 var(--purple-dk);
  text-shadow: 0 2px 0 rgba(0,0,0,.2);
}
.rankmid { flex: 1; min-width: 0; }
.rankmid b { font-size: 15px; font-weight: 900; display: block; }
.rankmid small { font-size: 11px; font-weight: 800; color: var(--ink-soft); }
.rankbar {
  height: 9px; border-radius: 5px; background: rgba(0,0,0,.13);
  margin: 5px 0 3px; overflow: hidden;
}
.rankbar i {
  display: block; height: 100%; width: 0; border-radius: 5px;
  background: linear-gradient(90deg, var(--lime), var(--yellow));
  box-shadow: inset 0 2px 0 rgba(255,255,255,.5);
  transition: width .3s ease-out;
}
.ranktop .hint { margin: 8px 0 0; }

/* ---------- contracts ---------- */
.contracts { display: flex; flex-direction: column; gap: 9px; }
.contract {
  position: relative; padding: 10px 88px 9px 12px; border-radius: 13px;
  background: linear-gradient(#FFFFFF, #F1F2EC);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.9), 0 3px 0 rgba(0,0,0,.09);
}
.contract.done {
  background: linear-gradient(#F6FFDF, #E3F5B4);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.9), 0 3px 0 var(--lime-dk);
}
.ctop { display: flex; gap: 8px; align-items: flex-start; }
.ctop span { font-size: 13px; font-weight: 800; line-height: 1.3; flex: 1; }
.cboost {
  font-style: normal; font-size: 9.5px; font-weight: 900; color: #FFF;
  padding: 2px 6px; border-radius: 6px; white-space: nowrap;
  text-shadow: 0 1px 0 rgba(0,0,0,.25);
}
.cbar { height: 7px; border-radius: 4px; background: rgba(0,0,0,.12); margin: 7px 0 5px; overflow: hidden; }
.cbar i {
  display: block; height: 100%; border-radius: 4px;
  background: linear-gradient(90deg, var(--blue), var(--lime));
  transition: width .25s ease-out;
}
.cfoot { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.cfoot small { font-size: 11px; font-weight: 800; color: var(--ink-soft); }
.creward { font-size: 11px; font-weight: 900; color: var(--lime-dk); }
.contract .smallbtn { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); min-width: 66px; }
.contract .smallbtn:active { transform: translateY(calc(-50% + 2px)); }
.contract .smallbtn.claim {
  color: #FFF; background: linear-gradient(var(--lime), var(--lime-dk));
  box-shadow: inset 0 2px 0 rgba(255,255,255,.6), 0 3px 0 var(--lime-dk);
  animation: claimpulse 1.4s ease-in-out infinite;
}
@keyframes claimpulse { 0%,100% { filter: brightness(1); } 50% { filter: brightness(1.14); } }

/* ---------- boosters ---------- */
.boosts { display: flex; flex-direction: column; gap: 8px; }
.boostchip {
  position: relative; text-align: left; border: 0; cursor: pointer; width: 100%;
  font-family: var(--ui); padding: 9px 60px 9px 12px; border-radius: 13px;
  background: linear-gradient(#FFFFFF, #F1F2EC);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.9), 0 3px 0 rgba(0,0,0,.09);
  border-left: 5px solid var(--bc, var(--lime));
}
.boostchip:active { transform: translateY(2px); box-shadow: inset 0 2px 0 rgba(255,255,255,.9), 0 1px 0 rgba(0,0,0,.09); }
.boostchip b { display: block; font-size: 13px; font-weight: 900; }
.boostchip small { font-size: 11px; font-weight: 700; color: var(--ink-soft); line-height: 1.3; }
.boostchip em {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  font-style: normal; font-size: 13px; font-weight: 900; color: var(--ink-soft);
}
.boostchip.live { cursor: default; background: linear-gradient(#FFFBE6, #FFF0B8); }
/* The "watch a clip for one" chip. Dashed and a shade quieter than a booster you
   already own, because a thing you have is worth more than a thing on offer and
   the tray should read that way at a glance. */
.boostchip.ask { background: linear-gradient(#FBFBF7, #F2F3ED); border-left-style: dashed; }
.boostchip.ask .bcount { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; }
.boostchip.ask:disabled { opacity: .6; cursor: default; }
.boostchip.live .btimer { color: var(--yellow-dk); }

/* ---------- streak ---------- */
.sdays { display: flex; gap: 5px; margin-bottom: 8px; }
.sday {
  flex: 1; text-align: center; padding: 7px 2px 5px; border-radius: 10px;
  background: rgba(0,0,0,.07);
}
.sday b { display: block; font-size: 14px; font-weight: 900; color: var(--ink-soft); }
.sday small { font-size: 8.5px; font-weight: 800; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .3px; }
.sday.got { background: linear-gradient(var(--lime), var(--lime-dk)); }
.sday.got b, .sday.got small { color: #FFF; text-shadow: 0 1px 0 rgba(0,0,0,.2); }
.sday.now { box-shadow: 0 0 0 3px var(--yellow); }

/* ---------- badges ---------- */
.badgecount { font-style: normal; font-size: 11px; color: var(--ink-soft); margin-left: 6px; }
.badges { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.badge {
  padding: 7px 9px; border-radius: 10px; background: rgba(0,0,0,.06);
  opacity: .55;
}
.badge.got {
  opacity: 1; background: linear-gradient(#FFFFFF, #EFF1E9);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.9), 0 2px 0 rgba(0,0,0,.08);
}
.badge b { display: block; font-size: 11.5px; font-weight: 900; }
.badge small { font-size: 10px; font-weight: 700; color: var(--ink-soft); line-height: 1.25; display: block; }

/* ---------- modal buttons + the night shift ---------- */
.modal-btns { display: flex; gap: 10px; }
.modal-btns .bigbtn { flex: 1; }
.mg-zone.warm { background: var(--orange, #FF8A3D); }
.mg-ticks { position: absolute; left: 0; right: 0; bottom: -20px; height: 16px; }
.mg-ticks span {
  position: absolute; transform: translateX(-50%); font-size: 11px;
  font-weight: 900; color: var(--ink-soft);
}

/* ---------- look picker ---------- */
.lookrow { display: flex; gap: 6px; }
.lookbtn {
  flex: 1; border: 0; cursor: pointer; font-family: var(--ui);
  font-size: 12px; font-weight: 900; padding: 9px 4px; border-radius: 11px;
  color: var(--ink); background: linear-gradient(#FFFFFF, var(--plastic-dk));
  box-shadow: inset 0 2px 0 rgba(255,255,255,.9), 0 3px 0 rgba(0,0,0,.12);
}
.lookbtn:active { transform: translateY(2px); box-shadow: inset 0 2px 0 rgba(255,255,255,.9), 0 1px 0 rgba(0,0,0,.12); }
.lookbtn.on {
  color: #FFF; background: linear-gradient(var(--purple), var(--purple-dk));
  box-shadow: inset 0 2px 0 rgba(255,255,255,.4), 0 3px 0 var(--purple-dk);
}

/* ---------- admission pricing ---------- */
.pricebox {
  margin-top: 12px; padding: 12px; border-radius: 13px;
  background: linear-gradient(#FFFFFF, #F1F2EC);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.9), 0 3px 0 rgba(0,0,0,.09);
}
.pricehead { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.pricehead b { font-size: 13px; font-weight: 900; }
.pricehead span { font-size: 13px; font-weight: 900; color: var(--purple-dk); }
.priceslider {
  -webkit-appearance: none; appearance: none; width: 100%; height: 10px;
  margin: 11px 0 5px; border-radius: 5px; outline: none; cursor: pointer;
  background: linear-gradient(90deg, var(--blue) 0%, var(--lime) 33%, var(--yellow) 62%, var(--red) 100%);
  box-shadow: inset 0 2px 3px rgba(0,0,0,.22);
}
.priceslider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 22px; height: 22px;
  border-radius: 50%; border: 0; cursor: grab;
  background: linear-gradient(#FFFFFF, var(--plastic-dk));
  box-shadow: 0 2px 0 rgba(0,0,0,.25), inset 0 2px 0 #fff;
}
.priceslider::-moz-range-thumb {
  width: 22px; height: 22px; border-radius: 50%; border: 0; cursor: grab;
  background: linear-gradient(#FFFFFF, var(--plastic-dk));
  box-shadow: 0 2px 0 rgba(0,0,0,.25), inset 0 2px 0 #fff;
}
.pricescale { display: flex; justify-content: space-between; font-size: 9.5px;
  font-weight: 800; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .3px; }
.pricestats { display: flex; gap: 6px; margin: 10px 0 4px; }
.pricestats div {
  flex: 1; text-align: center; padding: 6px 3px; border-radius: 9px;
  background: rgba(0,0,0,.06);
}
.pricestats span { display: block; font-size: 9px; font-weight: 800; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: .3px; }
.pricestats b { font-size: 14px; font-weight: 900; }
.pricestats b.good { color: var(--lime-dk); }
.pricestats b.bad { color: var(--red-dk); }
.pricebox .hint { margin: 4px 0 0; }

/* The per-second rate on a ride card. It had no rule at all, so it rendered as
   plain italic text jammed against the name. */
.c-rate {
  font-style: normal; font-size: 11px; font-weight: 900;
  color: var(--lime-dk); margin-left: 6px; white-space: nowrap;
}

/* ---------------- exit shops ---------------- */
.shopnote {
  font-size: 13px; line-height: 1.45; font-weight: 700;
  color: var(--ink-soft); background: rgba(0, 0, 0, .16);
  border-radius: 10px; padding: 9px 11px; margin: 2px 0 10px;
}
.shopnote b { color: var(--ink); }
.shopnote b.warn { color: #FFB27A; }
.c-host {
  font-size: 11.5px; font-weight: 800; margin-top: 3px;
  color: #9FD16B; letter-spacing: .2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.c-host.warnrow { color: #FFB27A; }
.c-host:empty { display: none; }

/* ================================================================== */
/* PHONES                                                              */
/*                                                                     */
/* The shop is a 372px sidebar, which on a 390px phone is the entire    */
/* screen - you could not see the park you were building. Below this    */
/* width it becomes a bottom sheet instead: the park keeps the top      */
/* two thirds, the panel takes the bottom, and collapsing it leaves     */
/* just the tab bar. Most traffic from a vertical video is a phone, so  */
/* this is the layout most first-time players will actually meet.       */
/* ================================================================== */
@media (max-width: 720px), (pointer: coarse) and (max-width: 900px) {
  /* 46vh: four ride cards fit and the park keeps more than half the screen.
     --handle is the collapsed tab row; the Visit button, the pegman and the
     toasts all measure from it so nothing sits under the sheet. */
  :root { --sheet: 46vh; --handle: 62px; }

  .shop {
    top: auto; right: 0; left: 0; bottom: 0;
    width: auto; height: var(--sheet);
    border-radius: 22px 22px 0 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    transition: transform .26s cubic-bezier(.2,.9,.3,1.2);
  }
  /* collapsed leaves the tab row poking up, so the tabs are the handle */
  .shop.closed { transform: translateY(calc(var(--sheet) - var(--handle))); }

  .shop-head { padding: 6px 8px 6px; }
  /* 44px tall: the tab row is the sheet's handle and the touch target guide */
  .tab { font-size: 12px; padding: 13px 2px; min-height: 44px; }
  #shopToggle { transform: rotate(-90deg); }
  .shop.closed #shopToggle { transform: rotate(90deg); }
  .qty { min-height: 34px; padding: 7px 12px; font-size: 13px; }

  /* HUD is two rows that never scroll: the coin line, then three tiles.
     Park Value and Ticket are dropped here because the Park tab's price box
     already shows both, and five tiles overflowed by a few pixels so the
     scroll mask greyed the rank bar in every screenshot. */
  .hud { top: env(safe-area-inset-top, 0px); left: 8px; right: 8px; gap: 6px; }
  .coinbox { padding: 8px 16px 8px 14px; }
  .coin-amount { font-size: 26px; }
  .coin-rate { font-size: 11px; }
  .statstrip { display: flex; flex-wrap: nowrap; gap: 6px; width: 100%; max-width: 420px; overflow: hidden; }
  .stat { flex: 1 1 0; min-width: 0; padding: 6px 6px; }
  .stat b { font-size: 16px; }
  .stat i { font-size: 11px; letter-spacing: .4px; }
  .stat-pv, .stat-ticket { display: none; }

  /* With the sheet down, a strip above the handle holds the Visit button on
     the left and the pegman on the right; both leave when the sheet is up,
     because the sheet is for shopping and a tap on the tab row brings the
     park back. The camera fit in main.js keeps the table above this strip. */
  .dock { left: 8px; bottom: calc(var(--handle) + 14px); z-index: 9; }
  .dockbtn { min-height: 44px; padding: 10px 14px; }
  .shop:not(.closed):not(.hidden) ~ .dock { display: none; }
  .pegman { right: 8px; bottom: calc(var(--handle) + 10px); top: auto; width: 66px; }
  .pegman canvas { width: 66px; height: 90px; }
  .pegman.wide { right: 8px; }
  .shop:not(.closed):not(.hidden) ~ .pegman { display: none; }
  .pegtip { font-size: 11px; background: rgba(24,27,22,.72); border-radius: 8px; padding: 3px 5px;
            margin-top: -4px; text-shadow: none; }

  /* Toasts sit just above the handle strip, never over the HUD or the park's
     top edge; with the sheet up they ride on the sheet's top edge. */
  .toasts { top: auto; bottom: calc(var(--handle) + 112px); width: 100%; }
  .shop:not(.closed):not(.hidden) ~ .toasts { bottom: calc(var(--sheet) + 8px); }
  .toast { font-size: 13.5px; padding: 8px 14px; max-width: 92vw; }

  /* the closed-sheet hint: one line above the handle naming what to buy */
  .sheethint {
    position: absolute; left: 50%; bottom: calc(var(--handle) + 66px); transform: translateX(-50%);
    z-index: 9; border: 0; cursor: pointer; font-family: var(--ui); font-weight: 900; font-size: 13px;
    padding: 9px 14px; border-radius: 12px; color: #2C3600; white-space: nowrap;
    background: linear-gradient(var(--lime-lt), var(--lime));
    box-shadow: inset 0 2px 0 rgba(255,255,255,.7), 0 3px 0 var(--lime-dk), 0 6px 16px rgba(0,0,0,.3);
    animation: hintbob 1.4s ease-in-out infinite;
  }
  .sheethint.hidden { display: none; }
  .tab.nudge { animation: tabnudge 1.4s ease-in-out infinite; }

  /* tighter cards so four fit in the 46vh sheet */
  .card { padding: 6px; gap: 8px; margin-bottom: 6px; }
  .c-thumb { width: 44px; height: 44px; flex: 0 0 44px; }
  .c-sub { display: none; }          /* blurbs do not fit; the icon and name do */
  .c-name { font-size: 13px; flex-wrap: wrap; row-gap: 0; }
  .c-name em, .c-rate { font-size: 10px; }
  .c-theme { font-size: 9px; padding: 1px 5px; }
  .c-count { font-size: 18px; min-width: 26px; }
  .modal { width: min(94vw, 420px); }
}
@keyframes hintbob { 50% { transform: translate(-50%, -4px); } }
@keyframes tabnudge {
  50% { transform: translateY(-2px); box-shadow: inset 0 2px 0 #fff, 0 3px 0 var(--plastic-sh), 0 0 0 5px rgba(181,216,39,.45); }
}
@media (prefers-reduced-motion: reduce) { .sheethint, .tab.nudge { animation: none; } }

/* Landscape phones have almost no height - the shop goes back to being a
   column on the right and the HUD becomes one inline row, so the table gets
   the height. The dock and pegman go back to the bottom corners. */
@media (pointer: coarse) and (max-height: 480px) {
  :root { --sheet: 70vh; }
  .shop { width: 300px; left: auto; right: 0; top: 0; bottom: 0; height: auto;
          border-radius: 20px 0 0 20px; }
  .shop.closed { transform: translateX(calc(100% - 46px)); }
  .hud { flex-direction: row; align-items: center; gap: 8px; right: auto; }
  .coinbox { padding: 6px 14px 6px 12px; }
  .coin-amount { font-size: 22px; }
  .statstrip { width: auto; max-width: none; }
  .stat { flex: 0 0 auto; min-width: 58px; padding: 5px 8px; }
  .stat b { font-size: 14px; }
  .dock { left: 8px; bottom: 8px; }
  .shop:not(.closed):not(.hidden) ~ .dock { display: flex; }
  .pegman { right: 312px; bottom: 8px; width: 60px; }
  .pegman canvas { width: 60px; height: 82px; }
  .pegman.wide { right: 58px; }
  .shop:not(.closed):not(.hidden) ~ .pegman { display: block; }
  .toasts { top: auto; bottom: 8px; }
  .shop:not(.closed):not(.hidden) ~ .toasts { bottom: 8px; }
  .sheethint { display: none; }
}

/* ---------- touch behaviour ---------- */
/* Without these a tap on the park zooms the page, a drag on the canvas
   scrolls it, and a pull at the top reloads the whole game. */
html, body { overscroll-behavior: none; }
#app, canvas#view { touch-action: none; }
button, .tab, .card, .chip { touch-action: manipulation; }

/* ---------------- clip recorder ---------------- */
.cliprow { display: flex; gap: 6px; margin-top: 8px; }
.clipbtn {
  flex: 1; border: 0; cursor: pointer; font-family: var(--ui);
  font-weight: 900; font-size: 13px; padding: 10px 4px; border-radius: 12px;
  color: var(--ink); background: linear-gradient(#FFF, var(--plastic-dk));
  box-shadow: inset 0 2px 0 #fff, 0 3px 0 var(--plastic-sh);
}
.clipbtn:hover { filter: brightness(1.05); }
.clipbtn:active { transform: translateY(2px); box-shadow: inset 0 2px 0 #fff, 0 1px 0 var(--plastic-sh); }
.clipbtn.rec {
  background: linear-gradient(#FF8468, #E8543A); color: #fff;
  box-shadow: inset 0 2px 0 rgba(255,255,255,.35), 0 3px 0 #B03823;
  animation: recpulse 1.1s ease-in-out infinite;
}
@keyframes recpulse { 50% { filter: brightness(1.18); } }

.buildstamp {
  margin: 10px 0 0; text-align: center;
  font-size: 10.5px; font-weight: 800; letter-spacing: .4px;
  color: var(--ink-soft); opacity: .65;
}
.buildstamp b { color: var(--ink); opacity: .8; }

/* ---------- park codes + supporter ---------- */
.codebox {
  width: 100%; margin-top: 8px; box-sizing: border-box;
  min-height: 92px; resize: vertical;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 11px; line-height: 1.5; letter-spacing: .4px;
  color: var(--ink); background: #FFF;
  border: 2px solid var(--plastic-dk); border-radius: 10px; padding: 8px 10px;
  box-shadow: inset 0 2px 4px rgba(0,0,0,.08);
  word-break: break-all;
}
.codebox:focus { outline: none; border-color: var(--lime); }
.hint.good { color: var(--lime-dk); font-weight: 800; }
.hint.bad  { color: var(--red-dk);  font-weight: 800; }
.hint.warn { color: #9A6B00;        font-weight: 800; }
.supporter-on { color: var(--lime-dk); font-weight: 900; }

.c-rate.firstoff {
  color: var(--lime-dk); font-weight: 900; font-style: normal;
  font-size: 10px; letter-spacing: .3px; text-transform: uppercase;
}

/* ---------- on-screen thumb stick (visit mode, touch only) ---------- */
.stick {
  position: fixed; z-index: 40; width: 0; height: 0;
  pointer-events: none; touch-action: none;
}
.stick.hidden { display: none; }
.stick-base, .stick-nub {
  position: absolute; left: 0; top: 0; border-radius: 50%;
  transform: translate(-50%, -50%);
}
.stick-base {
  width: 132px; height: 132px;
  background: rgba(255,255,255,.14);
  border: 2px solid rgba(255,255,255,.5);
  box-shadow: 0 2px 14px rgba(0,0,0,.3);
}
.stick-nub {
  width: 58px; height: 58px;
  background: rgba(255,255,255,.82);
  border: 2px solid rgba(0,0,0,.18);
  box-shadow: 0 2px 8px rgba(0,0,0,.35);
}

/* ---------- the "this browser cannot run 3D" card ---------- */
.bootfail { max-width: 460px; text-align: center; padding: 26px 28px 24px; }
.bootfail h2 { margin: 14px 0 10px; font-size: 20px; color: var(--ink); }
.bootfail p { margin: 0 0 12px; font-size: 14.5px; line-height: 1.55; color: var(--ink-soft); }
.bootfail .bootfix { background: rgba(0,0,0,.05); border-radius: 12px; padding: 12px 14px; }
.bootfail .bootwhy {
  font-family: ui-monospace, Consolas, monospace; font-size: 11px;
  opacity: .55; margin: 4px 0 0; word-break: break-word;
}

/* one gentle pulse when the tip button is pointed at, then it stops */
.pulse { animation: tippulse 1.1s ease-out 3; }
@keyframes tippulse {
  0%, 100% { transform: none; box-shadow: inset 0 2px 0 rgba(255,255,255,.7), 0 4px 0 var(--lime-dk); }
  50% { transform: translateY(-2px); box-shadow: inset 0 2px 0 rgba(255,255,255,.9), 0 6px 0 var(--lime-dk), 0 0 0 6px rgba(181,216,39,.35); }
}
@media (prefers-reduced-motion: reduce) { .pulse { animation: none; } }
/* the same pulse, but it keeps going until the thing it points at is done:
   the first affordable ride row, and Visit Park after the first riders */
.nudge { animation: tippulse 1.3s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .nudge { animation: none; outline: 3px solid var(--lime-dk); } }

/* ---------- game feel: bumps, bursts, banners (ui.js / main.js) ---------- */
/* One scale pulse. Used on the coin pill when money lands and on the rank chip. */
.bump { animation: bump .18s ease-out; }
@keyframes bump { 30% { transform: scale(1.12); } 100% { transform: scale(1); } }
/* A shop card that just sold: a brightness flash, then back to normal. */
.card.bought { animation: bought .28s ease-out; }
@keyframes bought { 0% { filter: brightness(1.6); transform: translateY(1px); } 100% { filter: brightness(1); } }
.c-count.pop { animation: pop .25s ease-out; }
/* the xN counter on a coalesced toast */
.toast .tcount {
  display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: 9px;
  font-style: normal; font-size: 12px; background: rgba(0,0,0,.28); color: #fff;
  vertical-align: 1px;
}
.toast.good .tcount, .toast.gold .tcount { background: rgba(0,0,0,.16); color: inherit; }

/* The rank-up banner: centred, above everything but the modal, 1.8 s. */
.rankbanner {
  position: absolute; left: 50%; top: 34%; transform: translate(-50%, -50%);
  z-index: 13; pointer-events: none; text-align: center;
  padding: 16px 34px 18px; border-radius: 22px;
  background: linear-gradient(var(--yellow-lt), var(--yellow-dk)); color: #4A3600;
  box-shadow: inset 0 3px 0 rgba(255,255,255,.8), 0 10px 0 rgba(0,0,0,.18), 0 18px 40px rgba(0,0,0,.45);
  animation: rankin .45s cubic-bezier(.2,.9,.3,1.4);
  max-width: min(92vw, 520px);
}
.rankbanner small { display: block; font-size: 13px; font-weight: 900; letter-spacing: 2px; text-transform: uppercase; opacity: .75; }
.rankbanner b { display: block; font-size: 34px; font-weight: 900; line-height: 1.1; margin: 2px 0 4px; text-shadow: 0 2px 0 rgba(255,255,255,.5); }
.rankbanner em { display: block; font-style: normal; font-size: 15px; font-weight: 800; }
.rankbanner.out { animation: rankout .45s ease-in forwards; }
@keyframes rankin { from { transform: translate(-50%, -50%) scale(.5); opacity: 0; } 60% { transform: translate(-50%, -50%) scale(1.06); opacity: 1; } }
@keyframes rankout { to { transform: translate(-50%, -70%) scale(.9); opacity: 0; } }
@media (max-width: 720px) { .rankbanner b { font-size: 26px; } .rankbanner { padding: 12px 22px 14px; } }

/* GOING VIRAL: one warm flash over the whole view. Pure CSS, no lights. */
#app.viral::after {
  content: ''; position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background: radial-gradient(circle at 50% 45%, rgba(255,224,102,.32), rgba(255,224,102,0) 70%);
  animation: viral .7s ease-out forwards;
}
@keyframes viral { 0% { opacity: 0; } 20% { opacity: 1; } 100% { opacity: 0; } }

/* HUD icon buttons: sound and fullscreen, side by side under the stats. */
.hudbtns { display: flex; gap: 8px; pointer-events: auto; }
.hudbtn { font-size: 17px; line-height: 32px; text-align: center; padding: 0; }
.hudbtn:active { transform: translateY(2px); }
#hudMute .spk-off { display: none; }
#hudMute.off .spk-on { display: none; }
#hudMute.off .spk-off { display: inline; }
#hudMute.off { opacity: .7; }

@media (prefers-reduced-motion: reduce) {
  .bump, .card.bought, .c-count.pop, #app.viral::after { animation: none; }
  .rankbanner { animation: none; }
}

/* the ride card warning line: "goes to storage - expand the table" */
.c-rate.warn { color: var(--orange, #F07A2A); font-weight: 700; }


/* Friends: the park code is read out loud and typed in by hand, so it wants to
   be big, monospaced and unambiguous. The alphabet it is generated from has no
   O or I in it for the same reason. */
.roomcode {
  font: 700 30px/1.1 ui-monospace, Consolas, "Courier New", monospace;
  letter-spacing: .22em; text-align: center; padding: 10px 0 12px;
  color: #F6C42A; text-shadow: 0 2px 0 rgba(0,0,0,.35);
  user-select: all; cursor: text;
}
.joinrow { margin-top: 8px; }
.joincode {
  flex: 0 0 7.5em; min-width: 0;
  font: 700 16px/1 ui-monospace, Consolas, "Courier New", monospace;
  letter-spacing: .16em; text-transform: uppercase; text-align: center;
  padding: 8px 6px; border-radius: 8px;
  border: 1px solid rgba(255,255,255,.22);
  background: rgba(0,0,0,.28); color: #fff;
}
.joincode::placeholder { color: rgba(255,255,255,.30); letter-spacing: .16em; }


/* ---------------------------------------------------------------- emotes
   A row over the bottom of the view while somebody else is in the park.
   pointer-events are off on the HUD as a whole, so the bar turns them back
   on for itself only, the same way the mute button does. */
.emotebar {
  /* fixed, not absolute. The HUD is itself absolutely positioned at the top
     left, so an absolute child is laid out inside a 300-odd pixel box in the
     corner and "bottom: 14px, centred" put the row a third of the way down the
     left hand side of the screen. */
  position: fixed;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  display: flex;
  gap: 6px;
  padding: 5px;
  border-radius: 14px;
  background: rgba(14, 16, 32, 0.58);
  backdrop-filter: blur(6px);
  pointer-events: auto;
  z-index: 6;
}
.emotebar button {
  width: 42px;
  height: 42px;
  border: 0;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.10);
  color: #fff;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.12s, transform 0.12s;
}
.emotebar button:hover { background: rgba(255, 255, 255, 0.22); }
.emotebar button:active { transform: scale(0.92); }
.emotebar button i {
  position: absolute;
  margin: 26px 0 0 26px;
  font-size: 9px;
  font-style: normal;
  opacity: 0.55;
}
/* On a phone the sheet owns the bottom of the screen and it changes height,
   so there is no fixed offset that clears it. Drop the bar back into the HUD's
   own column instead: it stacks under the buffs, moves when they do, and
   cannot collide with anything. */
@media (max-width: 720px) {
  .emotebar {
    position: static;
    transform: none;
    left: auto;
    bottom: auto;
  }
  .emotebar button { width: 38px; height: 38px; font-size: 18px; }
}


/* ---------------------------------------------------------------- today
   The day card. It sits under the stats in the HUD column, borrows the panel
   look, and takes its accent from the day itself so a heatwave and a downpour
   do not look the same at a glance. */
.todaycard {
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding: 7px 13px 8px 13px;
  border: 0;
  border-left: 5px solid var(--today, #BFD8E8);
  text-align: left;
  font: inherit;
  cursor: pointer;
  max-width: 260px;
}
.todaycard b { font-size: 13px; font-weight: 900; line-height: 1.15; }
.todaycard i {
  font-style: normal;
  font-size: 11px;
  font-weight: 700;
  opacity: 0.62;
  line-height: 1.25;
}
.todaycard:hover { filter: brightness(1.06); }
/* .good and .bad are only defined for toasts, where they are a whole
   background. Inside a stat list they need to be text colours. */
.statlist b.good { color: #2E7D32; }
.statlist b.bad  { color: #B23A2E; }
@media (max-width: 720px) {
  .todaycard { max-width: 200px; padding: 5px 10px 6px 10px; }
  .todaycard b { font-size: 12px; }
  .todaycard i { display: none; }
}
