/*
 * Mobile-responsive layer (2026-09-06). Every layout (Refined Classic,
 * Command Center, Modular Dashboard) and every secondary game page still
 * uses fixed pixel widths from the desktop-only era that predates this
 * file - none of them were built with a narrow viewport in mind. This
 * file is loaded LAST (after every other stylesheet, including the
 * per-layout layouts/*.css files) so its media-query-scoped rules win
 * specificity ties via source order without needing !important on every
 * single property.
 *
 * Scope, stated honestly: this pass covers the CORE experience actually
 * built out this session - game.php's 3 hub layouts, the shared chat
 * widget (hub/widgets/chat.php), and the popup overlay system
 * (hub/widgets/popup-overlay.php, which is how every secondary game page -
 * market, guilds, crafting, casino, etc. - actually renders when
 * popupui=1). Those secondary pages' OWN internal markup still uses fixed
 * widths and are not individually reflowed here - #popup/#popupresdisplay
 * are just made to fill the (now full-width, full-height) popup box, which
 * helps but doesn't rewrite every page's internal layout. Genuine
 * follow-up work, not attempted in this pass.
 */

@media (max-width: 768px) {
	/* ---- Global -------------------------------------------------------
	 * -webkit-text-size-adjust:100% stops iOS Safari's own auto-zoom-out-
	 * to-fit behaviour once a real viewport meta tag is present (it can
	 * otherwise still shrink text unpredictably on some fixed-width
	 * children). touch-action:manipulation removes the ~300ms tap delay
	 * mobile browsers use to disambiguate a tap from the start of a
	 * double-tap-to-zoom gesture - irrelevant once real pinch-zoom is the
	 * only zoom gesture anyway, but the delay itself makes every button
	 * feel laggy without it.
	 */
	body {
		-webkit-text-size-adjust: 100%;
		touch-action: manipulation;
	}

	/* User request: "hide the scroll bars everywhere on mobile." Every
	 * scrollable element on the page gets this, not just the settings
	 * menu it came up alongside - unscoped by hub-layout on purpose,
	 * same reasoning as the body rule above. #cc-rail (command-
	 * center.css era) already had its own one-off copy of this same
	 * 3-property combo; a universal `*` rule makes that instance (and
	 * every other scrollable element - popups, chat, side-panel bodies,
	 * settings nav, dropdown-heavy panels) redundant, but it's harmless
	 * left in place. Hiding the scrollbar never removes the ability to
	 * scroll - touch/wheel/keyboard scrolling all still work identically,
	 * this only hides the browser's own visual scrollbar chrome, which
	 * reads as native-app-like on a touch device that scrolls by drag
	 * anyway. */
	* {
		scrollbar-width: none;
		-ms-overflow-style: none;
	}

	*::-webkit-scrollbar {
		display: none;
		width: 0;
		height: 0;
	}

	/* .keep-revert-popup's desktop shape (css/main.css) is one row - message,
	 * then 2 buttons, side by side - too cramped for a narrow phone width.
	 * Stacked instead: message on its own line, buttons in their own
	 * full-width row underneath. */
	.keep-revert-popup {
		flex-direction: column;
		align-items: stretch;
		gap: 8px;
	}

	.keep-revert-popup .krp-actions {
		justify-content: flex-end;
	}

	/* User request: "make sure the backgrounds fit on mobile." Every
	 * css/main<N>.css theme sets its own background-image on <body> with
	 * background-size:cover and no background-attachment (defaults to
	 * scroll, sized against body's own box). On desktop, body's height is
	 * close to one viewport, so "cover" looks right; stacking every
	 * Command Center section vertically on mobile (topbar, rail, game
	 * window, chat) makes body many viewport-heights tall, so the SAME
	 * "cover" scaled the image up to fill that whole tall scrollable
	 * area instead of just what's visible - the image reads as
	 * over-zoomed/cropped wrong, and looks different depending on scroll
	 * position. background-attachment:fixed re-anchors sizing/positioning
	 * to the viewport itself regardless of how tall the page's real
	 * content grows - one, always-correctly-fitted background no matter
	 * how much you scroll. Applies to every theme/layout uniformly since
	 * it's a mechanism fix, not a per-theme value. */
	body {
		background-attachment: fixed;
	}

	/* iOS Safari auto-zooms the page in when a focused form field's own
	 * font-size is under 16px - applies to every page, not just the hub
	 * (login/registration forms included). */
	input[type="text"],
	input[type="password"],
	input[type="email"],
	input[type="number"],
	textarea,
	select {
		font-size: 16px;
	}

	/* Bootstrap 4's own grid is already loaded (dist/lib/bootstrap-4.1.3);
	 * login/registration already use its col-* classes and read fine at
	 * this width - no changes needed there. */

	/* ---- Action Timer / Active Dungeons (shared across every layout) ----
	 * User request: "make the action timer bigger and the active
	 * dungeons, dropdown, and go button all bigger on mobile." #timer
	 * (action-timer.php) is plain centered text with no control of its
	 * own; #maps/its Go button (moblist.php, rendered into #content by
	 * battle.php's flow) have no dedicated class or wrapper to hook - #maps
	 * + the adjacent-sibling combinator reaches the Go button without
	 * needing new markup, matching the "no markup changes for a CSS-only
	 * mobile pass" approach used throughout this file. */
	/* User request: "add a little space above the action timer in
	 * mobile, it's too close to the dropdowns above it." */
	#timer {
		font-size: 18px;
		margin-top: 10px;
	}

	#maps {
		min-height: 44px;
		font-size: 16px;
	}

	#maps + input[type="button"] {
		min-height: 44px;
		min-width: 64px;
		font-size: 16px;
	}

	/* ---- Refined Classic ------------------------------------------------ */
	/* min-width:0 matters here: css/fullwidth.css (loaded whenever a
	 * player's own widemode setting is on) sets #holder{width:80%;
	 * min-width:970px} - the SAME "widemode's own min-width floor forces
	 * #holder back to 970px on a narrow viewport" bug already found once
	 * this session for old-lyrania.css, recurring here since this new
	 * rule never reset it either. */
	body[data-hub-layout="classic"] #holder {
		width: 100%;
		min-width: 0;
		max-width: 100%;
		margin: 0;
		padding: 6px;
		box-sizing: border-box;
	}

	body[data-hub-layout="classic"] #header {
		height: auto;
		position: static;
	}

	body[data-hub-layout="classic"] #header .lrow {
		flex-wrap: wrap;
	}

	body[data-hub-layout="classic"] .headerbox {
		flex: 1 1 45%;
	}

	/* !important on height: content/js/chat.js's resize feature
	 * (applyChatSplitHeight(), a desktop-only draggable chat/content
	 * split) sets INLINE style.height directly on #middlesection (and
	 * #side1/#main/#side2 below) at runtime - an inline style always
	 * beats an external stylesheet rule regardless of specificity unless
	 * that rule is !important, the same recurring pattern this whole
	 * session already hit for header-nav's/guild-timers.php's own inline
	 * styles, just JS-applied instead of PHP-baked-in this time. */
	body[data-hub-layout="classic"] #middlesection {
		height: auto !important;
		display: flex;
		flex-direction: column;
		padding: 0;
	}

	/* min-width:0 on all three: css/fullwidth.css (widemode) also sets
	 * #side1{min-width:198px}/#side2{min-width:170px} - same floor-forces-
	 * desktop-width bug as #holder above. */
	body[data-hub-layout="classic"] #main,
	body[data-hub-layout="classic"] #side1,
	body[data-hub-layout="classic"] #side2 {
		float: none;
		width: 100%;
		min-width: 0;
		max-width: 100%;
		height: auto !important;
		min-height: 0;
		max-height: none;
		overflow: visible;
		box-sizing: border-box;
	}

	/* #questtracker/#sidecounter (inside #side1) are position:absolute
	 * with hardcoded top offsets tuned for the desktop layout's known
	 * fixed #side1 height - the exact same bug already found and fixed
	 * for Command Center/Modular Dashboard. Once #side1 becomes height:
	 * auto above, these absolutely-positioned children stop contributing
	 * to its height at all (only #side1's own static content, like the
	 * .rc-section-label text, does) - #side1 collapses to almost nothing
	 * while quest-tracker.php's real content floats independently at its
	 * old fixed pixel offset, overlapping whatever now sits below it. */
	body[data-hub-layout="classic"] #questtracker,
	body[data-hub-layout="classic"] #sidecounter,
	body[data-hub-layout="classic"] #bannerholder {
		position: static;
		top: auto;
		left: auto;
		height: auto;
	}

	/* ---- Command Center -----------------------------------------------
	 * Round 32 (user request): "we want to use the command center as the
	 * [mobile] layout, but modified for mobile" - this whole section was
	 * rewritten from the original pass, which fell back to plain upright
	 * stacked cards (abandoning the collapsed-tab space-saving trick
	 * entirely) and never accounted for the desktop-only column-resize/
	 * content-resize-sync features added later the same project. Real
	 * bugs fixed and new mobile-specific interactions added below - see
	 * each rule's own comment.
	 */
	/* Bug fix: Wide Mode (data-widemode="1") sets #cc-shell's own
	 * min-width:970px (command-center.css) - the exact same "widemode's
	 * own min-width floor forces the shell back to desktop width on a
	 * narrow viewport" bug already found and fixed for Refined Classic's
	 * #holder above, just never carried over to Command Center's own
	 * widemode rule. Left unreset, EVERY child here (chat dock, topbar,
	 * rail) inherits a 970px-wide ancestor regardless of the real
	 * viewport - confirmed live as the actual cause of the chat controls
	 * row overflowing off-screen instead of wrapping. */
	body[data-hub-layout="command_center"] #cc-shell {
		width: 100%;
		/* !important: the widemode rule this resets
		 * (body[data-hub-layout="command_center"][data-widemode="1"]
		 * #cc-shell.lrow) out-specifies this plain #cc-shell selector
		 * (2 attrs + a class vs. 1 attr here) regardless of this file's
		 * later source order - confirmed live (#cc-shell still measured
		 * 970px wide with Wide Mode on before adding this). */
		min-width: 0 !important;
		max-width: 100%;
		margin: 6px auto 0;
		padding: 0 6px;
		box-sizing: border-box;
	}

	body[data-hub-layout="command_center"] .cc-topbar-row {
		justify-content: flex-start !important;
	}

	/* ---- Header: 4 columns, exact field layout from the user's own
	 * troubleshoot.png mockup -------------------------------------------
	 * Column 1: Name, Exp, Pet Lvl, DP, Tokens
	 * Column 2: Health, Attack, Defence, Accuracy, Evasion (combat-stats,
	 *           unchanged - already icon-per-row in this order)
	 * Column 3: Level, Guild Lvl, Money, Jade, Jewel Frags
	 * Column 4: Diamonds, Sapphires, Rubies, Emeralds, Opals (gems,
	 *           unchanged)
	 * i.e. identity's and currencies' 10 fields between them are NOT one
	 * column each - they're interleaved across columns 1 and 3 in a
	 * specific new order the mockup spelled out field-by-field. combat-
	 * stats/gems keep their existing whole-widget internal order (columns
	 * 2/4), just moved to the correct grid-column slot.
	 *
	 * Every same-numbered row aligns across all 4 columns - only
	 * achievable by giving each individual field its own explicit
	 * grid-row (not e.g. Subgrid on whole widgets, since identity/
	 * currencies fields no longer stay grouped by their own widget).
	 * identity.php/currencies.php are UNCHANGED - each field's own
	 * <span id>/<span class> (already there for other JS) is what a
	 * :has(+ span#id) selector on its preceding <b> keys off of to place
	 * both together (same grid-column/grid-row on both b and span lets
	 * them share one cell and sit inline within it, exactly like normal
	 * running text - CSS grid allows more than one item per cell).
	 *
	 * Bug fix, still relevant: command-center.css has a DESKTOP-only,
	 * non-media-query-gated `.cc-topbar-col > .headerbox { display:
	 * contents !important; }` - beaten below with !important + the same
	 * selector (importance is checked before specificity).
	 *
	 * User request: "make sure the stats and gems sections are skinnier,
	 * so that the full text fits without ... on the other sections" -
	 * columns 2/4 (icon + a short number/percentage) need far less width
	 * than columns 1/3 (full label text + long formatted numbers) -
	 * `auto` sizes 2/4 to their own real content, `1fr` splits the
	 * genuinely leftover width evenly between 1/3 instead of all 4
	 * columns competing for equal shares. */
	body[data-hub-layout="command_center"] .cc-topbar-toggle {
		display: flex;
	}

	body[data-hub-layout="command_center"] #cc-topbar-columns[hidden] {
		display: none;
	}

	/* #cc-topbar-left (command-center.php) is a new desktop-only wrapper
	 * around #cc-topbar-toggle/#cc-topbar-columns, added for the desktop
	 * MOTD/logo bubble request - display:contents keeps it completely
	 * inert here, so #cc-topbar-toggle/#cc-topbar-columns stay direct-
	 * effective-children of #cc-topbar exactly as every rule in this file
	 * already expects, unaffected by the wrapper's presence in the DOM.
	 * The bubble itself (.cc-topbar-motd) isn't part of this mobile
	 * layout at all - hidden outright. */
	body[data-hub-layout="command_center"] #cc-topbar-left {
		display: contents;
	}

	body[data-hub-layout="command_center"] .cc-topbar-motd {
		display: none;
	}

	/* Fourth redesign (user report): "the lines should all be even ...
	 * Name: Nitemare Health: Amount Money: Amount, Diamonds: Amount ...
	 * those should be in line all the way across, and each line down
	 * should behave the same." The 3rd redesign traded row-for-row
	 * alignment away for guaranteed fit/thinness; this gets BOTH via CSS
	 * Subgrid: .cc-topbar-columns becomes a real 4-column x 5-row grid,
	 * and each of the 4 widgets becomes its own small grid that only
	 * subgrids the ROWS from this shared parent (grid-template-rows:
	 * subgrid) while keeping its own 2 columns (label|value) completely
	 * LOCAL - only vertical row height is ever shared across the 4
	 * sections, never a column's width, so a long label in one widget
	 * (currencies' "Tokens:") can never again inflate an unrelated
	 * widget's (identity's) column the way the 1st/2nd redesign's single
	 * shared-across-both-widgets columns did. identity.php/currencies.php/
	 * combat-stats.php/gems.php stay completely unmodified - hiding <br>
	 * (so it doesn't consume its own implicit row) is all it takes for
	 * each widget's plain b,span,b,span... child order to auto-place 2
	 * items per row across exactly 5 rows, zero explicit per-field
	 * placement rules needed this time (unlike the 1st/2nd redesign's 20+
	 * :has() selectors) - each widget only ever contains its OWN 5
	 * fields now, there's no cross-widget field interleaving left to
	 * hand-place. overflow-x stays as a pure safety net. */
	body[data-hub-layout="command_center"] .cc-topbar-columns {
		display: grid;
		grid-template-columns: repeat(4, auto);
		grid-template-rows: repeat(5, auto);
		column-gap: 10px;
		row-gap: 2px;
		overflow-x: auto;
	}

	/* Flatten only the pair wrapper (.cc-topbar-col-left/-right) so each
	 * of the 4 widgets becomes a direct grid item of .cc-topbar-columns. */
	body[data-hub-layout="command_center"] .cc-topbar-col {
		display: contents !important;
	}

	/* Explicit grid-column reproduces the same visual arrangement (text /
	 * icons / text / icons) the earlier designs used - identity+currencies
	 * are real siblings under .cc-topbar-col-left, combat-stats+gems
	 * under -right, DOM order unchanged. */
	body[data-hub-layout="command_center"] .cc-topbar-col-left > .headerbox:first-of-type {
		grid-column: 1;
	}
	body[data-hub-layout="command_center"] .cc-topbar-col-right > .headerbox:first-of-type {
		grid-column: 2;
	}
	body[data-hub-layout="command_center"] .cc-topbar-col-left > .headerbox:last-of-type {
		grid-column: 3;
	}
	body[data-hub-layout="command_center"] .cc-topbar-col-right > .headerbox:last-of-type {
		grid-column: 4;
	}

	/* Desktop's command-center.css turns every .headerbox into a flex
	 * "chip row" - undone here same as before, but now each widget
	 * becomes its own small grid (2 local columns: label, value) instead
	 * of plain block flow, so its 5 fields can subgrid the shared outer
	 * ROW tracks above. align-items:center keeps a row's text vertically
	 * centered against a taller sibling row (e.g. gems/combat-stats'
	 * icons) the same way the very first version of this card did.
	 * !important beats desktop's #cc-topbar .headerbox rule's ID-level
	 * specificity regardless of source order. */
	body[data-hub-layout="command_center"] .cc-topbar-col > .headerbox {
		display: grid !important;
		grid-row: 1 / span 5;
		grid-template-columns: auto auto;
		grid-template-rows: subgrid;
		column-gap: 4px;
		row-gap: 2px;
		align-items: center;
	}

	body[data-hub-layout="command_center"] .cc-topbar-col > .headerbox br {
		display: none !important;
	}

	/* User report: "you removed the tweak that keeps the value next to
	 * the field name" - each widget's label column (above) is still
	 * SHARED across its own 5 rows (needed for the value column to line
	 * up too), so it's sized to that widget's OWN longest label ("Level:"
	 * in identity, "Tokens:" in currencies) - a shorter label
	 * (default-aligned, i.e. left) in that same shared column left empty
	 * space trailing it before the value column starts, same underlying
	 * mechanism as the very first version of this bug, just rescoped from
	 * "shared across 2 widgets" down to "shared across one widget's own 5
	 * fields." justify-self:end is the same fix that worked the first
	 * time: sits each label flush against its own value regardless of
	 * how much longer another row's label in the same column is. Labels
	 * were explicitly left-aligned instead of this after the FIRST
	 * shared-column round, when the mismatch was across two different
	 * widgets ("Pet Lvl:" vs "DP:", ~5 characters) and reads as visibly
	 * ragged - now that every label in a column belongs to the SAME
	 * widget and several have already been shortened (Pet:/G Lvl:/
	 * J Frags:), the remaining spread is much smaller (identity:
	 * Name/Level/Exp/G Lvl/Pet, currencies: Money/DP/Jade/Tokens/
	 * J Frags), so the same fix reads as a normal right-aligned label
	 * column instead of a ragged one. */
	/* User report: "the fields and values seem to have a bit of up/down
	 * discrepancy on the alignment, can those all be straightened out" -
	 * both an explicit, identical line-height on every label AND every
	 * value type (span AND a - Pet Lvl's own value is an <a>, which the
	 * safety-net rule below never covered, only span, so it never got
	 * this same normalization) removes any drift from each one otherwise
	 * inheriting/computing its own line-height slightly differently
	 * (nested tippy spans, an <a>'s own UA-default styling, etc.) -
	 * align-items:center then centers genuinely equal-height boxes
	 * consistently row to row, instead of centering boxes that only
	 * LOOKED like they should match. */
	body[data-hub-layout="command_center"] .cc-topbar-col > .headerbox b,
	body[data-hub-layout="command_center"] .cc-topbar-col > .headerbox > span,
	body[data-hub-layout="command_center"] .cc-topbar-col > .headerbox > a {
		line-height: 1.25;
	}

	body[data-hub-layout="command_center"] .cc-topbar-col > .headerbox b {
		margin: 0 !important;
		justify-self: end;
	}

	/* Safety net only (see the .cc-topbar-columns comment above) - a
	 * genuinely long single value (an unabbreviated guild name, say)
	 * still truncates within its own line instead of forcing the whole
	 * card wider or overflowing into the next section. Fixed px, not a
	 * vw-relative cap - scales up to a genuinely wide allowance right
	 * when "thin" matters most, e.g. a resized browser window or a
	 * tablet in portrait, at the top end of this media query's own
	 * 768px range. vertical-align has no effect here any more (this is a
	 * blockified grid item now, not an inline-block sitting in a text
	 * line) - align-items:center on the widget's own grid (above) is
	 * what actually keeps it level with its label now. Extended to also
	 * cover <a> (Pet Lvl's own value element, previously uncapped/
	 * unblocked by this rule since only span matched). */
	body[data-hub-layout="command_center"] .cc-topbar-col > .headerbox > span,
	body[data-hub-layout="command_center"] .cc-topbar-col > .headerbox > a {
		max-width: 84px;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
		display: inline-block;
	}

	/* User report: "let's abbreviate the pet name, since it seems to be
	 * what's causing the space issue" - Pet Lvl's value is the one field
	 * here that's a NAME, not a number (initMobileNumberAbbreviation.js
	 * deliberately excludes it), so the generic 84px cap above still let
	 * a longer pet name run wider than the others. Tighter, field-
	 * specific cap. */
	body[data-hub-layout="command_center"] .cc-topbar-col-left span#petli,
	body[data-hub-layout="command_center"] .cc-topbar-col-left span#petli span {
		max-width: 50px;
	}

	/* User request: shorter labels reclaim more width than any per-value
	 * truncation can - "Pet Lvl:" -> "Pet:", "Jewel Frags:" -> "J Frags:".
	 * Same font-size:0 + ::before content-swap trick command-center.css
	 * already uses for the combat-stats/gems icon labels, just swapping
	 * in different text instead of an icon - identity.php/currencies.php
	 * stay completely unmodified (shared with every other layout and
	 * viewport size); this is purely a mobile Command Center display
	 * override; the real "Pet Lvl:"/"Jewel Frags:" text is what search-
	 * in-page, screen readers, and every other layout still see. */
	body[data-hub-layout="command_center"] .cc-topbar-col-left b:has(+ a span#petli) {
		font-size: 0;
	}
	body[data-hub-layout="command_center"] .cc-topbar-col-left b:has(+ a span#petli)::before {
		content: "Pet:";
		font-size: 11px;
	}

	body[data-hub-layout="command_center"] .cc-topbar-col-left b:has(+ span#fragments) {
		font-size: 0;
	}
	body[data-hub-layout="command_center"] .cc-topbar-col-left b:has(+ span#fragments)::before {
		content: "J Frags:";
		font-size: 11px;
	}

	body[data-hub-layout="command_center"] .cc-topbar-col-left b:has(+ span#guildli) {
		font-size: 0;
	}
	body[data-hub-layout="command_center"] .cc-topbar-col-left b:has(+ span#guildli)::before {
		content: "G Lvl:";
		font-size: 11px;
	}

	/* ---- Header: links row (header-nav.php), also collapsible --------- */
	body[data-hub-layout="command_center"] .cc-topbar-nav-toggle {
		display: flex;
	}

	body[data-hub-layout="command_center"] #cc-topbar-nav[hidden] {
		display: none;
	}

	/* User request: "when both player info and links are hidden, they
	 * should be in line as even sized buttons." Both toggle buttons
	 * already share identical .cc-topbar-toggle chrome (width:100% each,
	 * command-center.css) - the only thing making them stack as two full-
	 * width rows instead of sitting side by side is #cc-topbar's own
	 * flex-direction:column (its normal, always-correct arrangement for
	 * toggle+content, toggle+content). Only ever switch to a row - and
	 * only for these two buttons - in the specific state where BOTH
	 * content sections are actually hidden: with #cc-topbar-columns and
	 * #cc-topbar-nav both display:none, they don't occupy a flex slot
	 * either way, so flipping the container to row only visually affects
	 * the two remaining (visible) button children. */
	body[data-hub-layout="command_center"] #cc-topbar:has(#cc-topbar-columns[hidden]):has(#cc-topbar-nav[hidden]) {
		flex-direction: row;
	}

	body[data-hub-layout="command_center"] #cc-topbar:has(#cc-topbar-columns[hidden]):has(#cc-topbar-nav[hidden]) .cc-topbar-toggle {
		flex: 1 1 0;
		width: auto;
		margin-bottom: 0;
	}

	/* Divider-hiding and the bordered-card treatment for each nav group
	 * are universal now (command-center.css - every group gets the same
	 * card chrome the Chat group used to be the only one to have), not
	 * mobile-only. Just a touch-friendlier size/spacing tweak left here. */
	body[data-hub-layout="command_center"] #cc-topbar .cc-topbar-row-nav .cc-nav-group a {
		padding: 4px 2px;
		font-size: 13px;
	}

	body[data-hub-layout="command_center"] #cc-topbar .cc-topbar-row-nav .cc-nav-group-label {
		font-size: 12px;
	}

	/* ---- Icon rail: horizontal top tab bar -----------------------------
	 * Rail+content stack full-width above the side panel, and the icon
	 * rail itself becomes a horizontal row across its own top instead of
	 * a vertical sidebar - the common "top tab bar" mobile nav pattern,
	 * much more natural than a narrow column of icons down the left edge
	 * of a phone screen.
	 *
	 * Bug fix (user report): "the left column buttons expand past their
	 * limit, that shouldn't happen" - the rail's desktop-only column-
	 * resize feature (--cc-rail-columns, content/js/hub-command-center.js)
	 * and its content-resize-sync treatment (#cc-rail.cc-rail-mode-match,
	 * #cc-main:has(#cc-content.cc-content-resized) #cc-rail, both further
	 * up command-center.css) both assume this is still a vertical
	 * sidebar - a column count or synced height left over from earlier
	 * desktop use (both persist in localStorage) could leave the rail
	 * position:absolute with a width driven by that column count,
	 * fighting this horizontal-row layout instead of just being a plain
	 * wrapping row. Reset all of it explicitly rather than letting
	 * desktop-only state leak in unpredictably.
	 */
	body[data-hub-layout="command_center"] #cc-body.cc-body,
	body[data-hub-layout="command_center"] #cc-body.cc-side-collapsed-all {
		grid-template-columns: 1fr !important;
	}

	body[data-hub-layout="command_center"] #cc-main.cc-main {
		flex-direction: column;
	}

	/* Bug fix (user report): "the button bar More should be in line with
	 * the top row, it should only be 1 row of buttons that scrolls
	 * left/right (more will be the very last button to the right
	 * always)" - #cc-rail-buttons (icon buttons) and #cc-rail-more-btn
	 * are both direct row-level children of #cc-rail already (More was
	 * always last in DOM order, so it was already the visual last item -
	 * flex-wrap:wrap was the actual bug, breaking the row into multiple
	 * lines instead of one scrollable strip). nowrap + overflow-x:auto
	 * on both levels turns it into a single horizontally-scrolling row. */
	/* Bug fix (user report): "the more button should be the same size as
	 * all of the other buttons on mobile, it seems smaller" - desktop's
	 * own #cc-rail rule (further up this file) sets align-items: center
	 * for its vertical icon column, which this mobile override never
	 * reset. With a horizontal row, "center" leaves each direct child
	 * (the #cc-rail-buttons group, and #cc-rail-more-btn beside it) at its
	 * OWN natural height instead of matching the other - and since More's
	 * label is one line while several rail buttons (Jade Temple, Wishing
	 * Well, ...) wrap to two, #cc-rail-buttons' internal stretch grows
	 * those to a taller shared height that More, sitting outside that
	 * inner flex context, never picked up. align-items: stretch here
	 * makes both top-level children match whichever is taller. */
	/* User report: "the mobile button bar shouldn't show the scroll bar,
	 * and there needs to be a bit more space on the left of the first
	 * button, its overlapping the border." Desktop's own #cc-rail padding
	 * is `10px 0` (top/bottom only) - fine for a vertical column with
	 * align-items:center leaving natural side margins, but this row
	 * override never added any horizontal padding of its own, so the
	 * first/last buttons sat flush against the rail's left/right border
	 * once it became a horizontal strip. The visible OS scrollbar under
	 * overflow-x:auto is hidden cross-browser (WebKit/Blink's own
	 * pseudo-element, plus the standard scrollbar-width/-ms-overflow-style
	 * properties for Firefox/old Edge) - -webkit-overflow-scrolling:touch
	 * below already provides the native momentum-scroll feel without
	 * needing a visible track. */
	/* User report: "the more button popup is on screen, but appears below
	 * the game window, not next to the more button" - this rule used to
	 * force position:static (no comment ever explained why - nothing
	 * else in this ruleset needs it, the row layout below is purely
	 * flex/overflow properties, none of which depend on positioning),
	 * overriding desktop's own #cc-rail position:relative (command-
	 * center.css) with !important. .cc-rail-more-menu is position:
	 * absolute and a SIBLING of #cc-rail-more-btn, not its child - #cc-
	 * rail is their real shared ancestor, so #cc-rail losing its
	 * positioned-ancestor status is what let the menu's absolute
	 * position escape all the way out to the page's own initial
	 * containing block instead of resolving against the rail. Simply
	 * not overriding it restores the same positioned ancestor desktop
	 * already relies on - #cc-rail's own visual position/flow is
	 * completely unaffected either way (position:relative with no
	 * top/left/etc. offset renders identically to static; the only
	 * thing it changes is what absolutely-positioned DESCENDANTS anchor
	 * to). */
	body[data-hub-layout="command_center"] #cc-rail.cc-rail {
		flex: 0 0 auto !important;
		flex-direction: row;
		flex-wrap: nowrap;
		align-items: stretch;
		width: 100% !important;
		padding-left: 8px;
		padding-right: 8px;
		justify-content: flex-start;
		overflow-x: auto;
		overflow-y: hidden;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
		-ms-overflow-style: none;
	}

	body[data-hub-layout="command_center"] #cc-rail.cc-rail::-webkit-scrollbar {
		display: none;
		width: 0;
		height: 0;
	}

	/* Bug fix (user report): "the buttons bar... behind the game window
	 * and they stretch down to the bottom of it" - regression from
	 * removing the blanket position:static!important above (needed for
	 * the More-popup fix). That override was ALSO incidentally
	 * suppressing this completely unrelated desktop-only rule
	 * (command-center.css): the rail-resize "match content height" mode
	 * (persisted via localStorage, so it can still be set from an
	 * earlier DESKTOP visit) and the manual-drag-resized state both pin
	 * #cc-rail with position:absolute; top:0; bottom:0 against #cc-main
	 * so it can be taken out of flex flow and sized independently - a
	 * desktop-only column-resize concept with no meaning for mobile's
	 * horizontal row, but nothing ever scoped IT to desktop either, so
	 * once #cc-rail could be position:relative again on mobile, this
	 * unopposed absolute+top:0/bottom:0 stretched the rail the full
	 * height of #cc-main (rail+content together) instead of just its own
	 * button row. Forced back to the normal mobile row layout regardless
	 * of that class/state - width:100% here needs !important since
	 * .cc-rail-mode-match's own width (command-center.css) doesn't have
	 * it either, but specificity alone wouldn't reliably beat it (both
	 * selectors are equally specific; source order would decide, and
	 * this file loading after command-center.css already favours this
	 * rule, but !important removes any doubt). */
	body[data-hub-layout="command_center"] #cc-rail.cc-rail-mode-match,
	body[data-hub-layout="command_center"] #cc-main:has(#cc-content.cc-content-resized) #cc-rail {
		position: relative;
		top: auto;
		bottom: auto;
		left: auto;
		width: 100% !important;
	}

	body[data-hub-layout="command_center"] .cc-rail-buttons {
		display: flex !important;
		flex-direction: row;
		flex-wrap: nowrap;
		justify-content: flex-start;
		width: auto !important;
		flex: 0 0 auto;
	}

	body[data-hub-layout="command_center"] #cc-rail-more-btn {
		flex: 0 0 auto;
	}

	/* Bug fix (user report): "the more option popup appears off screen
	 * to the right." Desktop's own .cc-rail-more-menu is `position:
	 * absolute; left: calc(100% + 6px)` relative to #cc-rail (a narrow,
	 * ~48-64px VERTICAL sidebar there, so "100% of the rail's own width
	 * + 6px" lands the menu just past its right edge, still well inside
	 * the viewport). On mobile #cc-rail becomes a FULL-WIDTH (100%)
	 * horizontal strip - "100% of a full-width rail's own box" is the
	 * right edge of the viewport itself, so the menu rendered starting
	 * exactly there, entirely off-screen. right:0/top:100% below anchors
	 * it to #cc-rail's own bottom-right corner instead (still that same
	 * shared ancestor - #cc-rail-more-menu is a SIBLING of the More
	 * button, not its child, so a positioned ancestor has to be
	 * something that actually contains BOTH of them; #cc-rail itself,
	 * fixed to stay position:relative above, not the button - an earlier
	 * version of this comment claimed the button, which never actually
	 * did anything since position:relative on a sibling has no effect on
	 * another element's absolute positioning), right-aligned so it can't
	 * extend further right off-screen - keeps it on-screen regardless of
	 * the rail's own width. */
	body[data-hub-layout="command_center"] .cc-rail-more-menu {
		position: absolute;
		left: auto;
		right: 0;
		top: 100%;
		bottom: auto;
		margin-top: 6px;
	}

	/* The column-resize handle (drag the rail's own right border wider)
	 * has no meaning once the rail is a horizontal row instead of a
	 * vertical sidebar with columns - hidden rather than left to dangle
	 * at some arbitrary position along the row's edge. */
	body[data-hub-layout="command_center"] .cc-rail-column-handle {
		display: none;
	}

	/* #cc-content's own manually-resized/match-mode margin-left
	 * (reserving horizontal space for the now-absolutely-positioned
	 * desktop rail) has nothing to reserve space for here either, once
	 * the rail reset above takes it out of that positioning mode.
	 *
	 * Bug fix (user report): "the game screen (not attacking or autoing)
	 * is way too thin, it should fit the screen horizontally" - a PRIOR
	 * desktop drag-resize (content/js/hub-command-center.js, state
	 * persisted in localStorage) leaves #cc-content with `align-self:
	 * flex-start` (command-center.css's own .cc-content-resized rule,
	 * opting out of #cc-main's align-items:stretch so the manually-set
	 * pixel HEIGHT sticks). That opt-out is cross-axis-agnostic - on
	 * desktop's row layout it only affects height, but #cc-main becomes a
	 * COLUMN on mobile (rule above), which flips the cross axis to WIDTH,
	 * so this same leftover align-self shrink-wraps the content box to
	 * its own intrinsic content width instead of filling the screen.
	 * Forcing stretch back on for width while still allowing the
	 * explicit inline height (untouched) to hold is exactly the same
	 * "reset desktop-only positioning state" precedent as the rail fix
	 * above. */
	body[data-hub-layout="command_center"] #cc-content.cc-rail-sibling-mode-match,
	body[data-hub-layout="command_center"] #cc-content.cc-content-resized {
		margin-left: 0 !important;
		align-self: stretch !important;
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side {
		width: 100%;
	}

	/* ---- Side panel: STATIC bubble row + shared content area below -----
	 * User request (replaces the whole "buttons disappear/others expand"
	 * accordion this section used to be): "instead of the buttons
	 * disappearing and the others expanding... when they are expanded
	 * have the button just be outlined in a highlight color... that
	 * will disappear when they are collapsed." The bubble row must now
	 * stay completely fixed (same bubbles, same size, same positions)
	 * regardless of which card(s) are expanded - only an outline
	 * indicates state - and (per direct follow-up) expanded content
	 * appears stacked below the whole row, not under its own bubble.
	 *
	 * display:contents flattens each .cc-side-card so its header and
	 * body become independent grid items: every header is pinned to
	 * grid-row:1 (the fixed bubble row, never moves regardless of
	 * expand state), while every VISIBLE body has no explicit row and
	 * auto-places into row 2+ in DOM order, full width - a hidden body
	 * is display:none and consumes no row at all. Multiple simultaneously-
	 * expanded cards simply stack as multiple full-width rows below the
	 * bubble row, in their DOM order. */
	/* Fixed 7 columns - one per real bubble (Collapse/Expand All + the 6
	 * .cc-side-card widgets: equipment/quest/mail/weekly-gdp/ts-jobs/
	 * housing) - rather than auto-fill/minmax(56px,...). Content sections
	 * need to reliably split into left/right HALVES (below); auto-fill's
	 * actual column count varies with viewport width in a way that isn't
	 * predictable enough to target "column 4 of N" reliably (a narrower
	 * phone could produce fewer real tracks than a half-split assumes,
	 * pushing the "right" half into implicitly-created extra columns
	 * instead of the intended ones). Fixed to the exact current bubble
	 * count instead - if a 7th widget is ever added/removed here, this
	 * number needs updating too. */
	body[data-hub-layout="command_center"] #cc-side.cc-side {
		display: grid;
		grid-template-columns: repeat(7, 1fr);
		gap: 8px;
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card {
		display: contents;
	}

	/* "Collapse All"/"Expand All" stays its own fixed bubble, always
	 * first (content/js/hub-command-center.js's relocateToggleAllButton()
	 * skips moving it into Equipment's header on this same breakpoint).
	 *
	 * Text orientation, final answer after 3 rounds of guessing wrong on
	 * the actual device (90deg via writing-mode, then -45deg, then -90deg
	 * via transform - each one came back sideways/backwards/upside-down):
	 * per direct user instruction, NEVER rotate this text - plain
	 * horizontal, wrapping onto a 2nd line if it doesn't fit at this
	 * width, same as any other label would.
	 *
	 * Sizing, per this round's direct instruction: a plain fixed 100%-of-
	 * its-track/56px-tall bubble, same as every other header - it no
	 * longer grows/shrinks based on how many siblings are collapsed
	 * (that whole "grow as siblings collapse" behaviour from the
	 * previous round is exactly what this round asked to undo). */
	body[data-hub-layout="command_center"] #cc-side.cc-side > .cc-side-toggle-all {
		order: -1;
		grid-row: 1;
		width: 100%;
		height: 56px;
		display: flex;
		align-items: center;
		justify-content: center;
		font-size: 10px;
		white-space: normal;
		text-align: center;
		overflow: hidden;
		text-overflow: ellipsis;
		padding: 4px;
	}

	/* Same specificity-matching reason as every previous round here: an
	 * OLDER, desktop-only, non-media-query-gated rule in command-
	 * center.css (`#cc-body.cc-side-collapsed-all #cc-side.cc-side >
	 * .cc-side-toggle-all`) has HIGHER SPECIFICITY (2 IDs vs. 1) and
	 * applies at EVERY viewport width, not just desktop - #cc-body picks
	 * up .cc-side-collapsed-all the instant every card happens to be
	 * collapsed, mobile included, and would otherwise reapply its own
	 * flex/align-self/writing-mode:vertical-rl here even now that this
	 * element is a grid (not flex) item. Repeating the same selector
	 * chain (equal specificity, mobile.css loads last) keeps this fixed,
	 * unrotated bubble treatment in that state too. */
	body[data-hub-layout="command_center"] #cc-body.cc-side-collapsed-all #cc-side.cc-side > .cc-side-toggle-all {
		grid-row: 1;
		flex: none;
		min-height: 0;
		align-self: auto;
		width: 100%;
		height: 56px;
		writing-mode: horizontal-tb;
		font-size: 10px;
		white-space: normal;
		text-align: center;
		overflow: hidden;
		text-overflow: ellipsis;
		padding: 4px;
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card-header {
		grid-row: 1;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		width: 100%;
		height: 56px;
		padding: 4px;
		gap: 0;
		border-bottom: none;
		border-radius: var(--cc-radius, 8px);
		cursor: pointer;
		overflow: hidden;
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card-title,
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card-handle,
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card-collapse {
		display: none;
	}

	/* The highlight itself: a glow converging inward from every edge
	 * toward the center (an inset radial-style glow rather than a flat
	 * ring) - appears the instant this card's body is visible, gone the
	 * instant it's hidden again, no JS needed since :has() reads the
	 * sibling body's own [hidden] attribute live.
	 *
	 * User report: "make the outline glow match the theme" - this
	 * previously read var(--cc-accent), which despite its own comment's
	 * claim is NOT actually per-theme (command-center.css defines it
	 * exactly once, a fixed #FFCC33 for every scheme - stale comment,
	 * not stale code the first time this was written, presumably from an
	 * earlier draft that did vary it). --cc-border genuinely IS per-theme
	 * (command-center.css's own body[data-theme="N"] overrides, each
	 * value read directly out of that theme's own css/main<N>.css per
	 * that block's own docblock) - reusing it here is what actually
	 * makes this glow read as "this theme's colour" rather than always
	 * the same yellow regardless of scheme. */
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card-header:has(+ .cc-side-card-body:not([hidden])) {
		box-shadow: inset 0 0 0 2px var(--cc-border), inset 0 0 14px 2px var(--cc-border);
	}

	/* Mobile equivalent of command-center.css's own
	 * #cc-body.cc-side-collapsed-all .cc-side-badge.cc-badge-on rule -
	 * same badges, always shown on this bubble regardless of this card's
	 * own expand state now (the bubble itself no longer changes shape
	 * when expanded, so there's no more "collapsed-only" condition to
	 * gate this on). */
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-badge.cc-badge-on {
		display: block;
	}

	/* Icon-bubble content - shown always now (not conditional on
	 * collapsed state anymore, since the header itself no longer changes
	 * shape when expanded). Same 4 hand-drawn icons + hourglass/house
	 * emoji as desktop's own collapsed-tab strip, reused via the shared
	 * --cc-icon-* variables further up this file - not redrawn again. */
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card-header::before {
		content: '';
		width: 24px;
		height: 24px;
		background-repeat: no-repeat;
		background-position: center;
		background-size: contain;
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="equipment"] .cc-side-card-header::before {
		background-image: var(--cc-icon-equipment);
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="quest"] .cc-side-card-header::before {
		background-image: var(--cc-icon-quest);
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="mail"] .cc-side-card-header::before {
		background-image: var(--cc-icon-mail);
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="ts-jobs"] .cc-side-card-header::before {
		background-image: var(--cc-icon-tradeskills);
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="weekly-gdp"] .cc-side-card-header::before {
		content: '\23F3';
		font-size: 20px;
		width: auto;
		height: auto;
		background-image: none;
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="housing"] .cc-side-card-header::before {
		content: '\1F3E0';
		font-size: 20px;
		width: auto;
		height: auto;
		background-image: none;
	}

	/* User follow-up: "each of the right bar expanded sections should
	 * still be in it's own bubble... account for the equipment bubble
	 * to expand." Flattening .cc-side-card via display:contents (above)
	 * dropped the card's own background/border/radius along with its
	 * box - nothing gave the body its OWN "bubble" chrome to replace
	 * it, so expanded content read as bare, un-panelled text sitting
	 * directly in the grid. Restored that chrome directly on the body. */
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card-body:not([hidden]) {
		background: var(--cc-panel-bg);
		border: 1px solid var(--cc-border);
		border-radius: var(--cc-radius, 8px);
		padding: 8px;
		box-sizing: border-box;
	}

	/* User follow-up (2nd round): "move the mail, ts, and house to a 2nd
	 * column when they're expanded... arrange them all in 2 column but
	 * when all are expanded those 3 should be in the right column, that
	 * way they are more evenly spaced/sized." Explicit left/right column
	 * assignment (not just auto-flow span, which only loosely happened
	 * to let 2 share a row) - Quest and Bonus Timers always land in the
	 * left half (columns 1-3 of the fixed 7 above), Mail/Tradeskills/
	 * Housing always in the right half (columns 4-7), regardless of
	 * which subset is currently expanded or what order they were opened
	 * in. Multiple simultaneously-expanded cards in the SAME half simply
	 * auto-place into successive rows within that half's column range. */
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="quest"] .cc-side-card-body:not([hidden]),
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="weekly-gdp"] .cc-side-card-body:not([hidden]) {
		grid-column: 1 / 4;
	}

	/* User request: "squish the right column's 2nd column... so that the
	 * 3 mail, ts, and house all fit in the same space as the other 2 -
	 * kills and bonus timers." Three stacked cards vs. the left half's
	 * two will always be somewhat taller at equal type size - a tighter
	 * type scale (font-size/line-height) plus less padding on just these
	 * 3 bodies is what actually closes most of that gap, without
	 * touching mail.php/guild-timers-toggle.php/housing.php themselves
	 * (still the real, unmodified widgets - only the SIZE of their
	 * rendered text/spacing changes here, not their content or markup). */
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="mail"] .cc-side-card-body:not([hidden]),
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="ts-jobs"] .cc-side-card-body:not([hidden]),
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="housing"] .cc-side-card-body:not([hidden]) {
		grid-column: 4 / 8;
		padding: 5px;
		font-size: 11px;
		line-height: 1.25;
	}

	/* User follow-up: "the mail one should shrink enough to fit the house
	 * widget under it before the TS widget... make sure these 3 on the
	 * right are aligned so the tops and bottoms are aligned with the
	 * left (this means 2 on the left is the same size as the 3 on the
	 * right)." #cc-side is one shared grid (#cc-side.cc-side above) -
	 * Mail and Housing genuinely share grid ROWS with Quest and Bonus
	 * Timers respectively (auto-placement pairs them: Mail lands in
	 * Quest's row, Housing in Bonus Timers' row, both column ranges
	 * being equal-width halves of the same row), so those two rows
	 * ALREADY stretch to match their left-side partner with no extra
	 * CSS needed - the real height mismatch was Mail specifically:
	 * unread mail/ticket lists can run much longer than Quest's own
	 * content, inflating their SHARED row (and the whole card's ceiling)
	 * to Mail's height instead of Quest's. Capping just Mail (and, for
	 * the same reason, whichever of Housing/TS Jobs ends up the 3rd,
	 * unpaired card with no left-side row partner at all) with a scroll
	 * cap keeps every row close to what the LEFT side's own 2 cards
	 * naturally need, instead of letting the right side's own longer
	 * content dictate the shared height. */
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="mail"] .cc-side-card-body:not([hidden]),
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="housing"] .cc-side-card-body:not([hidden]),
	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="ts-jobs"] .cc-side-card-body:not([hidden]) {
		max-height: 130px;
		overflow-y: auto;
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card[data-widget-id="equipment"] .cc-side-card-body:not([hidden]) {
		grid-column: 1 / 8;
	}

	body[data-hub-layout="command_center"] #cc-side.cc-side .cc-side-card-body[hidden] {
		display: none;
	}

	/* User request: "have the equipment panel on mobile be divided
	 * vertically, so the gear list on the left and the upgrade portion
	 * on the right." #equipment (the 9-slot gear list, equipment.php) and
	 * #equipstuff (empty until a slot is clicked, then holds that item's
	 * upgrade/enchant options - content/js's equip()/closeequip()) are
	 * plain sibling divs inside this one expanded card's body; splitting
	 * that body into a 2-column row is enough, no changes needed to
	 * either widget's own markup.
	 *
	 * Bug fix (user report): "you shouldn't see the equipment list or
	 * upgrade section when the section is collapsed" - the same
	 * [hidden]-vs-class-selector specificity tie this file has hit
	 * before elsewhere: this rule's own `display: flex` out-specifies
	 * the browser's default `[hidden] { display: none }` UA rule, so a
	 * collapsed (body.hidden) Equipment card showed its 2-column content
	 * anyway. :not([hidden]) makes this only ever apply to the actually-
	 * visible body. */
	body[data-hub-layout="command_center"] .cc-side-card[data-widget-id="equipment"] .cc-side-card-body:not([hidden]) {
		display: flex;
		flex-direction: row;
		align-items: flex-start;
		gap: 10px;
	}

	body[data-hub-layout="command_center"] .cc-side-card[data-widget-id="equipment"] #equipment,
	body[data-hub-layout="command_center"] .cc-side-card[data-widget-id="equipment"] #equipstuff {
		flex: 1 1 50%;
		min-width: 0;
	}

	/* User request: "the equipment pane list should be centered until
	 * the upgrades are opened, then they should move left like they are
	 * currently, and return when the upgrades are closed." #equipstuff
	 * starts empty (command-center.php) and content/js/utilities.js's
	 * closeequip() resets it back to empty (`innerHTML = ''`) - :empty
	 * reliably reflects "no item's upgrade panel is currently open" in
	 * both directions with no JS changes needed. When empty, #equipment
	 * takes the full row and centers its own list instead of sitting in
	 * a half-width column with a blank half beside it; the moment
	 * equip() fills #equipstuff with real content, this stops matching
	 * and the rule above's plain 50/50 split takes back over. */
	body[data-hub-layout="command_center"] .cc-side-card[data-widget-id="equipment"] .cc-side-card-body:has(#equipstuff:empty) #equipment {
		flex: 1 1 100%;
		text-align: center;
	}

	body[data-hub-layout="command_center"] .cc-side-card[data-widget-id="equipment"] .cc-side-card-body:has(#equipstuff:empty) #equipstuff {
		display: none;
	}

	/* ---- Battle controls (moblist.php's AJAX response, injected into
	 * #content) ------------------------------------------------------
	 * User request: "the attack and auto buttons should both be below
	 * the enemy drop down, they should be bigger buttons as well."
	 * moblist.php echoes raw, unclassed markup
	 * (<div style='text-align:center'><select id="mob">...</select>
	 * <input value="Attack!"/> <input value="Auto!"/></div>) shared by
	 * every hub layout - :has(#mob) targets that div without needing a
	 * markup change. A 2-column grid with the select spanning both
	 * columns puts Attack!/Auto! on their own row below it, sized much
	 * larger than their default inline-button size for real fingers. */
	body[data-hub-layout="command_center"] #content div:has(> #mob) {
		display: grid !important;
		grid-template-columns: 1fr 1fr;
		gap: 8px;
	}

	body[data-hub-layout="command_center"] #content div:has(> #mob) > #mob {
		grid-column: 1 / -1;
		width: 100%;
		box-sizing: border-box;
		min-height: 40px;
		font-size: 16px;
	}

	body[data-hub-layout="command_center"] #content div:has(> #mob) > input[type="button"] {
		min-height: 48px;
		font-size: 16px;
		font-weight: bold;
		box-sizing: border-box;
	}

	/* ---- Modular Dashboard --------------------------------------------
	 * Its whole interaction model (drag anywhere, resize via a small
	 * corner handle) is built for mouse precision - real touch-friendly
	 * drag/resize would need its own dedicated design, not attempted
	 * here. Forcing every tile to one full-width column at least renders
	 * every widget readably instead of a cramped 12-column grid meant for
	 * a ~970px desktop screen; dragging/resizing still works (Pointer
	 * Events fire for touch too) but isn't the polished experience this
	 * layout has on desktop. */
	body[data-hub-layout="modular_dashboard"] #md-grid.md-grid {
		grid-template-columns: 1fr !important;
		grid-auto-rows: auto !important;
	}

	/* Every tile's grid-column AND grid-row are set as an inline style -
	 * baked into the initial PHP-rendered markup itself (hub/layouts/
	 * modular-dashboard.php's own `style="grid-column: ...; grid-row: ...;"`)
	 * and re-applied by content/js/hub-modular-dashboard.js's
	 * applyTileStyle() on every drag/resize - !important is needed to beat
	 * either source. grid-column alone isn't enough: two tiles that used
	 * to sit side-by-side (different columns, SAME desktop grid-row) both
	 * become full-width here, but without also resetting grid-row they'd
	 * still overlap at that same original row instead of stacking one
	 * after another. grid-row:auto + the grid's own grid-auto-rows:auto
	 * above (was a fixed 64px per "h" unit) together let each tile take
	 * exactly the next slot, sized to its own real content height instead
	 * of a fixed multiple of 64px that might clip it. */
	body[data-hub-layout="modular_dashboard"] .md-tile {
		grid-column: 1 / -1 !important;
		grid-row: auto !important;
	}

	/* The widget library panel is a fixed 220px box floating over the
	 * bottom-right corner - on a ~390px phone that's over half the screen
	 * width, obscuring whatever tile happens to be underneath it. Spans
	 * near-full-width along the bottom instead, a more standard mobile
	 * "sheet" pattern, and caps its height so it can't itself dominate
	 * the screen. */
	body[data-hub-layout="modular_dashboard"] .md-library {
		left: 8px;
		right: 8px;
		bottom: 8px;
		width: auto;
		max-height: 160px;
	}

	/* ---- Chat (shared across every layout) -----------------------------
	 * Bigger channel-colour pips and a taller input row for real fingers;
	 * the drag-to-resize handle (a precise few-pixel-tall grip) isn't
	 * practical on touch, so it's hidden and chat gets a comfortable fixed
	 * height instead (matches Command Center/Modular Dashboard's own
	 * fixed-height chat dock, which never had a resize handle at all).
	 */
	.chat-controls-row #chatchannel,
	.chat-controls-row #inputchat,
	.chat-controls-row #chatbutton {
		min-height: 40px;
		font-size: 16px;
	}

	.chatToggleButton {
		width: 28px !important;
		height: 28px !important;
	}

	/* Command Center only, user report: "shrink the chat text input and
	 * make sure the chat button is in line on the right. stretch the
	 * pips so they fit the space horizontally, that will make them
	 * easier to press." #chatchannel already caps at 110px (desktop CC
	 * rule) and #inputchat/#chatbutton are flex:1 1 auto/flex:0 0 auto -
	 * once #cc-shell's own min-width:970px floor is reset above, that's
	 * enough for channel+input+button to naturally share one row with
	 * the input filling (and shrinking into) whatever space is left and
	 * the button staying pinned on the right. .chat-toggles-row forced
	 * onto its own full-width row (flex-basis 100%) so its pips can
	 * stretch to fill it instead of both rows fighting for the same
	 * horizontal space. */
	body[data-hub-layout="command_center"] .chat-toggles-row {
		flex: 1 1 100%;
		display: flex;
		gap: 4px;
	}

	body[data-hub-layout="command_center"] .chat-toggles-row .chatToggleButton {
		flex: 1 1 0;
		width: auto !important;
		min-width: 0;
		height: 36px !important;
	}

	body[data-hub-layout="command_center"] .chat-toggles-row #dnd {
		flex: 0 0 auto;
	}

	/* User request: "the chat select dropdown needs to shrink/grow to the
	 * length of the chat selected, the text field should adapt, and the
	 * text field needs to be shorter on the right side to fit the chat
	 * button in line." content/js/chat.js's syncChatChannelWidth() writes
	 * --chatchannel-selected-width (a `ch`-sized measurement of just the
	 * SELECTED option's text, unlike the native auto-sizing desktop
	 * already relies on, which reserves room for the WIDEST option
	 * instead); consumed only here so desktop's own width:auto/max-width:
	 * 110px (command-center.css) is untouched. flex-wrap: nowrap
	 * guarantees #chatbutton never drops to its own line - #inputchat's
	 * existing flex:1 1 auto/min-width:0 does the actual shrinking to
	 * make room for whatever the channel select and button need. */
	body[data-hub-layout="command_center"] .chat-controls-row {
		flex-wrap: nowrap;
	}

	body[data-hub-layout="command_center"] .chat-controls-row #chatchannel {
		flex: 0 0 auto;
		width: var(--chatchannel-selected-width, 70px);
		max-width: 45vw;
	}

	#chatresizehandle {
		display: none;
	}

	/* User report: "the tab on the game window shouldn't shrink the
	 * window horizontally, only vertical" - content/js/hub-command-
	 * center.js's drag handler only ever reads/sets HEIGHT (clientY,
	 * applyContentHeight()/applyChatHeight()), never width, so the actual
	 * horizontal shrink was the same #cc-content align-self leak fixed
	 * above (a manually-resized game window opted out of #cc-main's
	 * width-stretch on mobile) - already covered by that fix. This part
	 * is the other half of the same report: "make the tabs for shrinking
	 * a bit bigger so they are easier to hit on mobile" - desktop's 56x14
	 * grip is a fine mouse target but too short for a fingertip; sized up
	 * to the same ~40px+ touch-target height used elsewhere in this file.
	 * Shared by both #cc-content-resize-handle and #cc-chat-resize-handle
	 * (both carry the .cc-content-resize-handle class). */
	body[data-hub-layout="command_center"] .cc-content-resize-handle {
		width: 88px;
		height: 32px;
	}

	/* Bug fix superseded: the #cc-content border-width fix (hairline-
	 * border-on-fractional-DPR + a real cross-stylesheet loading race this
	 * mobile-only override itself caused - see command-center.css's own
	 * comment on its base rule) now lives directly in command-center.css's
	 * base rule instead of here, so there's only one declaration and no
	 * race against this later-loading file. */

	body[data-hub-layout="command_center"] .cc-content-resize-handle-grip {
		height: 6px;
	}

	body[data-hub-layout="classic"] #chat_row {
		height: 320px !important;
		overflow: hidden;
	}

	/* ---- Popup overlay (market/guilds/crafting/casino/etc. all render
	 * through this) - full-screen instead of a small centred/aligned box,
	 * since there's no spare width on a phone to "centre within" at all.
	 * Command Center's own JS-tracked --cc-popup-left/--cc-popup-width
	 * (content/js/hub-command-center.js) still applies underneath this,
	 * but harmlessly - full-width/no-offset here simply wins since this
	 * file loads last. */
	#popupholder {
		position: fixed !important;
		inset: 0 !important;
		top: 0 !important;
		left: 0 !important;
		width: 100% !important;
		max-width: 100% !important;
		height: 100% !important;
		max-height: 100% !important;
		margin: 0 !important;
		border-radius: 0 !important;
	}

	/* User report: "the setting menu content needs to be shortened from
	 * the left to fit the menu options correctly." settings.php's own
	 * layout is a Bootstrap col-2 (nav)/col-10 (content) row with no
	 * breakpoint of its own - #popupholder above already goes full-
	 * screen, but col-2 at just 2/12 (~17%) of even a full phone screen
	 * is still only 60-70px wide, nowhere near enough for the nav's own
	 * text/icons ("Notifications", "AutoLoot", etc.) without cramping or
	 * clipping. Stacking nav above content (both 100% width) instead of
	 * squeezing them side by side is the standard responsive answer to a
	 * fixed Bootstrap column split with no mobile variant of its own. */
	#response.container-fluid > .row {
		flex-direction: column;
	}

	#response.container-fluid > .row > .col-2,
	#response.container-fluid > .row > .col-10 {
		flex: 0 0 100%;
		max-width: 100%;
		width: 100%;
	}

	/* .settings-nav's groups (each a vertical list under an icon+title
	 * header) still read fine stacked full-width, but a tall vertical
	 * stack of all 4 groups eats into the limited vertical room a phone
	 * has for the actual settings content below it - flowing the groups
	 * into a wrapping row instead keeps the nav itself compact. */
	#response.container-fluid .settings-nav {
		flex-direction: row;
		flex-wrap: wrap;
		gap: 10px;
	}

	#response.container-fluid .settings-nav-group {
		flex: 1 1 140px;
	}

	/* ---- Modern layout --------------------------------------------------
	 * Added 2026-09-10, two days after this file's original pass - the
	 * "Modern" hub layout (hub/layouts/modern.php, css/layouts/modern.css)
	 * didn't exist yet on 2026-09-06 and was never given a phone-width
	 * treatment: its own CSS has exactly one media query (max-width:1500px)
	 * that collapses the root grid to a single stacked column, which is
	 * enough to avoid horizontal overflow but leaves every INTERNAL density
	 * assumption (a 4-column status strip, a side-by-side chat/loot split,
	 * a sticky height-capped inventory dock) exactly as wide as a desktop
	 * needs, which is unusable at real phone widths. Mirrors the reference
	 * source's own mobile tier (docs/external-sources/lyrania-mod-suite/
	 * lyrania-modern-responsive-theme.css:3444-3644, @media max-width:799px)
	 * mapped onto this layout's own selectors/DOM shape - see each rule's
	 * own comment for the specific line(s) it mirrors. The root single-
	 * column stack itself needs no new rule here: .mh-shell/.mh-header
	 * already collapse to one column at <=1500px (css/layouts/modern.css),
	 * which is actually already as stacked as the reference source's OWN
	 * tightest (<=520px) header tier - nothing to change there.
	 */
	body[data-hub-layout="modern"] {
		font-size: 13px;
	}

	body[data-hub-layout="modern"] .mh-shell {
		gap: 7px;
		padding: 6px;
	}

	body[data-hub-layout="modern"] .mh-card {
		padding: 8px 9px;
	}

	/* Mirrors source:3461-3470 (#header > .lrow -> 2 columns at phone
	 * width) - the 4 status boxes (quest/guild-timers/bonus-timers/mail)
	 * stay legible at 2-per-row instead of squeezing all 4 into one row
	 * that was sized for a >=1500px-wide desktop header. */
	body[data-hub-layout="modern"] .mh-header-status {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 6px;
	}

	body[data-hub-layout="modern"] .mh-status-box {
		padding: 7px 8px;
		font-size: 11px;
	}

	/* Mirrors source:3530-3546 (#chatpanes -> stacked rows instead of
	 * side-by-side columns at phone width) - #chatwindow/#lootlog at
	 * ~180px each side-by-side on a 390px phone is unreadable; stacked,
	 * each gets the full width instead. */
	body[data-hub-layout="modern"] .mh-chat-row {
		height: auto;
	}

	body[data-hub-layout="modern"] #chatpanes {
		grid-template-columns: 1fr !important;
		grid-template-rows: minmax(210px, 1fr) minmax(190px, 0.9fr);
	}

	body[data-hub-layout="modern"] #chatwindow,
	body[data-hub-layout="modern"] #lootlog {
		height: auto !important;
		min-height: 190px;
	}

	/* Mirrors source:3548-3551 (the dock drops its height cap on phone -
	 * min-height:520px, max-height:none) - modern.css's own sticky +
	 * viewport-relative max-height (position:sticky, max-height:calc(100vh
	 * - 20px)) is a deliberate desktop convenience (keeps the dock's own
	 * internal scroller in view as the taller left column scrolls past
	 * it), but on a phone .mh-shell is already a single stacked column -
	 * "sticky" there just pins an internally-scrolling box mid-page, which
	 * reads as broken/janky under touch scrolling. Un-stick it and let it
	 * flow like every other stacked section; its own .mh-inventory-dock-
	 * content keeps overflow-y:auto regardless, so long lists still scroll
	 * fine, just as part of the page instead of a pinned pane. */
	body[data-hub-layout="modern"] .mh-inventory-dock {
		position: static;
		max-height: none;
		min-height: 0;
	}

	/* Touch targets - mirrors this file's own global 16px-input/
	 * touch-action rules further up, applied to Modern's own two custom
	 * controls (the real <select id="mainnav"> and the dock's Refresh
	 * button) that aren't already covered by a generic `input`/`select`
	 * selector elsewhere in this file. */
	body[data-hub-layout="modern"] .mh-mainnav-visible select {
		width: 100%;
		min-height: 40px;
	}

	body[data-hub-layout="modern"] .mh-inventory-dock-refresh {
		min-height: 36px;
		padding: 6px 12px;
	}
}

/* ---- Modern layout, phone-narrow tier ------------------------------------
 * Mirrors the reference source's own extra-tight tier (lyrania-modern-
 * responsive-theme.css:3646-3764 and :4086-4108, @media max-width:520px) -
 * a real phone in portrait (390-430px) still has the 2-column status strip
 * from the 768px tier above squeezing 4 widgets into 2 cramped rows; one
 * column per widget reads far better at this width. Kept as its own block
 * (matching the source's own two-tier mobile structure) rather than folded
 * into the 768px block above, since 768px already covers small tablets/
 * large phones in landscape where 2 columns still has room to breathe.
 */
@media (max-width: 520px) {
	body[data-hub-layout="modern"] .mh-header-status {
		grid-template-columns: 1fr;
	}
}
