/* ==================================== mobile.css =============================================
   The phone layer, in a file of its own: main.css is the desktop client, and everything a phone
   changes about it lives here. Linked from index.html directly after main.css, and served like
   the rest of web/ (the web server stamps ./css/* with the client's own mtime, so a deploy is a
   new URL and no player holds back an old stylesheet).

   The section below is the whole of it: nothing in main.css knows about a phone. The one value
   the two files share is --hud-scale - Mobile.deviceScale sets it through
   UserInterface.applyScales, main.css reads it for the HUD's font sizes and this file for the
   boxes' own geometry. */
/* ==================================== phones and touch ====================================
   Two body classes carry the whole layer (Mobile.js):

     body.mobile        the touch layer is on (a finger-driven device, or the UI tab's
                        "Mobile controls: On") - the stick and the buttons, the safe-area
                        padding, the arena's own touch behaviour, and HUD boxes that follow
                        the finger instead of dragging under it;
     body.compact-hud   the viewport's short side is under Mobile.HUD_FULL_AT, so the HUD's
                        geometry follows --hud-scale instead of keeping its desktop pixels.

   Nothing here applies without those classes, so a desktop is untouched. */

#mobile-controls {
    position: absolute;
    inset: 0;
    display: none;
    /* The layer itself is only a frame: the stick and the buttons take their own thumbs, and
       everything between them belongs to the arena. */
    pointer-events: none;
    /* The shipped size of a button. Mobile > Layout > Button size multiplies it (Mobile.apply
       writes --mobile-btn-scale, and the fallback is the shipped 1) and Button opacity dims the
       whole pad, so the two options need no rule of their own. */
    --mobile-btn: calc(clamp(54px, 16vmin, 82px) * var(--mobile-btn-scale, 1));
    /* ...and the top-left corner's own scale: the ☰ and the row of HUD switches are pressed
       between lives, not in the middle of a fight, so they are drawn a little smaller than a pad
       button (see their own rule below). */
    --mobile-toggles: .68;
}

#mobile-controls.on {
    display: block;
}

/* The pad sits OVER the HUD boxes, and that is deliberate on a phone. #mobile-controls is
   written before #hud-container in index.html, so the plates painted over the buttons: with
   Button layout > Thumb corner on Left the pad lands in the corner the log already holds
   (posChat bl), and what a player saw was "most of the buttons are out of the screen" - the log
   drawn on top of them, and taking their touches for the boxes it covers. The layer is below
   the lobby and the modals (their own z-indexes start at 6), because a card that is up has to
   keep the touches it is asking for. */
body.mobile #mobile-controls {
    z-index: 5;
}

/* ...and the two cards stay above it. Neither has a z-index of its own in main.css (they are
   painted over the HUD by DOM order alone), so raising the pad would have floated the buttons
   over the lobby and the modals and taken the taps that belong to a Play button. Everything the
   player is ASKED for keeps its place: the captcha overlay (10), the player panel and the rest
   of the modal subtree (inside this container), and the toast (60) are all above the pad. */
body.mobile #user-interface,
body.mobile #modals-container {
    z-index: 6;
}

/* The stick: drawn where the thumb landed (a floating stick, so there is nothing to find) and
   translucent enough to read the arena through. */
#mobile-stick {
    position: fixed;
    left: 0;
    top: 0;
    width: 108px;
    height: 108px;
    margin: -54px 0 0 -54px;
    border: 2px solid rgba(242, 242, 242, .3);
    border-radius: 50%;
    background-color: rgba(11, 11, 13, .22);
    box-shadow: 0 6px 24px rgba(0, 0, 0, .35);
    opacity: 0;
    transition: opacity .12s ease-out;
}

#mobile-stick.on {
    opacity: 1;
}

#mobile-stick-knob {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 46px;
    height: 46px;
    margin: -23px 0 0 -23px;
    border-radius: 50%;
    background-color: rgba(242, 242, 242, .42);
}

/* Mouse mode's cursor (Button layout > Mouse mode for joystick): ONE filled dot - the point the
   stick is driving, and nothing around it - because that point IS the aim, and a ring drawn
   around it read as a second thing to line up (a mark the eye kept centring, on a phone where the
   dot already sits exactly where the mouse is). Fixed (not absolute) because the layer writes
   screen pixels to it, and pointer-events:none: it is a mark on the glass, and a drag across it
   belongs to the arena. Drawn while the mode is on and the layer is up, wherever the cursor is. */
#mobile-cursor {
    position: fixed;
    left: 0;
    top: 0;
    width: 14px;
    height: 14px;
    margin: -7px 0 0 -7px;
    border-radius: 50%;
    background-color: rgba(242, 242, 242, .9);
    box-shadow: 0 0 8px rgba(0, 0, 0, .55);
    pointer-events: none;
    display: none;
    z-index: 2;
}

#mobile-cursor.on {
    display: block;
}

/* The four actions, in one bottom corner's reach: Split and Eject (the two a thumb works
   mid-fight) nearest its own edge, D and x16 beside them, and the row of HUD buttons on top.
   Button layout > Thumb corner moves the pad to the other corner (body.mobile-buttons-left) and
   Button opacity dims it.

   The columns are AUTO rather than two fixed --mobile-btn lanes: Split ships half again the size
   of the other three (the #mobile-split rule below), and a lane sizes itself to the biggest
   button in it - so the bigger Split widens its own column instead of overlapping the Eject sitting
   above it. */
#mobile-actions {
    position: fixed;
    right: max(10px, env(safe-area-inset-right));
    bottom: max(10px, env(safe-area-inset-bottom));
    display: grid;
    grid-template-columns: repeat(2, auto);
    gap: 8px;
    justify-items: center;
    align-items: center;
    opacity: var(--mobile-btn-opacity, 1);
}

body.mobile-buttons-left #mobile-actions {
    right: auto;
    left: max(10px, env(safe-area-inset-left));
}

/* WHICH button goes where. The pad is read from the corner it sits in, so the arrangement is a
   MIRROR of itself on the other side: with the pad on the right (the shipped side) D and x16 take
   the left column and Eject and Split the right one - Split and Eject nearest the thumb's own
   edge, which is what a player needs of the two they press in a fight - and with the pad on the
   left the same shape reads the other way round (Eject over Split on the left, D over x16 on the
   right). `order` is what moves them: the markup is one order and the corner decides which
   arrangement that order is laid out in. Vertical order never changes - D over x16, Eject over
   Split - so the thumb learns one cross and keeps it. */
#mobile-actions #mobile-double { order: 1; }
#mobile-actions #mobile-eject { order: 2; }
#mobile-actions #mobile-x16 { order: 3; }
#mobile-actions #mobile-split { order: 4; }

body.mobile-buttons-left #mobile-actions #mobile-eject { order: 1; }
body.mobile-buttons-left #mobile-actions #mobile-double { order: 2; }
body.mobile-buttons-left #mobile-actions #mobile-split { order: 3; }
body.mobile-buttons-left #mobile-actions #mobile-x16 { order: 4; }

/* Split is the button a thumb presses in the middle of a fight, so it ships half again the size of
   Eject, D and x16. That is a fact about the LAYOUT - one rule, one button - and not an option:
   the ✥ editor moves the four and sizes none of them (a pinch that resized one button took the
   whole group with it). Mobile > Layout > Button positions puts every one of them, and the pad
   itself, back where this rule and the corner above have them. */
#mobile-split {
    --mobile-btn: calc(clamp(54px, 16vmin, 82px) * var(--mobile-btn-scale, 1) * 1.5);
}

/* The row of in-game switches: the 🗣️ chat button and the ⇄ swap (plus the ✅ that joins them
   while the HUD positions are in hand). The board-and-map button is the FIRST of them, back in
   this corner where a thumb reaches it mid-fight (the owner's call - index.html says why); the
   ⛶ fullscreen is the one that left for the lobby card's own links row. What is left lives in
   the TOP-LEFT corner beside the ☰ (one gap to its right), not
   above the action pad: the pad moves with Thumb corner, and a row of switches that rode along
   with it ended up half off the screen when the pad was left-handed. Fixed where they are, they
   cannot. They are switches rather than actions, so they light up (.on) to say what is on. */
#mobile-toggles {
    position: fixed;
    left: calc(max(10px, env(safe-area-inset-left)) + var(--mobile-btn) + 8px);
    top: max(10px, env(safe-area-inset-top));
    display: flex;
    gap: 8px;
    align-items: center;
    width: max-content;
    /* The row is a handful of buttons across - wider than a phone held upright once Button size
       is turned up. It WRAPS rather than running off the edge: the last buttons drop to a second
       row, over the arena, instead of out of reach. */
    flex-wrap: wrap;
    max-width: calc(100vw - max(10px, env(safe-area-inset-left)) - var(--mobile-btn) - 8px - 8px);
}

/* The ☰ that leaves the game for the lobby: its own button, in the top-left corner - the one
   the pad does not use, and out of the way of both thumbs. A TAP is the lobby and a HOLD is the
   browser's fullscreen (Mobile.bindMenuHold); the ⛶ in the row beside it is that same fullscreen
   in one press. */
#mobile-menu {
    position: fixed;
    left: max(10px, env(safe-area-inset-left));
    top: max(10px, env(safe-area-inset-top));
    width: var(--mobile-btn);
    height: var(--mobile-btn);
}

.mobile-btn {
    /* Button size is one number for the whole pad (Mobile > Layout > Button size, which Mobile
       writes as --mobile-btn-scale): the shipped control, and Split's own rule above is the only
       button that is not exactly this. The font is a share of the button in the same way, so the
       glyph grows with whatever the button grew to. */
    width: var(--mobile-btn);
    height: var(--mobile-btn);
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    border: 2px solid rgba(242, 242, 242, .22);
    border-radius: 50%;
    background-color: rgba(11, 11, 13, .55);
    color: #f2f2f2;
    font-family: inherit;
    font-size: calc(var(--mobile-btn) * .32);
    font-weight: 700;
    line-height: 1;
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
    /* The button owns every touch that lands on it: no scrolling, no zoom, no long-press menu. */
    touch-action: none;
    pointer-events: auto;
    transition: background-color .1s ease-out, transform .1s ease-out;
}

.mobile-btn img {
    width: 62%;
    height: 62%;
    /* The reference client's own split.png / eject.png are BLACK artwork on a transparent
       ground - they were drawn for light plates, and on this client's dark button they are
       invisible without this. Inverted they are the white icons those two buttons want. */
    filter: invert(1);
    /* the artwork must not be draggable, and it must not swallow the button's own touch */
    pointer-events: none;
    -webkit-user-drag: none;
}

.mobile-btn.down {
    background-color: rgba(242, 242, 242, .34);
    transform: scale(.94);
}

/* One of the HUD buttons: a CIRCLE like everything else on this layer (.mobile-btn's own 50%)
   and the SAME size as an action button - one control set, not a row of small ones beside a pad
   of big ones. What is left of the modifier is how a switch reads: dim until it is on. When one
   is on (the board or the map showing, the log showing, the boxes in hand) it says so in green. */
/* The row of HUD switches - and the ☰ beside it - are the top-left corner's own strip: pressed
   between lives rather than in the middle of a fight, and therefore drawn a little smaller than a
   pad button, so the corner reads as switches and not as a second pad leaning on the arena. One
   factor (--mobile-toggles, on the layer above) sizes the ☰ and the whole row together; the row's
   own gap and offset stay measured in --mobile-btn - the shipped control size - so nothing else
   has to move with them. The factor itself is a touch under three quarters: small enough that the
   corner is switches, big enough that a thumb still hits one on the first try. */
#mobile-menu,
.mobile-btn-small {
    width: calc(var(--mobile-btn) * var(--mobile-toggles, .68));
    height: calc(var(--mobile-btn) * var(--mobile-toggles, .68));
    font-size: calc(var(--mobile-btn) * .32 * var(--mobile-toggles, .68));
}

.mobile-btn-small {
    min-width: 0;
    padding: 0;
    border-radius: 50%;
    opacity: .6;
}

.mobile-btn-small.on {
    opacity: 1;
    border-color: #56b949;
    color: #56b949;
}

/* A button Button layout switched off: out of the row, so the rest of the pad closes up and a
   thumb cannot press what is not there. */
.mobile-btn-off {
    display: none !important;
}

/* The chat's own popup (Mobile settings > Chat > Chat style, the shipped "Popup"): with the log
   box not drawn, a message shows itself here for a few seconds and takes itself away again. It
   is a GLANCE, not a control - the layer's own pointer-events: none is what lets a thumb keep
   steering through it - and it stands on the readout, in the corner the shipped layout puts that
   (Mobile.placeChatPopup writes the offset; this is the fallback corner, the bottom left). */
#mobile-chat-popup {
    position: fixed;
    left: max(10px, env(safe-area-inset-left));
    bottom: max(10px, env(safe-area-inset-bottom));
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    /* As wide as its own text, and never wider than one glance can read. */
    max-width: 78vw;
    font-size: calc(var(--mobile-ui) * .95);
    pointer-events: none;
}

.mobile-chat-popup-row {
    max-width: 100%;
    box-sizing: border-box;
    padding: .3em .6em;
    border-radius: .5em;
    background-color: rgba(11, 11, 13, .72);
    color: #f2f2f2;
    overflow-wrap: anywhere;
    /* Born, held, gone: the animation runs for the layer's own CHAT_POPUP_MS (Mobile writes it
       into --mobile-chat-popup-ms) and the timer that takes the node out ends with it, so a line
       never blinks out mid-sentence and no empty plate is left behind either. */
    animation: mobile-chat-popup-in var(--mobile-chat-popup-ms, 5000ms) ease-out forwards;
}

@keyframes mobile-chat-popup-in {
    0% { opacity: 0; transform: translateY(6px); }
    6% { opacity: 1; transform: none; }
    92% { opacity: 1; }
    100% { opacity: 0; }
}

/* The history the 🗣️ button opens (Mobile.showChatHistory - the corner taps that used to open it
   are gone: that corner is where a thumb steers and where the pad sits, and three taps of a
   player aiming and pressing Split opened the chat mid-fight): the same bottom-left the popups
   stand in, but a CONTROL rather than a glance - it is scrolled (pan-y only, so a drag inside it
   never turns into the page's own pan) and it takes its own touches, because a thumb that is
   reading back the chat is not steering. It is drawn only while it is open, and it puts itself
   away five seconds later (CHAT_HISTORY_OPEN_MS).

   The LIST has no ground of its own - it is transparent, so the arena is still visible behind the
   chat - and each LINE wears the plate the popups wear, one per row. */
#mobile-chat-history {
    position: fixed;
    left: max(10px, env(safe-area-inset-left));
    bottom: max(10px, env(safe-area-inset-bottom));
    /* The list a thumb looks back through, not a log the player reads: the last few lines, as
       small as they can stay legible, over the corner of the arena. It was a 330px column that
       could grow to half the screen (46vh), which on a phone in landscape is a panel over the
       game for the five seconds it is up. A glance's worth of width, a third of the height, and
       the type a step down with it. */
    width: min(66vw, 258px);
    max-height: 30vh;
    box-sizing: border-box;
    padding: 2px 0;
    background-color: transparent;
    color: #f2f2f2;
    font-size: calc(var(--mobile-ui) * .8);
    overflow-y: auto;
    overscroll-behavior: contain;
    touch-action: pan-y;
    display: none;
    flex-direction: column;
    gap: 3px;
}

#mobile-chat-history.open {
    display: flex;
}

/* One line, on the plate the popup's rows wear - the same ground, the same radius, the same
   padding, so a message read back and a message just arrived look like the same thing. */
.mobile-chat-history-row {
    max-width: 100%;
    box-sizing: border-box;
    padding: .2em .5em;
    border-radius: .5em;
    background-color: rgba(11, 11, 13, .72);
    overflow-wrap: anywhere;
    line-height: 1.25;
}

.mobile-chat-history-row.party {
    color: #ffd479;
}

/* The count of what is still above the list's first row: the hint that there is more to scroll up
   to, which is also the shape the older sets arrive in. It wears the same plate, thinner. */
.mobile-chat-history-more {
    padding: .15em .5em;
    border-radius: .5em;
    background-color: rgba(11, 11, 13, .5);
    font-size: .78em;
    text-align: center;
    color: var(--averrage);
}

/* ---------- the page itself ---------- */

/* A phone's browser chrome comes and goes, and a pull-down gesture at the top of the page is a
   reload: the page is pinned to the viewport, nothing scrolls under the arena, and the canvas
   and the HUD (every one of them position:absolute against this box) fill it. */
body.mobile {
    position: fixed;
    inset: 0;
    overflow: hidden;
    overscroll-behavior: none;
    -webkit-tap-highlight-color: transparent;
    -webkit-touch-callout: none;
    touch-action: manipulation;
}

body.mobile #view,
body.mobile #hud-container,
body.mobile #user-interface,
body.mobile #modals-container {
    width: 100%;
    height: 100%;
}

/* The arena is the stick's surface: a drag across it must never be a scroll, a zoom or a text
   selection. */
body.mobile #view {
    touch-action: none;
}

/* ---------- the HUD boxes on a small screen ---------- */

/* On a compact screen the HUD's geometry follows its scale: the boxes' own pixel sizes (the
   board's 200px, the chat's 370x260, the readout's margins) were what ran off a phone's screen
   even once the text was scaled. --hud-unit is 1 everywhere else, so a desktop is unchanged. */
body.compact-hud {
    --hud-unit: var(--hud-scale, 1);
}

body.compact-hud .hud-leaderboard {
    width: calc(200px * var(--hud-unit));
    margin: calc(15px * var(--hud-unit));
}

body.compact-hud .hud-leaderboard-tile {
    padding: calc(3px * var(--hud-unit)) calc(8px * var(--hud-unit));
}

body.compact-hud .hud-stats {
    margin: calc(10px * var(--hud-unit));
    padding: calc(4px * var(--hud-unit)) calc(10px * var(--hud-unit));
}

body.compact-hud .hud-stats-line {
    gap: calc(8px * var(--hud-unit));
}

body.compact-hud .hud-stats-wifi {
    width: calc(17px * var(--hud-unit));
    height: calc(17px * var(--hud-unit));
    flex-basis: calc(17px * var(--hud-unit));
}

body.compact-hud .hud-party {
    width: calc(190px * var(--hud-unit));
    margin: calc(10px * var(--hud-unit));
}

body.compact-hud .hud-party-list {
    max-height: calc(168px * var(--hud-unit));
}

body.compact-hud .hud-minimap,
body.compact-hud .hud-clock {
    margin: calc(15px * var(--hud-unit));
}

body.compact-hud .hud-chat {
    margin: calc(15px * var(--hud-unit));
}

body.compact-hud .hud-chat-content {
    padding: calc(10px * var(--hud-unit));
    gap: calc(5px * var(--hud-unit));
}

/* The chat's own size is an option (Chatbox width / height), written inline by
   UserInterface.applyChatOptions; these are the phone's ceilings on it, because a 300x300 log is
   half of a phone held sideways. max-* beats an inline size, which is what makes this work, and
   the log's own font is smaller on the layer as well (Mobile.CHAT_FONT_SCALE, applied with the
   other scales): a phone reads its chat at arm's length, over a keyboard that is already eating
   half the screen. */
body.mobile .hud-chat {
    max-width: 34vw;
    max-height: 26vh;
}

/* The chat input, which on a phone is raised by a tap on the log (Mobile.bindChatTap - there is
   no Enter key to raise it with). The bar's own 75% down the page is where the KEYS are the
   moment the field takes focus, so the layer puts it in the top third instead: over the arena,
   under the map, and clear of both the keyboard and the thumbs. */
body.mobile .hud-chat-bar {
    top: 28%;
    max-width: 64vw;
}

body.mobile .hud-stats {
    max-width: 46vw;
}

body.mobile .hud-party-list {
    max-height: 20vh;
}

/* On a phone the map is SMALL, whatever the Minimap UI scale says: a map is a glance, and the
   ceiling is measured off the shorter side of the screen so it can never eat the arena (a 250px
   map on a 360px-tall phone covered most of the play area). It keeps its own corner like every
   other box - the board's own, by default (posMinimap and posLeaderboard both ship "tr"), which
   is exactly what the one board-and-map button swaps between. The clock is the one HUD box with
   nothing a game needs in it, so a phone does not draw it. */
body.mobile #minimap-view {
    max-width: 30vmin;
    max-height: 30vmin;
}

/* The board and the map are ONE box in one corner on a phone. They ship in the same corner
   (posLeaderboard and posMinimap are both "tr") and the layer's own 1️⃣ button walks between
   them - board, then map, then neither - so stacking them in the corner's flex column would put
   one under the other for a player who can only ever see one at a time. Out of that flow and
   pinned to the corner, the map sits on the board's own spot: dropping the map on the board's
   corner IS "put the map where the board is", and switching between the two swaps the box that
   is drawn without moving anything. A desktop keeps the stacking - both boxes fit there. */
body.mobile .hud-corner > .hud-minimap-tile {
    position: absolute;
    top: 0;
    right: 0;
}

body.mobile .hud-corner-tl > .hud-minimap-tile,
body.mobile .hud-corner-bl > .hud-minimap-tile {
    left: 0;
    right: auto;
}

body.mobile .hud-corner-tc > .hud-minimap-tile,
body.mobile .hud-corner-bc > .hud-minimap-tile {
    left: 50%;
    right: auto;
    transform: translateX(-50%);
}

body.mobile .hud-corner-bl > .hud-minimap-tile,
body.mobile .hud-corner-bc > .hud-minimap-tile,
body.mobile .hud-corner-br > .hud-minimap-tile {
    top: auto;
    bottom: 0;
}

body.mobile #hud-clock-tile {
    display: none;
}

/* the clock's own margin is 10px, not the map's 15px (the rule above is a group) */
body.compact-hud .hud-clock {
    margin: calc(10px * var(--hud-unit));
}

/* ---------- who owns a finger ---------- */

/* On a phone the plates are read-only: a thumb that lands on the readout is steering, not
   dragging a box - the stick is the whole arena, and the bottom-left corner is exactly where a
   thumb rests. The HUD's own interactive pieces (the chat tabs and log, the chat field, the
   player panel) keep their touches, and the ✥ button hands the plates back while it is on. */
body.mobile .hud-draggable {
    pointer-events: none;
}

/* The ✥ editor moves the CONTROL buttons too, not only the HUD boxes: with it on, the pad and the
   row of HUD buttons are dragged as whole groups (Mobile.bindControlDrag) and the drop stores the
   nudge. touch-action:none is the same guard the plates carry - without it the browser is free to
   read a thumb crossing a button as a pan, cancel the pointer stream and leave the group midway. */
body.mobile.hud-edit #mobile-actions,
body.mobile.hud-edit #mobile-toggles {
    touch-action: none;
    cursor: move;
}

/* And the group being moved says so, the way .hud-drag-active does for a plate. */
body.mobile .mobile-control-dragging {
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, .45));
}

body.mobile.hud-edit .hud-draggable {
    pointer-events: auto;
    cursor: move;
    /* The whole gesture belongs to the drag: without this the browser is free to read a thumb
       moving across a box as a pan, cancel the pointer stream and leave the box halfway. It is
       the same rule the arena carries (body.mobile #view) for the same reason. */
    touch-action: none;
}

/* The field the log raises is a text box, so it keeps its own touch behaviour: a thumb has to be
   able to scroll inside it even while the plates are in hand. */
body.mobile.hud-edit .hud-chat-content,
body.mobile.hud-edit .hud-chat-bar-field {
    touch-action: auto;
}

/* With the plates handed back the stick stands down, so a thumb on a box moves the box. */
body.mobile.hud-edit #mobile-stick {
    display: none;
}

/* The chat's resize grip is a corner to DRAG: on a phone it is dead weight that eats a thumb
   that only wanted to steer, so it comes back with the plates (hud-edit) and stays out of the
   way otherwise. */
body.mobile .hud-chat-resize {
    pointer-events: none;
}

body.mobile.hud-edit .hud-chat-resize {
    pointer-events: all;
}

/* The server's AFK countdown ("move or you will be removed") sits just under the top row of HUD
   buttons on a phone: the top centre is always free (the boxes are in the corners and the map is
   no longer parked there). Its desktop slot - 75% down the page PLUS 84px - is off the bottom of
   a 360px-tall screen, which is exactly the one warning a player has to be able to read. */
body.mobile .hud-afk {
    top: max(8px, env(safe-area-inset-top));
    bottom: auto;
    /* 84px is the desktop's own step under the HUD row (main.css) and clears the buttons here
       too, whichever way Button size is turned. */
    transform: translate(-50%, 84px);
    max-width: 84vw;
}

/* ---------- the lobby and the panels ---------- */

/* A phone held sideways is shorter than the lobby card (460px wide, ~520px tall), so the card
   shrinks, the row of links moves under it, and the whole thing may scroll: a card that does not
   fit cannot be pressed. */
body.mobile #user-interface {
    flex-direction: column;
    justify-content: stretch;
    align-items: stretch;
    gap: 0;
    padding: 0;
    box-sizing: border-box;
    overflow: hidden;
}

/*   The card IS the screen: full width and height, no radius, no shadow - and it scrolls inside
   itself if a short landscape screen cannot hold its rows. The links row is the card's own last
   row (Leaderboard / Party link / Full screen), inside the safe area. */
body.mobile .panel {
    flex: 1 1 auto;
    width: 100%;
    max-width: none;
    min-height: 0;
    box-sizing: border-box;
    padding: calc(12px + env(safe-area-inset-top)) calc(12px + env(safe-area-inset-right)) 12px calc(12px + env(safe-area-inset-left));
    /* 8px, a step under the card's own 9px: the card's rows are measured with no slack at all on
       a phone held sideways (the links row inside the card takes the last row of pixels), so
       every pixel the boxes give back is a pixel that row gains. */
    gap: 8px;
    justify-content: center;
    border-radius: 0;
    box-shadow: none;
    overflow-y: auto;
}



body.mobile .title {
    font-size: 1.9em;
}

/* The lobby's boxes, a step shorter than a desktop's: the row is five plates plus two fields and
   a title, and on a phone held sideways the whole card has to stand inside the glass with the
   links row inside the card - which is the difference between one screen and a scroll. Text size is
   left alone (the labels are read, not measured); it is the PLATE that gives the room back. */
body.mobile .panel .button,
body.mobile .panel-actions .button {
    height: calc(var(--mobile-ui) * 2.125);      /* the desktop's 34px, was 2.375 (38px) */
    font-size: 1em;
}

body.mobile #skin {
    width: calc(var(--mobile-ui) * 2.75);        /* 44px */
    height: calc(var(--mobile-ui) * 2.75) !important;
    flex-basis: calc(var(--mobile-ui) * 2.75);
}

body.mobile #play,
body.mobile #spectate {
    height: calc(var(--mobile-ui) * 2.375);      /* 38px, one step under the old 42px */
}

/* The links row (Leaderboard / Party link / Full screen) is the card's own last row on a phone:
   the card IS the whole screen there, so the links are the one row of it a player reads on the
   way in. It wraps rather than running off the edge of a narrow screen, and a staff account's
   Admin line simply takes the next slot. */
body.mobile .panel-links {
    position: static;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 18px;
}

/* The lobby card and every modal take the whole screen on a phone: a floating 460px card on a
   screen that is 360 wide is a card that has to be scrolled sideways to be read, and the same
   goes for the settings, store and profile panels (ModalSystem writes their width and height
   inline, so those two need !important to be overridden; max-* would only cap them). */
body.mobile .modal {
    width: 100% !important;
    height: 100% !important;
    max-width: none;
    max-height: none;
    border-radius: 0;
    /* ...pinned to the corner, with the header and the body as a column. An absolutely
       positioned box with no offsets takes its place from the flex container's own centering
       (that is what centres a desktop card), and a card that is now 100% TALL centred in the same
       container came out half a header below the top - 52px, the phone's own close-button row -
       with those same 52px of its bottom off the screen. What sat in them was the end of every
       settings pane, which is exactly where the Reset row and its Button positions row live: on a
       phone they could not be reached unless the screen was taller (full screen). The body under
       the header then takes what is left, and its own scroll reaches the last row. */
    top: 0;
    left: 0;
    display: flex;
    flex-direction: column;
}

body.mobile .modal-header {
    flex: 0 0 auto;
}

body.mobile .modal-settings-content,
body.mobile .modal-store-content,
body.mobile .modal-admin-content {
    flex: 1 1 auto;
    min-height: 0;
    height: auto;
}

body.mobile .settings-tabs {
    overflow-x: auto;
    /* ...and the room the close button needs at its far end, now that the button rides this row
       (UserInterface.onSettings marks the modal .modal-settings, right below). */
    padding-right: calc(var(--mobile-ui) * 3.125);   /* the 44px */
}

/* The settings panel's own header, on a phone: the X every modal carries in a strip of its own
   leaves it and rides the tab strip instead, exactly the arrangement the store's head row uses
   (body.mobile .modal.store-modal .modal-header, below). That strip was a whole row of a
   390px-tall screen for one glyph, and the tabs under it were already a row. */
body.mobile .modal.modal-settings .modal-header {
    position: absolute;
    top: 0;
    right: 0;
    width: auto;
    padding: 0;
    z-index: 2;
}

body.mobile .settings-tab {
    flex: 0 0 auto;
    padding: calc(var(--mobile-ui) * .625) calc(var(--mobile-ui) * .75);   /* the 10px 12px above */
    white-space: nowrap;
}

body.mobile .modal-close {
    font-size: 1.4em;
    padding: 6px 10px;
}

/* ---------- the menus' own size, on a phone --------------------------------

   The lobby card and every modal are the DESKTOP client's furniture, and the layer above only
   gives them the screen to fill: 16px type, a 34px field, 38px buttons, twenty rows of it at
   mouse distances. On a phone they are read at arm's length - and a settings pane, a tab strip
   and an option per row come out a third bigger than the screen wants, which is what a phone
   player sees as "the UI is too big" rather than "the card is the wrong shape".

   --mobile-ui is that size, a LENGTH rather than a factor (the way --mobile-btn is already
   written above): one clamp off the viewport's short side, so it tracks the screen the way the
   HUD's --hud-scale does - 13.6px on a 390px phone, 12.6px on a 360px one, and capped so a
   tablet or the setting forced on over a desktop window comes out near the desktop's own.
   Nothing here goes below 12.5px, which is the floor this client's own log reads at.

   The em-based furniture inside the two containers - the title, a section heading, a hint, a
   tab, the store's rows, the account lines - needs nothing but the font-size below it, because
   em was already relative. The handful of lengths that are hard px on the desktop (the field,
   the buttons, the skin plate, a settings row's padding, the mode select, the room the rows sit
   in) follow the same number by hand, each written as the desktop pixel it replaces over the
   desktop's own 16px of type: 34px is calc(var(--mobile-ui) * 2.125). */
body.mobile {
    --mobile-ui: clamp(.78rem, 3.5vmin, .9rem);
}

body.mobile #user-interface,
body.mobile .modal {
    font-size: var(--mobile-ui);
}

/* The lobby: the two fields, and the row of them under the title. */
body.mobile #user-interface input[type=text] {
    height: calc(var(--mobile-ui) * 2.125);      /* 34px */
}

/* The settings panel: the row an option sits in, the Reset row that ends every pane, the mode
   select, and the room the tabs and rows are laid out in. The switches and the tab strip keep
   their own pixels - a switch is a thumb's target, and it is already the smallest thing here. */
body.mobile .modal-settings-tile,
body.mobile .settings-reset-tile {
    gap: calc(var(--mobile-ui) * .625);          /* 10px */
    padding: calc(var(--mobile-ui) * .5) calc(var(--mobile-ui) * .625);   /* 8px 10px */
}

body.mobile .settings-select {
    flex: 0 0 calc(var(--mobile-ui) * 8.125);    /* 130px */
    width: calc(var(--mobile-ui) * 8.125);
    height: calc(var(--mobile-ui) * 1.875);      /* 30px */
}

body.mobile .modal-settings-content {
    padding: 0 calc(var(--mobile-ui) * .9375) calc(var(--mobile-ui) * .9375);  /* 0 15px 15px */
}

/* ---------- the server list, on a phone ------------------------------------

   Servers was a column of blocks: each mode's name on a line of its own, then a Connect button
   stretched the full width of the panel under it - about 130px per server on a phone, four of
   them down a 360px-tall screen, so the last modes (Giga Split, Mobile) were below the fold on
   the one screen that is entirely about choosing between them. Here each server is one ROW - the
   name, then its own Connect at the thumb end - and the list is a grid that takes as many
   columns as the screen has room for, so a phone on its side shows all four at once and a phone
   held up shows four short rows that fit without scrolling. */
body.mobile .modal-servers-content {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    align-content: start;
    gap: calc(var(--mobile-ui) * .625);          /* 10px */
    padding: calc(var(--mobile-ui) * .9375);     /* 15px */
}

body.mobile .modal-servers-tile {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--mobile-ui) * .75);           /* 12px */
    padding: calc(var(--mobile-ui) * .5) calc(var(--mobile-ui) * .625);   /* 8px 10px */
    border-radius: 8px;
    /* The tile's own width: 100% is content-box in main.css, so without this each one is wider
       than the column it was given and the last spills off the edge of the grid. */
    box-sizing: border-box;
}

/* The name takes what is left of the row and never pushes Connect off it. */
body.mobile .modal-servers-tile .round {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Connect is a button at the end of the row rather than the row itself: the whole width is a
   target nobody can miss, and a 300px-wide "Connect" next to a mode name tells a player
   nothing about which mode it belongs to. */
body.mobile .modal-servers-tile .button {
    flex: 0 0 auto;
    width: auto;
    height: calc(var(--mobile-ui) * 2.125);      /* the 34px field */
    padding: 0 calc(var(--mobile-ui) * 1);
    font-size: .92em;
}

/* ---------- the store, on a phone ------------------------------------------

   The store is built by Account.js and drawn by main.css, and what it is made of is LENGTHS: a
   skin card's 110px picture in a four-wide grid, a 116px one in the shop's own card, an 84px
   potion in a 96px shelf row, a 112px booster, and a Coins & DNA row whose columns add up to about
   500px. A modal on a phone is the whole screen (see `body.mobile .modal` above), so the shelf is
   about 330px wide - which is four skin columns of ~57px with a 110px circle in each, the artwork
   hanging over the cards beside it, and a potion grid asking for two 182px cards where there is
   room for one and a half.

   Everything below is that same number over the desktop's own 16px of type, written the same way
   the rest of this file writes a hard px (110px is calc(var(--mobile-ui) * 6.875)). Every picture
   is ALSO capped to the box it is drawn in: the scaled size is a good guess at a phone, and
   max-width:100% with a square aspect is what makes it true whatever the screen turns out to be -
   which is the half that cannot be got wrong by a future shelf carrying a bigger asset. */
body.mobile .modal-store-content {
    padding: calc(var(--mobile-ui) * .9375);      /* the 15px */
    gap: calc(var(--mobile-ui) * .625);           /* the 10px */
}

/* The skin shelves: TWO across rather than four, and the picture takes the share of its card it
   is given. The direct child is the only picture in the card - the DNA mark inside the price is
   `.store-skin-price img`, which keeps its own 14-16px. */
body.mobile .store-skin-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: calc(var(--mobile-ui) * .625);           /* the 12px */
}

body.mobile .store-skin-grid-owned {
    grid-template-columns: repeat(auto-fill, minmax(calc(var(--mobile-ui) * 5.5), 1fr));
}

body.mobile .store-skin-card {
    /* The desktop's 190px was a column in a 4-wide shelf; a phone's card is as tall as its own
       picture, its name and its price. */
    min-height: 0;
    padding: calc(var(--mobile-ui) * .625);       /* the 12px 10px 10px */
}

body.mobile .store-skin-card > img {
    width: 100%;
    max-width: calc(var(--mobile-ui) * 6.875);    /* the 110px */
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: contain;
}

/* The pager's arms are a 34px column on either side of the shelf, and 150px tall. On a phone they
   are the difference between two usable columns and two cramped ones. */
body.mobile .store-pager-side {
    width: calc(var(--mobile-ui) * 2);            /* the 34px */
    height: calc(var(--mobile-ui) * 8);           /* the 150px */
    font-size: 1.5em;
}

/* The shop's own 126px slots column keeps its desktop place - a column down the LEFT of the
   shelf - and the page's four cards go in ONE row beside it. It used to be a full-width row of
   cells above a two-across grid: a page of four skins then took two rows out of a screen that
   had room for one, and the shelf itself, the thing the door is for, was what had to be
   scrolled to. Two cells stacked on the left is also what the desktop shows, so the shelf reads
   the same way on both. */
body.mobile .store-skin-split {
    flex-direction: row;
    gap: calc(var(--mobile-ui) * .625);           /* the 10px */
}

body.mobile .store-skin-slots {
    flex: 0 0 auto;
    position: static;
    flex-direction: column;
    width: auto;
    overflow: visible;
    gap: calc(var(--mobile-ui) * .375);           /* the 6px */
}

/* One cell to a row, its thumbnail over its name, so the pair reads down the side of the shelf
   in a column narrow enough to leave the four cards their own width. */
body.mobile .store-skin-slot {
    flex-direction: column;
    align-items: center;
    gap: calc(var(--mobile-ui) * .25);
    width: calc(var(--mobile-ui) * 4.25);         /* the 68px */
    padding: calc(var(--mobile-ui) * .375) calc(var(--mobile-ui) * .25);
}

body.mobile .store-skin-slot-text {
    font-size: .68em;
}

/* The shelf's own cards: four to a row, the same four a page the desktop draws, now that the
   cells are beside them rather than above them. The pager's arms come in with them - a 34px arm
   on either side of a page that has two thirds of the screen left is two cards' worth of room,
   and the card's own padding goes the same way. */
body.mobile .store-skin-split-list .store-skin-grid,
body.mobile .store-skin-split-list .store-skin-grid-owned {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: calc(var(--mobile-ui) * .375);           /* the 6px */
}

/* The shelf's cards keep the horizontal drag for the page turn (Account.bindSkinSwipe) and give
   the browser only the vertical one: a plain drag on a grid is a pan, and a pan takes the pointer
   stream away mid-gesture (pointercancel) - which is a swipe that never finishes. */
body.mobile .store-skin-split-list .store-skin-grid {
    touch-action: pan-y;
}

body.mobile .store-skin-split .store-pager-side {
    width: calc(var(--mobile-ui) * 1.5);          /* the 24px */
    height: calc(var(--mobile-ui) * 4);           /* the 64px */
    font-size: 1.4em;
}

body.mobile .store-skin-split-list .store-skin-card {
    gap: calc(var(--mobile-ui) * .25);
    padding: calc(var(--mobile-ui) * .375);       /* the 6px */
    border-radius: 10px;
}

/* The card's own picture is what sets its height, and the pager's row sits UNDER the shelf: a
   110px preview plus its name and its price pushed that row off the bottom of a phone, which is
   the one row of the shelf a thumb needs (it says where in the shelf you are). A little smaller
   than the panel's own cards - a quarter of the shelf wide already is - and the whole page fits. */
body.mobile .store-skin-split-list .store-skin-card > img {
    width: 100%;
    max-width: calc(var(--mobile-ui) * 4.5);      /* the 72px */
}

body.mobile .store-skin-split-list .store-skin-name,
body.mobile .store-skin-split-list .store-skin-price {
    font-size: .68em;
    padding: calc(var(--mobile-ui) * .25) calc(var(--mobile-ui) * .25);
}

/* The Custom Skins tiles carry a Replace button as well as the price line; in a card a quarter
   of the shelf wide it is the label that has to give, not the tile. */
body.mobile .store-skin-split-list .store-custom-replace {
    padding: calc(var(--mobile-ui) * .25);
    font-size: .62em;
    line-height: 1.15;
}

body.mobile .store-skin-slot-pic {
    flex: 0 0 calc(var(--mobile-ui) * 2.125);     /* the 34px */
    width: calc(var(--mobile-ui) * 2.125);
    height: calc(var(--mobile-ui) * 2.125);
}

/* Potions, cases and history: two columns of what the room actually holds, with the shelf row and
   the artwork following the type down (the 160px minimum was a grid that scrolled sideways). */
body.mobile .store-case-grid,
body.mobile .potion-slots-grid,
body.mobile .store-case-history-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: calc(var(--mobile-ui) * .625);           /* the 12px */
}

body.mobile .store-case-card,
body.mobile .potion-slot-card,
body.mobile .store-case-history {
    min-height: 0;
    padding: calc(var(--mobile-ui) * .625);       /* the 14px 10px 10px */
}

/* The brew slot is a 182px flex item with a 150px floor: the floor is what pushes a second slot
   off the screen, so on a phone a slot takes its share of the row and no more. */
body.mobile .potion-slot {
    flex: 1 1 auto;
    min-width: 0;
}

body.mobile .store-case-icon,
body.mobile .potion-slot-icon {
    height: calc(var(--mobile-ui) * 6);           /* the 96px */
}

body.mobile .store-case-art,
body.mobile .potion-slot-art,
body.mobile .store-buy-art {
    width: calc(var(--mobile-ui) * 5.25);         /* the 84px */
    height: auto;
    max-width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: contain;
}

body.mobile .store-buy {
    padding: calc(var(--mobile-ui) * .25) calc(var(--mobile-ui) * .625) calc(var(--mobile-ui) * .75);
}

/* The booster shelves: the 112px artwork is the block's own width on a desktop, and the compact
   four-across grids at the top of the drawer keep their own 56px - both move with the type. */
body.mobile .store-boost-block > .store-boost-icon {
    width: calc(var(--mobile-ui) * 6.5);          /* the 112px */
    height: auto;
    max-width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: contain;
}

body.mobile .store-owned-grid,
body.mobile .store-minion-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

body.mobile .store-owned-grid .store-boost-icon,
body.mobile .store-minion-grid .store-boost-icon {
    width: calc(var(--mobile-ui) * 3.5);          /* the 56px */
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: contain;
}

/* The VIP shelf: two 182x186px cards. On a phone they are two columns of the same cards, as tall
   as their own text. */
body.mobile .store-vip-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: calc(var(--mobile-ui) * .625);           /* the 12px */
}

body.mobile .store-vip-grid .store-card {
    width: auto;
    height: auto;
    min-height: 0;
    padding: calc(var(--mobile-ui) * .75) calc(var(--mobile-ui) * .625);
}

/* Coins & DNA: one row is a sentence in seven fixed columns - 92px + 16 + 84 + 16 + the total +
   90 + 100, about 500px - which on a phone is a row that scrolls sideways. As a wrapping line the
   sentence keeps its shape to the end of the total, and the price and its Buy button are what
   fall to the second row, the way a shop row reads. */
body.mobile .store-dna-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--mobile-ui) * .375) calc(var(--mobile-ui) * .5);   /* the 8px */
    padding: calc(var(--mobile-ui) * .625) calc(var(--mobile-ui) * .75);   /* the 10px 14px */
}

/* ...and its own button is a BUTTON, not the row: a full-width bar under every price reads as part
   of the row rather than as the thing to press, and on a phone the row is the whole panel. */
body.mobile .store-dna-buy {
    flex: 0 0 auto;
    width: auto;
    min-width: calc(var(--mobile-ui) * 6.25);     /* the 85px */
    margin-left: auto;
    padding: calc(var(--mobile-ui) * .5) calc(var(--mobile-ui) * 1.125);
}

/* The store's own header, on a phone. The X every modal carries in a strip of its own is pulled up
   onto the store's head row (Account.showStore marks the modal .store-modal for this), so the back
   arrow, the shelf's name, the balance and the close are ONE line: that strip was 52px of a
   390px-tall screen, which on a phone held sideways is a seventh of everything there is to read. */
body.mobile .modal.store-modal .modal-header {
    position: absolute;
    top: 0;
    right: 0;
    width: auto;
    padding: 0;
    z-index: 2;
}

/* The head row IS the header now, so the panel opens on it rather than a gap under a strip. */
body.mobile .modal.store-modal .modal-store-content {
    padding: calc(var(--mobile-ui) * .375) calc(var(--mobile-ui) * .9375) calc(var(--mobile-ui) * .75);
    gap: calc(var(--mobile-ui) * .5);
}

body.mobile .modal.store-modal .store-head {
    /* The close's own line, so the two rows are one. */
    min-height: calc(var(--mobile-ui) * 2.375);
    /* ...and the room that button needs at the far end, where the balance would sit under it. */
    padding-right: calc(var(--mobile-ui) * 4.125);
}

/* Every store tab takes the width it needs and the strip is SWIPED rather than squeezed: seven
   shelves in a 364px row gave each tab 47px - "PREMIU..." - and a row of clipped tabs is not a
   tab strip. The phone's own settings tabs already work this way. */
body.mobile .store-tabstrip {
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}

body.mobile .store-tabstrip::-webkit-scrollbar {
    display: none;
}

body.mobile .store-tab {
    flex: 0 0 auto;
    padding: calc(var(--mobile-ui) * .5) calc(var(--mobile-ui) * .875);
    font-size: .82em;
}

/* The cosmetics read DOWN the card on a phone. Name Position's three choices were a row of three
   (Lower / Middle / Higher) and Name Modifications' three colours a grid of three (Glow / Name /
   Stroke) - left to right, a third of the card each, which is where the labels of the wider ones
   were clipped or wrapped onto two lines, and where the last card of the row ran off the side at
   all (a flex item cannot shrink past its min-content, and a phone's third is narrower than an
   option's own label). A column gives every choice the whole card's width and its label one line,
   which is what "Name position: Lower" wants to be read as. */
body.mobile .store-cosmetic-opts {
    flex-direction: column;
    gap: calc(var(--mobile-ui) * .375);           /* the 8px */
    overflow-x: visible;
}

body.mobile .store-cosmetic-opts .store-opt {
    flex: 0 0 auto;
    min-width: 0;
    width: 100%;
    box-sizing: border-box;
    flex-direction: row;
    justify-content: center;
    gap: calc(var(--mobile-ui) * .375);
    padding: calc(var(--mobile-ui) * .5) calc(var(--mobile-ui) * .75);
}

body.mobile .store-glow-colors {
    grid-template-columns: minmax(0, 1fr);
    gap: calc(var(--mobile-ui) * .375);           /* the 10px */
    padding: calc(var(--mobile-ui) * .5) 0;
}

/* ...and each colour is its own full-width row: the name at one end, the swatch at the other. */
body.mobile .store-glow-color {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--mobile-ui) * .75);
    padding: calc(var(--mobile-ui) * .375) calc(var(--mobile-ui) * .625);
}

/* A booster card's Use: a pill in the middle of the card rather than a bar the width of it. */
body.mobile .store-case-btn {
    width: auto;
    min-width: calc(var(--mobile-ui) * 6.25);     /* the 85px */
    margin: 0 auto;
    padding: calc(var(--mobile-ui) * .5) calc(var(--mobile-ui) * 1.125);
}

body.mobile .store-dna-total img {
    width: calc(var(--mobile-ui) * 1.125);        /* the 18px */
    height: auto;
    aspect-ratio: 1 / 1;
}

/* Opening a potion: the artwork beside the rewards on a desktop. On a phone the two stack, so the
   rows that land one at a time are the full width of the screen rather than half of it. */
body.mobile .potion-open {
    padding: calc(var(--mobile-ui) * .25) calc(var(--mobile-ui) * .75) calc(var(--mobile-ui) * .75);
    gap: calc(var(--mobile-ui) * .625);
}

body.mobile .potion-open-body {
    grid-template-columns: minmax(0, 1fr);
}

body.mobile .potion-open-art {
    width: calc(var(--mobile-ui) * 6);            /* the 96px */
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: contain;
}

body.mobile .store-potion-info {
    padding: calc(var(--mobile-ui) * .625);       /* the 15px */
}

body.mobile .store-potion-info-art {
    width: calc(var(--mobile-ui) * 3);            /* the 48px */
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: contain;
}

/* The profile card (360px) and the categories (three tiles of a 64px picture) already fit a
   phone; they only need the type's own step, which the modal's font-size gives them. */
body.mobile .store-cat {
    min-height: 0;
    padding: calc(var(--mobile-ui) * .625);       /* the 12px */
}

/* The store's own main menu (the wall of categories, Account.storeHomeHTML) is a step smaller on
   a phone than the desktop's: five tiles on a 3-column grid are two rows, and the second row plus
   the shelf's header is the scroll a phone player was reading at. The artwork is scaled with the
   type (--mobile-ui) rather than in fixed pixels, so a tablet gets the desktop's own size back and
   a phone gets the room: the icon plate, the tile's own padding and the gap between them all come
   down one step, and the labels keep the size they are read at. */
body.mobile .store-categories {
    gap: calc(var(--mobile-ui) * .625);           /* the 10px, was 12px */
}

body.mobile .store-tile {
    padding: calc(var(--mobile-ui) * .625) calc(var(--mobile-ui) * .5);   /* 10px 8px, was 14px 12px */
    gap: calc(var(--mobile-ui) * .375);           /* the 6px, was 8px */
}

body.mobile .store-tile-icon {
    width: calc(var(--mobile-ui) * 4);            /* the desktop's 64px, a step down */
    height: calc(var(--mobile-ui) * 4);
}

body.mobile .store-tile-img,
body.mobile .store-tile-swatch {
    width: calc(var(--mobile-ui) * 2.875);        /* the 46px */
    height: calc(var(--mobile-ui) * 2.875);
}

body.mobile .store-tile-round {
    width: calc(var(--mobile-ui) * 3.5);          /* the 56px */
    height: calc(var(--mobile-ui) * 3.5);
}

/* ...and the tile that is a mock CELL (Cosmetics: the clipped skin with a nickname over it) comes
   down with the rest, or its fixed 58px would stand out of the plate every other tile now shares. */
body.mobile .store-tile-cell,
body.mobile .store-tile-cell-img {
    width: calc(var(--mobile-ui) * 3.625);        /* the 58px */
    height: calc(var(--mobile-ui) * 3.625);
}

body.mobile .store-tile-cell-name {
    max-width: calc(var(--mobile-ui) * 3.625);
}

/* ---------- the VIP card, and Mobile's own fullscreen line -------------------------------

   The VIP card - the ? beside the pass on the VIP shelf - is a CARD, not a screen: a title, one
   line of status, a handful of perks and a Close. Every other modal takes the whole display on a
   phone (body.mobile .modal, above), which stretched this one over the entire glass to hold four
   rows of text and left the rest of it empty. It keeps its own width - the desktop card's 360px,
   in a phone's type - and sits centred at its own height, with the close in the corner above it
   the way the desktop draws it. */
body.mobile .modal.modal-vip-info {
    position: relative;                           /* the container's centring, not the pinned corner */
    top: auto;
    left: auto;
    width: calc(var(--mobile-ui) * 20) !important;   /* the 360px */
    max-width: calc(100% - calc(var(--mobile-ui) * 2));
    height: auto !important;
    max-height: calc(100% - calc(var(--mobile-ui) * 3));
    border-radius: 14px;
    overflow-y: auto;
}

body.mobile .modal.modal-vip-info .modal-header {
    flex: 0 0 auto;
}

/* Its own rows, a phone's worth: the card is short, so the room inside it is what keeps the perks
   and the two status chips off its edges. The two passes were side by side on a desktop's 360px;
   stacked, each says its whole line without the label shrinking to fit beside the other. */
body.mobile .modal-vip-info .vip-info {
    gap: calc(var(--mobile-ui) * .5);
    padding: 0 calc(var(--mobile-ui) * 1.25) calc(var(--mobile-ui) * .875);
}

body.mobile .modal-vip-info .vip-info-title {
    font-size: 1.3em;
}

body.mobile .modal-vip-info .vip-info-states {
    flex-direction: column;
}

/* The phone's fullscreen, the one control of the in-game corner that is a LINE rather than a
   switch: it is a line of the card's own links row, in the row the phone reads on the way in,
   beside the party link. The element is unchanged - index.html keeps its id, so
   Mobile.toggleFullscreen, syncToggles and applyToggleVisibility are what they always were - and
   only where it sits is new.

   The board-and-map button is deliberately NOT one of these: it is a switch a thumb reaches
   mid-fight, so it is back in the top-left corner's row (#mobile-toggles) with the ⇄ and the 🗣️.

   It wears the plain link class's own type - index.html gives it .panel-link as well as its own
   name - so it is the same size, weight and white as Leaderboard and Party link beside it; a
   smaller, greyer line in that row reads as a caption rather than as something a player can
   press. On the layer only: main.css hides .panel-mobile-control everywhere else, which is what
   "for a mobile device only" means, and it leaves with the touch layer. */
body.mobile .panel-mobile-control {
    display: block;
    text-align: center;
    user-select: none;
    -webkit-user-select: none;
}

/* ...and the light each one carried as a button, because a switch with no answer is a switch a
   thumb presses twice: the board button says the board (or the map) is showing, and Full screen
   says the page already has the whole screen. It is the row's own green. */
body.mobile .panel-mobile-control.on {
    color: #56b949;
}

/* The ☰'s own confirm (index.html says why it exists). It is the layer's own overlay rather than
   a modal, because a phone's modal is the WHOLE screen and this is one question with two answers.
   The card is measured from --mobile-btn - the player's own Button size, which is a clamp() of
   the screen's short side - so it lands right on a small phone and on a tablet without a second
   breakpoint, and the 78vw cap keeps it inside the glass when the size is turned right up. */
.mobile-menu-popup {
    position: absolute;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    /* The layer is pointer-events:none (everything between the controls belongs to the arena);
       the question is the one thing in it that must take the press, and the backdrop takes it so
       a tap that misses the card cannot reach the arena underneath. */
    pointer-events: auto;
    touch-action: none;
    background: rgba(0, 0, 0, .55);
    z-index: 4;
}
.mobile-menu-popup.open {
    display: flex;
}
.mobile-menu-card {
    width: min(78vw, calc(var(--mobile-btn) * 4.4));
    padding: calc(var(--mobile-btn) * .34);
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    gap: calc(var(--mobile-btn) * .3);
    border-radius: 14px;
    background: var(--dark);
    box-shadow: 0 18px 50px rgba(0, 0, 0, .55);
    text-align: center;
}
.mobile-menu-question {
    font-size: 1.05em;
    font-weight: 700;
}
.mobile-menu-actions {
    display: flex;
    gap: calc(var(--mobile-btn) * .22);
}
/* One row of two, each taking half: the answer is a thumb, so the plate is a thumb's height and
   the two are never so close that the wrong one is hit. Resume is green (the safe answer, and the
   one a thumb finds first), Leave is red. */
.mobile-menu-btn {
    flex: 1 1 0;
    min-width: 0;
    height: calc(var(--mobile-btn) * .78);
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    font-weight: 700;
    color: #ffffff;
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    -webkit-tap-highlight-color: transparent;
}
.mobile-menu-btn.resume { background: #56b949; }
.mobile-menu-btn.leave { background: #d9534f; }
.mobile-menu-btn.down { filter: brightness(.86); }
