/* ============================================================
   Zoomachy styles
   The site is moving to the game's own pixel-art theme (the
   "Pixel UI" section). What has not been converted yet still
   uses the earlier flat palette below.
   ============================================================ */

:root {
    color-scheme: dark;

    /* Elevation */
    --surface-0: #313338; /* page background */
    --surface-1: #3f4147; /* cards, raised blocks */
    --surface-2: #1e1f22; /* wells: inputs, notices */
    --border: #111214;

    /* Brand & semantics */
    --blurple: #5865f2;
    --blurple-hover: #4752c4;
    --grey: #4e5058;
    --grey-hover: #6d6f78;
    --red: #da373c;
    --red-hover: #a12829;
    --green: #248046;

    /* Text */
    --text: #f2f3f5;
    --text-muted: #949ba4;

    --radius: 10px;
    --radius-sm: 6px;

    --font: "gg sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

* {
    box-sizing: border-box;
}

html, body {
    margin: 0;
    min-height: 100%;
}

/* The game's cursors, set up at the game's scale by the script in the layout. */
html {
    cursor: var(--cursor-arrow, auto);
}

/* Nothing to select, as in game: a click on a label or a row never leaves text highlighted.
   Fields stay selectable, or they could not be edited. */
html {
    -webkit-user-select: none;
    user-select: none;
}

input,
textarea {
    -webkit-user-select: text;
    user-select: text;
}

/* The game's VScrollBar: four game pixels wide, square, without arrows; its grabber brown
   on a dark see-through track, lighter under the pointer and darker held. Firefox has no
   such parts: the colours only, on its thin bar. */
::-webkit-scrollbar {
    width: calc(4 * var(--px));
    height: calc(4 * var(--px));
}

::-webkit-scrollbar-track {
    background: rgba(77, 43, 23, 0.3);
}

::-webkit-scrollbar-thumb {
    background: #804a29;
}

::-webkit-scrollbar-thumb:hover {
    background: #9e6136;
}

::-webkit-scrollbar-thumb:active {
    background: #663b1f;
}

::-webkit-scrollbar-button {
    display: none;
}

::-webkit-scrollbar-corner {
    background: transparent;
}

@supports not selector(::-webkit-scrollbar) {
    html {
        scrollbar-width: thin;
        scrollbar-color: #804a29 rgba(77, 43, 23, 0.3);
    }
}

a[href],
button,
select,
label.checkbox,
.btn {
    cursor: var(--cursor-hand, pointer);
}

body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
    background-color: var(--dirt);
    color: var(--text);
    font-family: var(--font);
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

main {
    flex: 1 0 auto;
    /* 480 game pixels: 960px at the usual scale, and wider in step with everything else
       when the scale grows (125% Windows scaling, browser zoom). */
    width: min(calc(480 * var(--px)), 100% - calc(16 * var(--px)));
    margin-inline: auto;
    padding-block: 2.5rem;
}

a {
    color: inherit;
}

h1, h2, h3 {
    line-height: 1.2;
    margin-block: 0 0.5rem;
}

.muted {
    color: var(--text-muted);
}

.center {
    text-align: center;
}

/* ---------- Buttons ---------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    min-height: 2.75rem;
    padding-inline: 1.25rem;
    border: none;
    border-radius: var(--radius-sm);
    background-color: var(--grey);
    color: #fff;
    font: inherit;
    font-weight: 500;
    text-decoration: none;
    transition: background-color 150ms ease;
}

.btn:hover { background-color: var(--grey-hover); }

.btn.blurple { background-color: var(--blurple); }
.btn.blurple:hover { background-color: var(--blurple-hover); }

.btn.red { background-color: var(--red); }
.btn.red:hover { background-color: var(--red-hover); }

.btn:focus-visible {
    outline: 2px solid var(--text);
    outline-offset: 2px;
}

.btn[disabled], .btn.disabled {
    background-color: #b4b4b4;
    cursor: var(--cursor-arrow, default); /* nothing to do on it, as in UiButton */
    pointer-events: none;
}

/* ============================================================
   Pixel UI — the game's theme (resources/ui/zoomachy_theme.tres)
   Everything here is measured in game pixels times --px: one
   scale for every texture, font, outline and gap. Only painted
   art (the logo, avatars) escapes it.
   ============================================================ */

@font-face {
    font-family: "m6x11";
    src: url("/fonts/m6x11plus.ttf") format("truetype");
    font-display: block;
}

:root {
    --px: 2px;                 /* overwritten by the script in the layout */
    --dirt: #322b28;           /* Terrain.cs: the fill under the ground line */
    --cream: #fff2d6;          /* Button/font_color */
    --cream-hover: #fff7e6;    /* Button/font_hover_color */
    --cream-disabled: #b8ad9e; /* Button/font_disabled_color */
    --outline: #331a0d;        /* Button/font_outline_color */
}

@media (max-width: 700px) {
    :root {
        --px: 1px;
    }
}

/* m6x11plus is drawn on a pixel grid at 18px, one font pixel per game pixel. Its line is
   18 game pixels, 15 above the baseline and 3 below, and capitals are 11 tall. */
.pixel-text,
.pxbtn,
.pxchip,
.brand-btn {
    font-family: "m6x11", monospace;
    font-size: calc(18 * var(--px));
    font-weight: normal;
    line-height: calc(18 * var(--px));
    color: var(--cream);
    -webkit-font-smoothing: none;
    /* On Windows, Chromium draws text with ClearType, whose coloured sub-pixel fringes
       blur a pixel font. Text on its own compositing layer falls back to plain greyscale,
       which at these exact sizes lands on the grid. */
    will-change: transform;
    /* A one-game-pixel outline, as blocky offsets rather than a smooth stroke. */
    text-shadow:
        var(--px) 0 var(--outline),
        calc(-1 * var(--px)) 0 var(--outline),
        0 var(--px) var(--outline),
        0 calc(-1 * var(--px)) var(--outline),
        var(--px) var(--px) var(--outline),
        calc(-1 * var(--px)) calc(-1 * var(--px)) var(--outline),
        var(--px) calc(-1 * var(--px)) var(--outline),
        calc(-1 * var(--px)) var(--px) var(--outline);
}

.glyph {
    flex: none;
    width: calc(var(--w) * var(--px));
    height: calc(var(--h) * var(--px));
    image-rendering: pixelated;
}

/* ---------- Textured boxes ----------
   The texture sits on a pseudo-element so it can be dimmed or swapped without touching
   the label. Slices are the theme's StyleBoxTexture margins, and the centre tiles rather
   than stretches, so a texel is always exactly one game pixel. */

.pxbtn,
.pxchip {
    position: relative;
    isolation: isolate;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: calc(5 * var(--px)); /* Button/constants/h_separation */
    border: 0;
    text-decoration: none;
    white-space: nowrap;
}

.pxbtn::before,
.pxchip::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-style: solid;
    image-rendering: pixelated;
}

/* Under each texture, its main colour, one pixel in from the edge so the rounded corners
   stay clear. On a box that starts half a pixel off, the slices of a 9-slice meet on a
   pixel each only half covers, and the page shows through as a thin line: this fills it
   with wood instead. The texture covers it everywhere else. */
.pxbtn,
.pxchip {
    --underlay: #955e30;
    background: linear-gradient(var(--underlay), var(--underlay)) var(--px) var(--px) / calc(100% - 2 * var(--px)) calc(100% - 2 * var(--px)) no-repeat;
}

.pxbtn:active {
    --underlay: #684121; /* button_pressed.png */
}

.pxchip {
    --underlay: #804b2a; /* header.png */
}

/* ---------- Button motion ----------
   UiButton.cs: a button is "selected" while hovered or focused, one look whatever the
   device. Selected, it grows to 106% and brightens to 115% in 0.12 s with an overshoot
   (Tween TRANS_BACK, EASE_OUT); held, it squashes to 94% in a third of that (TRANS_QUAD);
   let go, it springs back. --grow and --squash are capped by the layout script so the
   width never moves by more than 8 game pixels. The pressed sprite comes on top of it,
   from the theme, as in game. */

.pxbtn,
.brand-btn {
    transform-origin: center;
    transition:
        transform 120ms cubic-bezier(0.34, 1.56, 0.64, 1),
        filter 120ms cubic-bezier(0.61, 1, 0.88, 1);
}

.pxbtn:hover,
.pxbtn:focus-visible,
.brand-btn:hover,
.brand-btn:focus-visible {
    outline: none;
    transform: scale(var(--grow, 1.06));
    filter: brightness(1.15);
}

.pxbtn:active,
.brand-btn:active {
    transform: scale(var(--squash, 0.94));
    transition-duration: 40ms;
    transition-timing-function: cubic-bezier(0.5, 1, 0.89, 1), cubic-bezier(0.61, 1, 0.88, 1);
}

/* ---------- Buttons ---------- */

.pxbtn {
    height: calc(32 * var(--px));
    /* content_margin 16/4/16/4, with the label raised one game pixel so the capitals sit
       in the middle of the plank's face rather than in the middle of their line. */
    padding: 0 calc(16 * var(--px)) calc(2 * var(--px));
}

.pxbtn::before {
    border-width: calc(4 * var(--px)) calc(10 * var(--px));
    border-image: url("/img/ui/button.png") 4 10 fill / calc(4 * var(--px)) calc(10 * var(--px)) repeat;
}

.pxbtn:hover {
    color: var(--cream-hover);
}

/* Pressed: the theme's content_margin goes from 4/4 to 6/2, so the label sinks with the plank. */
.pxbtn:active {
    padding-top: calc(2 * var(--px));
    padding-bottom: 0;
}

.pxbtn:active::before {
    border-image-source: url("/img/ui/button_pressed.png");
}

/* TableButton: button_small.png with 4-pixel slices and content_margin 6/1/6/1. */
.pxbtn.small {
    height: calc(20 * var(--px));
    padding: var(--px) calc(6 * var(--px));
}

.pxbtn.small::before {
    border-width: calc(4 * var(--px));
    border-image: url("/img/ui/button_small.png") 4 fill / calc(4 * var(--px)) repeat;
}

/* Pressed: content_margin 6/2/6/0, the label sinks one pixel. Restated here because
   .pxbtn.small's own padding would otherwise win over .pxbtn:active. */
.pxbtn.small:active {
    padding-top: calc(2 * var(--px));
    padding-bottom: 0;
}

.pxbtn.small:active::before {
    border-image-source: url("/img/ui/button_small_pressed.png");
}

/* ---------- Account chip ----------
   A dark plank (header.png, 2-pixel slices) holding the account once it is linked. */

.pxchip {
    justify-content: flex-start;
    height: calc(32 * var(--px));
    padding: 0 calc(10 * var(--px)) 0 calc(4 * var(--px));
}

.pxchip::before {
    border-width: calc(2 * var(--px));
    border-image: url("/img/ui/header.png") 2 fill / calc(2 * var(--px)) repeat;
}

.pxavatar {
    flex: none;
    box-sizing: border-box;
    width: calc(24 * var(--px));
    height: calc(24 * var(--px));
    border: var(--px) solid var(--outline);
    object-fit: cover;
}

.pxchip .name {
    position: relative;
    top: calc(-1 * var(--px)); /* same optical centring as the buttons */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    /* Room for the outline, which overflow would otherwise cut off. */
    padding-inline: var(--px);
}

/* ---------- Main menu ----------
   The page as the game's main menu: its painted illustration over the whole window,
   the logo, and the Discord button. Signed out, the layout is the menu's own: a
   640x360 stage with the logo centred 75 pixels down and the button where the menu's
   buttons start, 175 pixels down. */

/* On a fixed pseudo-element rather than background-attachment: fixed, which mobile
   Safari ignores. Painted, so it scales smoothly. At least as tall as the 640x360 stage,
   pinned to the top: in a window shorter than the screen it keeps its fullscreen size and
   loses its bottom, like the rest of the page, instead of shrinking. */
body.menu::before {
    content: "";
    position: fixed;
    top: 0;
    right: 0;
    left: 0;
    z-index: -1;
    height: max(100%, calc(360 * var(--px)));
    background: url("/img/main_menu.webp") 62% 0 / cover no-repeat;
}

body.landing main {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 0;
}

/* Never taller than the window: a window shorter than the screen keeps the top of the
   stage, where the logo and button are, without a scrollbar. */
.stage {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: min(calc(640 * var(--px)), 100%);
    height: min(calc(360 * var(--px)), 100dvh);
}

/* The menu draws the logo at 256 pixels wide (0.2327 of its 1100-pixel crop): 108.4 tall,
   so a 109-pixel box from row 21 keeps everything after it on whole pixels. */
.menu-logo {
    margin: calc(21 * var(--px)) 0 0;
    height: calc(109 * var(--px));
    line-height: 0;
    text-align: center;
}

.menu-logo img {
    width: calc(256 * var(--px));
    max-width: 100%;
    height: 100%;
    object-fit: contain;
}

body:not(.landing) .menu-logo {
    margin-bottom: calc(12 * var(--px));
}

/* The sign-in buttons stacked like the menu's own, 4 pixels apart (a VBoxContainer's
   default separation) and all as wide as the widest. */
.menu-buttons {
    display: flex;
    flex-direction: column;
    gap: calc(4 * var(--px));
    margin-top: calc(60 * var(--px)); /* 21 + 109 + 60: 15 below the menu's first button */
}

/* The provider buttons (button_discord.png, button_steam.png: 48x32, same shape) as a
   horizontal 9-slice: the left 32 columns hold the rounded edge and the logo, the right 8
   the other rounded edge, and the 8 plain columns between them repeat. 32 pixels tall like
   the sprites, so nothing is ever stretched vertically. */
.brand-btn {
    position: relative;
    isolation: isolate;
    display: inline-flex;
    align-items: flex-start;
    height: calc(32 * var(--px));
    /* Left, the logo ends on column 24 and the label starts 8 pixels after it. Right, the
       last letter (its one-pixel outline included, which fills the font's trailing blank
       column) stops 6 pixels from the edge: the same 6 the Discord sprite leaves between
       its left edge and Clyde. The 11-row capitals are level with the logo's middle (an
       18-row line set 6 rows down). */
    padding: calc(6 * var(--px)) calc(6 * var(--px)) 0 calc(33 * var(--px));
    color: #fff;
    text-decoration: none;
    white-space: nowrap;
    /* Beneath the slices, the repeated column's own profile (outline, fill, shading,
       outline), drawn between the rounded corners. A centred button can start half a
       pixel off, and where two slices meet on a pixel each only half covers, this is what
       shows instead of the page. Everywhere else the sprite covers it. */
    background: linear-gradient(
            #000 0 var(--px),
            var(--fill) var(--px) calc(29 * var(--px)),
            var(--shade) calc(29 * var(--px)) calc(31 * var(--px)),
            #000 calc(31 * var(--px)))
        calc(4 * var(--px)) 0 / calc(100% - 8 * var(--px)) 100% no-repeat;
}

/* Centred in the room left of the logo by the layout script, which rounds the offset to
   a whole game pixel: plain CSS centring lands the text on half pixels whenever the room
   is an odd width, and the pixel font blurs. Without the script it sits on the left. */
.brand-btn .label {
    display: block;
    margin-left: calc(var(--label-left, 0) * var(--px));
}

.brand-btn::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-style: solid;
    border-width: 0 calc(8 * var(--px)) 0 calc(32 * var(--px));
    border-image: var(--sprite) 0 8 0 32 fill / 0 calc(8 * var(--px)) 0 calc(32 * var(--px)) repeat;
    image-rendering: pixelated;
}

/* Pressed: the pressed sprite is the same button 2 rows lower, and the label follows it.
   Its column profile moves down with it, without the shading. */
.brand-btn:active {
    padding-top: calc(8 * var(--px));
    background-image: linear-gradient(
        transparent 0 calc(2 * var(--px)),
        #000 calc(2 * var(--px)) calc(3 * var(--px)),
        var(--fill) calc(3 * var(--px)) calc(31 * var(--px)),
        #000 calc(31 * var(--px)));
}

.brand-btn:active::before {
    border-image-source: var(--sprite-pressed);
}

/* Each sprite's own colours; the label is outlined in its shading. */
.brand-btn.discord {
    --sprite: url("/img/ui/button_discord.png");
    --sprite-pressed: url("/img/ui/button_discord_pressed.png");
    --fill: #5166f7;
    --shade: #1e38d3;
    --outline: #1e38d3;
}

.brand-btn.steam {
    --sprite: url("/img/ui/button_steam.png");
    --sprite-pressed: url("/img/ui/button_steam_pressed.png");
    --fill: #2d4d65;
    --shade: #23344e;
    --outline: #23344e;
}


/* ---------- Identity row ----------
   Discord on the left, Steam on the right: each a button until its account is there,
   then the account itself with the action that undoes it. */

.identity-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(calc(224 * var(--px)), 1fr));
    gap: calc(12 * var(--px));
    width: min(calc(460 * var(--px)), 100%);
    margin-inline: auto;
}

.slot {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: calc(6 * var(--px));
    min-width: 0;
}

.slot > .pxbtn,
.slot > .pxchip,
.slot > .brand-btn {
    width: 100%;
}

.slot form {
    margin: 0;
}

/* ---------- Other pages ----------
   Everywhere but the home page: the devlog banner, darkened, and a bar across the top
   with the Z, which leads home. */

/* The banner, 320x180 pixel art, one of its pixels per game pixel like the rest of the UI,
   repeated sideways. Its first and last rows are plain sky and plain grass, which carry on
   above and below it: --sky more pixels of sky on top, and from where the grass starts a
   smooth gradient down to the earth's brown at the bottom of the window. */
body.page::before {
    --sky: 48;
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background:
        linear-gradient(transparent calc((var(--sky) + 120) * var(--px)), var(--dirt)),
        linear-gradient(rgb(0 0 0 / 0.55), rgb(0 0 0 / 0.55)),
        url("/img/banner.png") 50% calc(var(--sky) * var(--px)) / calc(320 * var(--px)) calc(180 * var(--px)) repeat-x,
        linear-gradient(#74c0fc, #74c0fc) 0 0 / 100% calc(var(--sky) * var(--px)) no-repeat,
        #91ab30;
    image-rendering: pixelated;
}

body.page main {
    padding-block: calc(16 * var(--px));
}

/* header.png across the whole width, its top edge and sides pushed just past the window
   so only the bottom outline shows: 30 rows of wood, then the outline. */
.topbar {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    flex: none;
    display: flex;
    align-items: flex-start;
    height: calc(31 * var(--px));
    padding: calc(3 * var(--px)) calc(8 * var(--px));
    background: linear-gradient(#804b2a, #804b2a) 0 0 / 100% calc(100% - var(--px)) no-repeat;
}

.topbar::before {
    content: "";
    position: absolute;
    top: calc(-1 * var(--px));
    right: calc(-1 * var(--px));
    bottom: 0;
    left: calc(-1 * var(--px));
    z-index: -1;
    border-style: solid;
    border-width: calc(2 * var(--px));
    border-image: url("/img/ui/header.png") 2 fill / calc(2 * var(--px)) repeat;
    image-rendering: pixelated;
}

.topbar-home {
    display: block;
    line-height: 0;
}

/* Painted, so it scales smoothly: 24 pixels tall, 3 from the top and bottom of the wood. */
.topbar-home img {
    width: auto;
    height: calc(24 * var(--px));
}

/* ---------- Tables ----------
   The game's table (LobbiesPopup.tscn, ScoreboardRow.tscn): a dark header plank
   (table_header.png, TableHeader) on top of a light body (table_body.png, TableBody,
   open at the top), whose rows are planks of their own (table_row.png, TableRow), 24
   pixels tall and 3 apart. Every slice is 2 pixels, and each part has its main colour
   underneath, as the buttons do, against seams. */

/* Side by side while they fit, then one under the other, all lined up at the top. Two
   224-pixel tables and their gap fill the page's 480 pixels at most. */
.pxtables {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: flex-start;
    gap: calc(12 * var(--px));
}

/* Or always one under the other, each alone on its line. */
.pxtables.stacked {
    flex-direction: column;
    align-items: center;
}

.pxtable {
    display: flex;
    flex-direction: column;
    width: min(calc(224 * var(--px)), 100%);
}

/* A table on its own on the page, a list: wider. */
.pxtable.wide {
    width: min(calc(320 * var(--px)), 100%);
}

.pxtable-header,
.pxtable-body,
.pxtable-row {
    position: relative;
    isolation: isolate;
}

.pxtable-header::before,
.pxtable-body::before,
.pxtable-row::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-style: solid;
    image-rendering: pixelated;
}

/* content_margin 12/3/10/3; the label in TableHeaderLabel's cream and outline, which are
   the buttons'. */
.pxtable-header {
    display: flex;
    align-items: flex-start;
    gap: calc(6 * var(--px)); /* the columns' HBoxContainer separation */
    padding: calc(3 * var(--px)) calc(10 * var(--px)) calc(3 * var(--px)) calc(12 * var(--px));
    background: linear-gradient(#804b2a, #804b2a) var(--px) var(--px) / calc(100% - 2 * var(--px)) calc(100% - 2 * var(--px)) no-repeat;
}

.pxtable-header::before {
    border-width: calc(2 * var(--px));
    border-image: url("/img/ui/table_header.png") 2 fill / calc(2 * var(--px)) repeat;
}

.pxtable-avatar {
    flex: none;
    box-sizing: border-box;
    width: calc(18 * var(--px));
    height: calc(18 * var(--px));
    border: var(--px) solid var(--outline);
    object-fit: cover;
}

.pxtable-title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    /* Room for the outline, which overflow would otherwise cut off, without moving the text. */
    padding-inline: var(--px);
    margin-inline: calc(-1 * var(--px));
}

/* content_margin 2/4/2/4, and the rows' own 4-pixel margin left and right. */
.pxtable-body {
    display: flex;
    flex-direction: column;
    gap: calc(3 * var(--px));
    padding: calc(4 * var(--px)) calc(6 * var(--px));
    background: linear-gradient(#b98450, #b98450) var(--px) 0 / calc(100% - 2 * var(--px)) calc(100% - var(--px)) no-repeat;
}

.pxtable-body::before {
    border-width: 0 calc(2 * var(--px)) calc(2 * var(--px));
    border-image: url("/img/ui/table_body.png") 0 2 2 fill / 0 calc(2 * var(--px)) calc(2 * var(--px)) repeat;
}

/* content_margin 6/2/4/2 around an 18-pixel line, which Godot centres in the 24 pixels
   of the row: one more pixel above and below. TableCellLabel is dark brown, unoutlined. */
.pxtable-row {
    display: flex;
    justify-content: space-between;
    gap: calc(6 * var(--px));
    padding: calc(3 * var(--px)) calc(4 * var(--px)) calc(3 * var(--px)) calc(6 * var(--px));
    color: #4a2917;
    text-shadow: none;
    white-space: nowrap;
}

/* The row's main colour under its texture, as elsewhere against seams: on the texture's own
   layer here, so that whatever brightens the plank brightens both. */
.pxtable-row::before {
    border-width: calc(2 * var(--px));
    border-image: url("/img/ui/table_row.png") 2 fill / calc(2 * var(--px)) repeat;
    background: linear-gradient(#d4a46a, #d4a46a) var(--px) var(--px) / calc(100% - 2 * var(--px)) calc(100% - 2 * var(--px)) no-repeat border-box;
}

/* The value, when there is one, on the right. */
.pxtable-row > :last-child:not(:first-child) {
    margin-left: auto;
}

/* A row to click: one that unfolds into its detail (a <details>' summary), or one that
   leads to a page. A TableRowButton, lit like the lobby list's rows but without their
   motion: selected, its plank brightens to 115% (SelfModulate: the plank, not the labels
   on it); held, the plank takes the pressed style's 80% on top. */
.pxtable-toggle,
.pxtable-link {
    list-style: none;
    text-decoration: none;
    cursor: var(--cursor-hand, pointer);
}

.pxtable-toggle:focus-visible,
.pxtable-link:focus-visible {
    outline: none;
}

.pxtable-toggle::-webkit-details-marker {
    display: none;
}

.pxtable-toggle::before,
.pxtable-link::before {
    transition: filter 120ms cubic-bezier(0.61, 1, 0.88, 1);
}

.pxtable-toggle:hover::before,
.pxtable-toggle:focus-visible::before,
.pxtable-link:hover::before,
.pxtable-link:focus-visible::before {
    filter: brightness(1.15);
}

.pxtable-toggle:active::before,
.pxtable-link:active::before {
    filter: brightness(0.92);
    transition-duration: 40ms;
}

/* A link's label takes the row, rather than going to the right as a value would. */
.pxtable-link .pxtable-title {
    flex: 1;
}

/* Level with the raccoon beside it, when there is one (the players list). */
.pxtable-link {
    align-items: center;
}

/* An unfolding row's arrow, right_arrow_small.png, turns down (down_arrow_small.png)
   while it is open. */

/* 9x9, the arrow's longer side, so the label does not move when it turns. Level with the
   capitals: they are rows 4 to 14 of the line, the arrow 5 to 13. */
.pxtable-arrow {
    flex: none;
    width: calc(9 * var(--px));
    height: calc(9 * var(--px));
    margin-top: calc(5 * var(--px));
    background: url("/img/ui/right_arrow_small.png") calc(2 * var(--px)) 0 / calc(6 * var(--px)) calc(9 * var(--px)) no-repeat;
    image-rendering: pixelated;
}

details[open] > .pxtable-toggle .pxtable-arrow {
    background: url("/img/ui/down_arrow_small.png") 0 calc(2 * var(--px)) / calc(9 * var(--px)) calc(6 * var(--px)) no-repeat;
}

/* The detail: rows of its own, indented so their labels line up with the label of the row
   they unfold from (past its 9-pixel arrow and the 6 after it). */
.pxtable-sub {
    display: flex;
    flex-direction: column;
    gap: calc(3 * var(--px));
    margin: calc(3 * var(--px)) 0 0 calc(15 * var(--px));
}

/* ---------- Search and pages ---------- */

/* A text field: section.png, the game's code-entry boxes (CodeDigit), its 2-pixel slices
   around a row's line, typed in TableCellLabel's brown. Lit like a selected row while
   being typed in. */
.pxfield {
    position: relative;
    isolation: isolate;
    display: flex;
    margin: 0;
}

.pxfield::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-style: solid;
    border-width: calc(2 * var(--px));
    border-image: url("/img/ui/section.png") 2 fill / calc(2 * var(--px)) repeat;
    background: linear-gradient(#b98450, #b98450) var(--px) var(--px) / calc(100% - 2 * var(--px)) calc(100% - 2 * var(--px)) no-repeat border-box;
    image-rendering: pixelated;
    transition: filter 120ms cubic-bezier(0.61, 1, 0.88, 1);
}

.pxfield:focus-within::before {
    filter: brightness(1.15);
}

.pxfield input {
    flex: 1;
    min-width: 0;
    height: calc(24 * var(--px));
    margin: 0;
    padding: calc(3 * var(--px)) calc(4 * var(--px)) calc(3 * var(--px)) calc(6 * var(--px));
    border: 0;
    border-radius: 0;
    outline: none;
    background: transparent;
    color: #4a2917;
    caret-color: #4a2917;
    text-shadow: none;
    -webkit-appearance: none;
    appearance: none;
}

.pxfield input::placeholder {
    color: #8f5d39; /* the box's own edge */
    opacity: 1;
}

.pxfield input::-webkit-search-cancel-button {
    display: none;
}

/* Selected text in the theme's browns (the scrollbar's grabber), lettered in the
   buttons' cream: the theme sets no selection colour of its own. */
.pxfield input::selection {
    background: #804a29;
    color: var(--cream);
}

/* The caret, as the game's LineEdit draws it: one game pixel wide and the line's height,
   blinking every 0.65 s (caret_blink_interval); brown, as the text. Drawn by the script in
   the layout, which hides the browser's own (thinner, and of no fixed width) once it runs. */
.pxfield.has-caret input {
    caret-color: transparent;
}

.pxcaret {
    position: absolute;
    left: 0;
    top: 0;
    width: var(--px);
    height: calc(18 * var(--px));
    background: #4a2917;
    pointer-events: none;
    visibility: hidden;
    animation: pxcaret-blink 1.3s steps(1) infinite;
}

.pxcaret.shown {
    visibility: visible;
}

@keyframes pxcaret-blink {
    50% {
        opacity: 0;
    }
}

.pxtable-list {
    display: flex;
    flex-direction: column;
    gap: calc(3 * var(--px));
}

/* Previous page, this page out of all, next page: the small buttons with the game's arrows. */
.pxpager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: calc(6 * var(--px));
    color: #4a2917;
    text-shadow: none;
}

.pxpager .pxbtn img {
    width: calc(6 * var(--px));
    height: calc(9 * var(--px));
    image-rendering: pixelated;
}

.pxpager .hidden {
    visibility: hidden;
    width: calc(18 * var(--px));
}

/* ---------- Raccoon ----------
   A raccoon in a look (RaccoonLooks): the game's layers, all the same size, stacked, seen
   through a box — all of them, or a portrait's part. --zoom game pixels per raccoon
   pixel: 4 by default, as the game's customize menu shows it, 1 in a list. In raccoon
   pixels: --w and --h, the box's size; --x and --y, where it sits in the layers; --lw and
   --lh, the layers' size. The layers are drawn 12 pixels per raccoon pixel (the menu on a
   1080p screen), so at that size they show as they are; at others, each raccoon pixel
   still lands on whole screen pixels. --centre-y (game pixels) moves the look to the
   middle of the box's height. */

.raccoon {
    position: relative;
    flex: none;
    overflow: hidden;
    width: calc(var(--w) * var(--zoom, 4) * var(--px));
    height: calc(var(--h) * var(--zoom, 4) * var(--px));
}

.raccoon img {
    position: absolute;
    left: calc(var(--x, 0) * var(--zoom, 4) * -1 * var(--px));
    top: calc((var(--centre-y, 0) - var(--y, 0) * var(--zoom, 4)) * var(--px));
    width: calc(var(--lw) * var(--zoom, 4) * var(--px));
    height: calc(var(--lh) * var(--zoom, 4) * var(--px));
    image-rendering: pixelated;
}

/* The frame the customize menu shows it in (PreviewPanel: Section, section.png with
   2-pixel slices, content_margin 2/4/2/4). */
.pxframe {
    position: relative;
    isolation: isolate;
    flex: none;
    padding: calc(4 * var(--px)) calc(2 * var(--px));
}

.pxframe::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-style: solid;
    border-width: calc(2 * var(--px));
    border-image: url("/img/ui/section.png") 2 fill / calc(2 * var(--px)) repeat;
    background: linear-gradient(#b98450, #b98450) var(--px) var(--px) / calc(100% - 2 * var(--px)) calc(100% - 2 * var(--px)) no-repeat border-box;
    image-rendering: pixelated;
}

/* ---------- Player card ----------
   The raccoon in its frame on the left, the player's rows on the right, 4 pixels apart
   (a BoxContainer's default separation). */

.pxprofile {
    display: flex;
    align-items: flex-start;
    gap: calc(4 * var(--px));
}

.pxprofile-rows {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: calc(3 * var(--px));
}

/* ---------- Cards ---------- */

.card {
    padding: 1.25rem 1.5rem;
    border-radius: var(--radius);
    background-color: var(--surface-1);
}

.card + .card {
    margin-top: 1.5rem;
}

.notice {
    padding: 1.25rem 1.5rem;
    border: 2px solid var(--border);
    border-radius: var(--radius);
    background-color: var(--surface-2);
}

.notice code {
    padding: 0.1em 0.4em;
    border-radius: 4px;
    background-color: #000;
    font-size: 0.9em;
}

/* ---------- Underlined fields ---------- */

.field {
    display: flex;
    flex-direction: column-reverse;
    gap: 0.25rem;
    margin-block: 1.25rem;
}

.field input,
.field select,
.field textarea {
    width: 100%;
    padding: 0.4rem 0;
    background-color: transparent;
    border: none;
    border-bottom: 2px solid var(--grey);
    outline: none;
    color: var(--text);
    font: inherit;
    font-weight: 600;
    transition: border-color 300ms ease;
}

.field input:focus,
.field select:focus,
.field textarea:focus {
    border-bottom-color: var(--text);
}

.field input[aria-invalid="true"],
.field select[aria-invalid="true"],
.field textarea[aria-invalid="true"] {
    border-bottom-color: var(--red);
}

.field label {
    font-weight: 300;
    color: var(--text-muted);
    transition: color 300ms ease;
}

.field:focus-within label {
    color: var(--text);
}

/* ---------- Tables ---------- */

.table {
    width: 100%;
    margin-top: 1rem;
    border-collapse: collapse;
    font-size: 0.95em;
}

.table th {
    padding: 0.5rem 0.75rem;
    text-align: left;
    font-weight: 600;
    color: var(--text-muted);
    border-bottom: 2px solid var(--surface-2);
}

.table td {
    padding: 0.6rem 0.75rem;
    border-bottom: 1px solid var(--surface-2);
    vertical-align: middle;
}

.table tr:last-child td {
    border-bottom: none;
}

.table tr.selected {
    background-color: rgba(88, 101, 242, 0.12);
}

.table .num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.table td .username,
.table td .handle {
    display: block;
}

/* Compact controls that live inside a table cell. */
.state-form {
    display: flex;
    gap: 0.4rem;
    align-items: center;
}

.state-form select {
    padding: 0.25rem 0.4rem;
    border: 1px solid var(--surface-2);
    border-radius: var(--radius-sm);
    background-color: var(--surface-2);
    color: var(--text);
    font: inherit;
    font-size: 0.9em;
}

.btn.small {
    min-height: 1.9rem;
    padding-inline: 0.7rem;
    font-size: 0.85em;
}

/* ---------- Checkbox ---------- */

.checkbox {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.checkbox input {
    width: 1.1rem;
    height: 1.1rem;
    accent-color: var(--blurple);
    cursor: inherit;
}

/* ---------- Tags ---------- */

.tag {
    display: inline-block;
    padding: 0.1em 0.6em;
    border-radius: var(--radius-sm);
    background-color: var(--surface-2);
    color: var(--text-muted);
    font-size: 0.85em;
    font-variant-numeric: tabular-nums;
}

.tag.ok {
    background-color: rgba(36, 128, 70, 0.25);
    color: #7fd99a;
}

.tag.warn {
    background-color: rgba(218, 55, 60, 0.2);
    color: #f3a0a2;
}

@media (max-width: 600px) {
    .card {
        overflow-x: auto;
    }
}

@media (prefers-reduced-motion: reduce) {
    * {
        transition: none !important;
    }
}
