/* ===========================================================================
   TYPING METRO — "SUNNY DEPOT"

   A bright cartoon world, the same one the train character already lives in:
   white bodies, thick ink outlines, candy colours, a sky with clouds in it.
   Every control is a toy — it has a body, a hard shadow under it, and it goes
   DOWN when pressed. Nothing on this screen may blur, glow over, or dim the
   text: the old scanline/vignette overlay and every backdrop-filter are gone,
   which is what made the letters soft.

   Three faces:
   - Lilita One letters the Latin signage (brand, GO, plates) — a chunky
     cartoon display face. Jua stands behind it for Hangul and Mochiy Pop One
     for kana/kanji, so a heading always comes out rounded and game-like.
   - Azeret Mono is the readout: the clock, the ranks, the gem counts. It is
     monospaced by construction so the timer can never shake.
   - Noto Sans carries body copy and every station name, because it is the one
     face that leaves no script unrendered.
   =========================================================================== */
@import url('https://fonts.googleapis.com/css2?family=Lilita+One&family=Jua&family=Mochiy+Pop+One&family=Azeret+Mono:wght@500;700&family=Noto+Sans+JP:wght@400;700;900&family=Noto+Sans+KR:wght@400;700;900&display=swap');

/* --app-height/--app-offset track the visual viewport, so the on-screen keyboard
   shrinks the app instead of covering it. The script overwrites them; the values
   here are the no-keyboard fallback. */
:root{
  --ui:'Noto Sans KR','Noto Sans JP',sans-serif;
  --display:'Lilita One','Jua','Mochiy Pop One','Noto Sans KR',sans-serif;
  /* Noto rides in the readout stack because fallback is per glyph: a nickname may
     be Hangul, and 타/분 tails a number. Latin and digits still come from Azeret,
     so the clock stays monospaced by construction. */
  --tabular:'Azeret Mono','Noto Sans KR','Noto Sans JP',ui-monospace,Consolas,monospace;
  --label:800 10px/1.4 var(--ui);
  --app-height:100dvh;--app-offset:0px;
  /* The daylight palette. --ink is the same ink the train is drawn with. */
  --sky:#8ed3ff;--sky-lo:#dcf3ff;
  --paper:#ffffff;--cream:#fff9ec;--tint:#eef6ff;
  --ink:#22314f;--muted:#5f7396;--faint:#9db0cc;
  --outline:#22314f;
  --brand:#ffb428;--brand-deep:#e08e00;--on-brand:#3c2a00;
  --accent:#ff5d6f;--accent-deep:#d63d50;
  --go:#26c465;--go-deep:#149a4a;
  --danger:#ff5d6f;
  --line:#3d8bfd;
  --line-deep:color-mix(in srgb,var(--line) 72%,#0a1220);
  --line-soft:color-mix(in srgb,var(--line) 14%,#fff);
  /* Hard toy shadows: an ink-tinted slab, never a blur. */
  --shadow:0 4px 0 rgba(34,49,79,.14);
  --shadow-lg:0 7px 0 rgba(34,49,79,.16);
  --r:16px;--r-sm:12px;
  --safe-bottom:env(safe-area-inset-bottom,0px)
}
html[lang="ja"]{--ui:'Noto Sans JP','Noto Sans KR',sans-serif;--display:'Lilita One','Mochiy Pop One','Jua','Noto Sans JP',sans-serif}
*{box-sizing:border-box}
html,body{margin:0;width:100%;height:100%;overflow:hidden;background:var(--sky);color:var(--ink);font-family:var(--ui)}
button,input{font:inherit}button{color:inherit}
::selection{background:var(--brand);color:var(--on-brand)}
/* The hidden attribute must win. Many controls here set their own display (flex,
   grid), and an author display rule beats the UA stylesheet's [hidden]{display:none},
   so a "hidden" ad-consent row or ad-spin button would otherwise stay on screen. */
[hidden]{display:none!important}

/* Fixed, not relative: html/body still span the full layout viewport when a keyboard
   is up, so an in-flow shell gets scrolled out of view when the browser reveals the
   focused field. Pinning to the visual viewport keeps it put. */
.app-shell{position:fixed;top:var(--app-offset);left:0;right:0;height:var(--app-height);min-height:560px;background:linear-gradient(#8ed3ff 0%,#b5e3ff 46%,#e6f7ff 100%);overflow:hidden;isolation:isolate}
@media(pointer:coarse){.app-shell{min-height:0}}
/* A faint dot texture, like printed paper — well under the text's contrast. */
.app-shell:before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;background-image:radial-gradient(rgba(255,255,255,.5) 1.5px,transparent 1.5px);background-size:26px 26px;opacity:.5}

/* The four <i> that used to be tunnel lights are clouds now, drifting across the
   sky the way scenery should: behind everything, slowly, in daylight. */
.tunnel{position:absolute;inset:0;z-index:0;overflow:hidden;pointer-events:none}
.tunnel i{position:absolute;height:26px;width:88px;border-radius:20px;background:#fff;opacity:.85;animation:cloud-drift 52s linear infinite}
.tunnel i:before{content:"";position:absolute;left:18px;top:-14px;width:34px;height:34px;border-radius:50%;background:#fff}
.tunnel i:after{content:"";position:absolute;right:16px;top:-9px;width:26px;height:26px;border-radius:50%;background:#fff}
.tunnel i:nth-child(1){top:9%;animation-duration:58s}
.tunnel i:nth-child(2){top:20%;transform:scale(1.5);opacity:.7;animation-duration:74s;animation-delay:-30s}
.tunnel i:nth-child(3){top:34%;transform:scale(.8);opacity:.6;animation-duration:46s;animation-delay:-14s}
.tunnel i:nth-child(4){top:52%;transform:scale(1.2);opacity:.45;animation-duration:88s;animation-delay:-52s}
@keyframes cloud-drift{from{left:-140px}to{left:105%}}

/* Top bar: a white shelf with a firm ink rule under it. The run replaces it with
   the game HUD, which frees its height for the map. */
.topbar{height:76px;padding:0 4vw;display:flex;align-items:center;justify-content:space-between;gap:10px;background:var(--paper);border-bottom:3px solid var(--outline);position:relative;z-index:20;box-shadow:0 4px 0 rgba(34,49,79,.10)}
.in-game .topbar{display:none}
.in-game .game-screen{height:var(--app-height)}
.brand{min-width:0;border:0;background:none;display:flex;align-items:center;gap:11px;text-align:left;cursor:pointer}
.brand>span:last-child{min-width:0;overflow:hidden}
.brand-mark{flex:0 0 auto;width:46px;height:46px;border-radius:14px;background:var(--sky);display:grid;place-items:center;border:3px solid var(--outline);box-shadow:0 3px 0 var(--outline);overflow:hidden}
.brand-mark svg{width:40px;height:40px;display:block}
.brand:hover .brand-mark svg{animation:mascot-hop .5s ease}
@keyframes mascot-hop{30%{transform:translateY(-3px) rotate(-4deg)}60%{transform:translateY(0) rotate(3deg)}}
.brand b{display:block;font:400 17px/1 var(--display);letter-spacing:.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink)}
.brand small{display:block;color:var(--muted);font-size:10px;font-weight:700;margin-top:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.top-actions{flex:0 0 auto;display:flex;align-items:center;gap:8px}
/* Toy buttons: a body, a hard shadow, and travel when pressed. */
.icon-button,.player-chip,.rank-button,.gem-chip{border:3px solid var(--outline);background:var(--paper);border-radius:13px;height:44px;padding:0 13px;cursor:pointer;box-shadow:0 3px 0 var(--outline);transition:transform .08s,box-shadow .08s,background .15s}
.icon-button:hover,.player-chip:hover,.gem-chip:hover{background:var(--tint)}
.icon-button:active,.player-chip:active,.rank-button:active,.gem-chip:active{transform:translateY(3px);box-shadow:0 0 0 var(--outline)}
.icon-button{width:44px;padding:0;color:var(--ink)}
.player-chip{display:flex;align-items:center;gap:8px;font:700 12px var(--tabular)}
.status-dot{width:8px;height:8px;border-radius:50%;background:var(--go);border:2px solid var(--outline);animation:beacon 2.4s ease-in-out infinite}
.rank-button{background:var(--brand);color:var(--on-brand);font-weight:900}
.rank-button:hover{background:#ffc44f}
/* The notice button wears the same 44px chip as its neighbours; the unread dot rides
   its top-right corner and pulses until the notice has been closed once. */
.notice-button{border:3px solid var(--outline);background:var(--paper);border-radius:13px;width:44px;height:44px;padding:0;font-size:17px;line-height:1;cursor:pointer;position:relative;box-shadow:0 3px 0 var(--outline);transition:transform .08s,box-shadow .08s,background .15s}
.notice-button:hover{background:var(--tint)}
.notice-button:active{transform:translateY(3px);box-shadow:0 0 0 var(--outline)}
.notice-dot{position:absolute;top:-5px;right:-5px;width:12px;height:12px;border-radius:50%;background:var(--danger);border:2.5px solid var(--outline);animation:beacon 2.4s ease-in-out infinite}
.gem-chip{font:700 14px var(--tabular);color:var(--ink)}
.gem-chip b{font-weight:700}
.gem-mark{font-size:14px}
/* Language picker. A grid dialog of flag tiles, opened from the menu row, so
   seven languages read as a picker rather than a stacked list. */
.lang-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:9px;margin-top:18px}
.lang-tile{display:flex;flex-direction:column;align-items:center;gap:2px;padding:14px 8px;border:3px solid rgba(34,49,79,.18);border-radius:var(--r-sm);background:var(--paper);color:var(--ink);cursor:pointer;transition:transform .1s,border-color .15s,background .15s,box-shadow .1s}
.lang-tile:hover{border-color:var(--outline);transform:translateY(-2px)}
.lang-tile:active{transform:translateY(0)}
.lang-tile .lang-flag{font-size:30px;line-height:1.1}
.lang-tile b{font-size:14px;font-weight:900}
.lang-tile small{color:var(--faint);font:700 10px var(--tabular)}
.lang-tile.active{border-color:var(--brand);background:color-mix(in srgb,var(--brand) 14%,var(--paper));box-shadow:0 3px 0 var(--brand)}
.lang-tile.active b{color:var(--brand-deep)}
.screen{display:none;height:calc(var(--app-height) - 76px);position:relative;z-index:2}.screen.active{display:flex}

/* A little flag label: bold, coloured, with a live dot. */
.eyebrow{color:var(--accent);letter-spacing:1.4px;font:900 11px/1.4 var(--ui);text-transform:uppercase;margin:0 0 14px;display:flex;align-items:center;gap:8px}
.eyebrow:before{content:"";width:8px;height:8px;flex:0 0 auto;border-radius:50%;background:var(--accent);border:2px solid var(--outline);animation:beacon 1.8s ease-in-out infinite}

/* Lobby: the title yard. Big rounded letters, the mascot riding its own track, and
   the departure board beside them — compact tiles, since the board only ever grows. */
.lobby-screen{align-items:center;justify-content:center;gap:5vw;padding:4vw}
.lobby-copy{max-width:520px;min-width:0;animation:rise .5s .05s backwards ease-out}
.lobby-copy h1{font:400 clamp(36px,4.8vw,68px)/1.08 var(--display);letter-spacing:0;margin:0;color:var(--ink);text-shadow:0 4px 0 rgba(255,255,255,.85)}
.lobby-copy h1 em{font-style:normal;color:var(--accent);text-shadow:0 4px 0 rgba(255,255,255,.85)}
.lobby-copy>p:not(.eyebrow){color:var(--ink);opacity:.75;font-size:15px;font-weight:700;line-height:1.85;max-width:52ch}
/* The mascot's stage: two rails, sleepers scrolling under a train that stays put —
   perpetual motion for the price of one background animation. */
.lobby-stage{position:relative;height:112px;margin-top:14px;max-width:470px}
.lobby-stage:before{content:"";position:absolute;left:0;right:0;top:50%;height:46px;transform:translateY(-50%);background:repeating-linear-gradient(90deg,rgba(34,49,79,.18) 0 7px,transparent 7px 36px);animation:track-scroll .9s linear infinite}
.lobby-stage:after{content:"";position:absolute;left:0;right:0;top:50%;height:30px;transform:translateY(-50%);border-top:4px solid rgba(34,49,79,.30);border-bottom:4px solid rgba(34,49,79,.30)}
.lobby-train{position:absolute;left:50%;top:50%;width:136px;transform:translate(-50%,-50%);z-index:1;animation:lobby-float 1.6s ease-in-out infinite alternate}
.lobby-train svg{width:100%;height:auto;display:block}
/* Steam puffs trailing off behind the train. */
.stage-puff{position:absolute;left:calc(50% - 80px);top:36%;width:14px;height:14px;border-radius:50%;background:rgba(255,255,255,.92);border:2px solid rgba(34,49,79,.16);animation:puff 1.6s ease-out infinite}
.stage-puff:nth-child(2){top:54%;width:10px;height:10px;animation-delay:.8s}
@keyframes track-scroll{to{background-position:-36px 0}}
@keyframes lobby-float{from{transform:translate(-50%,-54%) rotate(-1.2deg)}to{transform:translate(-50%,-46%) rotate(1.2deg)}}
@keyframes puff{from{opacity:.9;transform:none}to{opacity:0;transform:translate(-48px,-12px) scale(1.9)}}
/* Stretched to the screen's height rather than capped by a percentage: a percentage
   max-height collapses the whole column when the app is squeezed (a phone keyboard,
   a small pane), while stretch+min-height lets the grid scroll inside whatever is left. */
.country-picker{width:min(600px,48vw);align-self:stretch;display:flex;flex-direction:column;justify-content:center;min-height:0;animation:rise .5s .15s backwards ease-out}
.country-picker>p{flex:0 0 auto;margin:0 0 12px;color:var(--ink);font:900 12px/1.4 var(--ui);letter-spacing:1px;text-transform:uppercase;display:flex;align-items:center;gap:9px}
.country-picker>p:after{content:"";flex:1;height:3px;border-radius:2px;background:rgba(34,49,79,.15)}
/* The board's live tally: the world keeps growing, and the lobby says so. */
.live-count{display:inline-flex;align-items:center;gap:6px;background:var(--go);color:#fff;border:2px solid var(--outline);border-radius:999px;padding:3px 10px;font:800 10px/1.4 var(--ui);letter-spacing:.4px;box-shadow:0 2px 0 var(--outline);white-space:nowrap}
.live-count:empty{display:none}
.live-count:before{content:"";width:6px;height:6px;border-radius:50%;background:#fff;animation:beacon 1.8s ease-in-out infinite}
/* Compact boarding-gate tiles on an auto-fill grid: more countries just means more
   rows, and past a screenful the board scrolls under its own heading. */
.country-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(186px,1fr));gap:10px;overflow:auto;min-height:0;padding:3px 3px 8px}
.country-card{--plate:var(--brand);min-height:70px;border:3px solid var(--outline);background:var(--paper);border-radius:var(--r);box-shadow:0 4px 0 var(--outline);padding:11px 12px;text-align:left;display:flex;align-items:center;gap:11px;position:relative;overflow:hidden;cursor:pointer;transition:transform .15s,box-shadow .15s,background .15s;animation:rise .4s backwards ease-out}
.country-card:before{content:"";position:absolute;right:-28px;top:-28px;width:84px;height:84px;border-radius:50%;background:var(--plate);opacity:.14;transition:opacity .2s,transform .2s}
.country-card.available:hover{transform:translateY(-3px);box-shadow:0 7px 0 var(--outline);background:var(--cream)}
.country-card.available:hover:before{opacity:.3;transform:scale(1.2)}
.country-card.available:active{transform:translateY(3px);box-shadow:0 1px 0 var(--outline)}
.country-plate{flex:0 0 auto;min-width:46px;height:46px;padding:0 8px;display:grid;place-items:center;border-radius:12px;border:3px solid var(--outline);color:#fff;background:var(--plate);font:400 16px/1 var(--display);text-shadow:0 2px 0 rgba(0,0,0,.25);box-shadow:0 2px 0 var(--outline);position:relative;z-index:1}
.country-copy{display:block;flex:1;min-width:0;position:relative;z-index:1}
.country-copy b{display:block;font:400 clamp(16px,1.5vw,19px)/1.15 var(--display);color:var(--ink);word-break:keep-all;overflow-wrap:break-word}
.country-copy em{display:block;margin-top:3px;color:var(--faint);font:var(--label);font-style:normal;letter-spacing:.6px;text-transform:uppercase}
.card-arrow{position:absolute;right:12px;bottom:11px;z-index:1;width:34px;height:34px;display:grid;place-items:center;background:var(--plate);color:#fff;font-size:17px;font-weight:900;border-radius:11px;border:3px solid var(--outline);box-shadow:0 2px 0 var(--outline)}
/* On the compact tile the arrow rides the row's end, not a corner. */
.country-card .card-arrow{position:static;flex:0 0 auto;width:26px;height:26px;font-size:13px;border-radius:9px}
.country-card.locked{cursor:default;border-style:dashed;box-shadow:none;background:rgba(255,255,255,.55)}
.country-card.locked .country-plate{border-style:dashed;background:none;color:var(--faint);box-shadow:none;text-shadow:none}
.country-card.locked .country-copy b{color:var(--muted)}
.country-card.locked .card-arrow{background:none;color:var(--faint);border-style:dashed;box-shadow:none}
.country-card.loading{pointer-events:none}
.country-card.loading .card-arrow{animation:pulse .8s ease-in-out infinite alternate}

/* Line and course selection */
.select-screen,.course-screen,.mode-screen,.nav-screen,.friendly-screen{flex-direction:column;padding:32px 5vw 44px;overflow:auto}
.selection-head{display:flex;align-items:center;gap:18px;margin-bottom:22px;animation:rise .4s backwards ease-out}
.selection-head h2{font:400 clamp(27px,2.9vw,40px)/1.1 var(--display);color:var(--ink);margin:0;text-shadow:0 3px 0 rgba(255,255,255,.8)}
.selection-head,.region-tabs{flex-shrink:0}
.back-button{width:48px;height:48px;flex:0 0 auto;border-radius:14px;border:3px solid var(--outline);background:var(--paper);color:var(--ink);font-size:22px;font-weight:900;cursor:pointer;box-shadow:0 3px 0 var(--outline);transition:transform .08s,box-shadow .08s}
.back-button:hover{background:var(--tint)}
.back-button:active{transform:translateY(3px);box-shadow:0 0 0 var(--outline)}
.region-tabs{display:flex;gap:8px;overflow:auto;padding:3px 3px 16px;min-height:56px;position:relative;z-index:3;animation:rise .4s .05s backwards ease-out}
.region-tabs button{border:3px solid var(--outline);background:var(--paper);border-radius:999px;padding:9px 18px;color:var(--ink);white-space:nowrap;cursor:pointer;font-weight:900;box-shadow:0 3px 0 var(--outline);transition:transform .08s,box-shadow .08s,background .15s}
.region-tabs button:hover{background:var(--cream)}
.region-tabs button:active{transform:translateY(3px);box-shadow:0 0 0 var(--outline)}
.region-tabs button.active{background:var(--brand);color:var(--on-brand)}
/* The tab's line count: quiet, beside the name. */
.region-tabs button em{font:700 10px/1 var(--tabular);font-style:normal;opacity:.5;margin-left:5px}
.line-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(236px,1fr));gap:11px;padding:3px 3px 50px;flex:0 0 auto}
/* Each tile is a ticket signed in its line's own colour: a spine, a wash of the
   line's hue, and the two ends of the track it sells. */
.line-card{min-height:104px;border:3px solid var(--outline);background:linear-gradient(105deg,color-mix(in srgb,var(--card-line) 10%,#fff) 0%,#fff 60%);border-radius:var(--r);box-shadow:0 4px 0 var(--outline);padding:14px 14px 14px 20px;display:flex;align-items:center;gap:13px;text-align:left;cursor:pointer;transition:transform .12s,box-shadow .12s,filter .15s;position:relative;overflow:hidden;animation:rise .35s backwards ease-out}
.line-card:before{content:"";position:absolute;left:0;top:0;bottom:0;width:7px;background:var(--card-line)}
.line-card:hover{transform:translateY(-2px);box-shadow:0 6px 0 var(--outline);filter:brightness(1.02) saturate(1.15)}
.line-card:active{transform:translateY(2px);box-shadow:0 1px 0 var(--outline)}
.line-copy{flex:1;min-width:0}
.line-ends{display:block;margin-top:4px;color:var(--ink);opacity:.72;font-size:11.5px;font-weight:800;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.line-meta{display:block;margin-top:3px;color:var(--muted);font:var(--label)}
/* The badge is filled with the line's real colour; the shadow keeps pale marks
   (서울9호선 is #bdb092) legible without knowing which lines are light. */
.line-badge,.panel-route>span,.hud-route>span{width:48px;height:48px;flex:0 0 auto;border-radius:50%;display:grid;place-items:center;background:var(--card-line);color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.55);font-weight:900;font-size:14px;line-height:1;white-space:nowrap;border:3px solid var(--outline);box-shadow:inset 0 0 0 2px rgba(255,255,255,.5)}
.line-card b{display:block;font:400 17px/1.2 var(--display);color:var(--ink)}
/* 주사위 티켓: 다른 티켓과 같은 꼴로 서되, 한 노선의 색 대신 여러 색을 한 번에 두른다 —
   지역 탭과 상관없이 이 나라 전체에서 하나를 뽑는다는 뜻이 색으로 먼저 읽힌다. */
/* 주사위는 한 줄을 다 쓴다: 노선 한 칸에 끼면 옆 노선과 나란히 서서 그중 하나처럼
   읽히고, 아래 노선들이 어긋난 자리에서 시작한다. 폭을 다 쓰면 힌트도 다 읽힌다. */
.random-card{--card-line:#8b5cf6;grid-column:1/-1}
.random-card:before{background:linear-gradient(#ff5d6f,#ffb020 34%,#31c48d 66%,#3d8bfd)}
.random-card .line-badge{background:linear-gradient(140deg,#ff5d6f,#ffb020 35%,#31c48d 65%,#3d8bfd);font-size:22px;text-shadow:none}
/* Course and navigator setup both read as one centred column now — the phone layout,
   given to every width — rather than sprawling into a second desktop pane. */
.course-layout,.nav-setup{display:flex;flex-direction:column;gap:16px;width:100%;max-width:520px;margin:0 auto}
.course-list,.nav-mode-list{display:grid;grid-template-columns:1fr;gap:11px;align-content:start;padding:3px}
/* The fork: two posters side by side, each mode acted out as a little scene — the
   daylight dash down one line, and the night network to roam with a compass. */
.mode-choices{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;width:100%;max-width:780px;margin:0 auto}
/* "How to play" under the two mode cards: quiet, but there when it's wanted. */
.mode-help{display:block;margin:18px auto 0;border:3px solid var(--outline);background:var(--paper);color:var(--ink);font-weight:800;font-size:13px;padding:10px 20px;border-radius:999px;box-shadow:0 3px 0 var(--outline);cursor:pointer}
.mode-help:active{transform:translateY(2px);box-shadow:0 1px 0 var(--outline)}
.mode-card{display:flex;flex-direction:column;padding:0;border:3px solid var(--outline);background:var(--paper);border-radius:var(--r);box-shadow:0 5px 0 var(--outline);text-align:left;cursor:pointer;overflow:hidden;transition:transform .14s,box-shadow .14s;animation:rise .35s backwards ease-out}
.mode-card:nth-child(2){animation-delay:.07s}
.mode-card:hover{transform:translateY(-3px);box-shadow:0 8px 0 var(--outline)}
.mode-card:active{transform:translateY(3px);box-shadow:0 1px 0 var(--outline)}
.mode-scene{position:relative;display:block;height:150px;background:linear-gradient(#aadcff,#e8f7ff);border-bottom:3px solid var(--outline)}
.mode-card.nav .mode-scene{background:linear-gradient(160deg,#1b2742,#33476f)}
.mode-scene svg{position:absolute;inset:0;width:100%;height:100%}
.mode-tag{position:absolute;left:12px;top:12px;z-index:1;background:#fff;border:2px solid var(--outline);border-radius:999px;padding:3px 11px;font:700 10px/1.4 var(--tabular);letter-spacing:1.6px;color:var(--ink);box-shadow:0 2px 0 var(--outline)}
.mode-body{display:flex;align-items:center;gap:12px;padding:14px 16px}
.mode-copy{flex:1;min-width:0}
.mode-copy b{display:block;font:400 21px/1.2 var(--display);color:var(--ink)}
.mode-copy small{display:block;margin-top:4px;color:var(--muted);font:var(--label);line-height:1.5}
.mode-go{flex:0 0 auto;width:34px;height:34px;display:grid;place-items:center;border-radius:11px;border:3px solid var(--outline);background:var(--go);color:#fff;font-size:16px;font-weight:900;box-shadow:0 2px 0 var(--outline)}
.mode-card.nav .mode-go{background:var(--accent)}
/* The scenes breathe: the train bobs down its line, the compass keeps checking its
   bearing, the destination pings. Inner groups animate so the outer placement
   transforms (translate/rotate attributes) survive the CSS transform. */
.scene-bob{animation:scene-bob 1.3s ease-in-out infinite alternate;transform-box:fill-box;transform-origin:center}
.scene-compass{animation:compass-check 2.2s ease-in-out infinite;transform-box:fill-box;transform-origin:center}
.scene-goal-ring{animation:goal-ping 1.4s ease-out infinite;transform-box:fill-box;transform-origin:center}
@keyframes scene-bob{from{transform:rotate(-2.4deg)}to{transform:rotate(2.4deg)}}
@keyframes compass-check{0%,100%{transform:rotate(-10deg)}50%{transform:rotate(10deg)}}
@keyframes goal-ping{from{transform:scale(.7);opacity:1}to{transform:scale(1.7);opacity:0}}
.course-card{border:3px solid var(--outline);border-radius:var(--r);background:var(--paper);box-shadow:0 4px 0 var(--outline);padding:16px;text-align:left;cursor:pointer;transition:transform .12s,box-shadow .12s,background .15s;animation:rise .35s backwards ease-out}
.course-card:hover{background:var(--cream);transform:translateY(-2px);box-shadow:0 6px 0 var(--outline)}
.course-card.active{background:var(--line-soft);border-color:var(--line-deep);box-shadow:0 4px 0 var(--line-deep)}
.course-card small{color:var(--muted);font:var(--label);letter-spacing:.6px}
.course-card b{display:block;font-size:17px;font-weight:900;margin:7px 0}
.course-card.active b{color:var(--line-deep)}
.mini-track{display:flex;align-items:center;margin-top:14px}
.mini-track i{width:10px;height:10px;border-radius:50%;background:var(--line);border:2px solid var(--outline)}
.mini-track span{height:3px;flex:1;background:var(--line);opacity:.6}
/* The departure console: what is about to happen, and the key that starts it. */
.departure-panel{background:var(--paper);border:3px solid var(--outline);border-radius:var(--r);box-shadow:var(--shadow-lg);padding:22px;display:grid;gap:18px;align-content:start;animation:rise .4s .1s backwards ease-out}
.panel-route,.hud-route{display:flex;align-items:center;gap:13px}
.panel-route small,.hud-route small{color:var(--muted);display:block;font:var(--label);letter-spacing:.6px;text-transform:uppercase}
.panel-route b{font-size:16px;font-weight:900;margin-top:3px;display:block}
.lang-picker>small{display:block;color:var(--muted);font:var(--label);letter-spacing:.6px;text-transform:uppercase;margin-bottom:8px}
.lang-options{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.lang-options button{border:3px solid rgba(34,49,79,.25);background:var(--paper);border-radius:var(--r-sm);padding:11px 10px;text-align:left;cursor:pointer;transition:.15s;min-width:0}
.lang-options button:hover{border-color:var(--outline)}
.lang-options button b{display:block;font-size:13px;font-weight:900}
.lang-options button small{display:block;margin-top:3px;color:var(--muted);font-size:11px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lang-options button.active{border-color:var(--line-deep);background:var(--line-soft);box-shadow:0 2px 0 var(--line-deep)}
.lang-options button.active b{color:var(--line-deep)}
.lang-options button.active small{color:var(--line-deep);opacity:.8}
/* The GO key: the biggest, most pressable thing on the screen. */
.start-button{width:100%;border:3px solid var(--outline);border-radius:var(--r);background:var(--go);color:#fff;padding:16px;cursor:pointer;font:400 20px var(--display);text-shadow:0 2px 0 rgba(0,0,0,.22);box-shadow:0 6px 0 var(--go-deep),0 9px 0 rgba(34,49,79,.2);transition:transform .07s,box-shadow .07s,filter .1s}
.start-button small{display:block;font:800 10px/1.4 var(--ui);opacity:.9;margin-top:4px;letter-spacing:.4px}
.start-button:not(:disabled):hover{filter:brightness(1.06)}
.start-button:not(:disabled):active{transform:translateY(6px);box-shadow:0 0 0 var(--go-deep),0 2px 0 rgba(34,49,79,.2)}
.start-button:disabled{background:#c9d6e8;color:#8697b3;cursor:not-allowed;text-shadow:none;box-shadow:0 6px 0 #a9bad2}

/* Game screen: the map owns the whole stage and the controls float over it. */
.game-screen{flex-direction:column;padding:0}
/* --hud-h: HUD 한 줄의 높이. 그 아래로 이어 붙는 배틀 순위 띠와 유예 배너가 같은
   값을 읽으므로, 화면 폭마다 달라지는 이 수를 한 곳에서만 고치면 된다. */
.game-stage{position:relative;height:100%;min-height:0;--hud-h:74px}
.game-hud{position:absolute;z-index:3;top:0;left:0;right:0;height:74px;display:flex;align-items:center;justify-content:space-between;padding:10px 3vw;border-bottom:3px solid var(--outline);background:var(--paper);box-shadow:0 4px 0 rgba(34,49,79,.10)}
.hud-route>span{width:42px;height:42px}
.hud-route b{font-size:13px;font-weight:900;display:block;margin-top:2px}
.hud-metrics{display:flex;gap:30px;text-align:center}
.hud-metrics small{display:block;color:var(--muted);font:800 9px/1.4 var(--ui);letter-spacing:.6px;text-transform:uppercase}
.hud-metrics b{display:block;min-width:9ch;margin-top:3px;font:700 18px/1.25 var(--tabular);color:var(--ink)}
.exit-button{border:3px solid var(--outline);background:var(--paper);color:var(--ink);font-weight:900;padding:9px 13px;border-radius:12px;box-shadow:0 3px 0 var(--outline);cursor:pointer;transition:transform .08s,box-shadow .08s,color .15s}
/* The in-game "how to play": a small round ? beside the exit button. */
.help-button{width:38px;height:38px;border:3px solid var(--outline);background:var(--paper);color:var(--ink);font:900 18px var(--ui);border-radius:50%;box-shadow:0 3px 0 var(--outline);cursor:pointer;margin-right:8px}
.help-button:active{transform:translateY(2px);box-shadow:0 1px 0 var(--outline)}
.exit-button:hover{color:var(--danger)}
.exit-button:active{transform:translateY(3px);box-shadow:0 0 0 var(--outline)}

/* The route map: a paper map in daylight. Real metro colours are picked to sit on
   white paper, so on cream they finally read at full strength. */
.map-world{position:absolute;z-index:1;inset:0;overflow:hidden;background:linear-gradient(#f2fbff,#fdf8ec);touch-action:none}
.map-world:before{content:"";position:absolute;inset:0;background-image:linear-gradient(rgba(61,139,253,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(61,139,253,.07) 1px,transparent 1px);background-size:48px 48px}
.route-map{position:absolute;inset:0;width:100%;height:100%}
.route-map.mapless{display:none}
.map-others polyline{fill:none;stroke-width:9;stroke-linecap:round;stroke-linejoin:round;opacity:.2}
.map-route polyline{fill:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round}
.map-dots circle{fill:#fff;stroke-width:5;transition:fill .3s}
.map-dots circle.done{fill:var(--line)}
.map-dots circle.current{fill:#fff;stroke-width:8}
/* Each name is painted its line's colour (--label-fill, set per label by the script;
   a transfer gets an SVG gradient url). The white halo underneath keeps it legible even
   where the text sits on its own same-coloured track. */
.map-labels text{font:800 15px var(--ui);fill:var(--label-fill,var(--muted));text-anchor:middle;opacity:0;transition:opacity .35s}
.map-labels text.near{opacity:.95;fill:var(--label-fill,var(--ink));paint-order:stroke;stroke:#fff;stroke-width:4;stroke-linejoin:round}
.map-labels text.current{opacity:1;fill:var(--label-fill,var(--ink));font-size:21px;font-weight:900;paint-order:stroke;stroke:#fff;stroke-width:6;stroke-linejoin:round}
.map-labels .label-sub{display:none;font-size:13px;font-weight:800;fill:var(--muted)}
.map-labels text.current .label-sub{display:inline}
/* The arrow on a name pinned to the band's edge: that stop is out there, this way. */
.map-labels .label-pin{font-weight:900}
.map-labels text.clash{opacity:0}
/* Keyboard up — or the tour's step that names your choices — only the reachable stops
   are named, and a clash outranks even the goal's own colour so the destination label
   steps aside with the rest. */
.keyboard-up .map-labels text.clash,
.reach-only .map-labels text.clash{opacity:0}
/* The train keeps its one livery; only the roof wears the line. */
.map-train .train-shadow{fill:#22314f;opacity:.25;filter:blur(6px)}
.map-train .train-hull{fill:#eef4fb;stroke:#0a1220;stroke-width:2.5}
.map-train .train-roof{fill:var(--line);stroke:#0a1220;stroke-width:1.5}
.map-train .train-lamp{fill:#ffd84d;stroke:#0a1220;stroke-width:.8}
.map-train .train-blush{fill:#ff8fa8;opacity:.7}
.map-train .train-eye{fill:#12212f}
.map-train .train-shine{fill:#fff}
.map-train .train-smile{fill:none;stroke:#12212f;stroke-width:2;stroke-linecap:round}
.map-train .train-speech{font:900 17px var(--ui);fill:var(--ink);paint-order:stroke;stroke:#fff;stroke-width:4;opacity:0}
.map-train .train-effects text{font-size:20px;fill:var(--brand);opacity:0;text-anchor:middle}
.map-train .train-effects text:nth-child(2){fill:#ff6b8a}
.map-train .train-effects text:nth-child(3){fill:var(--go)}
.map-train .train-bob{transform-box:fill-box;transform-origin:center}
.game-screen.running .map-train .train-bob{animation:train-idle .7s ease-in-out infinite alternate}
.map-train.eager .train-bob{animation:train-eager .22s ease-in-out infinite alternate!important}
.map-train.oops .train-bob{animation:train-oops .32s ease!important}
.map-train.departing .train-speech{animation:speech-pop .6s ease both}
.map-train.departing .train-effects text{animation:star-pop .62s ease-out both}
.map-train.departing .train-effects text:nth-child(2){animation-delay:.05s}
.map-train.departing .train-effects text:nth-child(3){animation-delay:.1s}
/* The next-station board stays a dark LED sign — a prop from the real platform,
   and the one deliberately dark thing in the daylight UI. */
.station-sub{display:block;color:var(--muted);font-weight:700;font-size:11px;line-height:1.3}
.station-sub:empty{display:none}
.typing-copy .station-sub{font-size:13px;margin-top:1px}

/* The dock floats over the map; its children set their own heights. */
.game-dock{position:absolute;z-index:2;left:0;right:0;bottom:0;display:flex;flex-direction:column}
/* The navigator's field stands alone — the current stop rides in the HUD and on the
   map, so the console needn't repeat it. */
.game-dock.nav .console-context,.game-dock.nav .typing-hint{display:none}
.game-dock.nav .typing-console{min-height:0}
/* The HUD top-left carries only the run's route — origin→destination. Both modes drop
   the leading icon (line badge / mode 🧭) and the secondary label (course name / region·
   mode), so the whole corner reads as "시작역 → 종착역" beside the clock and the count. */
.hud-route>span,
.hud-route small{display:none}
.hud-route b{font-size:15px;margin-top:0}
/* Zoom control: a mouse gets the buttons, a thumb gets the pinch, both get the wheel. */
.map-zoom{position:absolute;z-index:3;right:12px;top:86px;display:flex;flex-direction:column;gap:8px}
.map-zoom-btn{width:44px;height:44px;border-radius:14px;border:3px solid var(--outline);background:var(--paper);color:var(--ink);font:900 24px/1 var(--ui);display:grid;place-items:center;box-shadow:0 4px 0 rgba(34,49,79,.14);cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none}
.map-zoom-btn:active{transform:translateY(3px);box-shadow:0 1px 0 rgba(34,49,79,.14)}

/* ---- 운행 모드 (navigator) ------------------------------------------------- */
/* The three contests racked side by side like mission cards, each behind its own
   pictogram; the departure panel keeps its narrower column beneath them. */
.nav-setup{max-width:780px}
/* Three rows, because the five contests come in three kinds: reach a place, race a
   clock, survive. Each row sets its own column count, so the lone endless card
   spans the width its row has rather than sitting in a gap. */
.nav-mode-row{display:grid;grid-template-columns:repeat(var(--cols,2),1fr);gap:11px}
.nav-setup .departure-panel{width:100%;max-width:520px;margin:0 auto}
.nav-mode-card{display:grid;justify-items:center;align-content:start;text-align:center;gap:8px;padding:18px 12px 16px}
.nav-mode-icon{width:54px;height:54px;border-radius:50%;display:grid;place-items:center;background:var(--tint);border:3px solid var(--outline);box-shadow:0 2px 0 var(--outline)}
.nav-mode-icon svg{width:32px;height:32px;display:block}
.nav-mode-card.active .nav-mode-icon{background:#fff}
.nav-mode-head small{display:block;font:700 9px/1.4 var(--tabular);letter-spacing:1.8px;color:var(--faint)}
.nav-mode-head b{display:block;margin-top:3px;font:400 18px/1.2 var(--display);color:var(--ink)}
.nav-mode-card.active .nav-mode-head b{color:var(--line-deep)}
.nav-mode-desc{display:block;color:var(--muted);font-size:12px;font-weight:700;line-height:1.35}
.nav-mode-card.active .nav-mode-desc{color:var(--line-deep)}
/* The region's lines all ride at full colour; slimmer than a course's single track. */
.map-route polyline.nav-line{stroke-width:8}
/* The destination dot beats like a beacon. */
.map-dots circle.goal{stroke:#e03131;stroke-width:6;fill:#fff;transform-box:fill-box;transform-origin:center;animation:goal-pulse .8s ease-in-out infinite alternate}
@keyframes goal-pulse{from{transform:scale(1)}to{transform:scale(1.7)}}
.map-labels text.goal{opacity:1;fill:#e03131;font-weight:900;paint-order:stroke;stroke:#fff;stroke-width:4.5;stroke-linejoin:round}
/* The compass arrow: the script rotates #compassRot to the bearing; the arrow's own
   nudge along +x reads as "that way, that way". */
.nav-compass[hidden]{display:none} /* [hidden]'s UA rule is HTML-only in some engines; SVG needs it said. */
.nav-compass .compass-arrow{fill:#ff5d5d;stroke:#fff;stroke-width:3;paint-order:stroke;animation:compass-nudge .8s ease-in-out infinite alternate;filter:drop-shadow(0 2px 2px rgba(34,49,79,.35))}
@keyframes compass-nudge{from{transform:translateX(0)}to{transform:translateX(4px)}}
.typing-console{min-height:150px;display:flex;flex-direction:column;justify-content:center;gap:11px;padding:15px 5vw;background:var(--paper);border-top:3px solid var(--outline)}
/* 이전 역 · 이번 역 · 다음 역 — the current stop sits centred and large, flanked by the
   one behind and the one ahead so the run reads at a glance without the timeline. */
.console-context{display:grid;grid-template-columns:1fr minmax(180px,auto) 1fr;align-items:center;gap:18px}
.ctx-station{min-width:0}
.ctx-station small{display:block;color:var(--muted);font:var(--label);letter-spacing:.6px;text-transform:uppercase}
.ctx-station b{display:block;margin-top:4px;font-size:15px;font-weight:800;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ctx-prev{justify-self:start;text-align:left}
.ctx-next{justify-self:end;text-align:right}
.typing-copy{text-align:center}
.typing-copy small{color:var(--muted);font:var(--label);letter-spacing:.6px;text-transform:uppercase}
.typing-copy h2{font:900 31px/1 var(--ui);margin:5px 0;color:var(--ink)}
.typing-hint{text-align:center;color:var(--faint);font-size:10px;font-weight:700;margin:0}
.typing-input-wrap{position:relative}
.typing-input-wrap input{width:100%;height:68px;border:3px solid var(--outline);background:var(--cream);color:var(--ink);border-radius:var(--r);padding:0 22px;font-size:25px;font-weight:800;outline:none;caret-color:var(--line);box-shadow:inset 0 3px 0 rgba(34,49,79,.08)}
.typing-input-wrap input:focus{border-color:var(--line-deep);background:#fff;box-shadow:0 0 0 4px var(--line-soft)}
.typing-input-wrap input.wrong{border-color:var(--danger);background:#fff0f2;animation:shake .24s}
.typing-input-wrap i{position:absolute;right:18px;top:50%;width:10px;height:10px;border-radius:50%;background:var(--go);border:2px solid var(--outline);transform:translateY(-50%)}

/* Overlays */
.countdown{position:fixed;inset:0;z-index:100;background:rgba(142,211,255,.9);place-items:center}
.countdown[open],.countdown:not([hidden]){display:grid}
.countdown span{grid-area:1/1;font:400 clamp(110px,20vw,230px)/1 var(--display);color:#fff;-webkit-text-stroke:6px var(--outline);text-shadow:0 8px 0 rgba(34,49,79,.25)}
.countdown span.pop{animation:count-pop .42s ease-out}
.countdown small{grid-area:1/1;align-self:end;margin-bottom:17vh;letter-spacing:3px;color:var(--ink);font:900 13px/1.4 var(--ui);text-transform:uppercase}
.toast{position:fixed;z-index:200;left:50%;bottom:30px;transform:translate(-50%,30px);background:var(--paper);color:var(--ink);border:3px solid var(--outline);border-radius:999px;padding:12px 22px;font-weight:800;opacity:0;pointer-events:none;transition:.2s;box-shadow:0 4px 0 var(--outline)}
.toast.show{opacity:1;transform:translate(-50%,0)}
/* The particle canvas rides over everything, and over the dialogs' top layer it
   cannot go — bursts that must beat a dialog are aimed away from one. */
.fx-canvas{position:fixed;inset:0;z-index:300;pointer-events:none}
/* Dialogs: white toy panels that bounce in. */
.game-dialog{width:min(560px,calc(100vw - 30px));border:4px solid var(--outline);border-radius:22px;background:var(--paper);color:var(--ink);padding:28px;box-shadow:0 10px 0 rgba(34,49,79,.28);animation:dialog-pop .32s cubic-bezier(.34,1.56,.64,1)}
.game-dialog::backdrop{background:rgba(23,38,68,.5)}
.game-dialog h2{font:400 27px var(--display);color:var(--ink);margin:0}
.dialog-head{display:flex;justify-content:space-between;align-items:start}
.dialog-head>button{border:0;background:none;font-size:28px;color:var(--faint);cursor:pointer;line-height:1;transition:.15s}
.dialog-head>button:hover{color:var(--danger)}
/* The badge deliberately hangs above the dialog, so opt out of the UA's
   dialog{overflow:auto} that would otherwise clip it. */
/* ---- 하는 방법 + 첫 실행 튜토리얼 ------------------------------------------ */
.help-dialog{max-width:440px}
.help-dialog section{margin-top:18px}
.help-dialog h3{font:900 16px var(--ui);color:var(--ink);margin:0 0 8px}
.help-dialog ol{margin:0;padding-left:22px;display:flex;flex-direction:column;gap:6px}
.help-dialog li{font-weight:700;font-size:13px;color:var(--muted);line-height:1.45}
/* 두 모드에 함께 걸리는 입력 규칙: 목록 밖에 쪽지처럼 붙어 어느 한 모드의 항목으로
   읽히지 않는다. */
.help-tip{margin:18px 0 0;padding:10px 12px;border:3px solid var(--outline);border-radius:12px;background:var(--tint);font-weight:800;font-size:12.5px;color:var(--ink);line-height:1.45}
.help-dialog .dialog-actions{margin-top:20px}
/* The tour: a dim sheet whose spotlight is a box-shadow cutout, so nothing else
   needs a z-index change; the card floats near the spot and walks the steps. */
.tut-overlay{position:fixed;inset:0;z-index:70}
.tut-overlay[hidden]{display:none}
.tut-spot{position:fixed;border:3px dashed rgba(255,255,255,.9);border-radius:16px;box-shadow:0 0 0 9999px rgba(15,23,42,.62);transition:left .28s ease,top .28s ease,width .28s ease,height .28s ease;pointer-events:none}
.tut-card{position:fixed;left:50%;transform:translateX(-50%);width:min(340px,calc(100vw - 32px));background:var(--paper);border:3px solid var(--outline);border-radius:16px;padding:16px;box-shadow:0 6px 0 rgba(34,49,79,.3)}
.tut-card p{font-weight:800;font-size:15px;color:var(--ink);margin:0 0 12px;line-height:1.5}
.tut-actions{display:flex;gap:8px;justify-content:flex-end}
.tut-actions button{border:3px solid var(--outline);background:var(--paper);color:var(--ink);font-weight:900;font-size:13px;padding:8px 16px;border-radius:12px;box-shadow:0 3px 0 var(--outline);cursor:pointer}
.tut-actions button.primary{background:var(--line,#3d8bfd);color:#fff}
.tut-actions button:active{transform:translateY(2px);box-shadow:0 1px 0 var(--outline)}

.finish-dialog{text-align:center;overflow:visible}
.finish-burst{width:104px;height:104px;border-radius:50%;display:grid;place-items:center;margin:-80px auto 20px;background:var(--accent);color:#fff;font:400 24px var(--display);text-shadow:0 2px 0 rgba(0,0,0,.25);border:4px solid var(--outline);box-shadow:0 6px 0 rgba(34,49,79,.3);transform:rotate(-7deg);animation:stamp .45s cubic-bezier(.34,1.56,.64,1) backwards}
.result-time{margin:24px 0}
.result-time small{display:block;color:var(--muted);font:var(--label);letter-spacing:1.2px;text-transform:uppercase}
.result-time b{font:700 46px var(--tabular);color:var(--ink);letter-spacing:-1px}
.result-score{display:flex;align-items:center;justify-content:center;gap:10px;margin:-14px 0 18px}
.result-score small{color:var(--muted);font:var(--label);letter-spacing:1.2px;text-transform:uppercase}
.result-score b{font:800 30px var(--tabular);color:var(--brand-deep);letter-spacing:-.5px}
.result-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
.result-grid div{background:var(--tint);border:3px solid var(--outline);padding:12px;border-radius:var(--r-sm);box-shadow:0 3px 0 rgba(34,49,79,.15)}
.result-grid small{display:block;color:var(--muted);font:800 9px/1.4 var(--ui);letter-spacing:.4px;text-transform:uppercase}
.result-grid b{display:block;margin-top:4px;font:800 15px/1.3 var(--ui);color:var(--ink)}
.finish-gems{margin:12px 0 0;text-align:center;color:var(--brand-deep);font:900 15px var(--ui);animation:stamp .4s cubic-bezier(.34,1.56,.64,1) backwards}
.finish-gems[hidden]{display:none}
.finish-note{margin:12px 0 0;text-align:center;color:var(--muted);font-size:12.5px;font-weight:700;line-height:1.45}
.finish-note[hidden]{display:none}
.guest-claim{background:color-mix(in srgb,var(--brand) 16%,#fff);border:3px dashed var(--brand-deep);border-radius:var(--r-sm);margin:18px 0;padding:14px}
.guest-claim b{color:var(--brand-deep)}
.guest-claim p{font-size:11px;font-weight:700;color:var(--muted);margin:5px}
.guest-claim button{border:0;background:none;color:var(--brand-deep);font-weight:900;cursor:pointer;text-decoration:underline}
.dialog-actions{display:flex;gap:8px;margin-top:18px}
/* The finish dialog's share row: one wide button above the replay pair, wearing the
   same gem hint the menu's share item wears, since it pays the same reward. */
.share-actions{margin-top:18px}
.share-actions+.dialog-actions{margin-top:10px}
.share-run{display:flex;align-items:center;justify-content:center;gap:8px}
.share-run em{font-style:normal;font-weight:900;color:var(--brand-deep)}
.dialog-actions button,.submit-account{flex:1;border:3px solid var(--outline);background:var(--paper);color:var(--ink);border-radius:var(--r-sm);padding:12px;cursor:pointer;font-weight:900;box-shadow:0 4px 0 var(--outline);transition:transform .08s,box-shadow .08s,background .15s}
.dialog-actions button:hover,.submit-account:hover{background:var(--cream)}
.dialog-actions button:active,.submit-account:active{transform:translateY(4px);box-shadow:0 0 0 var(--outline)}
.dialog-actions .primary,.submit-account.primary{background:var(--brand);color:var(--on-brand)}
.dialog-actions .primary:hover,.submit-account.primary:hover{background:#ffc44f}

/* Rankings — a full screen that slides up like the shop, its board sliced by
   language, then region, then line. */
.ranking-screen{position:fixed;inset:0;z-index:72;display:flex;flex-direction:column;background:linear-gradient(#8ed3ff,#cdecff 40%,#fdf6e6);transform:translateY(100%);transition:transform .34s cubic-bezier(.2,.85,.25,1);pointer-events:none}
.ranking-screen.open{transform:none;pointer-events:auto}
.ranking-head{position:relative;z-index:2;display:flex;align-items:center;gap:14px;padding:14px 4vw;border-bottom:3px solid var(--outline);background:var(--paper)}
.ranking-title{flex:1;min-width:0}
.ranking-title h2{font:400 24px var(--display);color:var(--ink);margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ranking-title .eyebrow{margin-bottom:4px}
/* The language toggle parks in the header's top-right corner, so the region tabs
   keep their whole shelf to themselves. */
.ranking-head .rank-lang-tabs{flex:0 0 auto;margin-left:auto}
.ranking-filters{position:relative;z-index:1;display:flex;flex-wrap:wrap;align-items:center;gap:9px 12px;padding:11px 4vw;background:rgba(255,255,255,.55);border-bottom:3px solid var(--outline)}
/* The region row is borrowed from the line-select screen; here it needs neither its
   entrance animation nor its tall footprint. */
.ranking-filters .region-tabs{flex:1;min-width:0;min-height:0;margin:0;padding:2px 0;animation:none}
.rank-line-tabs{width:100%}
/* The navigator's contests as one tab row over one board — stacked boards buried
   the second and third a scroll away. */
/* Five contests, and names like "3분 타임어택" that will not squeeze onto a phone —
   so the shelf scrolls sideways rather than crushing every tab evenly. */
.nav-rank-tabs{max-width:680px;margin:0 auto 12px;display:flex;gap:8px;overflow-x:auto;padding:2px 2px 6px}
.nav-rank-tabs button{flex:0 0 auto;font-size:12.5px;border:3px solid var(--outline);background:var(--paper);border-radius:999px;padding:9px 10px;color:var(--ink);font-weight:900;white-space:nowrap;cursor:pointer;box-shadow:0 3px 0 var(--outline);transition:transform .08s,box-shadow .08s,background .15s}
.nav-rank-tabs button:hover{background:var(--cream)}
.nav-rank-tabs button:active{transform:translateY(3px);box-shadow:0 0 0 var(--outline)}
.nav-rank-tabs button.active{background:var(--brand);color:var(--on-brand)}
/* Course tabs wear the navigator tabs' clothes, but a line can carry many courses
   with long names, so these scroll sideways instead of squeezing evenly. */
.course-rank-tabs{overflow-x:auto;justify-content:flex-start;padding:2px 2px 6px}
.course-rank-tabs button{flex:0 0 auto;font-size:12px;padding:8px 14px}
.rank-mode-dialog h2{text-align:center}
.rank-mode-options{display:grid;gap:10px;margin-top:16px}
.rank-mode-options button{display:flex;align-items:center;gap:12px;border:3px solid var(--outline);background:var(--paper);border-radius:var(--r-sm);padding:14px 16px;color:var(--ink);font-weight:900;font-size:15px;cursor:pointer;box-shadow:0 4px 0 var(--outline);transition:transform .08s,box-shadow .08s,background .15s}
.rank-mode-options button:hover{background:var(--cream)}
.rank-mode-options button:active{transform:translateY(4px);box-shadow:0 0 0 var(--outline)}
.rank-mode-options .mode-ico{font-size:24px}
/* The lobby's country dialog reuses the lobby grid's own cards, just capped so a
   list of eleven scrolls inside the dialog instead of stretching past the fold.
   The dialog is a column so the title and the 닫기 row keep their place and only
   the grid scrolls — otherwise a tall list pushes the button off the screen and
   spills its last card over the dialog's own border. */
.rank-country-dialog{width:min(620px,calc(100vw - 30px))}
.rank-country-dialog[open]{display:flex;flex-direction:column;max-height:calc(100dvh - 32px)}
.rank-country-dialog .rank-country-grid{flex:1;min-height:0;max-height:52vh;overflow:auto;margin-top:14px;padding:3px 3px 10px}
.rank-country-dialog .dialog-actions{flex:0 0 auto}
.rank-line-tabs{display:flex;gap:8px;overflow:auto;padding:2px 0}
.rank-line-tabs[hidden]{display:none}
.rank-line-tabs button{display:flex;align-items:center;gap:7px;border:3px solid var(--outline);background:var(--paper);border-radius:999px;padding:5px 15px 5px 5px;color:var(--ink);white-space:nowrap;cursor:pointer;font-weight:900;font-size:13px;box-shadow:0 3px 0 var(--outline);transition:transform .08s,box-shadow .08s,background .15s}
.rank-line-tabs button .line-badge{width:30px;height:30px;font-size:10px;border-width:2px}
.rank-line-tabs button:hover{background:var(--cream)}
.rank-line-tabs button:active{transform:translateY(3px);box-shadow:0 0 0 var(--outline)}
.rank-line-tabs button.active{background:var(--brand);color:var(--on-brand)}
/* 앱 · 모바일 · PC. It takes the whole first row of the filter shelf so the three
   boards read as the top-level fork they are — the region tabs below slice whichever
   one is chosen. The device's own board wears a dot: among three tabs a player should
   never have to work out which one they are actually racing on. */
/* One line at the top of every board a browser opens: it plays and is recorded,
   but the placing is the app's. */
.ranking-app-only{margin:0 18px;padding:11px 14px;border:3px solid var(--outline);border-radius:var(--r-sm);background:var(--tint);color:var(--muted);font:800 12.5px/1.5 var(--ui);text-align:center}
.ranking-app-only[hidden]{display:none}
.rank-lang-tabs{display:flex;gap:4px;padding:4px;background:var(--tint);border:3px solid var(--outline);border-radius:999px}
.rank-lang-tabs button{flex:1;border:0;border-radius:999px;background:none;padding:7px 15px;color:var(--muted);font-weight:900;white-space:nowrap;cursor:pointer;transition:.15s}
.rank-lang-tabs button:hover{color:var(--ink)}
.rank-lang-tabs button.active{background:var(--brand);color:var(--on-brand);box-shadow:0 2px 0 var(--brand-deep)}
.ranking-list{flex:1;overflow:auto;padding:16px 4vw 34px}
/* Each board is a toy panel of its own, floating on the sky like the shop's cards,
   rather than rows bleeding into the backdrop under a sticky strip. */
.rank-board{max-width:680px;margin:0 auto;background:var(--paper);border:3px solid var(--outline);border-radius:var(--r);box-shadow:var(--shadow);padding:14px 14px 12px}
.rank-board+.rank-board{margin-top:14px}
/* 전체: the whole-network board carries a title card instead of a line badge. */
.overall-board>header.overall-head{position:static;display:block;padding:0 0 12px;background:transparent}
.overall-head b{display:block;font:400 20px var(--display);color:var(--ink)}
.overall-head small{display:block;margin-top:3px;color:var(--muted);font:var(--label);line-height:1.5}
.rank-row time .per-unit{font-size:10px;font-weight:700;color:var(--faint);font-style:normal;margin-left:1px}
.rank-board>header{display:flex;align-items:center;gap:11px;padding:2px 2px 12px}
.rank-board>header .line-badge{width:36px;height:36px;font-size:11px;border-width:2px}
.rank-board>header b{display:block;font:400 16px/1.2 var(--display);color:var(--ink)}
.rank-board>header small{color:var(--muted);font:var(--label)}
/* Every entry is its own little card; the podium's three are tinted in their metal,
   and a driver's own train rides in their row. */
.rank-rows{display:grid;gap:7px}
.rank-row{display:grid;grid-template-columns:32px 46px minmax(0,1fr) auto;align-items:center;gap:10px;padding:8px 12px;border:2px solid rgba(34,49,79,.10);border-radius:12px;background:var(--tint)}
.rank-row .medal{width:28px;height:28px;display:grid;place-items:center;font:700 12px var(--tabular);color:var(--muted);background:#fff;border:2px solid rgba(34,49,79,.18);border-radius:10px}
.rank-row .medal.m1,.rank-row .medal.m2,.rank-row .medal.m3{font-size:15px;border-color:var(--outline)}
.rank-row.t1{background:color-mix(in srgb,#ffd84d 30%,#fff);border-color:#e8a000}
.rank-row.t2{background:color-mix(in srgb,#9fb4d0 24%,#fff);border-color:#7e93b5}
.rank-row.t3{background:color-mix(in srgb,#cd7f32 18%,#fff);border-color:#cd7f32}
.rank-train{display:block}
.rank-train svg{width:46px;height:auto;display:block}
.rank-who{min-width:0}
.rank-who b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;font-size:14.5px;font-weight:900}
.rank-row small{display:block;margin-top:2px;color:var(--faint);font:var(--label)}
.rank-row time{font:700 15px var(--tabular);color:var(--ink);text-align:right}
.rank-row time b{font-weight:700}
.rank-row time small{margin-top:1px;font:700 10px var(--tabular);letter-spacing:0}
.rank-row.own{background:color-mix(in srgb,var(--brand) 22%,#fff);border-color:var(--brand-deep)}
.rank-row.own b,.rank-row.own time{color:var(--brand-deep)}
/* The player's own row rides pinned at the top of the board, above the field, with
   the dashed rule now underneath it. */
.rank-mine{margin-bottom:10px;padding-bottom:10px;border-bottom:3px dashed rgba(34,49,79,.25)}
/* No record yet: the slot stays and says so, rather than silently vanishing. */
.rank-none{display:flex;align-items:center;justify-content:center;padding:11px 12px;border:2px dashed rgba(34,49,79,.25);border-radius:12px;color:var(--faint);font-weight:700;font-size:12px;text-align:center;line-height:1.5}
.empty-ranking{max-width:680px;margin:0 auto;text-align:center;color:var(--muted);font-weight:700;padding:45px 20px;font-size:13px;line-height:1.7;background:rgba(255,255,255,.7);border:3px dashed rgba(34,49,79,.3);border-radius:var(--r)}
.account-tabs{display:flex;margin:20px 0;border-bottom:3px solid rgba(34,49,79,.12)}
.account-tabs button{flex:1;border:0;background:none;padding:11px;color:var(--muted);cursor:pointer;border-bottom:3px solid transparent;margin-bottom:-3px;font-weight:800;transition:.15s}
.account-tabs button.active{color:var(--accent);border-color:var(--accent)}
.account-dialog form,.nickname-dialog form{display:grid;gap:12px}
.account-dialog label,.nickname-dialog label{text-align:left;color:var(--muted);font:var(--label);letter-spacing:.6px;text-transform:uppercase}
.account-dialog input,.nickname-dialog input{display:block;width:100%;margin-top:5px;border:3px solid rgba(34,49,79,.3);border-radius:var(--r-sm);background:var(--cream);color:var(--ink);padding:12px;outline:none;font-family:var(--ui);font-size:14px;font-weight:700}
.account-dialog input:focus,.nickname-dialog input:focus{border-color:var(--outline);background:#fff}
#accountMessage,#nicknameMessage{min-height:20px;color:var(--danger);font-weight:700;font-size:11px;margin:0}
.nickname-dialog form{margin-top:20px}
.nickname-guest{margin-top:16px;padding-top:14px;border-top:2px solid rgba(34,49,79,.12);text-align:center}
.nickname-guest p{font-size:12px;font-weight:700;color:var(--muted);margin:0 0 8px}
.nickname-guest button{border:0;background:none;color:var(--brand-deep);font-weight:900;cursor:pointer;text-decoration:underline}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* The settings menu. */
.menu-switch{position:relative}
#menuButton{font-size:18px}
.app-menu{position:absolute;z-index:30;top:calc(100% + 8px);right:0;width:232px;padding:7px;list-style:none;border:3px solid var(--outline);background:var(--paper);border-radius:var(--r);box-shadow:var(--shadow-lg)}
.app-menu[hidden]{display:none}
.app-menu-item{width:100%;display:flex;align-items:center;gap:10px;padding:11px 10px;border:0;border-radius:10px;background:none;color:var(--ink);cursor:pointer;text-align:left;font-weight:800}
.app-menu-item span{width:22px;text-align:center;font-size:15px}
.app-menu-item:hover{background:var(--tint)}
.app-menu-item.accent b{color:var(--brand-deep)}
.app-menu-item.quiet b{color:var(--muted);font-weight:700}
.app-menu-value{margin-left:auto;color:var(--faint);font-size:12px;font-weight:800;font-style:normal}

/* ---------------------------------------------------------------------------
   Shop — the depot, in daylight. A full screen the player walks into. The
   current train stands on a plinth under a sunburst, the draw is a tunnel a
   new one arrives from, and the whole collection parks in bays below.
   --------------------------------------------------------------------------- */
.shop-screen{position:fixed;inset:0;z-index:70;display:flex;flex-direction:column;background:linear-gradient(#8ed3ff,#cdecff 40%,#fdf6e6);transform:translateY(100%);transition:transform .34s cubic-bezier(.2,.85,.25,1);pointer-events:none}
.shop-screen.open{transform:none;pointer-events:auto}
/* Tracks receding into the depot, faint behind everything. */
.depot-rails{position:absolute;inset:0;z-index:0;overflow:hidden;pointer-events:none;opacity:.35}
.depot-rails i{position:absolute;bottom:-12%;width:4px;height:120%;border-radius:2px;background:linear-gradient(transparent,rgba(34,49,79,.5))}
.depot-rails i:nth-child(1){left:32%;transform:rotate(9deg)}
.depot-rails i:nth-child(2){left:50%}
.depot-rails i:nth-child(3){left:68%;transform:rotate(-9deg)}
.shop-head{position:relative;z-index:1;display:flex;align-items:center;gap:14px;padding:14px 4vw;border-bottom:3px solid var(--outline);background:var(--paper)}
.shop-title{flex:1;min-width:0}
.shop-title h2{font:400 24px var(--display);color:var(--ink);margin:0}
.shop-title .eyebrow{margin-bottom:4px}
.shop-balance{display:flex;align-items:center;gap:7px;padding:8px 14px;border:3px solid var(--outline);border-radius:13px;background:var(--cream);box-shadow:0 3px 0 var(--outline)}
.shop-balance .gem-mark{font-size:15px}
.shop-balance b{font:700 20px var(--tabular);color:var(--ink)}
/* The three bays as chunky tabs. */
.shop-bays{position:relative;z-index:1;display:flex;gap:8px;padding:12px 4vw 0;background:transparent}
.shop-bay{flex:1;border:3px solid var(--outline);border-bottom:0;border-radius:14px 14px 0 0;background:rgba(255,255,255,.6);padding:8px 4px 7px;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:1px;color:var(--muted);transition:background .15s,color .15s}
.bay-icon{font-size:19px;line-height:1.25;filter:grayscale(.6);transition:filter .15s,transform .15s}
.shop-bay b{font-size:14px;font-weight:900}
.shop-bay small{font:700 8px var(--tabular);letter-spacing:2px;opacity:.7}
.shop-bay:hover:not(.active){background:rgba(255,255,255,.85);color:var(--ink)}
.shop-bay:hover .bay-icon{filter:none}
.shop-bay.active{color:var(--on-brand);background:var(--brand)}
.shop-bay.active .bay-icon{filter:none;transform:scale(1.12)}
.shop-scroll{position:relative;z-index:1;flex:1;overflow-y:auto;padding:20px 4vw 34px;border-top:3px solid var(--outline);background:rgba(255,255,255,.5)}
.shop-panel[hidden]{display:none!important}
.panel-eyebrow{color:var(--ink);font:900 11px var(--ui);letter-spacing:1.6px;text-transform:uppercase;margin:0 0 11px;display:flex;align-items:center;gap:8px}
.panel-eyebrow:before{content:"";width:8px;height:8px;border-radius:50%;background:var(--brand);border:2px solid var(--outline)}

/* The garage: current train, the draw, and the fleet. */
.shop-panel.garage{display:grid;grid-template-columns:352px minmax(0,1fr);gap:20px;align-items:start;max-width:1000px;margin:0 auto}
.garage-lead{display:flex;flex-direction:column;gap:14px}
.showcase{position:relative;overflow:hidden;border:3px solid var(--outline);border-radius:var(--r);background:var(--paper);box-shadow:var(--shadow);padding:18px 18px 24px;text-align:center}
/* A slowly turning sunburst behind the pride piece — always on, always cheap. */
/* A soft spotlight glow behind the featured train — a smooth radial gradient, not
   the radiating spokes of a conic gradient, so it reads as a stage light and carries
   none of a rising-sun-flag resemblance. */
.showcase-rays{position:absolute;left:50%;top:56%;width:360px;height:360px;transform:translate(-50%,-50%);background:radial-gradient(circle,rgba(255,206,84,.30) 0%,rgba(255,206,84,.12) 40%,transparent 68%);border-radius:50%;animation:glow-pulse 4.2s ease-in-out infinite;pointer-events:none}
.showcase-train{position:relative;display:grid;place-items:center;filter:drop-shadow(0 10px 8px rgba(34,49,79,.2))}
.showcase-svg{width:210px;height:auto;animation:showcase-bob 1.6s ease-in-out infinite alternate}
.showcase-train.dispatch{animation:dispatch .5s ease-out}
/* The plinth is a live track: sleepers scrolling under the pride piece, the same
   perpetual motion the lobby's train rides on. */
.showcase-rail{position:relative;display:block;height:16px;margin:-14px 6px 0;border-top:3px solid rgba(34,49,79,.28);border-bottom:3px solid rgba(34,49,79,.28);background:repeating-linear-gradient(90deg,rgba(34,49,79,.16) 0 5px,transparent 5px 26px);animation:rail-scroll .8s linear infinite}
@keyframes rail-scroll{to{background-position:-26px 0}}
@keyframes showcase-bob{from{transform:translateY(-2px) rotate(-1.2deg)}to{transform:translateY(2px) rotate(1.2deg)}}
.showcase-meta{position:relative;margin-top:10px;display:flex;flex-direction:column;align-items:center;gap:7px}
.showcase-meta b{font:700 14px var(--tabular);color:var(--ink)}
.rarity-stars{display:flex;gap:4px}
.pip{width:9px;height:9px;border-radius:50%;background:rgba(34,49,79,.15)}
.pip.on{background:var(--brand);border:2px solid var(--outline);width:11px;height:11px;margin:-1px}
/* The two pull buttons sit side by side, splitting the row in half. */
.draw{display:grid;grid-template-columns:1fr 1fr;gap:10px}
/* The draw window — a tunnel mouth a new train arrives from. Deliberately dark:
   the one place in the shop where a headlight can matter. */
.draw-window{position:relative;width:min(360px,86vw);height:210px;overflow:hidden;border:3px solid var(--outline);border-radius:var(--r);background:radial-gradient(120% 150% at 50% 130%,#33456b,#141d33);display:grid;place-items:center;box-shadow:var(--shadow)}
.draw-window:before{content:"";position:absolute;left:10%;right:10%;bottom:12px;height:4px;border-radius:2px;background:repeating-linear-gradient(90deg,#55688f 0 18px,transparent 18px 30px)}
.draw-window.rolling{animation:rumble .12s linear infinite}
.draw-window.flash{animation:none}
.draw-window.flash:after{content:"";position:absolute;inset:0;background:#fff;animation:flash-out .5s ease-out forwards}
.draw-idle{position:relative;color:#aebcd8;font-size:12px;font-weight:700;text-align:center;padding:0 22px}
.draw-headlight{width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 0 44px 22px rgba(255,224,138,.7);animation:approach 1s ease-in infinite}
/* The rarity sunburst that spins behind a fresh pull. */
.reveal-rays{position:absolute;left:50%;top:50%;width:460px;height:460px;transform:translate(-50%,-50%);background:radial-gradient(circle,rgba(255,214,90,.45) 0%,rgba(255,214,90,.16) 36%,transparent 64%);border-radius:50%;animation:reveal-glow .85s ease-out}
.reveal-train{position:relative;width:152px;height:auto}
.draw-window.arrive .reveal-train{animation:arrive .6s cubic-bezier(.2,.9,.3,1.25)}
.reveal-tag{position:absolute;bottom:6px;right:11px;color:#ffd84d;font:800 11px var(--tabular);text-shadow:0 1px 2px rgba(0,0,0,.6)}
/* The big keys that draw and spin. */
.draw-btn{width:100%;border:3px solid var(--outline);border-radius:var(--r);background:var(--accent);color:#fff;padding:14px;font:400 19px var(--display);letter-spacing:.5px;text-shadow:0 2px 0 rgba(0,0,0,.22);cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:3px;box-shadow:0 6px 0 var(--accent-deep),0 9px 0 rgba(34,49,79,.18);transition:transform .07s,box-shadow .07s,filter .12s}
.draw-btn em{font:700 13px var(--tabular);font-style:normal;opacity:.95}
/* The ten-pull's saving, flown like the premium card's flag. */
.draw-btn{position:relative}
.draw-flag{position:absolute;top:-11px;right:-7px;background:var(--brand);color:var(--on-brand);font:800 11px/1.4 var(--tabular);font-style:normal;padding:2px 9px;border-radius:999px;border:2px solid var(--outline);transform:rotate(6deg);box-shadow:0 2px 0 var(--outline)}
.draw-btn:not(:disabled):hover{filter:brightness(1.06)}
.draw-btn:not(:disabled):active{transform:translateY(6px);box-shadow:0 0 0 var(--accent-deep),0 2px 0 rgba(34,49,79,.18)}
.draw-btn.ghost{background:var(--paper);color:var(--muted);text-shadow:none;box-shadow:0 6px 0 rgba(34,49,79,.25)}
.draw-btn:disabled{background:#c9d6e8;color:#8697b3;cursor:not-allowed;text-shadow:none;box-shadow:0 6px 0 #a9bad2}

/* The gacha overlay: dims the whole depot while the pull plays, so the reveal owns
   the screen for its moment and needs no box reserved in the panel. Sits under the
   fx canvas (z-index 300), so the confetti still rains over the arriving train. */
.gacha-stage{position:absolute;inset:0;z-index:5;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:24px;background:rgba(20,29,51,.62);backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);animation:gacha-fade .2s ease-out}
.gacha-stage[hidden]{display:none}
.gacha-stage .draw-btn{width:min(360px,86vw)}

/* The fleet — every train the player owns, parked in its own bay. */
.garage-fleet{min-width:0}
.fleet-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(98px,1fr));gap:9px}
.inv-empty{grid-column:1/-1;text-align:center;color:var(--muted);font-weight:700;padding:34px 20px;font-size:12px;border:3px dashed rgba(34,49,79,.3);border-radius:var(--r)}
.fleet-cell{position:relative;border:3px solid var(--outline);border-radius:var(--r-sm);background:var(--paper);box-shadow:0 3px 0 rgba(34,49,79,.18);padding:8px 5px 5px;cursor:pointer;display:flex;flex-direction:column;align-items:center;transition:transform .14s,box-shadow .14s,background .14s}
.fleet-cell:hover{transform:translateY(-2px);box-shadow:0 5px 0 rgba(34,49,79,.18)}
.fleet-cell:active{transform:translateY(2px);box-shadow:0 1px 0 rgba(34,49,79,.18)}
.fleet-cell.active{border-color:var(--brand-deep);background:color-mix(in srgb,var(--brand) 18%,#fff);box-shadow:0 3px 0 var(--brand-deep)}
.fleet-cell svg{width:100%;height:auto}
.fleet-cell small{font:800 9px var(--ui);color:var(--muted);margin-top:3px}
.fleet-cell.active small{color:var(--brand-deep)}
.fleet-rarity{position:absolute;top:4px;right:6px;z-index:1;font:800 9px var(--tabular);color:var(--brand-deep)}
/* The grade badge on each parked train: ★N, gold from five flourishes up. */
.fleet-stars{position:absolute;top:3px;right:4px;z-index:1;font:800 9px/1.5 var(--tabular);font-style:normal;color:var(--muted);background:rgba(255,255,255,.9);border:2px solid rgba(34,49,79,.18);border-radius:999px;padding:0 6px}
.fleet-stars.high{color:var(--on-brand);background:var(--brand);border-color:var(--outline)}

/* Roulette bay: a real prize wheel — ink rim, gold band, blinking bulbs, and a
   pointer that kicks as the pegs pass under it. */
.shop-panel.roulette{display:flex;flex-direction:column;align-items:center;padding-top:12px}
.wheel-stage{position:relative;width:min(320px,80vw);aspect-ratio:1;margin:14px auto 4px}
.wheel{width:100%;height:100%;filter:drop-shadow(0 10px 0 rgba(34,49,79,.2))}
.wheel-num{fill:#fff;font:900 16px var(--ui);text-anchor:middle;dominant-baseline:middle;paint-order:stroke;stroke:#22314f;stroke-width:3.5;stroke-linejoin:round}
.wheel-hub{fill:#fff;stroke:#22314f;stroke-width:4}
.wheel-hub-star{fill:var(--brand);stroke:#22314f;stroke-width:2}
.wheel-rim{fill:none;stroke:#22314f;stroke-width:10}
.wheel-band{fill:none;stroke:#ffca3a;stroke-width:14}
.wheel-bulb{fill:#fff3c4;stroke:#22314f;stroke-width:1.6}
.wheel.spinning .wheel-bulb.odd{animation:bulb-blink .34s steps(1) infinite}
.wheel.spinning .wheel-bulb.even{animation:bulb-blink .34s steps(1) .17s infinite}
.wheel-pointer{position:absolute;top:-12px;left:50%;transform:translateX(-50%);z-index:3;width:0;height:0;border-left:15px solid transparent;border-right:15px solid transparent;border-top:30px solid var(--accent);filter:drop-shadow(0 3px 0 rgba(34,49,79,.8));transform-origin:50% 15%}
.wheel-pointer.kick{animation:pointer-kick .09s ease-out}
.wheel-burst{position:absolute;inset:0;display:grid;place-content:center;text-align:center;pointer-events:none;animation:count-pop .4s ease-out}
.wheel-burst[hidden]{display:none}
.wheel-burst b{font:400 54px var(--display);color:#fff;-webkit-text-stroke:3px var(--outline);text-shadow:0 4px 0 rgba(34,49,79,.4)}
.wheel-burst small{display:block;color:var(--ink);font:900 12px var(--ui);text-shadow:0 1px 0 #fff}
/* The wheel stands on its own dark base, and the spin status reads off a little
   LED sign — the same deliberately dark prop the platform board is. */
.wheel-stage:after{content:"";position:absolute;left:50%;bottom:-15px;transform:translateX(-50%);z-index:-1;width:150px;height:26px;background:linear-gradient(#2c3d63,#1b2742);border:3px solid var(--outline);border-radius:9px;box-shadow:0 4px 0 rgba(34,49,79,.22)}
.roulette-controls{width:min(340px,100%);margin-top:26px}
.spin-status{display:flex;align-items:center;justify-content:center;gap:8px;background:#1b2742;color:#ffd84d;border:3px solid var(--outline);border-radius:12px;padding:9px 12px;font:700 13px var(--tabular);box-shadow:inset 0 0 14px rgba(0,0,0,.4),0 3px 0 rgba(34,49,79,.2);margin:0 0 12px;text-align:center}
.spin-status:before{content:"";flex:0 0 auto;width:7px;height:7px;border-radius:50%;background:#ffd84d;animation:beacon 1.6s ease-in-out infinite}
.roulette-controls .draw-btn{margin-top:8px}

/* Support bay. */
.shop-panel.support{max-width:540px;margin:0 auto;padding-top:16px}
.support-intro{text-align:center;color:var(--ink);opacity:.8;font-size:13px;font-weight:700;line-height:1.7;margin:0 0 18px}
.support-cards{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.support-card{position:relative;border:3px solid var(--outline);border-radius:var(--r);padding:20px 14px 14px;background:var(--paper);box-shadow:var(--shadow);text-align:center}
.support-card.premium{border-color:var(--brand-deep);background:color-mix(in srgb,var(--brand) 14%,#fff)}
.support-card b{display:block;font-size:15px;font-weight:900}
.support-card p{margin:8px 0 12px;color:var(--muted);font-size:11px;font-weight:700;line-height:1.55;min-height:50px}
.support-card .draw-btn{padding:11px;font-size:15px}
.support-flag{position:absolute;top:-12px;left:50%;transform:translateX(-50%) rotate(-3deg);background:var(--accent);color:#fff;font:800 10px var(--ui);padding:3px 12px;border-radius:999px;border:2px solid var(--outline);letter-spacing:1px}
/* The store's own renewal price, shown on the card before a subscription starts. */
.support-card .support-price{margin:0 0 10px;min-height:0;color:var(--brand-deep);font:800 13px var(--tabular)}
/* Restore, and the links Apple requires beside a subscription. */
.support-legal{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:6px 14px;margin-top:14px}
.support-legal .legal-links{display:inline-flex;align-items:center;gap:6px;color:var(--muted)}
.support-legal a,.link-btn{background:none;border:0;padding:0;color:var(--muted);font:800 11px var(--ui);text-decoration:underline;cursor:pointer}
.support-legal a:hover,.link-btn:hover{color:var(--ink)}
.link-btn[disabled]{opacity:.5;cursor:default}
/* The store's two sections: subscriptions up top, one-time gem packs below. */
.store-gems-heading{margin-top:24px}
.gem-packs{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.gem-card{padding:16px 10px 12px}
.gem-card .gem-amount{display:block;font:800 15px var(--tabular);color:var(--brand-deep)}
.gem-card b{margin-top:6px;font-size:13px}
.gem-card .gem-price{display:block;margin:6px 0 10px;color:var(--muted);font:800 13px var(--tabular)}
.gem-card .draw-btn{padding:10px;font-size:14px}
.gem-note{margin:14px 0 0;text-align:center;color:var(--muted);font-size:11px;font-weight:700;line-height:1.6}
/* A phone keeps the three packs shoulder to shoulder — three rows of one read as a
   list, not a shelf — so the cards just slim down instead of lying down. */
@media (max-width:420px){
  .gem-packs{gap:8px}
  .gem-card{padding:12px 6px 8px}
  .gem-card .gem-amount{font-size:12px}
  .gem-card b{font-size:11px;margin-top:4px}
  .gem-card .gem-price{font-size:11px;margin:4px 0 8px}
  .gem-card .draw-btn{padding:8px 4px;font-size:11px}
}
/* The ten-pull reveal: the whole batch parked in a grid inside the tunnel mouth. */
.draw-window.multi{height:auto;min-height:210px;padding:14px}
.reveal-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:6px;width:100%;z-index:1}
.reveal-grid .reveal-mini{width:100%;height:auto}
.reveal-grid .reveal-mini.r3,.reveal-grid .reveal-mini.r4{filter:drop-shadow(0 0 6px rgba(255,224,138,.9))}

/* Market bay: the player's trains on one shelf, everyone else's stalls on the other. */
.shop-panel.market{max-width:720px;margin:0 auto}
.market-guest{max-width:420px;margin:26px auto;text-align:center;border:3px dashed rgba(34,49,79,.3);border-radius:var(--r);padding:26px 20px;background:var(--paper)}
.market-guest b{display:block;font-size:15px;font-weight:900}
.market-guest p{margin:10px 0 16px;color:var(--muted);font-size:12px;font-weight:700;line-height:1.7}
.market-mine{margin-bottom:20px}
.market-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:9px}
.market-cell{position:relative;border:3px solid var(--outline);border-radius:var(--r-sm);background:var(--paper);box-shadow:0 3px 0 rgba(34,49,79,.18);padding:8px 6px 6px;display:flex;flex-direction:column;align-items:center;transition:transform .14s,box-shadow .14s}
.market-cell:hover{transform:translateY(-2px);box-shadow:0 5px 0 rgba(34,49,79,.18)}
.market-cell.listed{border-style:dashed;background:color-mix(in srgb,var(--brand) 8%,#fff)}
.market-cell svg{width:100%;height:auto}
/* The asking price rides a proper tag, not a bare line of text. */
.market-price{font:800 12px/1.5 var(--tabular);color:var(--brand-deep);margin-top:3px;background:color-mix(in srgb,var(--brand) 20%,#fff);border:2px solid var(--outline);border-radius:999px;padding:0 9px;box-shadow:0 2px 0 var(--outline)}
.market-seller{font:700 10px var(--ui);color:var(--muted);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:3px}
.market-actions{display:flex;gap:5px;margin-top:6px;width:100%}
.market-actions button{flex:1;border:2px solid var(--outline);border-radius:9px;background:#fff;box-shadow:0 2px 0 rgba(34,49,79,.18);font:800 10px var(--ui);color:var(--ink);padding:6px 0;cursor:pointer;transition:transform .12s,box-shadow .12s}
.market-actions button:hover{transform:translateY(-1px);box-shadow:0 3px 0 rgba(34,49,79,.18)}
.market-actions button:active{transform:translateY(1px);box-shadow:0 1px 0 rgba(34,49,79,.18)}
.market-actions button.buy{background:var(--brand);color:#fff;border-color:var(--brand-deep);box-shadow:0 2px 0 var(--brand-deep)}

/* The ad spin's bonus, called out inside the win burst. */
.wheel-bonus{display:block;margin-top:4px;font:900 13px var(--ui);color:#fff;background:var(--accent);border:2px solid var(--outline);border-radius:999px;padding:3px 12px;justify-self:center}
.wheel-bonus[hidden]{display:none}

/* The two footer pages, shown as dialogs. */
.info-body{margin-top:14px;color:var(--muted);font-size:13px;font-weight:500;line-height:1.75}
.info-body p{margin:0 0 10px}
.info-body b{color:var(--ink)}
.info-body ul{margin:0 0 10px;padding-left:18px}
.info-body li{margin:4px 0}
.info-date{color:var(--faint);font-size:11px;margin-top:14px}
/* The notice is typed as plain text in the back office, so its own line breaks are
   the whole of its formatting — they have to survive to the screen. */
.notice-body{white-space:pre-wrap;word-break:break-word;max-height:min(52vh,420px);overflow-y:auto}
.notice-updated{color:var(--faint);font:700 11px var(--tabular);margin:14px 0 0;text-align:right}
.notice-updated:empty{display:none}

/* SweetAlert, dressed to match the dialogs rather than arriving in its stock skin. */
.metro-swal{border-radius:22px;border:4px solid var(--outline);background:var(--paper);font-family:var(--ui);box-shadow:0 10px 0 rgba(34,49,79,.28)}
.metro-swal .swal2-title{font:900 23px var(--ui);color:var(--ink)}
.metro-swal .swal2-html-container{color:var(--muted);font-weight:600;font-size:14px}
.metro-swal .swal2-actions{gap:8px;width:100%;padding:0 1.6em}
.metro-swal .swal2-styled{margin:0;flex:1;border-radius:12px;padding:12px;font-weight:900;box-shadow:none}
.metro-swal .swal2-styled:focus{box-shadow:none}
.metro-swal-danger{background:var(--danger);color:#fff;border:3px solid var(--outline)!important}
.metro-swal-quiet{background:var(--paper);color:var(--ink);border:3px solid var(--outline)!important}
/* Pin the dialog to the visible viewport so an open keyboard pushes it up into view
   rather than covering its input and buttons. --app-height/--app-offset track the
   visual viewport (set by the script), and a bit of top padding keeps a tall dialog
   off the status bar when the keyboard has eaten most of the height. */
body>.swal2-container{top:var(--app-offset,0px)!important;bottom:auto!important;height:var(--app-height,100dvh)!important;padding-top:12px;padding-bottom:12px;overflow-y:auto}

/* The new train arriving out of the tunnel, and the headlight that grows before it. */
@keyframes arrive{from{transform:translateX(-150px);opacity:0}55%{opacity:1}80%{transform:translateX(6px)}to{transform:none;opacity:1}}
@keyframes approach{from{transform:scale(.3);opacity:.35}to{transform:scale(1.15);opacity:1}}
@keyframes dispatch{0%{transform:none}30%{transform:scale(1.07) translateX(7px)}100%{transform:none}}
@keyframes rumble{0%{transform:translate(0,0)}25%{transform:translate(1.5px,-1px)}50%{transform:translate(-1.5px,1px)}75%{transform:translate(1px,1.5px)}100%{transform:translate(-1px,-1.5px)}}
@keyframes flash-out{from{opacity:1}to{opacity:0}}
@keyframes glow-pulse{0%,100%{opacity:.72;transform:translate(-50%,-50%) scale(.9)}50%{opacity:1;transform:translate(-50%,-50%) scale(1.06)}}
@keyframes reveal-glow{from{opacity:0;transform:translate(-50%,-50%) scale(.4)}60%{opacity:1}to{opacity:.85;transform:translate(-50%,-50%) scale(1)}}
@keyframes gacha-fade{from{opacity:0}to{opacity:1}}
@keyframes bulb-blink{0%,49%{fill:#fff3c4}50%,100%{fill:#ffb428}}
@keyframes pointer-kick{0%{transform:translateX(-50%) rotate(0)}40%{transform:translateX(-50%) rotate(-16deg)}100%{transform:translateX(-50%) rotate(0)}}
@keyframes stamp{from{transform:rotate(-7deg) scale(2.2);opacity:0}to{transform:rotate(-7deg) scale(1);opacity:1}}
@keyframes dialog-pop{from{transform:scale(.85);opacity:0}to{transform:none;opacity:1}}

@keyframes shake{25%{transform:translateX(-6px)}75%{transform:translateX(6px)}}
@keyframes pulse{from{opacity:.4}to{opacity:1}}
@keyframes beacon{0%,100%{opacity:1}50%{opacity:.25}}
@keyframes rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
@keyframes count-pop{from{opacity:.15;transform:scale(1.45)}to{opacity:1;transform:none}}
@keyframes train-idle{from{transform:rotate(-.6deg)}to{transform:rotate(.6deg)}}
@keyframes train-eager{from{transform:scale(1.01) rotate(-1.6deg)}to{transform:scale(1.05) rotate(1.6deg)}}
@keyframes train-oops{0%,100%{transform:translateX(0)}25%{transform:translateX(-7px) rotate(-3deg)}75%{transform:translateX(7px) rotate(3deg)}}
@keyframes speech-pop{0%{opacity:0;transform:scale(.4)}25%,70%{opacity:1;transform:scale(1)}100%{opacity:0;transform:translateY(-12px) scale(.8)}}
@keyframes star-pop{0%{opacity:0;transform:translateY(12px) scale(.2)}35%{opacity:1;transform:translateY(0) scale(1.2)}100%{opacity:0;transform:translateY(-30px) scale(.7)}}

/* A squat desktop window: let the lobby scroll as a page instead of clipping the
   centred columns, and pull the type and the stage in a notch. */
@media(min-width:761px) and (max-height:600px){
  .lobby-screen{align-items:flex-start;overflow:auto}
  .lobby-copy{margin-top:12px}
  .country-picker{align-self:auto;min-height:auto}
  .lobby-copy h1{font-size:clamp(30px,4.2vw,46px)}
  .lobby-stage{height:88px;margin-top:8px}
  .lobby-train{width:110px}
}
@media(min-width:761px) and (max-height:520px){
  .select-screen,.course-screen,.mode-screen,.nav-screen,.friendly-screen{padding:16px 3vw 28px}
  .selection-head{margin-bottom:8px}.selection-head h2{font-size:28px}.selection-head .eyebrow{margin-bottom:5px}
  .region-tabs{min-height:48px;padding-bottom:8px}
  .line-card{min-height:92px}
  .mode-scene{height:104px}
}
@media(min-width:761px) and (max-height:700px){
  .topbar{height:62px}.screen{height:calc(var(--app-height) - 62px)}
  .game-hud{height:62px}
  .game-stage{--hud-h:62px}
  .typing-console{min-height:126px}
}
@media(max-width:760px){
  .app-shell{min-height:0}
  .shop-panel.garage{grid-template-columns:1fr;gap:14px}
  .shop-head{padding:12px 16px;gap:10px}.shop-title h2{font-size:20px}
  .shop-bays{padding:10px 10px 0;gap:6px}.shop-bay b{font-size:13px}.bay-icon{font-size:16px}
  .shop-scroll{padding:16px 16px 26px}
  .ranking-head{padding:12px 12px;gap:8px}.ranking-title h2{font-size:18px}
  .ranking-filters{padding:10px 16px;gap:8px 10px}
  .rank-lang-tabs{padding:3px;border-width:2px}
  .rank-lang-tabs button{padding:5px 9px;font-size:11px}
  .nav-rank-tabs button{padding:8px 6px;font-size:12px}
  .ranking-list{padding:14px 16px 28px}
  .rank-board{padding:11px 10px 10px}
  .rank-row{grid-template-columns:28px 40px minmax(0,1fr) auto;gap:8px;padding:7px 9px}
  .rank-row .medal{width:24px;height:24px;font-size:10px;border-radius:8px}
  .rank-row .medal.m1,.rank-row .medal.m2,.rank-row .medal.m3{font-size:13px}
  .rank-train svg{width:40px}
  .rank-who b{font-size:13px}
  .rank-row time{font-size:13px}
  .showcase-svg{width:172px}
  /* The two subscriptions stay shoulder to shoulder on a phone; the cards slim
     down rather than stacking into two full-width rows. */
  .support-cards{gap:9px}
  .support-card{padding:16px 10px 10px}
  .support-card b{font-size:13px}
  .support-card p{font-size:10px;min-height:0;margin:6px 0 10px}
  .support-card .draw-btn{padding:9px 6px;font-size:13px}
  .support-card .support-price{font-size:12px;margin-bottom:8px}
  .topbar{height:58px;padding:0 12px}
  .brand-mark{width:36px;height:36px;border-radius:11px}.brand-mark svg{width:31px;height:31px}.brand b{font-size:13px}.brand small{display:none}
  .icon-button,.player-chip,.rank-button,.gem-chip{height:38px;border-width:2px;box-shadow:0 2px 0 var(--outline)}
  .player-chip{max-width:104px;padding:0 9px;font-size:11px}
  .player-chip span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .rank-button{flex:0 0 auto;width:38px;padding:0;font-size:0}.rank-button:first-letter{font-size:16px}
  .top-actions{gap:6px}
  .gem-chip{padding:0 8px;font-size:12px}
  #menuButton{width:38px}
  .app-menu{width:min(232px,calc(100vw - 20px))}
  .screen{height:calc(var(--app-height) - 58px)}
  .lobby-screen{display:none;flex-direction:column;justify-content:flex-start;overflow:auto;padding:24px 16px 30px}
  .lobby-screen.active{display:flex}
  .lobby-copy{width:100%}.lobby-copy h1{font-size:clamp(32px,10vw,42px)}.lobby-copy>p:not(.eyebrow){font-size:13px}
  .lobby-stage{height:86px;margin-top:8px;max-width:none}
  .lobby-stage:before{height:38px}
  .lobby-stage:after{height:25px;border-width:3px}
  .lobby-train{width:106px}
  .stage-puff{left:calc(50% - 64px)}
  /* The lobby scrolls as one column here, so neither the copy nor the board may be
     flex-squeezed — they keep their height and the screen does the scrolling. */
  .lobby-copy,.country-picker{flex:0 0 auto}
  .country-picker{width:100%;margin-top:18px;align-self:auto;justify-content:flex-start}
  .country-grid{grid-template-columns:repeat(auto-fill,minmax(148px,1fr));gap:8px;overflow:visible;min-height:auto}
  .country-card{min-height:58px;padding:9px 10px;gap:8px;box-shadow:0 3px 0 var(--outline)}
  .country-plate{min-width:38px;height:38px;font-size:13px;border-radius:10px}
  .country-copy b{font-size:15px}.country-copy em{font-size:8px}
  .country-card .card-arrow{width:22px;height:22px;font-size:11px;border-radius:8px}
  .select-screen,.course-screen,.mode-screen,.nav-screen,.friendly-screen{padding:20px 14px}
  .selection-head{margin-bottom:16px;gap:12px}.selection-head h2{font-size:26px}.selection-head .eyebrow{margin-bottom:6px}
  .back-button{width:40px;height:40px;font-size:19px;border-radius:12px}
  .region-tabs{margin-right:-14px;min-height:50px}
  .line-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
  .line-card{min-height:88px;padding:11px 11px 11px 15px;gap:9px}
  .line-badge{width:38px;height:38px;font-size:11px}
  .line-card b{font-size:14px}
  .line-card .line-ends{font-size:10px;margin-top:3px}
  .line-card .line-meta{font-size:8px}
  .course-list,.nav-mode-list{grid-template-columns:1fr}
  .nav-mode-row{grid-template-columns:1fr}
  /* The mission cards lie down on a phone: pictogram left, words right. */
  .nav-mode-card{grid-template-columns:46px 1fr;grid-template-areas:"icon head" "icon desc";justify-items:start;align-items:center;text-align:left;gap:3px 12px;padding:12px 14px}
  .nav-mode-icon{grid-area:icon;width:44px;height:44px}
  .nav-mode-icon svg{width:26px;height:26px}
  .nav-mode-head{grid-area:head}
  .nav-mode-head b{font-size:16px}
  .nav-mode-desc{grid-area:desc}
  .mode-choices{grid-template-columns:1fr;gap:12px}
  .mode-scene{height:112px}
  .mode-copy b{font-size:18px}
  .mode-body{padding:12px 14px}
  .departure-panel{gap:14px}
  .game-screen{height:calc(var(--app-height) - 58px)}
  .game-stage{height:100%}
  .game-hud{height:58px;padding:6px 10px}
  .game-stage{--hud-h:58px}
  .hud-route>span{width:36px;height:36px;font-size:10px}.hud-route b{font-size:11px}
  .hud-metrics{gap:10px}.hud-metrics div:nth-child(2){display:none}.hud-metrics b{font-size:14px}
  .exit-button{font-size:0;width:34px;height:34px;padding:0}.exit-button:after{content:'×';font-size:22px}
  .help-button{width:34px;height:34px;font-size:15px;margin-right:6px}
  /* 폰에서는 띠도 한 뼘 낮고 촘촘하게. */
  .battle-standbar{height:26px;padding:0 10px}
  .stand-cell{font-size:11px;gap:4px}
  .stand-cell .stand-pos{font-size:10px}
  .stand-cell small{font-size:11px}
  .map-labels text{font-size:13px}.map-labels text.current{font-size:18px}
  .typing-console{min-height:0;gap:8px;padding:9px 12px calc(9px + var(--safe-bottom))}
  .console-context{gap:10px}.ctx-station b{font-size:12px}.ctx-station small{font-size:8px}
  /* Under the keyboard the dock is the field and nothing else, so every pixel the
     browser leaves goes to the map. */
  .keyboard-up .typing-console{padding-bottom:9px}
  .map-zoom{top:70px;right:10px;gap:7px}
  .map-zoom-btn{width:40px;height:40px;font-size:22px;border-radius:12px}
  .typing-copy h2{font-size:22px}.typing-hint{display:none}
  .typing-input-wrap input{height:46px;font-size:19px;padding:0 14px;border-radius:12px}
  .game-dialog{padding:22px}
  .result-time b{font-size:38px}
  .finish-burst{width:82px;height:82px;margin-top:-64px;font-size:19px}
  .wheel-stage{width:min(280px,78vw)}
  .toast{bottom:calc(20px + var(--safe-bottom))}
}
@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}

/* ---- Battle mode -----------------------------------------------------------
   The third card on the mode screen and everything a duel adds: the waiting
   badge, the queue sheet with its radar, the opponent's ghost on the map, the
   HUD chip counting their stations, and the result sheet's breakdown. Same toy
   language as everything else — hard shadows, ink outlines, no blur on text. */
/* 네 모드가 2×2 로 나란히 선다 — 어느 카드도 줄을 독차지하지 않는다. */
.mode-card.battle{animation-delay:.14s}
.mode-card.battle .mode-scene{background:linear-gradient(160deg,#ffe9c9,#ffd2a1)}
.mode-card.battle .mode-go{background:var(--accent)}
.mode-card.battle.locked{opacity:.72}
.mode-card.battle.locked .mode-go{background:var(--faint)}
.mode-tag.battle-wait{left:auto;right:12px;background:var(--accent);color:#fff;border-color:var(--outline)}
/* 배틀 순위 띠. 지도 오른쪽 위에 카드로 떠 있던 시절에는 노선이 지나가는 자리를
   통째로 덮었다 — 달리는 사람이 봐야 할 것을 가리는 순위표였다. 이제 HUD 아래에
   이어 붙은 28px짜리 한 줄이고, sizeMap 이 이 높이까지 밴드에서 빼므로 기차도 역
   이름도 띠 밑으로 들어가지 않는다. */
.battle-standbar{position:absolute;z-index:3;top:var(--hud-h);left:0;right:0;height:28px;display:flex;align-items:center;padding:0 3vw;background:var(--paper);border-bottom:3px solid var(--outline);box-shadow:0 3px 0 rgba(34,49,79,.08);overflow:hidden}
/* The ghost train: the other player on the same map, unmistakably not mine —
   half there, no roof colour, riding under my own train in paint order. */
.ghost-train{opacity:.5}
.ghost-train[hidden]{display:none}
.ghost-hull{fill:#e6edf7;stroke:#54637a;stroke-width:3}
.ghost-eye{fill:#54637a}
.ghost-smile{fill:none;stroke:#54637a;stroke-width:2.4;stroke-linecap:round}
/* The queue sheet: a radar sweep while the line is watched, then the match card. */
.battle-queue{display:grid;justify-items:center;gap:10px;padding:10px 0 4px}
.battle-radar{position:relative;width:96px;height:96px}
.battle-radar i{position:absolute;inset:0;border:3px solid var(--accent);border-radius:50%;opacity:0;animation:radar-ping 1.8s ease-out infinite}
.battle-radar i:nth-child(2){animation-delay:.9s}
.battle-radar span{position:absolute;inset:0;display:grid;place-items:center;font-size:34px;animation:scene-bob 1.3s ease-in-out infinite alternate}
@keyframes radar-ping{from{transform:scale(.45);opacity:.85}to{transform:scale(1.15);opacity:0}}
.battle-wait-line{margin:0;font-weight:900}
.battle-count{margin:0;font:700 13px/1.4 var(--tabular);color:var(--muted)}
.battle-mine{margin:0;font-size:13px;font-weight:700;color:var(--muted)}
.battle-match{display:grid;justify-items:center;gap:12px;padding:8px 0 4px}
.battle-found{margin:0;font-weight:900;font-size:17px}
.battle-versus{display:flex;align-items:center;gap:16px}
.battle-side{display:grid;justify-items:center;gap:6px;max-width:140px}
.battle-side b{font-size:14px;max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.battle-train{width:86px;height:64px;display:block}
.battle-train svg{width:100%;height:100%}
.battle-train.ghosted{opacity:.5;filter:grayscale(.6)}
/* 셋 이상이 한 판에 탄 출발 명단. VS 대진표를 대신하며, 각 칸이 그 사람의 자리 색을
   두르고 있다 — 지도 위 유령 기차가 바로 그 색으로 달리므로 여기서 얼굴을 익힌다. */
.battle-roster{display:grid;grid-template-columns:repeat(auto-fit,minmax(92px,1fr));gap:8px;width:100%;max-height:38vh;overflow:auto;padding:2px}
.battle-roster[hidden]{display:none}
.battle-rider{display:grid;justify-items:center;gap:2px;padding:8px 6px 6px;background:var(--paper);border:3px solid var(--outline);border-radius:var(--r-sm);box-shadow:0 3px 0 var(--outline);border-bottom-color:var(--seat);border-bottom-width:6px}
.battle-rider b{font-size:12px;max-width:96px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.battle-rider .battle-train{width:62px;height:46px}
/* 내 칸만 자리 색으로 가득 찬다 — 여덟 칸 중 나를 찾는 데 시간이 들면 안 된다. */
.battle-rider.mine{background:color-mix(in srgb,var(--seat) 18%,var(--paper))}
.battle-vs{font:400 22px/1 var(--display);color:var(--accent);text-shadow:0 2px 0 rgba(34,49,79,.2)}
.battle-loading{margin:0;font-size:13px;color:var(--muted)}
/* The result sheet: stamp first, then where the number came from, line by line. */
.battle-result-dialog{text-align:center}
.battle-stamp{font:400 54px/1 var(--display);letter-spacing:2px;margin:2px 0 4px}
.battle-stamp.win{color:var(--go)}
.battle-stamp.lose{color:var(--accent)}
.battle-stamp.draw{color:var(--muted)}
.battle-reason{margin:0 0 12px;font-size:13px;color:var(--muted)}
.battle-breakdown{display:grid;gap:6px;margin:0 auto 14px;max-width:320px}
.battle-breakdown div{display:flex;justify-content:space-between;gap:12px;background:var(--cream);border:2px solid var(--outline);border-radius:10px;padding:7px 12px;font-size:13px;font-weight:700}
.battle-breakdown div b{font-family:var(--tabular)}
.battle-breakdown .gain b{color:var(--go-deep)}
.battle-breakdown .loss b{color:var(--accent-deep)}
.battle-total b{font-family:var(--tabular)}
.battle-points b{font-family:var(--tabular)}
/* The ladder rows borrow the rank board wholesale; only the W/L meta is new. */
.battle-record{font:700 11px/1.4 var(--tabular);color:var(--muted)}

/* 친선전. 카드는 배틀 옆에 나란히 서되 초록 쪽으로 — 사다리가 아니라 악수라는 걸
   색이 먼저 말한다. 시트 안은 두 장면(입구 / 로비)이 갈아 끼워지는 한 자리다. */
.mode-card.friendly{animation-delay:.18s}
.mode-card.friendly .mode-scene{background:linear-gradient(160deg,#d8f6df,#aee9c4)}
.mode-card.friendly .mode-go{background:var(--go)}
.mode-card.friendly.locked{opacity:.72}
.mode-card.friendly.locked .mode-go{background:var(--faint)}
/* 친선전 페이지. 입구와 로비가 한 기둥(최대 540px) 안에 서고, 로비의 채팅 로그가
   남는 세로 공간을 전부 마신다 — 팝업 시절처럼 버튼이 잘릴 곳이 없다. */
.friendly-stage{width:100%;max-width:540px;margin:0 auto;display:flex;flex-direction:column;flex:1;min-height:0}
/* 트랙은 minmax(0,1fr): auto 로 두면 코드 입력창의 고유 폭(size 기본 20자)이 트랙을
   벌려 카드 밖으로 밀어낸다 — 팝업 시절 버튼이 잘리던 바로 그 병. */
.friendly-intro{display:grid;grid-template-columns:minmax(0,1fr);gap:14px;padding:24px;text-align:center;background:var(--paper);border:3px solid var(--outline);border-radius:var(--r);box-shadow:0 5px 0 var(--outline);animation:rise .35s backwards ease-out}
.friendly-lead{margin:0;font-size:14px;color:var(--muted);line-height:1.5}
/* 입구의 버튼들은 .dialog-actions 밖에 서 있으므로 그 자리의 생김새를 직접 입는다. */
.friendly-intro button,.friendly-share{border:3px solid var(--outline);background:var(--paper);color:var(--ink);border-radius:var(--r-sm);padding:12px;cursor:pointer;font-weight:900;box-shadow:0 4px 0 var(--outline);transition:transform .08s,box-shadow .08s,background .15s}
.friendly-intro button:active,.friendly-share:active{transform:translateY(4px);box-shadow:0 0 0 var(--outline)}
.friendly-intro .primary{background:var(--brand);color:var(--on-brand)}
.friendly-intro .primary:hover{background:#ffc44f}
.friendly-create{width:100%}
.friendly-or{margin:0;font:700 12px/1 var(--tabular);color:var(--faint);text-transform:uppercase;letter-spacing:.14em}
.friendly-join{display:flex;gap:8px}
.friendly-join input{flex:1;min-width:0;font:800 20px/1 var(--tabular);letter-spacing:.28em;text-align:center;padding:12px 8px 12px 14px;border:3px solid var(--outline);border-radius:14px;background:var(--cream);color:inherit}
.friendly-join button{flex:none}
.friendly-lobby{display:flex;flex-direction:column;gap:10px;flex:1;min-height:0;text-align:center;animation:rise .35s backwards ease-out}
/* 코드와 공유는 제목 줄의 오른쪽 어깨에 산다. 좁은 화면에서는 제목 아래로
   줄바꿈하되 margin-left:auto 가 오른쪽 정렬을 지킨다. */
.friendly-screen .selection-head{flex-wrap:wrap;row-gap:10px}
.friendly-codebar{margin-left:auto;display:flex;align-items:center;gap:12px;padding:6px 8px 6px 14px;border:3px solid var(--outline);border-radius:var(--r-sm);background:var(--paper);box-shadow:0 3px 0 var(--outline)}
.codebar-code{text-align:left}
.friendly-codebar .code-label{margin:0;font:700 9px/1 var(--tabular);letter-spacing:.16em;text-transform:uppercase;color:var(--faint)}
/* 코드는 눈으로 읽어 불러 줄 수 있는 크기를 지킨다 — 제목 옆이라도 작아지지 않는다. */
.friendly-code{margin:1px 0 0;font:400 24px/1 var(--display);letter-spacing:.12em;color:var(--accent-deep);user-select:all}
/* 공유는 코드 옆의 작은 알약 — 한 번 누르고 마는 버튼이 큰 자리를 차지할 이유가 없다. */
.friendly-share{flex:none;background:var(--go);color:#fff;padding:8px 14px;font-size:12.5px;border-radius:999px;box-shadow:0 3px 0 var(--outline)}
.friendly-share:active{transform:translateY(3px)}
/* 자리 그리드: 2×2 네 자리. 빈 자리는 점선 — 초대장이 갈 곳이다. */
.friendly-players{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;width:100%}
.friendly-seat{display:flex;align-items:center;gap:8px;min-height:56px;padding:6px 10px;border:3px solid var(--outline);border-radius:var(--r-sm);background:var(--paper);text-align:left}
/* 빈 자리는 낮은 점선 띠 — 8자리를 다 그려도 로비가 세로로 무너지지 않도록. */
.friendly-seat.vacant{border-style:dashed;opacity:.5;justify-content:center;font:700 11px/1.2 var(--tabular);color:var(--faint);min-height:30px;padding:3px 8px}
.friendly-seat.mine{background:var(--cream);box-shadow:inset 0 0 0 2px var(--brand)}
.friendly-seat .seat-train{width:46px;height:36px;flex:none}
.friendly-seat .seat-train svg{width:100%;height:100%}
.friendly-seat .seat-body{min-width:0;display:grid;gap:2px}
.friendly-seat b{font-size:13px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.friendly-ready-tag{font:700 10px/1 var(--tabular);color:var(--faint);text-transform:uppercase;letter-spacing:.08em}
.friendly-ready-tag.on{color:var(--go-deep)}
.friendly-ready-tag.bot{color:#4d5de0}
/* 봇 자리: 사람 자리와 한눈에 갈리도록 테두리·바탕에 인디고 기운을 살짝 얹고,
   오른쪽 끝에 내보내기 ✕ 를 둔다. seat-body 가 남는 폭을 먹어 ✕ 를 끝으로 민다. */
.friendly-seat .seat-body{flex:1}
.friendly-seat.is-bot{border-color:#6c7cff;background:linear-gradient(#fff,#f2f3ff)}
.seat-remove{flex:none;align-self:center;width:28px;height:28px;border:2px solid var(--outline);border-radius:9px;background:var(--paper);color:var(--muted);font-weight:900;font-size:12px;line-height:1;cursor:pointer;box-shadow:0 2px 0 var(--outline);transition:transform .08s,box-shadow .08s,background .12s,color .12s}
.seat-remove:hover{background:var(--danger);color:#fff;border-color:var(--danger)}
.seat-remove:active{transform:translateY(2px);box-shadow:0 0 0 var(--outline)}
/* 빈 자리 중 첫 칸은 봇을 부르는 버튼 — 점선 안에 로봇과 "봇 추가"가 또렷이 서서,
   흐린 다른 빈 자리와 달리 손을 부른다. 큰 탭 타깃으로 모바일에서도 누르기 쉽게. */
.friendly-seat.vacant.add-bot{opacity:1;cursor:pointer;justify-content:center;gap:7px;min-height:44px;color:#4d5de0;border-color:#6c7cff;background:#f2f3ff;font:800 12px/1.2 var(--tabular);letter-spacing:0;text-transform:none;transition:transform .08s,box-shadow .08s,background .12s}
.friendly-seat.vacant.add-bot:hover{background:#e7e9ff}
.friendly-seat.vacant.add-bot:active{transform:translateY(2px)}
.add-bot-icon{font-size:17px;line-height:1}
/* 대기방 채팅: 방과 함께 살고 방과 함께 사라지는 잡담 창구. */
.friendly-chat{width:100%;display:flex;flex-direction:column;gap:6px;flex:1;min-height:0}
.friendly-chat-log{flex:1;min-height:96px;overflow-y:auto;border:3px solid var(--outline);border-radius:var(--r-sm);background:var(--cream);padding:8px 10px;display:grid;gap:4px;align-content:start;text-align:left;font-size:12.5px;line-height:1.45;overscroll-behavior:contain}
.friendly-chat-log .chat-line{margin:0;word-break:break-word}
.friendly-chat-log .chat-line b{margin-right:6px}
.friendly-chat-form{display:flex;gap:6px}
.friendly-chat-form input{flex:1;min-width:0;padding:9px 12px;border:3px solid var(--outline);border-radius:12px;background:var(--paper);color:inherit;font-size:13px}
.friendly-chat-form button{flex:none;width:46px;border:3px solid var(--outline);border-radius:12px;background:var(--brand);color:var(--on-brand);font-weight:900;cursor:pointer;box-shadow:0 3px 0 var(--outline)}
.friendly-chat-form button:active{transform:translateY(3px);box-shadow:0 0 0 var(--outline)}
/* 준비(종이색)와 시작(초록)은 코드바 바로 아래, 손이 먼저 닿는 자리에 선다.
   잠긴 시작 버튼은 흐려진 채 제자리를 지킨다. */
.friendly-actions{display:flex;gap:10px;width:100%;flex-shrink:0}
/* 버튼 밑의 안내 한 줄은 버튼에 붙어 그 버튼들의 설명으로 읽힌다. */
.friendly-lobby .battle-loading{margin-top:-2px;font-size:12px}
.friendly-actions button{flex:1;min-width:0;border:3px solid var(--outline);background:var(--paper);color:var(--ink);border-radius:var(--r-sm);padding:14px 10px;font-weight:900;font-size:15px;cursor:pointer;box-shadow:0 4px 0 var(--outline);transition:transform .08s,box-shadow .08s,background .15s,opacity .15s}
.friendly-actions button:active:not(:disabled){transform:translateY(4px);box-shadow:0 0 0 var(--outline)}
.friendly-actions .primary{background:var(--go);color:#fff}
.friendly-actions button:disabled{opacity:.45;cursor:default}
/* 띠 한 칸: 등수 · 색점 · 닉네임 · 지나간 역. 색점은 지도 위 그 사람의 유령 기차와
   같은 말을 한다. 줄바꿈은 없다 — 한 줄이 이 띠의 전부다.
   폭이 모자랄 때 무엇이 먼저 물러나는지가 이 규칙의 요지다:
     1등  이름 그대로. 쫓아야 할 사람이니 절대 줄이지 않는다
     2·3등 이름이 말줄임으로 줄고, 그래도 모자라면 3등이 통째로 빠진다
     내 칸 오른쪽 끝에 못박혀 무슨 일이 있어도 남는다 */
.stand-row{display:flex;align-items:center;gap:12px;width:100%;min-width:0;white-space:nowrap}
.stand-cell{flex:none;display:inline-flex;align-items:center;gap:5px;min-width:0;font-size:12px;font-weight:900;line-height:1}
.stand-cell .stand-pos{flex:none;font:700 11px/1 var(--tabular);font-style:normal;color:var(--faint)}
.stand-cell .stand-dot{flex:none;width:9px;height:9px;border-radius:50%;border:2px solid var(--outline)}
.stand-cell b{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.stand-cell small{flex:none;font:700 12px/1 var(--tabular);color:var(--muted)}
/* 선두는 등수가 진해진다. 2·3등만 줄어드는 칸이다. */
.stand-cell.rank1 .stand-pos{color:var(--ink)}
.stand-cell.rank2,.stand-cell.rank3{flex:0 1 auto}
.stand-cell.rank2 b{max-width:5.5em}
.stand-cell.rank3 b{max-width:4.5em}
/* 내 칸은 오른쪽 끝. 1~3등 안에 있으면 그 자리에서 노란 테만 두른다. */
.stand-cell.rankMe{margin-left:auto}
.stand-cell.mine{padding:2px 6px;border-radius:8px;background:var(--cream);box-shadow:inset 0 0 0 2px var(--brand)}
.stand-cell.mine small{color:var(--ink)}
.stand-cell.gone{opacity:.45;text-decoration:line-through}
/* 좁은 화면에서는 3등이 물러난다 — 선두와 내 자리가 온전한 편이 낫다. */
@media (max-width:430px){.stand-cell.rank3:not(.mine){display:none}.stand-row{gap:9px}}
/* 1등이 들어온 뒤의 유예 카운트다운 — HUD 바로 아래, 지도 위에 뜬다. 순위 띠가
   서 있으면 그 아래로 한 칸 물러난다(예전에는 오른쪽 카드를 피해 옆으로 비켰다).
   읽을 만큼만 머문다: 1초는 또렷하게 서 있고, 이후 2초에 걸쳐 글자도 알약도 함께
   투명해져 3초째에는 자취가 없다. 소식은 한 번 읽으면 그만인데 배너는 유예 내내
   지도 한복판을 물고 있었다. 사라진 뒤에도 자리를 차지하지 않도록 클릭도 통과시킨다. */
.grace-banner{position:absolute;z-index:3;top:calc(var(--hud-h) + 10px);left:50%;transform:translateX(-50%);max-width:92%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;background:var(--paper);border:3px solid var(--outline);border-radius:999px;padding:8px 16px;font-weight:900;font-size:13px;box-shadow:0 4px 0 var(--outline);pointer-events:none;animation:graceFade 3s ease-in both}
@keyframes graceFade{0%,33%{opacity:1}100%{opacity:0}}
.battle-standbar:not([hidden]) ~ .grace-banner{top:calc(var(--hud-h) + 38px)}
.friendly-tally-label{margin:0 0 8px;font:700 11px/1 var(--tabular);letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
/* 결과 순위표: 트로피는 1·2·3등의 것이다. 가운데가 가장 높은 시상대 셋이 서고,
   4등부터는 아래에 가로로 흘려 담는다 — 여덟 줄짜리 목록은 정작 내 점수를 화면
   밖으로 밀어냈다. 역 수는 내 칸에만 적힌다(남의 숫자는 읽을 이유가 없다). */
.battle-standings{margin:0 auto 14px;max-width:320px}
.podium{display:flex;align-items:flex-end;justify-content:center;gap:8px}
.podium-slot{flex:1 1 0;min-width:0;display:grid;justify-items:center;gap:2px;background:var(--cream);border:3px solid var(--outline);border-radius:14px;padding:8px 6px;box-shadow:0 4px 0 var(--outline)}
.podium-slot .podium-trophy{font-size:26px;line-height:1;filter:drop-shadow(0 2px 0 rgba(0,0,0,.12))}
.podium-slot b{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:900}
.podium-slot em{font:900 13px/1 var(--tabular);font-style:normal;color:var(--brand-deep)}
/* 1등만 한 뼘 더 크고 높다. 2·3등은 어깨를 나란히 한다. */
.podium-slot.p1{padding:14px 6px 12px;background:color-mix(in srgb,var(--brand) 22%,#fff);box-shadow:0 5px 0 var(--outline)}
.podium-slot.p1 .podium-trophy{font-size:38px}
.podium-slot.p1 b{font-size:14px}
.podium-slot.mine{background:var(--paper);box-shadow:0 4px 0 var(--outline),inset 0 0 0 3px var(--brand)}
/* 4등부터: 이름표 한 줄. 줄바꿈은 되되 스크롤은 없다. */
.standing-rest{display:flex;flex-wrap:wrap;justify-content:center;gap:5px;margin-top:8px}
.rest-chip{display:inline-flex;align-items:center;gap:5px;max-width:100%;background:var(--cream);border:2px solid var(--outline);border-radius:999px;padding:4px 10px;font-size:11px;font-weight:800;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rest-chip i{font:700 10px/1 var(--tabular);font-style:normal;color:var(--faint)}
.rest-chip em{font:900 11px/1 var(--tabular);font-style:normal}
.rest-chip.mine{background:var(--paper);box-shadow:inset 0 0 0 2px var(--brand)}
.friendly-nopoints{margin:0 0 12px;font-size:12px;color:var(--faint)}
.rematch-dialog{text-align:center}
@media (max-width:560px){
  .grace-banner{top:calc(var(--hud-h) + 8px);font-size:12px}
  .battle-standbar:not([hidden]) ~ .grace-banner{top:calc(var(--hud-h) + 34px)}
  .friendly-chat-log{min-height:80px}
  .friendly-code{font-size:20px}
  .friendly-codebar{gap:8px;padding:5px 6px 5px 10px}
}

/* ===========================================================================
   기차 능력 + 합성 — the toy language continues: white bodies, ink outlines,
   hard shadows, candy colours. Nothing blurs, nothing dims the letters.
   =========================================================================== */

/* The clock's receipts: +0.5s / -0.8s / AUTO chips that rise from under the HUD
   and fade. Purely painterly — they can never catch a tap. */
.time-floats{position:absolute;left:0;right:0;top:64px;z-index:30;pointer-events:none}
.time-float{position:absolute;left:50%;transform:translateX(-50%);font:400 17px var(--display);letter-spacing:.5px;padding:5px 15px;border-radius:999px;border:3px solid var(--outline);box-shadow:0 3px 0 var(--outline);animation:time-float-rise 1.25s cubic-bezier(.22,.9,.3,1) forwards}
.time-float.bad{background:var(--danger);color:#fff;text-shadow:0 2px 0 rgba(0,0,0,.2)}
.time-float.good{background:var(--go);color:#fff;text-shadow:0 2px 0 rgba(0,0,0,.2)}
.time-float.gem,.time-float.auto{background:var(--brand);color:var(--on-brand)}
.time-float.shield{background:#3d8bfd;color:#fff;text-shadow:0 2px 0 rgba(0,0,0,.2)}
@keyframes time-float-rise{0%{opacity:0;transform:translateX(-50%) translateY(10px) scale(.7)}14%{opacity:1;transform:translateX(-50%) translateY(0) scale(1.1)}26%{transform:translateX(-50%) translateY(-2px) scale(1)}72%{opacity:1}100%{opacity:0;transform:translateX(-50%) translateY(-38px) scale(.94)}}

/* The HUD's ability metric pops when a streak fires or a shield is spent. */
#abilityMetricValue.pop{animation:ability-pop .45s cubic-bezier(.34,1.56,.64,1)}
@keyframes ability-pop{35%{transform:scale(1.35)}70%{transform:scale(.94)}}

/* The ability tag a parked train wears: icon + stars, top-left of its bay,
   opposite corner to the rarity grade so the two never collide. */
.fleet-ab{position:absolute;left:4px;top:3px;z-index:1;font:800 9px/1.5 var(--tabular);font-style:normal;color:#fff;background:#3d8bfd;border:2px solid var(--outline);border-radius:999px;padding:0 6px;box-shadow:0 2px 0 rgba(34,49,79,.25)}
.fleet-ab.s3,.fleet-ab.s4{background:var(--accent)}
.fleet-ab.s5{background:var(--brand);color:var(--on-brand)}
/* The ⋯ sell/gift menu on an owned bay: top-right, a firm round target that beats
   the equip tap under it. 30px keeps it thumb-friendly on a 98px cell. */
.fleet-menu{position:absolute;top:2px;right:2px;z-index:2;width:30px;height:30px;padding:0;border:2.5px solid var(--outline);border-radius:50%;background:var(--paper);color:var(--ink);font:900 16px/1 var(--ui);cursor:pointer;box-shadow:0 2px 0 rgba(34,49,79,.25);display:grid;place-items:center}
.fleet-menu:hover{background:var(--tint)}
.fleet-menu:active{transform:translateY(2px);box-shadow:0 0 0 rgba(34,49,79,.25)}

/* The gacha's rare moment: a single pull flies a full banner under the train,
   a ten-pull pins a gold chip on each blessed slot. */
.reveal-ability{z-index:1;margin-top:10px;font:400 15px var(--display);letter-spacing:.4px;background:var(--brand);color:var(--on-brand);border:3px solid var(--outline);border-radius:999px;padding:7px 16px;box-shadow:0 3px 0 var(--outline);animation:dialog-pop .4s cubic-bezier(.34,1.56,.64,1);text-align:center;max-width:92%}
.reveal-slot{position:relative;display:block}
.reveal-slot svg{width:100%;height:auto}
.mini-ab{position:absolute;top:-4px;right:-2px;z-index:2;font-size:13px;font-style:normal;background:var(--brand);border:2.5px solid var(--outline);border-radius:50%;width:24px;height:24px;display:grid;place-items:center;box-shadow:0 2px 0 var(--outline);animation:beacon 2.4s ease-in-out infinite}

/* ---- FUSION BAY -----------------------------------------------------------
   The garage's counter: one row per ability the depot holds, counting toward
   five. A full set wears the brand tint and opens the bay. */
.garage-fuse{margin-top:22px}
/* In its own bay the counter starts at the top — no garage above to clear. */
.shop-panel.fuse{max-width:640px;margin:0 auto}
.shop-panel.fuse .garage-fuse{margin-top:4px}
.fuse-hint{margin:2px 0 10px;color:var(--muted);font-size:11px;font-weight:700;line-height:1.6}
.fuse-groups{display:flex;flex-direction:column;gap:9px}
.fuse-group{display:flex;align-items:center;gap:12px;width:100%;text-align:left;border:3px solid var(--outline);border-radius:var(--r-sm);background:var(--paper);padding:10px 12px;cursor:pointer;box-shadow:0 3px 0 rgba(34,49,79,.18);transition:transform .1s,box-shadow .1s,background .15s}
.fuse-group:not(:disabled):hover{transform:translateY(-2px);box-shadow:0 5px 0 rgba(34,49,79,.18)}
.fuse-group:not(:disabled):active{transform:translateY(2px);box-shadow:0 1px 0 rgba(34,49,79,.18)}
.fuse-group:disabled{cursor:default;opacity:.75;box-shadow:0 3px 0 rgba(34,49,79,.1)}
.fuse-group.ready{background:color-mix(in srgb,var(--brand) 16%,#fff);border-color:var(--brand-deep)}
.fuse-ico{flex:0 0 auto;width:40px;height:40px;display:grid;place-items:center;font-size:20px;background:var(--tint);border:3px solid var(--outline);border-radius:12px}
.fuse-group.ready .fuse-ico{background:var(--brand)}
.fuse-copy{flex:1;min-width:0}
.fuse-copy b{display:block;font-size:13px;font-weight:900;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fuse-copy small{display:block;margin-top:2px;color:var(--muted);font-size:10px;font-weight:700;line-height:1.4}
.fuse-count{flex:0 0 auto;text-align:center}
.fuse-count b{display:block;font:700 15px var(--tabular);color:var(--muted)}
.fuse-count b.ready{color:var(--brand-deep);animation:beacon 2.4s ease-in-out infinite}
.fuse-count .fuse-max{color:var(--brand-deep)}
.fuse-count small{display:block;font:800 10px var(--tabular);color:var(--brand-deep)}

/* The bay itself: pick five, hand one the paintbrush, pull the lever. */
.fuse-dialog{max-height:calc(var(--app-height) - 36px);overflow:auto;overscroll-behavior:contain}
.fuse-lead{margin:14px 0 4px;font-size:14px;font-weight:900;display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.fuse-lead small{flex-basis:100%;color:var(--muted);font-size:11px;font-weight:700}
.fuse-lead-ico{font-size:19px}
.fuse-next{color:var(--brand-deep)}
.fuse-step{margin:14px 0 8px;font-size:11px;font-weight:900;color:var(--muted);letter-spacing:.4px;display:flex;justify-content:space-between;align-items:center}
.fuse-step b{font:700 12px var(--tabular);color:var(--ink)}
.fuse-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:8px;max-height:34vh;overflow:auto;padding:2px}
.fuse-cell{position:relative;border:3px solid var(--outline);border-radius:var(--r-sm);background:var(--paper);padding:6px 4px;cursor:pointer;box-shadow:0 3px 0 rgba(34,49,79,.18);transition:transform .1s,box-shadow .1s,background .12s,border-color .12s}
.fuse-cell svg{width:100%;height:auto;display:block}
.fuse-cell:active{transform:translateY(2px);box-shadow:0 1px 0 rgba(34,49,79,.18)}
.fuse-cell.sel{border-color:var(--go-deep);background:color-mix(in srgb,var(--go) 14%,#fff)}
.fuse-check{position:absolute;top:-8px;left:-6px;z-index:1;width:22px;height:22px;display:grid;place-items:center;font:400 12px var(--display);font-style:normal;background:var(--go);color:#fff;border:2.5px solid var(--outline);border-radius:50%;box-shadow:0 2px 0 var(--outline)}
.fuse-keep-row{display:flex;gap:8px;overflow-x:auto;padding:4px 2px 6px}
.fuse-keep{position:relative;flex:0 0 76px;border:3px solid var(--outline);border-radius:var(--r-sm);background:var(--paper);padding:5px 3px;cursor:pointer;box-shadow:0 3px 0 rgba(34,49,79,.18);transition:transform .1s,background .12s,border-color .12s}
.fuse-keep svg{width:100%;height:auto;display:block}
.fuse-keep.keep{border-color:var(--brand-deep);background:color-mix(in srgb,var(--brand) 20%,#fff)}
.fuse-keep.keep i{position:absolute;top:-8px;right:-6px;width:22px;height:22px;display:grid;place-items:center;font-style:normal;font-weight:900;font-size:12px;background:var(--brand);color:var(--on-brand);border:2.5px solid var(--outline);border-radius:50%;box-shadow:0 2px 0 var(--outline)}
.fuse-keep-empty{color:var(--muted);font-size:11px;font-weight:700;padding:8px 2px}
.fuse-dialog .draw-btn{margin-top:14px}

/* The finish sheet's ability receipt. */
.finish-ability{margin:8px 0 0;text-align:center;font:800 13px var(--ui);color:var(--brand-deep)}

/* A market stall's train wears the same ability tag the garage pins on. */
.market-train{position:relative;display:block}
.market-train svg{width:100%;height:auto;display:block}

/* The worn ability's card under the showcase plinth: icon, name+stars, one line
   of what it does. It is a button — tapping it opens the guide. */
.showcase-ability{display:flex;align-items:center;gap:10px;margin:10px auto 0;max-width:320px;width:100%;text-align:left;border:3px solid var(--outline);border-radius:var(--r-sm);background:color-mix(in srgb,var(--brand) 16%,#fff);padding:8px 12px;cursor:pointer;box-shadow:0 3px 0 rgba(34,49,79,.18);transition:transform .1s,box-shadow .1s}
.showcase-ability:hover{transform:translateY(-2px);box-shadow:0 5px 0 rgba(34,49,79,.18)}
.showcase-ability:active{transform:translateY(2px);box-shadow:0 1px 0 rgba(34,49,79,.18)}
.showcase-ability i{font-size:20px;font-style:normal}
.showcase-ability b{display:block;font-size:12px;font-weight:900;color:var(--ink)}
.showcase-ability small{display:block;margin-top:1px;color:var(--muted);font-size:10px;font-weight:700;line-height:1.4}

/* The little "what are abilities?" pill beside the fusion heading. */
.guide-open{margin-left:8px;border:2.5px solid var(--outline);background:var(--paper);color:var(--ink);font:800 10px var(--ui);padding:3px 10px;border-radius:999px;cursor:pointer;box-shadow:0 2px 0 var(--outline);transition:transform .08s,box-shadow .08s,background .15s}
.guide-open:hover{background:var(--tint)}
.guide-open:active{transform:translateY(2px);box-shadow:0 0 0 var(--outline)}

/* 능력 도감: seven rows, each an icon block, the ★1 line, and the star ladder. */
.ability-dialog{max-height:calc(var(--app-height) - 36px);overflow:auto;overscroll-behavior:contain}
.guide-note{margin:14px 0 12px;color:var(--muted);font-size:12px;font-weight:700;line-height:1.65}
.guide-list{display:flex;flex-direction:column;gap:10px}
.guide-row{display:flex;align-items:flex-start;gap:12px;border:3px solid var(--outline);border-radius:var(--r-sm);background:var(--paper);padding:11px 12px;box-shadow:0 3px 0 rgba(34,49,79,.14)}
.guide-ico{flex:0 0 auto;width:42px;height:42px;display:grid;place-items:center;font-size:21px;background:var(--tint);border:3px solid var(--outline);border-radius:12px}
.guide-copy{flex:1;min-width:0}
.guide-copy b{display:block;font-size:13px;font-weight:900}
.guide-copy small{display:block;margin-top:2px;color:var(--muted);font-size:11px;font-weight:700;line-height:1.5}
.guide-stars{display:flex;gap:5px;margin-top:7px;flex-wrap:wrap}
.guide-stars i{font:800 9px/1.5 var(--tabular);font-style:normal;color:var(--muted);background:var(--tint);border:2px solid rgba(34,49,79,.2);border-radius:999px;padding:1px 7px;display:flex;gap:4px;align-items:center}
.guide-stars i b{font:800 10px var(--tabular);color:var(--brand-deep)}

/* Phones: the floats start under the shorter HUD, the bay grid tightens. */
@media (max-width:640px){
  .time-floats{top:56px}
  .time-float{font-size:15px;padding:4px 12px}
  .fuse-grid{grid-template-columns:repeat(auto-fill,minmax(72px,1fr));gap:6px}
  .fuse-keep{flex-basis:66px}
  .fuse-copy small{display:none}
  .fuse-dialog{padding:20px 16px}
}
