/*
  The whole site's styling. Bootstrap does the grid, the buttons and the dropdowns; this is the
  part that makes it look like an archive of a 1993 city builder rather than a bootstrap page.

  It is one file because there is not much of it. What belongs to one component — a card, a
  facet button — lives in that component's own .razor.css, which Blazor scopes to it.
*/

:root {
    --sc-sky: #1b2a3a;
    --sc-header: rgba(18, 27, 38, 0.85);
    --sc-panel: #ffffff;
    --sc-ink: #1d2733;
    --sc-muted: #5d6b7a;
    --sc-accent: #d98324;
    --sc-accent-dark: #a75f12;
    --sc-line: #d7dee5;

    /*
      The colour the game puts behind a city, and the one the thumbnailer is told to fill the
      transparent sky and the letterbox bars with — `Archive.Backdrop`, which is where it is
      decided. It is repeated here because a stylesheet cannot read a constant, and it has to
      match: this is what shows in a picture's box while the proxy is still fetching, and a light
      grey that turns dark brown a moment later is a flash on every card on the page.
    */
    --sc-backdrop: #371700;
}

html, body {
    min-height: 100%;
}

body {
    color: var(--sc-ink);
    background: var(--sc-sky);
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    scroll-behavior: smooth;
}

/*
  A city behind every page, drifting slowly as if someone were scrolling round it.

  The picture is city b83992d9's rotation 3, cropped to the largest 2:1 rectangle that sits inside
  the map's diamond with a margin to spare, so every part of it is city: there is no edge of the
  map anywhere in the file to pan to.

  It is drawn bigger than the window in both directions — at least 130% of the width, and twice
  130% of the height since the picture is 2:1 — and the drift only ever moves it by some fraction
  of how much bigger, so the window never gets past its edge whatever shape it is. It moves by
  transform so the browser can hand it to the compositor rather than repainting the whole
  background every frame.
*/
.city-backdrop {
    position: fixed;
    inset: 0;
    z-index: -1;
    overflow: hidden;
    pointer-events: none;

    --backdrop-width: max(1920px, 130vw, 260vh);
    --backdrop-height: calc(var(--backdrop-width) / 2);
    --spare-x: calc(100vw - var(--backdrop-width));
    --spare-y: calc(100vh - var(--backdrop-height));

    /*
      Which way the drift goes. backdrop.js picks these at random on load: --flip-x and --flip-y
      are 0 or 1 and mirror the route across and down, so a stop at 0.15 becomes one at 0.85, which
      is still inside the spare room. The delay starts it partway round, and the direction decides
      whether it runs the route forwards or backwards. Without the script it is the route as
      written, from the start.
    */
    --flip-x: 0;
    --flip-y: 0;
    --drift-delay: 0s;
    --drift-direction: alternate;
    --from-x: calc(var(--spare-x) * var(--flip-x));
    --from-y: calc(var(--spare-y) * var(--flip-y));
    --step-x: calc(var(--spare-x) * (1 - 2 * var(--flip-x)));
    --step-y: calc(var(--spare-y) * (1 - 2 * var(--flip-y)));
}

.city-backdrop::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: var(--backdrop-width);
    height: var(--backdrop-height);
    background: url("images/city-background.webp") 0 0 / 100% 100% no-repeat;

    /*
      The drift is switched off for now, and the city sits still in the middle of the room it would
      drift over. To turn it back on, replace these two lines with
          animation: city-drift 480s ease-in-out var(--drift-delay) infinite var(--drift-direction);
          will-change: transform;
      and load js/backdrop.js again from App.razor, after <Routes/>.
    */
    animation: none;
    transform: translate(calc(var(--spare-x) * 0.5), calc(var(--spare-y) * 0.5));
}

/* Darkened so the header and footer text on top of it stays readable. */
.city-backdrop::after {
    content: "";
    position: absolute;
    inset: 0;
    background: rgba(10, 16, 24, 0.45);
}

/* Each stop is a fraction of the spare room in each direction: 0 is one edge, 1 the other. */
@keyframes city-drift {
    0% { transform: translate(calc(var(--from-x) + var(--step-x) * 0.15), calc(var(--from-y) + var(--step-y) * 0.2)); }
    20% { transform: translate(calc(var(--from-x) + var(--step-x) * 0.55), calc(var(--from-y) + var(--step-y) * 0.05)); }
    40% { transform: translate(calc(var(--from-x) + var(--step-x) * 0.95), calc(var(--from-y) + var(--step-y) * 0.45)); }
    60% { transform: translate(calc(var(--from-x) + var(--step-x) * 0.7), calc(var(--from-y) + var(--step-y) * 0.95)); }
    80% { transform: translate(calc(var(--from-x) + var(--step-x) * 0.25), calc(var(--from-y) + var(--step-y) * 0.8)); }
    100% { transform: translate(calc(var(--from-x) + var(--step-x) * 0.05), calc(var(--from-y) + var(--step-y) * 0.5)); }
}

@media (prefers-reduced-motion: reduce) {
    .city-backdrop::before {
        animation: none;
        transform: translate(calc(var(--spare-x) * 0.5), calc(var(--spare-y) * 0.5));
    }
}

a {
    color: var(--sc-accent-dark);
}

a:hover {
    color: var(--sc-accent);
}

/* ---- The bar across the top ---------------------------------------------------------- */

.site-header {
    padding: 0.75rem 0;
    background: var(--sc-header);
    border-bottom: 3px solid var(--sc-accent);
}

/* A paragraph and not a heading, so the weight and leading a heading would have brought are set. */
.site-title {
    margin: 0;
    font-size: 1.4rem;
    font-weight: 500;
    line-height: 1.2;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.site-title a {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    color: #fff;
    text-decoration: none;
}

/* The fusion plant, sprite 1206, at half its 128 by 69. */
.site-title img {
    width: 64px;
    height: auto;
}

/* Beside the title on the right from md up; the full width of its own row on a phone. */
@media (min-width: 768px) {
    .header-search {
        width: 18rem;
    }
}

/* ---- The white sheet everything sits on ---------------------------------------------- */

main.container {
    margin-top: 1.5rem;
    margin-bottom: 1.5rem;
    padding: 1.5rem;
    background: var(--sc-panel);
    border-radius: 4px;
    box-shadow: 0 2px 16px rgba(0, 0, 0, 0.3);
}

h1 {
    font-size: 1.9rem;
}

h1:focus {
    outline: none;
}

/* ---- The front page ------------------------------------------------------------------ */

.home-hero {
    padding: 1rem 0 2rem;
}

.home-hero h1 {
    font-size: 2.6rem;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.home-search {
    max-width: 34rem;
    margin: 1.5rem auto 0;
}

.category-links a {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    height: 100%;
    padding: 0.4rem 0.75rem 0.4rem 0.5rem;
    background: #f3f6f9;
    border: 1px solid var(--sc-line);
    border-radius: 3px;
    color: var(--sc-ink);
    text-decoration: none;
}

/*
  A 50-pixel square, every link the same, so the grid stays even. The icons run from 22 pixels to
  50 and the big ones were resized to fit beforehand (Docs/category-icons.md), so each is drawn at
  its own size. scale-down rather than contain all the same, so a small one is never stretched to
  a blur and a big one added later still fits. Centred, so a short one sits level with its label
  rather than dropping to the bottom of the square.
*/
.category-links img {
    flex: none;
    width: 50px;
    height: 50px;
    object-fit: scale-down;
    object-position: center;
}

.category-links a:hover {
    background: var(--sc-accent);
    border-color: var(--sc-accent-dark);
    color: #fff;
}

/* ---- Results ------------------------------------------------------------------------- */

.results-heading {
    margin-bottom: 1rem;
}

.facets {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

/*
  **The menu is as wide as its longest value.** Bootstrap pins a dropdown at min-width 10rem and
  never grows it, which is fine for "Fire" and not for "Pollution Accident" — and the facet values
  are enum names, so how long the longest one is depends on what the archive happens to contain
  rather than on anything decided here. max-content sizes to whatever turned up; the min-width
  keeps a menu of short values from looking mean, and the max-width stops a long one running off a
  phone.
*/
.facets .dropdown-menu {
    width: max-content;
    min-width: 10rem;
    max-width: min(22rem, 90vw);
}

/*
  **The count is a flex item, not a float.** As a float it is a block box that drops onto a line of
  its own the moment the label beside it gets too wide — which is the same failure as wrapping, and
  it arrives at whatever width the reader's font happens to make "Pollution Accident". Flex has
  nowhere else to put it: label and count share the line or the line does not exist.
*/
.facets .dropdown-item {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1.5rem;
}

@media (max-width: 575px) {
    /*
      Seven page numbers with three digits each, and the arrows either side, come to more than a
      phone is wide at Bootstrap's padding. Tighter links keep the row on the screen rather
      than showing fewer of them.
    */
    .pagination .page-link {
        padding-inline: 0.5rem;
    }
}

/* ---- One city ------------------------------------------------------------------------ */

.city-picture img,
.city-picture-missing {
    width: 100%;
    height: auto;
    background: var(--sc-backdrop);
    border: 1px solid var(--sc-line);
    border-radius: 3px;
}

/*
  The two buttons under the picture that step through the city's renders. Centred under it, and
  nothing else on the row — which is why the row can be a plain centred flex box.
*/
.city-rotations {
    display: flex;
    justify-content: center;
    gap: 0.75rem;
    margin: 0.75rem 0;
}

/*
  The two buttons are artwork rather than a bordered box with an icon in it — the bevel is drawn,
  so there is no border, no padding and no bootstrap button class on them.

  **One image holding all four states, offset into.** The held-down states are the point: as two
  separate files the browser would not fetch the pressed one until the first press, so the first
  press of the session would flash — the button vanishing for as long as the request took, at
  exactly the moment the user is looking at it. One file means every state is there before any of
  them is needed.

    turn-buttons.png, 70x54, four 35x27 tiles:

        col 0 (back)   col 1 (forward)
  row 0  resting        resting
  row 1  held           held

  Drawn at the size they were made: a tile is 35x27 on screen and the sheet is its own 70x54, so
  every offset below is a whole tile and no pixel is ever resampled. This is the one scale at
  which the bevel is exactly what the artist drew.
*/
.turn-button {
    display: block;
    width: 35px;
    height: 27px;
    padding: 0;
    border: 0;
    border-radius: 0;
    background-image: url('/images/turn-buttons.png');
    background-repeat: no-repeat;
    background-size: 70px 54px;

    /*
      Does nothing at 100%, since nothing is being scaled — it is here for the reader who zooms,
      where smoothing would turn the bevel to mud.
    */
    image-rendering: pixelated;
}

.turn-back {
    background-position: 0 0;
}

.turn-forward {
    background-position: -35px 0;
}

.turn-back:active {
    background-position: 0 -27px;
}

.turn-forward:active {
    background-position: -35px -27px;
}

/*
  These have no text and no border, so the focus ring is the only thing telling a keyboard user
  where they are. Offset outwards so it doesn't sit on top of the drawn bevel.
*/
.turn-button:focus-visible {
    outline: 2px solid var(--sc-accent);
    outline-offset: 3px;
}

.city-id {
    font-size: 0.75rem;
    overflow-wrap: anywhere;
}

.facts dt {
    font-weight: 600;
}

.facts dd {
    margin-bottom: 0.35rem;
}

.scenario {
    margin-top: 2rem;
    padding: 1rem 1.25rem;
    background: #fdf6ea;
    border-left: 4px solid var(--sc-accent);
}

.scenario-headline {
    font-size: 1.2rem;
    font-style: italic;
}

/*
  The newspaper illustration. It is 63x63 of the game's own pixel art, which is a postage stamp at
  its own size, so it is blown up to three times that — and `pixelated` is what keeps it crisp:
  the browser's default smoothing turns sixty-three pixels of deliberate art into a smear.
  Floated, because the briefing beside it is the point and the picture is the decoration.
*/
.scenario-picture {
    display: block;
    float: right;
    margin: 0 0 0.75rem 1rem;
}

.scenario-picture img {
    width: 189px;
    height: 189px;
    image-rendering: pixelated;
    background: #fff;
    border: 1px solid var(--sc-line);
}

@media (max-width: 575px) {
    /* Too narrow to read a briefing wrapped around it. */
    .scenario-picture {
        float: none;
        margin: 0 0 0.75rem;
    }
}

.download-box {
    margin-bottom: 1.5rem;
    padding: 1rem;
    background: #f3f6f9;
    border: 1px solid var(--sc-line);
    border-radius: 3px;
    text-align: center;
}

/* ---- Downloading --------------------------------------------------------------------- */

/*
  The download dialog's tiles: one per file the city can be had as (Models/Downloads.cs), each a
  game icon on a grey bevelled button like the ones in the game's own toolbar. The bevel is two
  inset shadows rather than borders, so pressing one only has to swap them round.
*/
/*
  One row: there are never more than three tiles (a save, its terrain and the Mac archive), and
  three fit on a phone at 5.5rem. A wrapping row would put the third on a line of its own first.
*/
.download-tiles {
    display: flex;
    justify-content: center;
    gap: 0.75rem;
}

.download-tile {
    flex: 0 1 8rem;
    min-width: 5.5rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
    padding: 0.75rem 0.5rem 0.6rem;
    background: #c3c7cb;
    box-shadow: inset 2px 2px 0 #fff, inset -2px -2px 0 #7b7f83;
    border: 1px solid #000;
    color: #000;
    text-align: center;
    text-decoration: none;
}

/*
  Every icon in the same 64-pixel square, so the labels line up. The 32-pixel ones are doubled, and
  pixelated so they stay pixel art rather than a blur.
*/
.download-tile img {
    width: 64px;
    height: 64px;
    object-fit: contain;
    image-rendering: pixelated;
}

/* OpenSC2K's icon is painted rather than pixel art, and 128 pixels: shrunk smoothly, it stays sharp. */
.download-sc2x img {
    image-rendering: auto;
}

.download-label {
    font-weight: bold;
    line-height: 1.2;
}

.download-file {
    font-size: 0.75rem;
    overflow-wrap: anywhere;
    color: #333;
}

.download-tile:hover,
.download-tile:focus-visible {
    background: var(--sc-accent);
    color: #fff;
}

.download-tile:hover .download-file,
.download-tile:focus-visible .download-file {
    color: #fff;
}

.download-tile:active,
.download-tile.chosen {
    box-shadow: inset 2px 2px 0 #7b7f83, inset -2px -2px 0 #fff;
}

/* While one is coming in, the others wait. */
.download-tiles.busy .download-tile:not(.chosen) {
    opacity: 0.5;
    pointer-events: none;
}

.download-progress {
    margin-top: 1rem;
}

.download-progress .progress {
    height: 1.25rem;
}

.download-progress .progress-bar {
    background-color: var(--sc-accent);
}

/* ---- Nothing there ------------------------------------------------------------------- */

.error-page {
    padding: 3rem 0;
}

.error-page h1 {
    font-size: 5rem;
    color: var(--sc-muted);
}

/* An empty search is still a 400, but the page is a question rather than a number. */
.error-page h1.search-prompt {
    font-size: 2rem;
    color: inherit;
}

/* ---- The bit underneath -------------------------------------------------------------- */

.site-footer {
    padding: 1.5rem 0 3rem;
    color: #cfdae4;
    font-size: 0.9rem;
}

.site-footer a {
    color: #fff;
}

.footer-links a + a {
    margin-left: 1rem;
}

/* ---- Blazor's own error strip -------------------------------------------------------- */

#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1000;
    padding: 0.8rem 1.4rem;
    background: #b32121;
    color: #fff;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
}

#blazor-error-ui a {
    color: #fff;
    text-decoration: underline;
}

#blazor-error-ui .dismiss {
    position: absolute;
    top: 0.6rem;
    right: 0.9rem;
    cursor: pointer;
}
