html {
    font-size: 16px;
    height: 100%;
}
body {
    height: 100%;
    margin: 0;
    box-sizing: border-box;

    display: flex;
    flex-direction: column;

    font-family: Ubuntu, Source Sans Pro, DejaVu Sans, sans-serif;
    line-height: 1.33;
    background: hsl(220, 5%, 5%);
    background-image: url(background.svg);
    background-size: 12em;
    color: #ececec;

    --panel-bg-color: hsl(220, 10%, 15%);
    --button-bg-color: hsl(220, 20%, 25%);
    --button-bg-shadow-color: #fff1;
    --button-bg-hover-color: hsl(220, 30%, 30%);
    --generic-bg-hover-on-white: hsl(220, 60%, 90%);
    --generic-bg-selected-on-white: hsl(220, 60%, 85%);
    --generic-border-selected-on-white: hsl(220, 60%, 75%);
}

/* Generic element styling */
main[hidden] {
    display: none !important;
}
input[type=radio],
input[type=checkbox],
input[type=range] {
    margin: 0.125em;
    vertical-align: middle;
}
input[type=number] {
    font-size: inherit;
    width: 4em;
    width: 8ch;
    text-align: right;
}
button,
.radio-faux-button-set > label > input + span {
    font-size: inherit;
    padding: 0.25em 0.5em;
    font-family: inherit;
    color: white;
    background-color: var(--button-bg-color);
    background-image: linear-gradient(to bottom, var(--button-bg-shadow-color), transparent 75%);
    border: 1px solid hsl(220, 10%, 7.5%);
    box-shadow:
        inset 0 0 1px 1px #fff2,
        0 1px 1px hsl(220, 10%, 7.5%);
    border-radius: 0.25em;
    text-transform: lowercase;
    cursor: pointer;
}
button:hover,
.radio-faux-button-set > label:hover > input + span {
    background-color: var(--button-bg-hover-color);
}
button:active,
.radio-faux-button-set > label:active > input + span {
    transform: translateY(1px);
    /* Need this for the editor's tool help things and i'm not questioning it */
    z-index: 1;
}
button:enabled.button-bright {
    background-color: hsl(220, 50%, 25%);
}
button:enabled.button-bright:hover {
    background-color: hsl(220, 70%, 30%);
}
button:disabled {
    color: #606060;
    background-color: #202020;
    cursor: auto;
}
button.button-big {
    display: block;
    width: 100%;
    margin: 0.5em 0;
    padding: 1em;
}
button.--button-glow-ok {
    background: hsl(220, 100%, 50%);
}
button.--button-glow {
    transition: background-color 0.5s ease-out;
}
button.--image {
    padding: 0;
    border: none;
    background: none;
    box-shadow: none;
}
button.--image img {
    display: block;
}
h1, h2, h3, h4, h5, h6 {
    font-weight: normal;
    margin: 0;
}
ul, ol {
    margin: 0;
    padding: 0;
    list-style: none;
}
ul.normal-list {
    margin-left: 1em;
    list-style: disc;
}
ol.normal-list {
    margin-left: 1.5em;
    list-style: decimal;
}
p {
    margin: 0.5em 0;
}
p:first-child {
    margin-top: 0;
}
p:last-child {
    margin-bottom: 0;
}
pre {
    white-space: pre-wrap;
}
code {
    color: #c0c0e0;
}
kbd {
    padding: 0 0.25em;
    border: 1px solid currentColor;
    border-radius: 0.25em;
    box-shadow: 0 2px 0 currentColor;
    text-align: center;
    text-transform: uppercase;
}

a {
    color: #c0c0c0;
}
a:link,
a:visited {
    text-decoration: underline dotted;
}
a:link {
    color: hsl(220, 50%, 75%);
}
a:visited {
    color: hsl(255, 50%, 75%);
}
a:link:hover,
a:visited:hover {
    text-decoration: underline;
}
a:active {
    color: hsl(0, 50%, 60%);
}

svg#svg-iconsheet {
    /* This is a collection of SVG icons to be re-<use>d */
    display: none;
}
svg.svg-icon {
    width: 1em;
    height: 1em;
    vertical-align: middle;

    stroke: none;
    fill: currentColor;
    fill-rule: evenodd;
}

/* Set of radio buttons in a row, styled like buttons */
/* (you know, like /actual/ radio buttons) */
/* Button-esque styling is shared with the button definition above */
.radio-faux-button-set {
    display: flex;
    align-items: stretch;
    margin: 0.5em 0;
}
.radio-faux-button-set > label {
    flex: 1;
}
.radio-faux-button-set > label > input {
    display: none;
}
.radio-faux-button-set > label > input + span {
    display: block;
    box-sizing: border-box;
    height: 100%;
    border-radius: 0;
    text-align: center;
}
.radio-faux-button-set > label:first-child > input + span {
    border-top-left-radius: 0.25em;
    border-bottom-left-radius: 0.25em;
}
.radio-faux-button-set > label:last-child > input + span {
    border-top-right-radius: 0.25em;
    border-bottom-right-radius: 0.25em;
}
button.--pressed,
.radio-faux-button-set > label > input:checked + span {
    background: hsl(220, 80%, 50%);
    box-shadow:
        inset 0 1px 3px 1px hsl(220, 50%, 15%),
        inset 0 0.25em 1em 0.5em hsl(220, 50%, 30%),
        0 1px 1px hsl(220, 10%, 10%)
}

.button-row {
    display: grid;
    /* Put the buttons in a row most of the time, but change to a column when out of space */
    grid: auto-flow auto / repeat(auto-fit, minmax(10em, 1fr));
    gap: 0.5em;
    align-items: stretch;
    margin: 0.5em 0;
}
.button-row > button {
    margin: 0;
}

/* Overlay styling */
.overlay {
    display: flex;
    align-items: center;
    justify-content: center;

    isolation: isolate;
    z-index: 1;
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background: #fff4;
}
.overlay.--transient {
    align-items: start;
    justify-content: start;
    background: none;
}
.popup-menu {
    position: absolute;
    min-width: 10vw;
    border: 1px solid #444;
    color: black;
    background: hsl(220, 20%, 95%);
    box-shadow: 0 1px 3px 1px #0009;
}
.popup-menu > li {
    padding: 0.375em 0.625em;
    cursor: pointer;
}
.popup-menu > li:hover {
    color: hsl(220, 90%, 10%);
    background: hsl(220, 90%, 75%);
}
.dialog {
    display: flex;
    flex-direction: column;

    min-width: 33vw;
    max-width: 75vw;
    max-height: 75vh;
    border: 1px solid black;
    color: black;
    background: #f4f4f4;
    box-shadow: 0 1px 3px #000c;
}
.dialog > header {
    padding: 0.5em;
    line-height: 1;
    background: hsl(220, 20%, 40%);
    color: white;
}
.dialog > header h1 {
    font-size: 1em;
}
.dialog > footer {
    display: flex;
    justify-content: flex-end;
    gap: 0.5em;

    padding: 0.5em;
    background: #d0d0d0;
}
.dialog > footer > .-spacer {
    flex: 1;
}
.dialog > header:empty,
.dialog > footer:empty {
    display: none;
}
.dialog > section {
    flex: auto;
    overflow: auto;
    padding: 1em;
}
.dialog pre.error {
    color: #400000;
    background: #f0d0d0;
    padding: 0.5em 1em;
}
.dialog a:link {
    color: hsl(220, 50%, 50%);
}
.dialog a:visited {
    color: hsl(255, 50%, 50%);
}
dl.formgrid {
    display: grid;
    grid: auto-flow min-content / 1fr 4fr;
    align-items: baseline;
    gap: 1em;
    margin: 0;
}
dl.formgrid > dt {
    grid-column: 1;
    text-align: right;
    color: hsl(220, 50%, 25%);
}
dl.formgrid > dd {
    grid-column: 2;
    margin: 0;
}
dl.formgrid > dd button {
    margin: 0.25em 0;
}
dl.formgrid > dd button + button {
    margin-left: 0.25em;
}
dl.formgrid > dd.-one-field {
    display: flex;
}
dl.formgrid > dd.-textarea {
    display: flex;
    align-self: end;  /* make the <dt> align to the top */
}
dl.formgrid > dd.-one-field > *,
dl.formgrid > dd.-textarea > * {
    flex: auto;
}
dl.formgrid > dd.-with-buttons {
    display: flex;
    justify-content: space-between;
}

/* Individual overlays */
table.level-browser {
    width: 100%;
    /* for some reason the table ignores the bottom padding when it overflows */
    margin-bottom: 1em;
    line-height: 1.25;
    border-spacing: 0;
}
table.level-browser thead {
    position: sticky;
    top: -1em;  /* counteract padding so cells don't appear above us */
    background: #f4f4f4;  /* match dialog background */
}
table.level-browser thead tr th {
    border-bottom: 2px solid hsl(220, 20%, 60%);
}
table.level-browser tfoot {
    position: sticky;
    bottom: -1em;
    background: #f4f4f4;  /* match dialog background */
}
table.level-browser tfoot tr th {
    border-top: 2px solid hsl(220, 20%, 60%);
    text-align: right;
}
table.level-browser th,
table.level-browser td {
    padding: 0.25em;
}
table.level-browser td.-number {
    color: #404040;
    text-align: right;
}
table.level-browser th.-time,
table.level-browser th.-score,
table.level-browser td.-time,
table.level-browser td.-score {
    text-align: right;
}
table.level-browser button {
    font-size: 0.833em;
}
table.level-browser tr.--current {
    background: var(--generic-bg-selected-on-white);
    outline: 1px solid var(--generic-border-selected-on-white);
}
table.level-browser tr.--unvisited {
    color: #606060;
    font-style: italic;
}
table.level-browser tr.--error {
    color: #600000;
    font-style: italic;
}
table.level-browser tbody tr {
    cursor: pointer;
}
table.level-browser tbody tr:hover {
    background: var(--generic-bg-hover-on-white);
}
table.level-browser tbody tr:nth-child(10n) td {
    border-bottom: 2px solid hsl(220, 20%, 80%);
}
@media (max-width: 600px) {
    /* Unique media query: this is only necessary for VERY narrow screens */
    /* In order to wrap the rows, turn the table markup into a stack of grids */
    table.level-browser {
        display: block;
    }
    table.level-browser tr {
        display: grid;
        grid:
            "number star    name    name    forget"
            "number .       clock   time    score"
            / 3em 1em 1fr 1fr 1fr
        ;
    }
    table.level-browser td,
    table.level-browser th {
        display: block;
    }
    table.level-browser thead .-title,
    table.level-browser tfoot .-title {
        /* "Level" and "Total" column headers are not useful and eat a lot of space */
        display: none;
    }
    table.level-browser thead th:empty,
    table.level-browser tfoot th:empty {
        /* These are filler for table layout purposes */
        display: none;
    }
    table.level-browser .-number {
        grid-area: number;
    }
    table.level-browser .-title {
        grid-area: name;
    }
    table.level-browser .-time {
        grid-area: clock;
    }
    table.level-browser .-time + .-time {
        grid-area: time;
    }
    table.level-browser .-score {
        grid-area: score;
    }
    table.level-browser .-aid {
        grid-area: star;
        /* We overflow a bit because of our padding, so just leak into it */
        justify-self: center;
    }
    table.level-browser .-button {
        grid-area: forget;
        justify-self: end;
    }
    /* Move borders off cells and onto rows */
    table.level-browser thead tr th {
        border: none;
    }
    table.level-browser tfoot tr th {
        border: none;
    }
    table.level-browser tbody tr:nth-child(10n) td {
        border: none;
    }
    table.level-browser thead tr {
        border-bottom: 2px solid hsl(220, 20%, 60%);
    }
    table.level-browser tfoot tr {
        border-top: 2px solid hsl(220, 20%, 60%);
    }
    table.level-browser tbody tr {
        border-bottom: 1px solid #ddd;
    }
    table.level-browser tbody tr.--current {
        border: none;
    }
    table.level-browser tbody tr:nth-child(10n) {
        border-bottom: 2px solid hsl(220, 20%, 80%);
    }
}

/* Compat dialog */
.dialog-compat {
    max-width: 60em;
}
ul.compat-flags > li {
    padding: 0.125em;
}
ul.compat-flags > li.-checked {
    background: var(--generic-bg-selected-on-white);
}
ul.compat-flags > li:hover {
    background: var(--generic-bg-hover-on-white);
}
ul.compat-flags > li > label {
    display: flex;
    align-items: center;
    gap: 0.25em;
}
ul.compat-flags > li > label > span.-desc {
    flex: 1;
}
img.compat-icon,
.compat-icon-gap {
    display: inline-block;
    width: 32px;
    height: 32px;
    vertical-align: middle;
}

@media (max-width: 800px) {
    /* Stack the formgrid, it doesn't fit very well as columns */
    .dialog dl.formgrid {
        display: block;
    }
    .dialog dl.formgrid > dt {
        margin: 0.5em 0;
        text-align: left;
    }
    .dialog dl.formgrid > dt:first-child {
        margin-top: 0;
    }
    .dialog dl.formgrid > * + dt {
        border-top: 1px solid #ccc;
        padding-top: 0.5em;
    }
    .dialog dl.formgrid > * + dt:empty {
        padding-top: 0;
    }
    .dialog dl.formgrid > dd {
        margin: 0.5em 0;
    }

    .dialog-compat .radio-faux-button-set {
        font-size: 0.83em;
        flex-wrap: wrap;
    }
    .dialog-compat .radio-faux-button-set > * {
        flex: 1 0 30%;
    }
    .dialog-compat .radio-faux-button-set .-button {
        border-radius: 0;
    }
    ul.compat-flags img.compat-icon,
    ul.compat-flags span.compat-icon-gap {
        width: 16px;
        height: 16px;
    }
}


/* Options dialog */
.dialog-options {
}
.option-volume {
    display: flex;
    gap: 1em;
}
.option-volume > input[type=range] {
    flex: auto;
}
.option-tileset canvas {
    vertical-align: middle;
}
label.option {
    display: flex;
    align-items: center;
    padding: 0.25em;
}
label.option:hover {
    outline: 2px solid #d0d0d0;
}
label.option .option-label {
    flex: 1;
}
.option-help {
    display: none;
    background: #e8e8e8;
    padding: 0.5em 0.75em;
    border-radius: 0.5em;
}
.option-help.--visible {
    /* TODO */
}

@media (max-width: 800px) {
    .dialog {
        max-width: 90%;
        max-height: 90%;
    }
    .dialog-options {
    }
}


/**************************************************************************************************/
/* Main page structure */

body > header {
    display: flex;
    align-items: center;
    gap: 0.5em;

    padding: 0.25em 0.5em;
    line-height: 1.125;
}
body > header h1 {
    font-size: 1.66em;
}
body > header h2 {
    font-size: 1.33em;
}
body > header h3 {
    font-size: 1.75em;
}
body > header > nav {
    flex: 1;
    display: flex;
    justify-content: flex-end;
    gap: 0.5em;
}
body > header button {
    white-space: nowrap;
}
body > header h1 a {
    color: inherit !important;
    text-decoration: none !important;
}

body[data-mode=failed] #header-pack,
body[data-mode=failed] #header-level,
body[data-mode=failed] #header-main > nav,
body[data-mode=loading] #header-pack,
body[data-mode=loading] #header-level,
body[data-mode=loading] #header-main > nav {
    display: none;
}
body[data-mode=splash] #header-pack,
body[data-mode=splash] #header-level {
    display: none;
}
body[data-mode=editor] #player-edit,
body[data-mode=player] #editor-play {
    display: none;
}
#failed,
#loading {
    margin: auto;
    text-align: center;
}
#failed .-with-error {
    display: none;
}
#failed.--got-error .-with-error {
    display: revert;
}
#loading {
    font-size: 2em;
}
.scrolling-sidewalk {
    height: 32px;
    width: 50vw;
    margin: auto;
    background: url(icons/tool-bg-selected.png) repeat;
    animation: scrolling-sidewalk linear 0.4s infinite;
    --mask: linear-gradient(to right, transparent 0, #ffffff40 10%, white 20%, white 80%, #ffffff40 90%, transparent 100%);
    -webkit-mask-image: var(--mask);
    mask-image: var(--mask);
}
.scrolling-sidewalk > img {
    display: block;
    margin: auto;
}
@keyframes scrolling-sidewalk {
    0% {
        background-position: 0px 0px;
    }
    100% {
        background-position: -32px 0px;
    }
}
pre.stack-trace {
    overflow: auto;
    width: 90vw;
    max-width: 50em;
    padding: 0.5em;
    margin: 1em auto;
    white-space: pre;
    text-align: left;
    background: hsl(345, 20%, 10%);
    border: 3px double hsl(345, 75%, 20%);
}

#header-main {
    order: 3;
    color: #606060;
}
#header-icon {
    image-rendering: crisp-edges;
    image-rendering: pixelated;
}

@media (orientation: portrait) and (max-width: 800px), (orientation: landscape) and (max-height: 600px) {
    body > header {
        padding: 1px;
    }
    /* All these headings are way too big on phones */
    body > header h1 {
        font-size: 1.125em;
    }
    body > header h2 {
        font-size: 1.125em;
    }
    body > header h3 {
        font-size: 1.0625em;
    }
    body > header p {
        /* "a game by eevee" takes up too much space :( */
        display: none;
    }

    /* Hide the top/bottom nav while playing entirely */
    body[data-mode=player] #header-pack,
    body[data-mode=player] #header-level {
        display: none;
    }
}

/**************************************************************************************************/
/* Splash (intro part) */

#splash {
    display: grid;
    grid:
        "header header"
        "links  links"
        "disclaimer disclaimer"
        "stock  yours"
        / 3fr minmax(18em, 1fr)
    ;
    gap: 1em;

    position: relative;
    padding: 1em 7.5%;
    margin: auto 0;
    overflow: auto;
}
#splash::after {
    /* Force some breathing room at the bottom of the scroll */
    content: '';
    display: block;
    height: 2em;
}
#splash > .drag-overlay {
    display: none;
    justify-content: center;
    align-items: center;

    font-size: 10vmin;
    position: fixed;
    top: 0.5rem;
    bottom: 0.5rem;
    left: 0.5rem;
    right: 0.5rem;
    background: #0004;
    border: 0.125rem dashed white;
    border-radius: 0.25rem;
    text-shadow: 0 1px 5px black;
    text-align: center;
}
#splash > .drag-overlay::before {
    content: "drop levels here";
}
#splash.--drag-hover > .drag-overlay {
    display: flex;
}

#splash > header {
    grid-area: header;
    display: grid;
    grid:
        "image  title   fullscreen" 2fr
        "image  tagline fullscreen" 1fr
        / min-content 1fr min-content
    ;
    gap: 0 2em;

    margin: auto;
}
#splash > header img {
    grid-area: image;
    align-self: center;
}
#splash > header h1 {
    grid-area: title;
    align-self: end;
    font-size: 4em;
    margin: 0;
}
#splash > header p {
    grid-area: tagline;
    align-self: start;
    font-size: 1.5em;
    margin: 0;
    font-style: italic;
    color: #909090;
}
#splash-fullscreen {
    /* FIXME this makes the title SLIGHTLY offcenter bc of grid gap */
    display: none;
    grid-area: fullscreen;
    align-self: center;
}
#splash-fullscreen svg {
    width: 2em;
    height: auto;
}
#splash h2 {
    border-bottom: 1px solid #404040;
    color: #909090;
    text-shadow: 0 1px #0004;
}
#splash * + h2 {
    margin-top: 1rem;
}
#splash > section {
    padding: 1em;
    background: var(--panel-bg-color);
    box-shadow: 0 0.25em 1em black;
}
#splash-links {
    grid-area: links;
    font-size: 1.5em;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0 2em;
}
#splash > #splash-intro {
    grid-area: intro;
    font-size: 20px;
}
#splash > #splash-stock-levels {
    grid-area: stock;
}
#splash > #splash-upload-levels {
    grid-area: upload;
}
#splash-upload-file,
#splash-upload-dir {
    /* Hide the file upload control, which is ugly */
    display: none;
}
#splash > #splash-your-levels {
    grid-area: yours;
}
#splash > #splash-disclaimer {
    grid-area: disclaimer;
    font-size: 0.83em;
    margin: 0;
    text-align: center;
    color: #c0c0c0;
}

@media (max-width: 800px) {
    #splash {
        /* Grid layout doesn't fit, just stack everything */
        display: flex;
        flex-direction: column;
        gap: 0.5em;
        /* 10% padding is way way too much */
        padding: 0.5em;
    }
    #splash::after {
        /* This needs different handling in a flex container */
        flex: 0 0 0.5em;
        height: auto;
    }
    /* Shrink logo and title, and left-align both */
    #splash > header {
        grid-template-rows: auto auto;
        grid-template-columns: min-content 1fr;
        column-gap: 0.5em;
        margin: 0;
    }
    #splash > header img {
        width: 48px;
    }
    #splash > header h1 {
        font-size: 1.5em;
    }
    #splash > header p {
        font-size: 0.9em;
    }
    /* No need to boost the font size here */
    #splash > #splash-links {
        font-size: inherit;
        column-gap: 1em;
    }
    #splash > section {
        padding: 0.5em;
    }
    /* This takes up an incredible amount of space on a phone; push it down, they have to scroll to
     * see much of anything anyway.  TODO possibly revert if i can find a way to shorten wording */
    #splash > #splash-disclaimer {
        order: 99;
    }
}

.played-pack-list {

}
#splash-stock-levels .played-pack-list {
    display: grid;
    grid: auto-flow auto / repeat(auto-fill, minmax(320px, 1fr));  /* 10x10 */
    gap: 1em;
    margin: 1em 0;
}
.played-pack-list .-preview {
    display: block;
    height: 320px;
    margin: auto;
    object-fit: none;
}
.played-pack-list > li > button {
    font-size: 1.25em;
    padding: 0.5em;
    margin: 0.25em 0;
    text-transform: none;
    text-align: left;
    /* this also forces the button to be 1 line of text high even for empty title */
    white-space: pre-wrap;
}
.played-pack-list p {
    color: #c0c0c0;
    font-style: italic;
}
.played-pack-list .-progress {
    display: grid;
    grid:
        "levels levels  levels"
        "score  time    button"
        / 2fr 2fr 1fr
    ;
    gap: 0.5em;
    margin: 0.5em 0;
    align-items: center;
}
.played-pack-list > li.--unplayed .-progress {
    display: none;
}
.played-pack-list .-progress > .-levels {
    grid-area: levels;

    position: relative;
    z-index: 1;
    padding: 0.25em;
    border: 1px solid hsl(220, 25%, 40%);
    text-shadow: 0 1px 1px black;
    text-align: center;
}
.played-pack-list .-progress > .-levels::before,
.played-pack-list .-progress > .-levels::after {
    content: '';
    position: absolute;
    z-index: -1;
    top: 0;
    bottom: 0;
    left: 0;
}
.played-pack-list .-progress > .-levels::before {
    width: calc(var(--cleared) * 100%);
    background: hsl(220, 25%, 30%);
}
.played-pack-list .-progress > .-levels::after {
    width: calc(var(--aidless) * 100%);
    background: hsl(220, 25%, 40%);
}
.played-pack-list .-progress > .-score {
    grid-area: score;
}
.played-pack-list .-progress > .-time {
    grid-area: time;
}
.played-pack-list .-progress > .-levels {
    grid-area: levels;
}
.played-pack-list .-progress > .-score::before {
    content: "Score: ";
    color: #909090;
}
.played-pack-list .-progress > .-time::before {
    content: "Time: ";
    color: #909090;
}
.played-pack-list .-editor-status {
    display: flex;
    gap: 0.5em;
    margin: 0.5em 0 1em;
}
.played-pack-list .-editor-status > .-level-count {
    flex: auto;
}
.played-pack-list .-editor-status > .-timestamp {
    flex: auto;
    text-align: right;
}

/* "Bulk test" button, only available in debug mode */
#main-test-pack {
    display: none;
}
body.--debug #main-test-pack {
    display: initial;
}
.packtest-dialog {
    width: 75vw;
    height: 75vh;
}
ol.packtest-summary {
    display: flex;
    align-items: stretch;
    height: 1em;
    border: 1px solid #606060;
}
ol.packtest-summary > li {
    /* Give a meaty flex-basis; the dialog has a max-width so it won't blow out, and these will
     * simply shrink if necessary */
    flex: 1 1 1em;
    background: white;
}
.packtest-row {
    display: flex;
    gap: 0.5em;
    align-items: center;
    margin: 0.5em 0;
}
.packtest-row > p {
    flex: 9;
    margin: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.packtest-row > button {
    flex: 1;
}
.packtest-results {
    width: 100%;
    table-layout: fixed;
    border-collapse: collapse;
    margin-bottom: 1em;
}
.packtest-results th {
    font-weight: normal;
}
.packtest-results tbody {
    cursor: pointer;
    border-top: 1px solid #0004;
}
.packtest-results tbody:hover {
    outline: 2px solid #000;
}
.packtest-results td {
    padding: 0.25em;
}
.packtest-results .-level {
    text-align: left;
}
.packtest-results .-result {
    width: 8em;
    text-align: center;
}
.packtest-results .-clock {
    width: 6em;
    text-align: right;
}
.packtest-results .-delta {
    width: 8em;
    text-align: right;
}
.packtest-results .-speed {
    width: 8em;
    text-align: right;
}
.packtest-results canvas {
    display: block;
    margin: 0.5em auto;
}
.packtest-colorcoded [data-status=no-replay] {
    background: hsl(0, 0%, 25%);
}
.packtest-colorcoded [data-status=running] {
    background: hsl(30, 100%, 75%);
}
.packtest-colorcoded [data-status=success] {
    background: hsl(120, 60%, 75%);
}
.packtest-colorcoded [data-status=early] {
    background: hsl(75, 60%, 75%);
}
.packtest-colorcoded [data-status=failure] {
    background: hsl(0, 60%, 60%);
}
.packtest-colorcoded [data-status=short] {
    background: hsl(330, 60%, 75%);
}
.packtest-colorcoded [data-status=error] {
    background: black;
    color: white;
}
.packtest-dialog .grade-A {
    color: hsl(120, 60%, 45%);
    font-weight: bold;
}
.packtest-dialog .grade-B {
    color: hsl(220, 60%, 45%);
    font-weight: bold;
}
.packtest-dialog .grade-C {
    color: hsl(270, 60%, 45%);
    font-weight: bold;
}
.packtest-dialog .grade-D {
    color: hsl(36, 60%, 60%);
    font-weight: bold;
}
.packtest-dialog .grade-F {
    color: hsl(0, 60%, 60%);
    font-weight: bold;
}


/**************************************************************************************************/
/* Player */

#player {
    flex: 1;
    position: relative;
    display: flex;

    image-rendering: crisp-edges;
    image-rendering: pixelated;

    --tile-width: 32px;
    --tile-height: 32px;
    --scale: 1;
}
#player-main {
    /* This element basically just exists so that #player can have relative positioning and the
     * debug panel can use that to sit against the right edge; absolute positioning excludes
     * margins, so if it were positioned as a child of THIS element, it would be stuffed into the
     * game area (oops!) */
    /* It does also make auto-sizing easier! */
    /* Default to a landscape layout, with the buttons on the left */
    display: grid;
    grid:
        "buttons    game    actions"
        "buttons    game    actions"
        ".          music   music"
        / 1fr auto 1fr
    ;
    justify-content: stretch;
    gap: 0.5em;
    margin: auto;  /* center in both directions baby */
}
#player-controls,
#player-actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.25em;
}
#player-controls {
    grid-area: buttons;
    align-self: start;
}
#player-controls button,
#player-actions button {
    padding: 0.5em;
    line-height: 1;
}
#player-controls button svg,
#player-actions button svg {
    font-size: 1.5em;
    display: block;
    margin: 0.125em auto;
}
#player-controls .radio-faux-button-set {
    margin: 0;
}
#player-actions {
    grid-area: actions;
    align-self: end;
}
#player-music {
    grid-area: music;
    margin: 0 calc(var(--tile-width) * var(--scale) / 4);
    text-transform: lowercase;
    color: #909090;
}
/* Key hints are placed on the sides */
#player button {
    position: relative;
}
#player button .keyhint {
    font-size: 1rem;
    position: absolute;
    top: 1.25em;
    margin: auto;
    color: #404040;
}
#player-controls button .keyhint {
    left: -2em;
}
#player-actions button .keyhint {
    right: -2em;
}
#player button:disabled .keyhint {
    display: none;
}
@media (orientation: portrait) {
    /* On a portrait screen, put the controls on top */
    #player-main {
        grid:
            "buttons    actions"
            "game       game"
            "music      music"
        ;
    }
    #player-controls,
    #player-actions {
        flex-direction: row;
        white-space: nowrap;
    }
    #player-controls button svg,
    #player-actions button svg {
        font-size: 1em;
    }
    #player-controls button {
        padding: 0.25em 0.5em;
        line-height: 1.33;
    }
    /* Hackily remove the <br>s in "turn based mode" */
    #player-controls .radio-faux-button-set br {
        display: none;
    }
    #player-actions {
        justify-content: end;
    }
    #player-actions button svg {
        display: inline-block;
        margin: 0.125em;
    }
    #player button .keyhint {
        top: -2em;
        left: 0;
        right: 0;
    }
}
@media (orientation: portrait) and (max-width: 800px) {
    /* On a /small/ portrait screen, put the controls on their own bottom row */
    #player-main {
        grid:
            "buttons"
            "game"
            "actions"
        ;
    }
    #player-controls,
    #player-actions {
        /* Not much in these rows so make them a bit bigger for hittability */
        font-size: 1.33em;
        justify-content: center;
    }
    #player-controls button,
    #player-actions button {
        flex: auto;
        padding: 0.25em 0.5em;
    }
    #player-controls .control-restart {
        /* This is a dedicated pause-menu button */
        display: none;
    }
    #player .keyhint {
        /* Hide key hints; there's nowhere to put them and they take up surprisingly a lot of space */
        display: none;
    }
}
@media (orientation: landscape) and (max-height: 600px) {
    /* On a small landscape screen, remove the music row (it matters!) */
    #player-main {
        grid:
            "buttons    game    actions"
            "buttons    game    actions"
            / 1fr auto 1fr
        ;
    }
}
@media (orientation: portrait) and (max-width: 800px), (orientation: landscape) and (max-height: 600px) {
    #player-controls .-optional-label {
        display: none;
    }
    #splash-fullscreen {
        display: revert;
    }
    #player-music {
        /* TODO :( */
        display: none;
    }
}

#player-game-area {
    grid-area: game;
    /* don't stretch if the buttons or music blow out somehow */
    justify-self: center;
    align-self: center;

    isolation: isolate;
    display: grid;
    align-items: center;
    grid:
        "level      chips"      min-content
        "level      time"       min-content
        "level      bonus"      min-content
        "level      rules"      1fr
        "level      inventory"  min-content
        /* Need explicit min-content to force the hint to wrap */
        / min-content min-content
    ;
    column-gap: calc(var(--tile-width) * var(--scale) / 4);
    row-gap: calc(var(--tile-height) * var(--scale) / 4);

    padding: calc(var(--tile-height) * var(--scale) / 4) calc(var(--tile-width) * var(--scale) / 4);
    background: hsl(220, 10%, 15%);
    box-shadow: 0 0.25em 1em black;
}

.level {
    grid-area: level;

    position: relative;
    outline: 1px solid hsl(220, 10%, 5%);
}
.level canvas {
    display: block;
    width: calc(var(--viewport-width) * var(--tile-width) * var(--scale));
    height: calc(var(--viewport-height) * var(--tile-height) * var(--scale));
    --viewport-width: 9;
    --viewport-height: 9;
}

.player-overlay-message {
    grid-area: level;
    place-self: stretch;

    position: relative;
    display: grid;
    grid:
        "pack"      calc(1.25em * 1.25 * 1)
        "level"     calc(1.333em * 1.25 * 2)
        "author"    calc(1em * 1.25 * 1)
        "space"     1fr
        "controls"  1.5em
    ;
    align-items: center;
    gap: 0.25em;
    /* Prevent blowout; force using the canvas's size */
    height: 0;
    min-height: 100%;
    width: 0;
    min-width: 100%;
    box-sizing: border-box;

    z-index: 2;
    font-size: calc(0.5 * var(--tile-width) * var(--scale));
    line-height: 1.25;
    background: #0009;
    color: white;
    text-align: center;
    text-shadow: 0 1px 1px black;
}
/* Allow clicking through the overlay in debug mode */
body.--debug .player-overlay-message {
    pointer-events: none;
}
.player-overlay-message > * {
    padding: 0 0.25em;
}
.player-overlay-message h1 {
    /* Pack title, doesn't need to be too big */
    grid-area: pack;
    font-size: 1em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: hsl(220, 25%, 60%);
}
.player-overlay-message > h2 {
    grid-area: level;
    font-size: 2em;
}
.player-overlay-message[data-reason='waiting'] > h2 {
    /* For 'waiting' this is a level name, so make it two lines of smaller text */
    font-size: 1.333em;
}
.player-overlay-message > h3 {
    grid-area: author;
    font-size: 1em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: hsl(220, 10%, 90%);
}
.player-overlay-message > .scoreboard {
    grid-row: author / space;
}
.player-overlay-message .-controls-hint {
    grid-area: controls;
    font-size: 0.75em;
    color: #c0c0c0;
}
.player-overlay-message .mobile-pause-menu {
    grid-area: space;
}
.mobile-pause-menu {
    font-size: 1.25em;
    display: none;  /* flex */
    flex-direction: column;
    align-items: stretch;
    gap: 0.33em;
    width: 80%;
    margin: auto;
}
.mobile-pause-menu button {
    padding: 0.33em;
}
.mobile-pause-menu > p {
    display: flex;
    flex-direction: row;
    align-items: stretch;
    justify-content: stretch;
    margin: 0;
    gap: 0.33em;
}
.mobile-pause-menu > p > button {
    flex: 1;
    line-height: 1;
}
.mobile-pause-menu > p > button.-narrow {
    flex: initial;
}
.mobile-pause-menu .-only-waiting,
.mobile-pause-menu .-only-paused,
.mobile-pause-menu .-only-failure,
.mobile-pause-menu .-only-success,
.mobile-pause-menu .-only-ended {
    display: none;
}
.player-overlay-message[data-reason=waiting] .mobile-pause-menu .-only-waiting,
.player-overlay-message[data-reason=paused] .mobile-pause-menu .-only-paused,
.player-overlay-message[data-reason=failure] .mobile-pause-menu .-only-failure,
.player-overlay-message[data-reason=success] .mobile-pause-menu .-only-success,
.player-overlay-message[data-reason=ended] .mobile-pause-menu .-only-ended {
    display: initial;
}
.player-overlay-message[data-reason=paused] .mobile-pause-menu p.-only-paused {
    display: flex;
}
.player-overlay-message[data-reason=""] {
    display: none;
}
.player-overlay-message[data-reason=waiting] {
    background: linear-gradient(to bottom, #000d, #0008 40%, #0008 60%, #000d);
}
.player-overlay-message[data-reason=failure] {
    background: hsla(330, 20%, 10%, 0.5);
    background: radial-gradient(#0004, hsla(330, 10%, 10%, 0.5) 40%, hsl(330, 20%, 10%));
}
.player-overlay-message[data-reason=success] {
    background: radial-gradient(hsla(220, 60%, 5%, 0.75), 60%, hsla(220, 60%, 25%, 0.75));
}
.player-overlay-message[data-reason=ended] {
    /* Rearrange this entirely, to fit the ending image in */
    grid:
        "congrats"  min-content
        "."         0.5em
        "menu"      1fr
        "."         0.5em
        "score"     min-content
    ;
    overflow: hidden;
    background: url(ending.png) no-repeat center center / cover;
    box-shadow: inset 0 0 calc(4 * var(--tile-width)) hsl(220, 50%, 25%);
}
.player-overlay-message[data-reason=ended] .mobile-pause-menu {
    grid-area: menu;
}
.player-overlay-message[data-reason=ended] > .-congrats {
    grid-area: congrats;
    margin: 0;
    padding: 0.25em;
    background: #0009;
}
.player-overlay-message[data-reason=ended] > .-score {
    grid-area: score;
    margin: 0;
    padding: 0.25em;
    background: #0006;
}
.player-overlay-message[data-reason=ended] > .-score output {
    font-size: 2em;
    display: block;
}
@supports (mask-image: none) or (-webkit-mask-image: none) {
    /* Do this complicated rotating sunburst thing only if masks work */
    .player-overlay-message[data-reason=ended]::before {
        content: '';
        position: absolute;
        z-index: -1;
        top: -50%;
        bottom: -50%;
        left: -50%;
        right: -50%;
        background: repeating-conic-gradient(at center, #fbf8ad 0turn 0.025turn, #f6d87e 0.025turn 0.075turn, #fbf8ad 0.075turn 0.1turn);
        /* Alas, the spinning part apparently eats a lot of CPU */
        /* animation: ending-spinner 10s linear infinite; */
        -webkit-mask-image: radial-gradient(at center, #0000 0%, #000c 50%);
        mask-image: radial-gradient(at center, #0000 0%, #000c 50%);
    }
}
@keyframes ending-spinner {
    0% {
        transform: rotate(0turn);
    }
    100% {
        transform: rotate(1turn);
    }
}
.scoreboard {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
    grid-auto-rows: min-content;
    align-items: center;
    row-gap: 0.75em;
    margin: auto 5%;
    font-weight: normal;
    text-align: center;
}
.scoreboard .-subscore {
    grid-column: span 2;
    color: #f4f4f4;
}
.scoreboard .-level-score {
    grid-column: span 3;
}
.scoreboard .-improvement {
    grid-column: span 3;
}
.scoreboard .-improvement.--same h4 {
    color: hsl(240, 50%, 60%);
}
.scoreboard .-improvement.--same p {
    color: hsl(240, 50%, 80%);
}
.scoreboard .-improvement.--worse h4 {
    color: hsl(330, 50%, 60%);
}
.scoreboard .-improvement.--worse p {
    color: hsl(330, 50%, 80%);
}
.scoreboard .-improvement.--better h4 {
    color: hsl(210, 50%, 60%);
}
.scoreboard .-improvement.--better p {
    color: hsl(210, 50%, 80%);
}
.scoreboard .-total-score {
    grid-column: span 3;
    color: hsl(45, 50%, 75%);
}
.scoreboard h4 {
    font-size: 0.833em;
    color: hsl(220, 10%, 80%);
}
.scoreboard .-total-score h4 {
    color: hsl(30, 50%, 60%);
}
.scoreboard p {
    margin: 0;
}
.scoreboard .-total-score p {
    font-size: 1.333em;
}

.player-level-number {
    grid-area: number;
    /* This is only for portrait, and mostly to fill space */
    display: none;
    line-height: 1;
}
.chips {
    grid-area: chips;
}
.time {
    grid-area: time;
}
.bonus {
    grid-area: bonus;
}
.chips,
.time,
.bonus {
    font-size: calc(var(--tile-height) * var(--scale) * 3/4);
    display: flex;
    align-items: center;
    gap: 0.25em;
}
.chips h3,
.time h3,
.bonus h3 {
    flex: 0;
    order: 2;
    font-size: 0.75em;
    line-height: 1;
    color: hsl(220, 20%, 80%);
}
.chips output,
.time output,
.bonus output {
    flex: 1;
    min-width: 2em;
    min-height: 1em;
    line-height: 1;
    text-align: right;
    font-family: monospace;
    color: hsl(220, 20%, 60%);
}
/* nb: the hex colors are all taken from the lexy palette */
.chips output {
    color: #feafc9;
}
.time output {
    color: #6ca2a7;
}
.time output.--warning {
    color: #f48457;
}
.time output.--danger {
    color: #e1565f;
    /* TODO this can get out of sync and keeps going at 0, but is a neat idea */
    /* animation: time-pulse 1s linear infinite; */
}
@keyframes time-pulse {
    0% {
        transform: scale(1.1);
    }
    100% {
        transform: scale(1);
    }
}
.chips output.--done,
.time output.--frozen,
.bonus output {
    color: hsl(220, 10%, 30%);
}
#player.--bonus-visible .bonus output {
    color: #e2c9ff;
}
.player-rules {
    font-size: calc(var(--tile-height) * var(--scale) / 4);
    grid-area: rules;
    align-self: end;
    display: flex;
    flex-direction: column;
    gap: 0.5em;
    color: hsl(220, 20%, 80%);
}
.player-rules p {
    display: none;
    margin: 0;
}
#player.--hide-logic .player-rules #player-rule-logic-hidden {
    display: revert;
}
#player.--cc1-boots .player-rules #player-rule-cc1-boots {
    display: revert;
}

/* This has a wrapper because scrollables don't work too well as the direct child of a grid or flex
 * parent; they really want to expand to their natural size, and I do not want that */
#player-game-area > .player-hint-wrapper {
    grid-area: rules;
    align-self: stretch;

    display: none;
    position: relative;
    overflow: hidden;
    font-size: calc(var(--tile-height) * var(--scale) / 4);
    font-family: serif;
    color: hsl(220, 20%, 80%);
    background: url(#svg-icon-hint) hsl(220, 10%, 10%);
    border: 3px double hsl(220, 10%, 15%);
}
#player-game-area > .player-hint-wrapper > .player-hint-bg-icon {
    position: absolute;
    width: 8em;
    height: auto;
    bottom: -2em;
    right: -2em;
    opacity: 0.05;
    transform: rotate(-30deg);
}
#player-game-area > .player-hint-wrapper > .player-hint {
    overflow: auto;
    /* Set our inherent height or whatever to zero, but then force us to expand to whatever size our
     * parent happens to be.  Magic! */
    box-sizing: border-box;
    height: 0;
    min-height: 100%;
    width: 0;
    min-width: 100%;
    padding: 0.5em 0.625em;
    line-height: 1.5;
    white-space: pre-wrap;
}
#player-game-area > .player-hint-wrapper.--visible {
    display: initial;
}

#player .inventory {
    grid-area: inventory;
    justify-self: center;
    display: grid;
    grid: auto-flow calc(var(--tile-height) * var(--scale)) / repeat(4, calc(var(--tile-width) * var(--scale)));

    background-size: calc(var(--tile-width) * var(--scale)) calc(var(--tile-height) * var(--scale));
    width: calc(4 * var(--tile-width) * var(--scale));
    height: calc(2 * var(--tile-height) * var(--scale));
}
#player .inventory img {
    width: calc(var(--tile-width) * var(--scale));
}
#player .inventory .--hidden {
    visibility: hidden;
    pointer-events: none;
}
#player .inventory > span {
    position: relative;
}
#player .inventory .-count {
    font-size: calc(0.25 * var(--tile-height) * var(--scale));
    position: absolute;
    top: 0;
    right: 0;
    padding: 0.25em 0.5em;
    margin: 0.25em;  /* 2px, for a 32px tileset */
    line-height: 1;
    border-radius: 0.25em;
    background: #0009;
    color: white;
}

@media (orientation: portrait) {
    #player-game-area {
        /* Rearrange the grid to be vertical */
        grid:
            "inventory   chips   chips   time" min-content
            "inventory   rules   bonus   bonus" min-content
            "level       level   level   level" min-content
            / min-content 1fr 1fr 2fr
        ;
    }
    .player-level-number {
        /* TODO this makes us too big on my phone, damn */
        /*display: initial;*/
    }
    .chips,
    .time,
    .bonus {
        /* These numbers need to be sliiightly smaller */
        font-size: calc(var(--tile-height) * var(--scale) * 2/3);
    }
    #player .inventory {
        /* stick me in the center left */
        place-self: center start;
    }
    #player-game-area > .player-hint-wrapper {
        /* Overlay hints on the inventory area */
        grid-row: chips / bonus;
        grid-column: level;
        z-index: 1;
        font-size: calc(var(--tile-height) * var(--scale) / 2.5);
    }
    #player-game-area > .player-hint-wrapper > .player-hint {
        padding: 0.33em 0.5em;
        line-height: 1.33;
    }
    .player-rules {
        align-self: center;
        flex-direction: row;
    }
    .player-rules p span {
        /* There's only room for the icons, since there's no dedicated hint space */
        display: none;
    }
}
@media (orientation: portrait) and (max-width: 800px), (orientation: landscape) and (max-height: 600px) {
    /* Overlay is a bit different on what I assume is a touchscreen */
    .player-overlay-message[data-reason='waiting'] > p {
        /* Hide the "Ready!" and controls, since there's a menu */
        display: none;
    }
    .mobile-pause-menu {
        display: flex;
    }
}
@media (orientation: portrait) and (max-width: 800px) {
    #player-game-area {
        padding: 0;
        background: none;
        box-shadow: none;
    }
    .level {
        outline: 1px solid black;
    }
}

/* Debug stuff */
body.--debug #player-debug {
    display: flex;
}
#player-debug {
    flex: none;
    align-self: center;
    display: none;
    /* FIXME this blows out the height instead of scrolling and i cannot figure out how to make it
     * stop, so here is an arbitrary max height on it which i extremely hate */
    overflow: auto;
    max-height: 75vh;
    flex-direction: column;
    justify-content: start;
    gap: 0.25em;
    padding: 0.5em;
    background-image: repeating-linear-gradient(135deg,
        hsl(0, 0%, 4%) 0px,
        hsl(0, 0%, 4%) 16px,
        hsl(30, 25%, 8%) 16px,
        hsl(30, 25%, 8%) 32px);
    border: 0.25em solid hsl(30, 75%, 50%);
    border-right-width: 0;
    box-shadow: inset 0 0 0.25em 0.25em #0004;
}
#player-debug > h3 {
    margin: -0.25em -0.25em 0.25em;
    padding: 0 0.5em;
    background: hsl(30, 60%, 20%);
    color: black;
}
#player-debug > * + h3 {
    margin-top: 0.5em;
}
#player-debug hr {
    border: none;
    margin: 0.25em 10%;
    border-bottom: 1px solid #404040;
}
#player-debug table.-time-controls {
    border-collapse: collapse;
    width: 100%;
    margin-bottom: 0.5em;
}
#player-debug table.-time-controls button {
    width: 100%;
    text-align: inherit;
}
#player-debug table.-time-controls td:nth-child(1) {
    /* go backwards button */
    text-align: left;
}
#player-debug table.-time-controls td:nth-child(2) {
    /* value */
    width: 4em;
    text-align: right;
}
#player-debug table.-time-controls td:nth-child(3) {
    /* label */
}
#player-debug table.-time-controls td:nth-child(4) {
    /* go forward button */
    text-align: right;
}
#player-debug .-inventory {
    display: grid;
    margin: 0 auto;
    grid: auto-flow min-content / repeat(8, min-content);
    gap: 2px;
}
#player-debug > .-inventory > button {
    padding: 0;
}
#player-debug > .-inventory > button > img {
    display: block;
}
#player-debug > .-inventory > button.-wide {
    grid-column: span 5;
    padding: 0.25em;
}
#player-debug .-buttons {
    display: flex;
    justify-content: space-between;
    gap: 0.25em;
}
#player-debug .-buttons button {
    flex: auto;
}

#player-debug .-replay-columns {
    display: flex;
    align-items: flex-start;
    gap: 0.5em;
}
#player-debug .-replay-columns .-buttons {
    flex-direction: column;
}
#player-debug-input {
    flex: none;
    display: grid;
    grid:
        "drop   up      cycle"  1em
        "left   .       right"  1em
        ".      down    swap"   1em
        / 1em 1em 1em
    ;
    gap: 0.5em;
}
#player-debug-input > svg {
    fill: #404040;
}
#player-debug-input > svg.--held {
    fill: white;
}
#player-debug > .-replay-available {
    display: flex;
    gap: 0.5em;
}
#player-debug > .-replay-available > h4 {
    flex: 1 0 0;
}
#player-debug > .-replay-available > p {
    flex: 2 0 0;
    margin: 0;
}
#player-debug .-replay-status {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 4em;
}
#player-debug .-replay-status > .-none {
}
#player.--replay-playback #player-debug .-replay-status > .-none,
#player.--replay-recording #player-debug .-replay-status > .-none {
    display: none;
}
#player-debug .-replay-status > .-playback {
    flex: 1;
    display: none;
    grid:
        "duration   percent"    1em
        "progress   progress"   0.5em
        "button     button"     2em
        / 3fr 1fr
    ;
    gap: 0.25em;
    align-items: center;
}
#player.--replay-playback #player-debug .-replay-status > .-playback {
    display: grid;
}
#player-debug .-replay-status > .-playback > progress {
    grid-area: progress;
    width: 100%;
    height: 100%;
}
#player-debug .-replay-status > .-playback > output {
    grid-area: percent;
    min-width: 0;
    text-align: right;
}
#player-debug .-replay-status > .-playback > span {
    grid-area: duration;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
#player-debug .-replay-status > .-playback > button {
    grid-area: button;
}
#player-debug .-replay-status > .-recording {
    display: none;
    align-items: center;
    justify-content: center;
}
#player.--replay-recording #player-debug .-replay-status > .-recording {
    display: flex;
}

.player-debug-actor-tooltip {
    position: absolute;
    display: none;

    /* similar to editor tooltip */
    padding: 0.33em 0.75em;
    border: 1px solid black;
    color: #d8d8d8;
    background: hsl(220, 10%, 20%);
    box-shadow: 0 1px 2px 1px #0004;

    opacity: 0.9;
    font-family: monospace;
    pointer-events: none;
}
.player-debug-actor-tooltip.--visible {
    display: block;
}
.player-debug-actor-tooltip h3 {
    font-size: 1.25em;
    margin-bottom: 0.25rem;
    border-bottom: 1px solid currentColor;
    color: white;
}
.player-debug-actor-tooltip dl {
    display: grid;
    grid: auto-flow min-content / auto auto;
    align-items: baseline;
    gap: 0.25em 1em;
    margin: 0;
}
.player-debug-actor-tooltip dl > dt {
    grid-column: 1;
}
.player-debug-actor-tooltip dl > dd {
    grid-column: 2;
    margin: 0;
}


/**************************************************************************************************/
/* Editor */

#editor {
    flex: 1 1 auto;
    display: grid;
    grid:
        "controls   controls"   min-content
        "palette    level"      1fr
        "palette    status"     min-content
        / min-content 1fr
    ;
    gap: 0.5em;

    min-height: 0;
    margin: auto 1em;
}

#editor .editor-canvas {
    grid-area: level;
    overflow: auto;
    position: relative;
    background: #101010;
    border: 0.125em solid black;
}
#editor .editor-canvas::before {
    /* Clever abuse of sticky positioning to draw a box shadow on top of the container and inside
     * the scrollbars (thanks, leafo!) */
    content: '';
    display: block;
    position: sticky;
    top: 0;
    left: 0;
    height: 100%;
    box-shadow: inset 0 0 0.5em black;
    z-index: 1;
    pointer-events: none;
}
#editor .editor-canvas.--crispy {
    image-rendering: crisp-edges;
    image-rendering: pixelated;
}
#editor .editor-canvas .-container {
    /* The shadow overlay is sticky-positioned, meaning it defaults to being in-flow, so this
     * container needs to compensate by absolutely positioning itself back up top.  It'll still
     * create scrollbars, so this shouldn't cause any issues. */
    position: absolute;
    top: 0;
    margin: auto;
    /* Give the canvas/overlay a bit of a margin; it has to be a border because, due to some quirk
     * of overflowing flexboxes I guess, padding and margins won't extend the scroll area on the
     * right and bottom.  It's 75vmin because that /ROUGHLY/ allows panning the level to the edge of
     * the viewport but not completely off of it. */
    /* NOTE: This MUST be large enough to guarantee being bigger than the viewport; we aren't in a
     * flex container, so if the canvas + border aren't sufficiently tall, we won't even fill the
     * viewport and the canvas will be off-center.  Also, with no border at all, a mysterious
     * interaction with the sticky box-shadow causes the top of the canvas to go off the top of the
     * viewport entirely!  So, twiddle this with care. */
    /* TODO probably a better way to measure this; i really want relative to parent size, but
     * percentage padding and margins are specifically relative to our width */
    border: 50vmax solid transparent;
    /* This is necessary to force us to be as wide as the canvas; without it, we have an auto width,
     * and there's technically no space left after our border, so we become zero width and the
     * canvas is entirely overflow, which fucks up positioning of the SVG overlay */
    width: -moz-fit-content;
    width: fit-content;
}
#editor .editor-canvas canvas {
    display: block;
    width: calc(var(--viewport-width) * var(--tile-width) * var(--scale));
    --viewport-width: 9;
    --viewport-height: 9;
    --scale: 1;
}
/* SVG overlays */
svg.level-editor-overlay {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    /* allow clicks to go through us! */
    pointer-events: none;

    /* default svg properties */
    stroke-width: 0.0625;
    fill: none;
}
svg.level-editor-overlay .overlay-transient {
    display: none;
}
svg.level-editor-overlay .overlay-transient.--visible {
    display: initial;
}
svg.level-editor-overlay rect.overlay-cursor {
    x-stroke: hsla(220, 100%, 60%, 0.5);
    fill: hsla(220, 100%, 75%, 0.25);
}
svg.level-editor-overlay rect.overlay-pending-selection {
    stroke: hsla(220, 100%, 60%, 0.5);
    fill: hsla(220, 100%, 75%, 0.25);
}
svg.level-editor-overlay rect.overlay-selection {
    stroke: #000c;
    fill: hsla(220, 0%, 75%, 0.25);
    stroke-dasharray: 0.125, 0.125;
    animation: marching-ants 1s linear infinite;
    pointer-events: auto;
    cursor: move;
}
@keyframes marching-ants {
    0% {
        stroke-dashoffset: 0.25;
    }
    100% {
        stroke-dashoffset: 0;
    }
}
#overlay-arrowhead {
    fill: white;
}
svg.level-editor-overlay g.overlay-connection {
    stroke: white;
    filter: url(#overlay-filter-outline);
}
svg.level-editor-overlay g.overlay-connection line.-arrow {
    marker-end: url(#overlay-arrowhead);
}
svg.level-editor-overlay rect.overlay-camera {
    stroke: #808080;
    fill: #80808040;
    pointer-events: auto;
}
svg.level-editor-overlay text {
    /* Each cell is one "pixel", so text needs to be real small */
    font-size: 1px;
}
svg.level-editor-overlay text.overlay-edit-tip {
    stroke: none;
    fill: black;
}

.editor-big-tooltip {
    /* shared between toolbar and palette tooltips */
    opacity: 0;
    visibility: hidden;
    z-index: 1;
    position: absolute;
    padding: 0.33em 0.75em;
    pointer-events: none;

    transition-property: margin, opacity, visibility;
    transition-timing-function: ease-out;
    transition-duration: 0.125s, 0.125s, 0s;
    transition-delay: 0s, 0s, 0.125s;

    border: 1px solid black;
    white-space: pre-wrap;
    line-height: 1.5;
    text-transform: none;
    text-align: left;
    color: #d8d8d8;
    background: hsl(220, 10%, 20%);
    box-shadow: 0 1px 2px 1px #0004;
}
.editor-big-tooltip h3 {
    font-size: 1.25em;
    margin-bottom: 0.25rem;
    border-bottom: 1px solid currentColor;
    color: white;
}
#editor .controls {
    /* TODO with the hint area gone i don't think this needs to be a grid?  could just flex */
    grid-area: controls;
    display: grid;
    grid:
        "tile   toolbar    layer   direction   .       menu" auto
        / auto auto auto auto 1fr auto
    ;
    align-items: center;
    column-gap: 1em;
}
#editor .controls .editor-tile-controls {
    grid-area: tile;
    display: flex;
    align-items: center;
    gap: 0.25em;
}
#editor .controls #editor-tile canvas {
    display: block;
}
#editor .controls #editor-toolbar {
    grid-area: toolbar;
}
#editor-toolbar .-help {
    width: max-content;
    margin-top: -0.25em;
    margin-left: -0.5em;
}
#editor-toolbar button:hover .-help {
    opacity: 1;
    z-index: 2;  /* show above any that are in mid-fade */
    visibility: visible;
    margin-top: 0.25em;
    transition-delay: 0.5s;
    transition-timing-function: ease-in;
}
#editor .controls .-buttons {
    grid-area: menu;
}
.icon-button-set {
    display: flex;
    flex-wrap: wrap;
}
.icon-button-set button {
    width: auto;
    height: auto;
    padding: 0;
    margin: 0;
    line-height: 1;
    background: url(icons/tool-bg-unselected.png) no-repeat;
    border: none;
    border-radius: 0;
    box-shadow: none;
}
.icon-button-set button.-selected {
    background-image: url(icons/tool-bg-selected.png);
}
.icon-button-set button img {
    display: block;
}

#editor .palette {
    isolation: isolate;
    grid-area: palette;
    width: calc(32px * 8 + 4px * 9);
    padding-right: 1em;  /* make room for scrollbar so we don't get a horizontal one */
    /* TODO when there IS a scrollbar, the h2s are slightly narrower.  think this is an
     * unexpected consequence of using min-content, which i guess does not take the
     * possibility of a scrollbar into account */
    overflow-y: auto;
}
#editor .palette h2 {
    font-size: 1em;
    margin-top: 1em;
    border-bottom: 1px solid currentColor;
    color: #909090;
}
#editor .palette h2:first-child {
    margin-top: 0;
}
#editor .palette section {
    display: grid;
    margin: 0.33em 4px;  /* matches gap */
    grid: auto-flow 32px / repeat(8, 32px);
    gap: 4px;
}
.palette-entry {
}
.palette-entry:hover {
    box-shadow: 0 0 0 1px black, 0 0 0 3px hsl(220, 100%, 75%);
}
.palette-entry.--selected {
    z-index: 1;
    box-shadow: 0 0 0 1px black, 0 0 0 3px white;
}
.editor-palette-tooltip {
    width: 20em;

    /* Don't immediately hide me when mousing between entries */
    /* FIXME if it's in mid-fade and you mouse over an entry again, it stays frozen in mid-fade
     * for 0.5s  :( */
    transition-delay: 0.5s, 0.5s, 0.625s;
}
.editor-palette-tooltip.--visible {
    opacity: 1;
    z-index: 2;  /* show above any that are in mid-fade */
    visibility: visible;
    margin-left: 1em;
    transition-delay: 0.5s;
    transition-timing-function: ease-in;
}
#editor #editor-statusbar {
    grid-area: status;
    display: flex;
    align-items: center;
    gap: 0.5em;
    /* Try very hard to minimize reflow, since this is updated frequently */
    height: 1.25em;
    line-height: 1.25;
    overflow: hidden;
}
#editor #editor-statusbar > .-zoom {
    display: flex;
    align-items: center;
    gap: 0.25em;
    width: 11.5em;
}
#editor #editor-statusbar > .-zoom > input {
    width: 6em;
    margin: 0;
}
#editor #editor-statusbar > .-zoom > output {
    width: 4em;
}
#editor #editor-statusbar > .-cursor {
    width: 5em;
}

.editor-level-browser {
    display: grid;
    grid: auto-flow auto / repeat(auto-fill, minmax(13em, 1fr));  /* 12em preview width + padding */
    gap: 0.5em;
    width: 70vw;
    /* seems to go into the parent's right padding fsr, i guess because the scrollbar is there */
    margin-right: 1em;
    list-style: none;
}
.editor-level-browser li {
    display: grid;
    grid:
        "preview    preview"
        "number     title"
        / min-content 1fr
    ;
    gap: 0.25em;
    padding: 0.5em;
}
.editor-level-browser li.--selected {
    background: var(--generic-bg-selected-on-white);
    outline: 1px solid var(--generic-border-selected-on-white);
}
.editor-level-browser li:hover {
    background: var(--generic-bg-hover-on-white);
}
.editor-level-browser li > .-preview {
    grid-area: preview;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 12em;
    height: 12em;
    margin: auto;
}
.editor-level-browser li > .-preview:empty::before {
    content: '···';
    display: block;
    font-size: 5em;
    color: #c0c0c0;
}
.editor-level-browser li > .-preview canvas {
    display: block;
    max-width: 100%;
    max-height: 100%;
}
.editor-level-browser li > .-number {
    grid-area: number;
    font-size: 2em;
}
.editor-level-browser li > .-title {
    grid-area: title;
    align-self: center;
}

/* Mini editors for specific tiles with complex properties */
/* FIXME should this stuff be on an overlay container class? */
form.editor-popup-tile-editor {
    position: relative;
    padding: 0.5em;
    color: black;
    background: white;
    border: 1px solid black;
    box-shadow: 0 2px 4px #0004;

    margin-top: 1em;

    --chevron-offset: 0px;
}
form.editor-popup-tile-editor.--above {
    margin-top: -1em;
}
form.editor-popup-tile-editor h3 {
    border-bottom: 1px dotted #606060;
}
form.editor-popup-tile-editor * + h3 {
    margin-top: 0.25em;
}
/* Use ::before for a chevron pointing at the tile in question */
form.editor-popup-tile-editor::before {
    content: '';
    display: block;
    position: absolute;
    border: 1em solid transparent;
    left: var(--chevron-offset);
    margin-left: -1em;

    top: -1em;
    border-top: none;
    border-bottom-color: white;
    filter: drop-shadow(0 -1px 0 black);
}
form.editor-popup-tile-editor.--above::before {
    top: auto;
    bottom: -1em;
    border: 1em solid transparent;
    border-bottom: none;
    border-top-color: white;
    filter: drop-shadow(0 1px 0 black);
}
/* Letter floor tiles, which let you pick the character to use; show them as a grid.  Note the
 * characters are preceded by radio buttons, which we hide for simplicity */
ol.editor-letter-tile-picker {
    display: grid;
    grid: auto-flow 1.5em / repeat(17, 1.5em);
    text-align: center;
    font-family: monospace;
}
ol.editor-letter-tile-picker label,
ol.editor-letter-tile-picker .-glyph {
    display: block;
    height: 100%;
}
ol.editor-letter-tile-picker input[type=radio] {
    display: none;
}
ol.editor-letter-tile-picker input[type=radio]:checked + .-glyph {
    background: hsl(220, 75%, 90%);
    outline: 2px solid hsl(220, 75%, 80%);
}
/* Hint tiles accept prose */
textarea.editor-hint-tile-text {
    font-size: 1.5em;
    width: 20vw;
    height: 20vh;
    min-width: 15rem;
    min-height: 5rem;
    border: none;
    font-family: serif;
}
/* Class for a list that uses hidden inputs with svg icons, shared by directional blocks and
 * railroad tracks */
.editor-tile-editor-svg-parts input {
    display: none;
}
.editor-tile-editor-svg-parts svg {
    display: block;
    width: 3em;
    fill: none;
    stroke: #c0c0c0;
    stroke-width: 2;
}
.editor-tile-editor-svg-parts input:checked + svg {
    stroke: hsl(220, 90%, 50%);
}
/* Directional blocks have arrows */
ol.editor-directional-block-tile-arrows {
    display: grid;
    grid: auto-flow 3em / repeat(3, 3em);
}
/* Railroad tracks are...  complicated */
ul.editor-railroad-tile-tracks {
    display: grid;
    grid: auto-flow 3em / repeat(3, 3em);
    gap: 0.25em;
}
ul.editor-railroad-tile-tracks.--switch input:checked + svg {
    stroke: hsl(15, 90%, 50%);
}
