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(225, 5%, 5%); background-image: url(background.svg); background-size: 12em; color: #ececec; --panel-bg-color: hsl(225, 10%, 15%); --button-bg-color: hsl(225, 10%, 25%); --button-bg-hover-color: hsl(225, 15%, 30%); --generic-bg-hover-on-white: hsl(225, 60%, 90%); --generic-bg-selected-on-white: hsl(225, 60%, 85%); --generic-border-selected-on-white: hsl(225, 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: var(--button-bg-color); border: 1px solid hsl(225, 10%, 10%); box-shadow: inset 0 0 0 1px hsl(225, 10%, 33%), 0 1px 1px hsl(225, 10%, 10%); border-radius: 0.25em; text-transform: lowercase; cursor: pointer; } button:hover, .radio-faux-button-set > label:hover > input + span { background: 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:disabled { color: #606060; background: #202020; box-shadow: inset 0 0 2px 1px hsl(225, 0%, 10%), 0 1px 0 hsl(225, 10%, 10%); cursor: auto; } button.button-big { display: block; width: 100%; margin: 0.5em 0; padding: 1em; } button.--button-glow-ok { background: hsl(225, 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(225, 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-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; } .radio-faux-button-set > label > input:checked + span { background: hsl(225, 80%, 50%); box-shadow: inset 0 0 1px 1px hsl(225, 50%, 40%), inset 0 -0.125em 0.5em 0.25em hsl(225, 50%, 30%), 0 1px 1px hsl(225, 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; } .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(225, 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(225, 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(225, 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
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(225, 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(225, 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(225, 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; } /* 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%; } } /**************************************************************************************************/ /* 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 { font-size: 0.75em; 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; } @media (max-width: 800px) { body > header { padding: 0.125em 0.25em; } /* All these headings are way too big on phones */ body > header h1 { font-size: 1.25em; } 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; } } /**************************************************************************************************/ /* 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 > li > button:enabled { background: hsl(225, 30%, 25%); box-shadow: inset 0 0 0 1px hsl(225, 30%, 33%), 0 1px 1px hsl(225, 10%, 10%); } .played-pack-list > li > button:enabled:hover { background: hsl(225, 40%, 30%); } .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(225, 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(225, 25%, 30%); } .played-pack-list .-progress > .-levels::after { width: calc(var(--aidless) * 100%); background: hsl(225, 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(210, 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 .-optional-label { display: none; } #player-controls button { padding: 0.25em 0.5em; line-height: 1.33; } /* Hackily remove the
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" "music" ; } #player-controls, #player-actions { justify-content: center; } #player-controls button, #player-actions button { flex: auto; } #player .keyhint { /* Hide key hints; there's nowhere to put them and they take up surprisingly a lot of space */ display: none; } } @media (max-width: 800px) { #splash-fullscreen { display: revert; } #player-music { font-size: 0.875em; } } #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(225, 10%, 20%); box-shadow: 0 0.25em 1em black; } .level { grid-area: level; position: relative; outline: 2px solid black; } .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-template-rows: 2fr 6fr 2fr 1fr; justify-content: stretch; align-items: center; /* Prevent blowout; force using the canvas's height */ height: 0; min-height: 100%; box-sizing: border-box; z-index: 2; font-size: calc(0.5 * var(--tile-width) * var(--scale)); background: #0009; color: white; text-align: center; text-shadow: 0 2px 1px black; } #player .overlay-message > * { padding: 0 5%; } /* Allow clicking through the overlay in debug mode */ body.--debug .overlay-message { pointer-events: none; } #player .overlay-message p { margin: 0; } #player .overlay-message .-top { font-size: 1.5em; } #player .overlay-message .-middle { } #player .overlay-message .-bottom { } #player .overlay-message .-keyhint { align-self: end; font-size: 0.5em; color: #c0c0c0; } #player .overlay-message[data-reason=""] { display: none; } #player .overlay-message[data-reason=failure] { box-shadow: inset 0 0 calc(4 * var(--tile-width)) var(--tile-width) black; } #player .overlay-message[data-reason=success] { background: hsla(225, 50%, 25%, 0.5); box-shadow: inset 0 0 calc(4 * var(--tile-width)) hsl(225, 50%, 25%); } #player .overlay-message[data-reason=ended] { /* Shove the middle + bottom parts down so they don't overlay the busiest part of the ending image */ grid-template-rows: 8fr 4fr 2fr 0; overflow: hidden; x-color: black; background: url(ending.png) no-repeat center center / cover; box-shadow: inset 0 0 calc(4 * var(--tile-width)) hsl(225, 50%, 25%); x-text-shadow: 0 0 2px white, 0 2px 2px white; } #player .overlay-message[data-reason=ended] .-middle { background: #0009; } #player .overlay-message[data-reason=ended] .-bottom { font-size: 1.5em; background: #0006; } @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); } } dl.score-chart { display: grid; grid-auto-columns: 1fr 1fr; grid-auto-rows: 1.33em; margin: auto 10%; font-weight: normal; } dl.score-chart dt { grid-column: 1; text-align: left; } dl.score-chart dd { grid-column: 2; margin: 0; text-align: right; } dl.score-chart .-component { color: #d8d8d8; } dl.score-chart .-sum { border-top: 1px solid white; } dl.score-chart .-total { color: hsl(40, 75%, 80%); } dl.score-chart .-star { position: absolute; } .chips { grid-area: chips; } .time { grid-area: time; } .bonus { grid-area: bonus; } .chips, .time, .bonus { font-size: calc(var(--tile-height) * var(--scale) / 3); display: flex; align-items: center; gap: 0.5em; } .chips h3, .time h3, .bonus h3 { flex: 0; order: 2; font-size: 1.5em; line-height: 1; color: hsl(225, 20%, 80%); } .chips output, .time output, .bonus output { flex: 1; font-size: 2em; min-width: 2em; min-height: 1em; line-height: 1; text-align: right; font-family: monospace; color: hsl(225, 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 { color: hsl(225, 10%, 30%); } .bonus output { color: #e2c9ff; } #player .bonus { visibility: hidden; display: flex; } #player.--bonus-visible .bonus { visibility: initial; } .player-rules { font-size: calc(var(--tile-height) * var(--scale) / 4); grid-area: rules; align-self: end; color: hsl(225, 20%, 80%); } .player-rules p { display: none; margin: 0.25em 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(225, 20%, 80%); background: url(#svg-icon-hint) hsl(225, 10%, 10%); border: 3px double hsl(225, 10%, 20%); } #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; } #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: "level level level" "inventory rules chips" calc((var(--tile-height) * var(--scale) * (2 - 1/6)) / 3) "inventory rules time" calc((var(--tile-height) * var(--scale) * (2 - 1/6)) / 3) "inventory rules bonus" calc((var(--tile-height) * var(--scale) * (2 - 1/6)) / 3) / min-content min-content 1fr ; row-gap: calc(var(--tile-height) * var(--scale) / 6); } #player .inventory { /* stick me in the center right */ place-self: center end; } #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.25em 0.33em; line-height: 1.33; } .player-rules { align-self: center; } .player-rules p span { /* There's only room for the icons, since there's no dedicated hint space */ display: none; } } /* 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(225, 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 / min-content 1fr ; gap: 0.5em; min-height: 0; margin: auto 1em; } #editor .editor-canvas { grid-area: level; overflow: auto; position: relative; /* Flex layout to center the canvas and overlay together */ display: flex; /* This isn't supported in Chrome (still!!), so use auto margins on the child instead align-items: safe center; justify-content: safe center; */ background: #101010; border: 0.125em solid black; /* FIXME would love if this were above the canvas too, but can't figure out how to do that * easily in a scrolling element */ box-shadow: inset 0 0 3px black; } #editor .editor-canvas .-container { position: relative; 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 */ /* TODO padding should be half a cell? */ border: 2em solid transparent; } #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 */ #editor 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; } #editor .level-editor-overlay .overlay-transient { display: none; } #editor .level-editor-overlay .overlay-transient.--visible { display: initial; } #editor .level-editor-overlay rect.overlay-cursor { x-stroke: hsla(225, 100%, 60%, 0.5); fill: hsla(225, 100%, 75%, 0.25); } #editor .level-editor-overlay rect.overlay-pending-selection { stroke: hsla(225, 100%, 60%, 0.5); fill: hsla(225, 100%, 75%, 0.25); } #editor .level-editor-overlay rect.overlay-selection { stroke: #000c; fill: hsla(225, 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; } } #editor .level-editor-overlay rect.overlay-cxn { stroke: red; } #editor .level-editor-overlay line.overlay-cxn { stroke: red; } #editor .level-editor-overlay rect.overlay-camera { stroke: #808080; fill: #80808040; pointer-events: auto; } #editor .level-editor-overlay text { /* Each cell is one "pixel", so text needs to be real small */ font-size: 1px; } #editor .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(225, 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(225, 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-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(225, 75%, 90%); outline: 2px solid hsl(225, 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(225, 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%); }