/* ==========================================================================
   DRUG WARS CLASSIC ONLINE
   A premium DOS remake: CRT phosphor, scanlines, and a terminal that never was.

   Four themes are defined as custom-property sets on [data-theme]. Everything
   downstream reads the variables, so adding a fifth theme means adding one
   block and nothing else.
   ========================================================================== */

:root,
[data-theme="green"] {
    --bg:            #050b06;
    --bg-panel:      #08140a;
    --bg-raised:     #0c1d0f;
    --fg:            #4dfd7a;
    --fg-dim:        #2a9d4b;
    --fg-bright:     #b6ffc8;
    --accent:        #7dff9e;
    --border:        #1c5c2d;
    --glow:          rgba(77, 253, 122, 0.45);
    --good:          #7dff9e;
    --bad:           #ff6b5e;
    --warn:          #ffd45e;
    --market:        #5ee7ff;
    --legend:        #ff9ef7;
}

[data-theme="amber"] {
    --bg:            #0b0703;
    --bg-panel:      #150e04;
    --bg-raised:     #1f1506;
    --fg:            #ffb642;
    --fg-dim:        #a06f1c;
    --fg-bright:     #ffe0a8;
    --accent:        #ffcc70;
    --border:        #5c3c10;
    --glow:          rgba(255, 182, 66, 0.45);
    --good:          #ffd98a;
    --bad:           #ff6b5e;
    --warn:          #fff07a;
    --market:        #7fd4ff;
    --legend:        #ffa8e6;
}

[data-theme="blue"] {
    --bg:            #02060f;
    --bg-panel:      #050d1c;
    --bg-raised:     #081428;
    --fg:            #9db8ff;
    --fg-dim:        #5570b8;
    --fg-bright:     #dce6ff;
    --accent:        #7aa2ff;
    --border:        #1d3563;
    --glow:          rgba(157, 184, 255, 0.4);
    --good:          #86ffc0;
    --bad:           #ff7b7b;
    --warn:          #ffd45e;
    --market:        #6fe0ff;
    --legend:        #d6a8ff;
}

[data-theme="cyber"] {
    --bg:            #08030d;
    --bg-panel:      #120720;
    --bg-raised:     #1b0b2e;
    --fg:            #f070ff;
    --fg-dim:        #9a3fb5;
    --fg-bright:     #ffd6ff;
    --accent:        #58f7ff;
    --border:        #4a1866;
    --glow:          rgba(240, 112, 255, 0.45);
    --good:          #58ffc8;
    --bad:           #ff5b8a;
    --warn:          #ffe15e;
    --market:        #58f7ff;
    --legend:        #ffe45e;
}

/* --------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    background: #000;
    /* A pixel webfont can be dropped into wwwroot/fonts and added at the front of
       this stack; the strict CSP forbids loading one from a CDN, so the default is
       a hinted monospace that renders crisply at small sizes on every platform. */
    font-family: "Consolas", "DejaVu Sans Mono", "Courier New", ui-monospace, monospace;
    font-size: 16px;
}

body {
    margin: 0;
    min-height: 100vh;
    background: var(--bg);
    color: var(--fg);
    line-height: 1.45;
    letter-spacing: 0.02em;
    text-shadow: 0 0 6px var(--glow);
    overflow-x: hidden;
}

/* The CRT overlays. Both are pointer-events:none and sit above everything, so
   they are decoration only and can never swallow a click. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 9000;
    pointer-events: none;
    background: repeating-linear-gradient(
        to bottom,
        rgba(0, 0, 0, 0) 0px,
        rgba(0, 0, 0, 0) 2px,
        rgba(0, 0, 0, 0.28) 3px,
        rgba(0, 0, 0, 0.28) 4px
    );
}

body::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 9001;
    pointer-events: none;
    background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, 0.55) 100%);
    animation: flicker 5.5s infinite steps(60);
}

@keyframes flicker {
    0%, 96%, 100% { opacity: 1; }
    97%           { opacity: 0.88; }
    98%           { opacity: 1; }
    99%           { opacity: 0.93; }
}

/* Respect the platform setting. A permanent flicker is a genuine problem for
   photosensitivity and for anyone reading this for half an hour. */
@media (prefers-reduced-motion: reduce) {
    body::after,
    .cursor,
    .flash {
        animation: none !important;
    }
}

/* Turns off scanlines and flicker without losing the palette. */
body.plain::before,
body.plain::after {
    display: none;
}

a {
    color: var(--accent);
    text-decoration: none;
    border-bottom: 1px dotted var(--border);
}

a:hover,
a:focus-visible {
    color: var(--fg-bright);
    border-bottom-color: var(--fg-bright);
}

:focus-visible {
    outline: 2px solid var(--fg-bright);
    outline-offset: 2px;
}

h1, h2, h3 {
    margin: 0 0 0.6rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--fg-bright);
}

h1 { font-size: 1.5rem; }
h2 { font-size: 1.1rem; }
h3 { font-size: 0.95rem; }

/* --------------------------------------------------------------------------
   Layout
   -------------------------------------------------------------------------- */

.shell {
    max-width: 1180px;
    margin: 0 auto;
    padding: 1rem 1rem 3rem;
}

.topbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 0;
    border-bottom: 1px solid var(--border);
    margin-bottom: 1rem;
}

.topbar .brand {
    font-weight: bold;
    letter-spacing: 0.18em;
    color: var(--fg-bright);
    border: none;
}

.topbar nav {
    display: flex;
    gap: 0.9rem;
    flex-wrap: wrap;
}

.topbar .spacer {
    flex: 1 1 auto;
}

.panel {
    border: 1px solid var(--border);
    background: var(--bg-panel);
    padding: 0.85rem 1rem;
    margin-bottom: 1rem;
    position: relative;
}

/* The retro double border: a second inset rule, drawn with a pseudo-element so
   it costs no markup. */
.panel::after {
    content: "";
    position: absolute;
    inset: 3px;
    border: 1px solid var(--border);
    opacity: 0.5;
    pointer-events: none;
}

.panel > h2:first-child,
.panel > h3:first-child {
    margin-top: 0;
}

.grid {
    display: grid;
    gap: 1rem;
}

.grid.two   { grid-template-columns: 1fr 1fr; }
.grid.status { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

/* --------------------------------------------------------------------------
   Status readout
   -------------------------------------------------------------------------- */

.stat {
    border: 1px solid var(--border);
    background: var(--bg-raised);
    padding: 0.5rem 0.7rem;
}

.stat .label {
    display: block;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--fg-dim);
}

.stat .value {
    display: block;
    font-size: 1.15rem;
    color: var(--fg-bright);
    /* Digits must not jitter as they change; the readout updates every action. */
    font-variant-numeric: tabular-nums;
}

.value.good { color: var(--good); }
.value.bad  { color: var(--bad); }

/* The health and coat meters.

   display:block is load-bearing, not tidying. These are <span> elements, and
   height on a non-replaced INLINE element is ignored -- its box comes from
   line-height instead. So the declared 6px did nothing, the block-level fill
   inside resolved its height:100% against an indefinite parent, and the whole
   thing rendered 133px tall and hung 79px out of the bottom of the stat card.

   overflow:hidden keeps the fill inside the border at 0% and 100%. */
.bar {
    display: block;
    height: 8px;
    margin-top: 0.35rem;
    background: var(--bg);
    border: 1px solid var(--border);
    overflow: hidden;
}

.bar > span {
    display: block;
    height: 100%;
    background: var(--fg);
    transition: width 0.25s ease-out;
}

.bar.danger > span { background: var(--bad); }

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

table {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}

th, td {
    padding: 0.35rem 0.5rem;
    text-align: left;
    border-bottom: 1px solid var(--border);
}

th {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--fg-dim);
    white-space: nowrap;
}

td.num, th.num { text-align: right; }

tbody tr:hover { background: var(--bg-raised); }

.spike  { color: var(--warn); font-weight: bold; }
.crash  { color: var(--market); font-weight: bold; }
.muted  { color: var(--fg-dim); }

/* The nudge to set a nickname (_Layout.cshtml), shown only for a self-signup
   account with no nickname yet. Animated for the same reason the shared
   account page's password helper is (auth A-11): a static prompt beside
   other nav gets tuned out and never clicked. External file rather than an
   inline <style> or style="" attribute -- this app's CSP is style-src 'self'
   with nothing inline allowed. */
.change-name-pulse {
    border: 1px solid var(--accent, #6aa9ff);
    border-radius: 4px;
    padding: 1px 8px;
    animation: change-name-pulse 1.6s ease-in-out infinite;
}

@keyframes change-name-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.6; }
}

@media (prefers-reduced-motion: reduce) {
    .change-name-pulse { animation: none; }
}

.scroll-x {
    overflow-x: auto;
}

/* --------------------------------------------------------------------------
   Controls
   -------------------------------------------------------------------------- */

button,
.btn {
    font: inherit;
    color: var(--fg);
    background: var(--bg-raised);
    border: 1px solid var(--border);
    padding: 0.35rem 0.7rem;
    cursor: pointer;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.78rem;
    text-shadow: inherit;
    transition: background 0.12s, color 0.12s;
}

button:hover:not(:disabled),
.btn:hover {
    background: var(--fg-dim);
    color: var(--bg);
}

button:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}

button.primary {
    border-color: var(--fg);
    color: var(--fg-bright);
}

button.danger {
    border-color: var(--bad);
    color: var(--bad);
}

input[type="text"],
input[type="number"],
input[type="password"],
input[type="email"],
select {
    font: inherit;
    color: var(--fg-bright);
    background: var(--bg);
    border: 1px solid var(--border);
    padding: 0.3rem 0.45rem;
    width: 100%;
    font-variant-numeric: tabular-nums;
}

/* Spinners are useless at these magnitudes and steal horizontal space. */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

.field {
    margin-bottom: 0.8rem;
}

.field label {
    display: block;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--fg-dim);
    margin-bottom: 0.2rem;
}

.row {
    display: flex;
    gap: 0.4rem;
    align-items: center;
    flex-wrap: wrap;
}

.trade-controls {
    display: flex;
    gap: 0.3rem;
    align-items: center;
}

.trade-controls input {
    width: 6.5rem;
}

/* --------------------------------------------------------------------------
   Terminal log
   -------------------------------------------------------------------------- */

.terminal {
    background: var(--bg);
    border: 1px solid var(--border);
    padding: 0.7rem 0.9rem;
    height: 15rem;
    overflow-y: auto;
    font-size: 0.85rem;
    scrollbar-width: thin;
}

.terminal p {
    margin: 0 0 0.25rem;
}

.terminal .day {
    color: var(--fg-dim);
    margin-right: 0.5rem;
}

.terminal .good      { color: var(--good); }
.terminal .bad       { color: var(--bad); }
.terminal .market    { color: var(--market); }
.terminal .police    { color: var(--warn); }
.terminal .rare      { color: var(--warn); font-weight: bold; }
.terminal .legendary { color: var(--legend); font-weight: bold; }

.cursor {
    display: inline-block;
    width: 0.6em;
    height: 1em;
    background: var(--fg);
    vertical-align: text-bottom;
    animation: blink 1.1s steps(2, start) infinite;
}

@keyframes blink {
    to { visibility: hidden; }
}

/* --------------------------------------------------------------------------
   Modal prompts (police stops, offers, game over)
   -------------------------------------------------------------------------- */

.modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 8000;
    background: rgba(0, 0, 0, 0.82);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.modal {
    background: var(--bg-panel);
    border: 1px solid var(--fg);
    box-shadow: 0 0 30px var(--glow);
    padding: 1.2rem 1.4rem;
    max-width: 34rem;
    width: 100%;
}

.modal h2 {
    color: var(--warn);
}

.modal .choices {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-top: 1rem;
}

[hidden] { display: none !important; }

/* --------------------------------------------------------------------------
   Notices
   -------------------------------------------------------------------------- */

.notice {
    border: 1px solid var(--border);
    border-left-width: 3px;
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.75rem;
    font-size: 0.85rem;
}

.notice.error   { border-left-color: var(--bad); color: var(--bad); }
.notice.success { border-left-color: var(--good); color: var(--good); }

.validation-summary ul {
    margin: 0;
    padding-left: 1.1rem;
}

.flash { animation: flash 0.45s ease-out; }

@keyframes flash {
    from { background: var(--fg-dim); }
    to   { background: transparent; }
}

/* --------------------------------------------------------------------------
   Title screen
   -------------------------------------------------------------------------- */

.title-art {
    text-align: center;
    padding: 2rem 0 1rem;
}

.title-art pre {
    display: inline-block;
    margin: 0;
    font-size: clamp(0.32rem, 1.55vw, 0.9rem);
    line-height: 1.05;
    color: var(--fg-bright);
    text-shadow: 0 0 12px var(--glow);
}

.title-sub {
    text-align: center;
    color: var(--fg-dim);
    letter-spacing: 0.3em;
    text-transform: uppercase;
    font-size: 0.75rem;
    margin-bottom: 2rem;
}

.menu {
    max-width: 26rem;
    margin: 0 auto;
    display: grid;
    gap: 0.5rem;
}

.menu button,
.menu .btn {
    width: 100%;
    padding: 0.6rem;
    font-size: 0.9rem;
    text-align: center;
}

/* --------------------------------------------------------------------------
   Theme + sound switcher
   -------------------------------------------------------------------------- */

.switcher {
    display: flex;
    gap: 0.3rem;
    align-items: center;
    font-size: 0.7rem;
}

.switcher button {
    padding: 0.2rem 0.45rem;
    font-size: 0.65rem;
}

.switcher button[aria-pressed="true"] {
    background: var(--fg-dim);
    color: var(--bg);
}

/* --------------------------------------------------------------------------
   Tabs
   -------------------------------------------------------------------------- */

.tabs {
    display: flex;
    gap: 0.4rem;
    margin-bottom: 0.8rem;
    flex-wrap: wrap;
}

.tabs a[aria-current="page"] {
    background: var(--fg-dim);
    color: var(--bg);
}

/* --------------------------------------------------------------------------
   Responsive. Desktop first, per the brief; these are the concessions to a
   phone, where the market table is the one thing that must stay readable.
   -------------------------------------------------------------------------- */

@media (max-width: 820px) {
    .grid.two { grid-template-columns: 1fr; }
}

@media (max-width: 620px) {
    html { font-size: 15px; }

    .shell { padding: 0.6rem 0.6rem 2rem; }

    .topbar { gap: 0.5rem; }

    .terminal { height: 11rem; }

    .trade-controls { width: 100%; }
    .trade-controls input { flex: 1 1 auto; width: auto; }

    /* Below this width the market table stops being a table and becomes a list
       of cards, because six numeric columns cannot be read on a phone. */
    table.market thead { display: none; }

    table.market tr {
        display: grid;
        grid-template-columns: 1fr auto;
        gap: 0.15rem 0.5rem;
        padding: 0.5rem 0;
        border-bottom: 1px solid var(--border);
    }

    table.market td {
        border: none;
        padding: 0.1rem 0;
    }

    table.market td[data-label]::before {
        content: attr(data-label) ": ";
        color: var(--fg-dim);
        font-size: 0.7rem;
        text-transform: uppercase;
    }

    table.market td.actions {
        grid-column: 1 / -1;
    }
}

@media print {
    body::before,
    body::after { display: none; }
}

/* --------------------------------------------------------------------------
   Link back to the parent site, when this app is mounted under one.
   -------------------------------------------------------------------------- */

.parent-link {
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--fg-dim);
    border-bottom: none;
    white-space: nowrap;
}

.parent-link:hover,
.parent-link:focus-visible {
    color: var(--fg-bright);
}

/* --------------------------------------------------------------------------
   Honeypot field.

   Moved off-screen rather than display:none on purpose: some form-spam bots
   skip fields that are display:none, and the whole point is that they fill it.
   Kept out of the accessibility tree by aria-hidden on the wrapper, so nobody
   using a screen reader is ever invited to complete it.
   -------------------------------------------------------------------------- */

.hp-field {
    position: absolute;
    left: -9999px;
    top: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* --------------------------------------------------------------------------
   City picker (the new-game panel).

   The radio itself is moved off-screen rather than hidden, so it keeps its
   place in the tab order and still answers to the arrow keys -- the whole
   control is a native radio group wearing a card, not a div pretending to be
   one. Selection is drawn by the adjacent-sibling rule below, which is why the
   input is the first child of the label and .city-body the second.
   -------------------------------------------------------------------------- */

.city-field {
    margin-bottom: 1rem;
}

.city-field .label-text {
    display: block;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--fg-dim);
    margin-bottom: 0.4rem;
}

.city-choice {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 0.5rem;
}

.city-card {
    display: block;
    position: relative;
    cursor: pointer;
}

.city-card input {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
}

.city-body {
    display: block;
    height: 100%;
    border: 1px solid var(--border);
    background: var(--bg-raised);
    padding: 0.55rem 0.7rem;
    transition: border-color 0.12s, background 0.12s;
}

.city-card:hover .city-body {
    border-color: var(--fg-dim);
}

.city-card input:checked + .city-body {
    border-color: var(--fg);
    background: var(--bg-panel);
    box-shadow: 0 0 0 1px var(--border) inset;
}

.city-card input:focus-visible + .city-body {
    outline: 1px dashed var(--accent);
    outline-offset: 2px;
}

.city-name {
    display: block;
    color: var(--fg-bright);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-size: 0.9rem;
}

/* The tick sits in the gutter the uppercase name leaves, so a chosen card
   reads as chosen without colour alone carrying the message. */
.city-card input:checked + .city-body .city-name::before {
    content: "> ";
    color: var(--accent);
}

/* Hidden rather than absent, so the marker appearing does not shunt the name sideways. */
.city-card input:not(:checked) + .city-body .city-name::before {
    content: "> ";
    visibility: hidden;
}

.city-sub {
    display: block;
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--fg-dim);
    margin-top: 0.1rem;
}

.city-blurb {
    display: block;
    font-size: 0.8rem;
    margin-top: 0.35rem;
}

.city-districts {
    display: block;
    font-size: 0.72rem;
    color: var(--fg-dim);
    margin-top: 0.35rem;
    line-height: 1.5;
}

.city-note {
    font-size: 0.78rem;
    margin: 0.5rem 0 0;
}

@media (prefers-reduced-motion: reduce) {
    .city-body { transition: none; }
}


/* --------------------------------------------------------------------------
   District banner.

   A shallow strip of artwork across the top of the district panel, with the
   city line and district name sitting on it. The height is FIXED rather than
   derived from an aspect ratio: the band must occupy exactly the same space in
   every district, including the ones nobody has drawn, so that travelling
   never shifts the market table up or down the page. object-fit: cover does
   the rest, which also means artwork that arrives at the wrong ratio is
   cropped rather than allowed to resize anything.

   Only the heading goes over the picture. Controls stay below it on solid
   panel, because glowing terminal text over a photograph is legible enough for
   two words of chapter heading and not legible enough for anything you have to
   click.
   -------------------------------------------------------------------------- */

.eyebrow {
    display: block;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--fg-dim);
}

/* No artwork: the image is hidden, and this collapses to an ordinary heading
   block. This is the state every district is in until a file exists for it. */
.district-head {
    margin-bottom: 0.6rem;
}

.district-caption > h2 {
    margin: 0.15rem 0 0;
}

/* Artwork: the band. */
.district-head.has-art {
    position: relative;
    height: 150px;
    overflow: hidden;
    border: 1px solid var(--border);
    background: var(--bg);
}

.district-banner {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Skylines sit above the middle of most renders, and the interesting half of
       a street scene is the top half. Favouring the upper part of the source
       crops the road and the empty foreground rather than the buildings. */
    object-position: center 35%;
}

/* The same scanlines as the rest of the screen, plus a vignette, so the art
   reads as part of the terminal. Kept light -- the picture is the point. */
.district-head.has-art::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: repeating-linear-gradient(
        to bottom,
        rgba(0, 0, 0, 0.20) 0px,
        rgba(0, 0, 0, 0.20) 1px,
        transparent 1px,
        transparent 3px
    );
    box-shadow: inset 0 0 26px var(--bg);
}

.district-head.has-art .district-caption {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
    padding: 1.6rem 0.9rem 0.5rem;
    /* The scrim. Text over an unknown image is a contrast gamble otherwise, and
       these images are generated by somebody else, later. A gradient to near
       opaque at the baseline means the heading is readable over a white sky. */
    background: linear-gradient(
        to bottom,
        rgba(0, 0, 0, 0) 0%,
        rgba(0, 0, 0, 0.55) 45%,
        rgba(0, 0, 0, 0.88) 100%
    );
}

.district-head.has-art .eyebrow {
    color: var(--fg);
}

@media (max-width: 620px) {
    .district-head.has-art {
        height: 104px;
    }

    .district-head.has-art .district-caption {
        padding: 1.2rem 0.7rem 0.4rem;
    }
}

/* A consequence the player needs to read before pressing the button under it --
   currently only "this will abandon your game". Loud on purpose, and sharing the
   .notice family's left rule so it reads as the same kind of thing. */
.warning {
    border: 1px solid var(--bad);
    border-left-width: 3px;
    background: var(--bg);
    color: var(--warn);
    padding: 0.6rem 0.8rem;
    margin: 0 0 0.6rem;
    font-size: 0.85rem;
}

.warning strong {
    color: var(--bad);
}
