/* =============================================================================
   LEXICRON — portrait touch build.

   Phone-first: one column, no horizontal scroll, every control a real tap
   target. The look is carried over from the CLI build — monospace throughout,
   box-drawn borders, the same truecolor palette on near-black.
   ============================================================================= */

:root {
    --bg:      #0f1218;
    --panel:   #171b26;
    --panel-2: #1c202c;
    --active:  #2d374b;
    --line:    #3c414b;
    --txt:     #c8cdd7;
    --white:   #fafafa;
    --dim:     #787d87;

    --gold:    #ffd700;
    --amber:   #ffaa00;
    --orange:  #ff6e1e;
    --fire:    #ff3c28;
    --crimson: #dc143c;
    --rose:    #ff648c;
    --cyan:    #00f0ff;
    --ice:     #a0e6ff;
    --emerald: #28f064;
    --mint:    #78ffb4;
    --purple:  #b450ff;
    --cosmic:  #dc8cff;

    --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas,
            "DejaVu Sans Mono", "Liberation Mono", monospace;

    /* Room the notch and the home indicator need. */
    --pad-top:    env(safe-area-inset-top, 0px);
    --pad-bottom: env(safe-area-inset-bottom, 0px);

    --gap: 6px;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
    height: 100%;
    background: var(--bg);
    color: var(--txt);
    font-family: var(--mono);
    font-size: 14px;
    line-height: 1.35;
    overflow: hidden;
    overscroll-behavior: none;
    -webkit-text-size-adjust: 100%;
}

body {
    /* Nothing in the game is text you select or drag, and letting the browser
       think otherwise turns a fast tap on a rune into a text selection. */
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
}

#app {
    /* A size container, so the compaction rules below key off the play area
       rather than the viewport. That is the honest measure: on iOS the URL bar
       collapses and expands under the page, and a desktop window is not a
       phone at all. */
    container-type: size;
    container-name: app;
    display: flex;
    flex-direction: column;
    height: 100dvh;
    max-width: 560px;         /* on a desktop browser it stays a phone-shaped column */
    margin: 0 auto;
    padding: calc(var(--pad-top) + 4px) 8px calc(var(--pad-bottom) + 6px);
    position: relative;
    overflow: hidden;
}

@media (min-width: 600px) {
    #app { border-left: 1px solid var(--line); border-right: 1px solid var(--line); }
}

.screen {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    gap: var(--gap);
}

/* Screens that are read rather than played scroll as one column. */
.screen.scrolls { overflow-y: auto; -webkit-overflow-scrolling: touch; }

/* --- type ----------------------------------------------------------------- */

.dim   { color: var(--dim); }
.silver{ color: var(--txt); }
.white { color: var(--white); }
.gold  { color: var(--gold); }
.cyan  { color: var(--cyan); }
.fire  { color: var(--fire); }
.emerald { color: var(--emerald); }
.purple  { color: var(--purple); }
.amber   { color: var(--amber); }
.b     { font-weight: 700; }
.center{ text-align: center; }
.small { font-size: 12px; }
.tiny  { font-size: 11px; }
.nowrap{ white-space: nowrap; }

.rule {
    border: 0;
    border-top: 1px solid var(--line);
    margin: 2px 0;
}

/* --- panels --------------------------------------------------------------- */

.panel {
    border: 1px solid var(--line);
    background: var(--panel);
    padding: 6px 8px;
    position: relative;
}

/* The label sits in the top border, the way the CLI draws ┌─ 📜 Combat Log ─┐ */
.panel > .panel-label {
    position: absolute;
    top: -0.72em;
    left: 8px;
    padding: 0 5px;
    background: var(--bg);
    font-size: 11px;
    letter-spacing: 0.05em;
    color: var(--dim);
}

.panel.labelled { margin-top: 6px; }

/* A panel that takes the leftover height and centres itself in it — used for
   the one-thing-on-screen moments (which room you are about to enter). */
.panel.fill {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
}

/* --- header --------------------------------------------------------------- */

.hud {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    background: var(--panel-2);
    border: 1px solid var(--line);
    padding: 4px 7px;
    font-size: 11.5px;
    line-height: 1.3;
    flex: 0 0 auto;
}
.hud .sep { color: var(--line); }
.hud .grow { flex: 1 1 auto; }

/* --- stat bars ------------------------------------------------------------ */
/* Block glyphs rather than a styled div: at this size ████░░ reads as the same
   meter the terminal draws, and it never lands on a half pixel. */

.bar { font-size: 12px; letter-spacing: -0.04em; white-space: nowrap; }
.bar .off { color: var(--line); }

.statline {
    display: flex;
    align-items: baseline;
    gap: 6px;
    font-size: 12px;
    white-space: nowrap;
}
.statline .label { width: 2.2em; color: var(--dim); flex: 0 0 auto; }
.statline .num { margin-left: auto; font-variant-numeric: tabular-nums; }

/* --- ASCII blocks (sprites, logo) ----------------------------------------- */

.artwrap {
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    min-height: 0;
}

/* The sprite sizes itself to the width it gets (see ui.fit); the stage then
   only claims the height it actually needs. */
.stage > .artwrap { flex: 0 0 auto; }

pre.art {
    font-family: var(--mono);
    line-height: 1.02;
    white-space: pre;
    transform-origin: center center;
    text-shadow: 0 0 12px currentColor;
    opacity: 0.95;
}

/* Gradient across the wordmark, the way ANSI.gradient paints it in the CLI.
   The glow has to come from a drop-shadow filter rather than text-shadow:
   background-clip:text makes the glyphs transparent, and a text-shadow would
   be painted behind them and show straight through. */
pre.art.logo {
    background: linear-gradient(100deg, #ff641e, #ff3caa 52%, #8c50ff);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    text-shadow: none;
    filter: drop-shadow(0 0 14px rgba(200, 80, 255, .35));
    opacity: 1;
}

/* --- buttons -------------------------------------------------------------- */

.btn {
    font-family: var(--mono);
    font-size: 13px;
    color: var(--txt);
    background: var(--panel-2);
    border: 1px solid var(--line);
    padding: 9px 10px;
    min-height: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    cursor: pointer;
    text-align: center;
    line-height: 1.2;
    transition: background .08s linear, border-color .08s linear;
}

.btn:disabled, .btn.off {
    color: var(--line);
    border-color: #23262f;
    background: #12151d;
}

.btn.pressed:not(:disabled):not(.off) {
    background: var(--active);
    border-color: currentColor;
}

.btn.primary {
    color: var(--gold);
    border-color: var(--gold);
    background: #241f07;
    font-weight: 700;
    font-size: 15px;
    letter-spacing: 0.06em;
}
.btn.primary:disabled { color: var(--line); border-color: #23262f; background: #12151d; }

.btn.wide { width: 100%; }
.btn .k { color: var(--dim); font-size: 11px; }

/* A throwaway opt-out line: a button for tapping, not for looking at. */
.btn.subtle {
    font-size: 11px;
    color: var(--dim);
    background: transparent;
    border-color: transparent;
    min-height: 32px;
    padding: 4px;
}

.btnrow {
    display: flex;
    gap: var(--gap);
    flex: 0 0 auto;
}
.btnrow > .btn { flex: 1 1 0; min-width: 0; }

/* A menu-style stacked list of choices (loot, campfire, merchant, mystery). */
.choice {
    display: block;
    width: 100%;
    text-align: left;
    justify-content: flex-start;
    padding: 10px 12px;
    min-height: 52px;
}
.choice .title { display: block; color: var(--white); font-weight: 700; }
/* Rarity rides along in the title line, in the same colour as the border. */
.choice .title .rarity {
    float: right;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: .1em;
    color: currentColor;
    opacity: .85;
    padding-top: 3px;
}
.choice .sub   { display: block; color: var(--dim); font-size: 11.5px; margin-top: 2px; }

/* --- rune rack ------------------------------------------------------------ */

/* Tile width is computed in ui.fitRacks from the room actually available: as
   many runes per row as fit at a real thumb size, wrapping to a second centred
   row rather than shaving every tile down to a fingernail. */
.rack {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-content: center;
    gap: 5px;
    flex: 0 0 auto;
}

.rune {
    position: relative;
    /* The letter is sized from the tile, not the viewport: a packed rack has
       narrower tiles and the glyph has to follow them down. */
    container-type: inline-size;
    flex: 0 0 auto;
    width: var(--rune-w, 52px);
    height: 60px;
    background: var(--panel-2);
    border: 1px solid currentColor;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-family: var(--mono);
    cursor: pointer;
    padding: 0;
    transition: transform .06s ease-out, background .08s linear, opacity .1s linear;
}

.rune .letter {
    font-size: min(25px, 52cqw);
    font-weight: 700;
    line-height: 1;
    color: var(--white);
    text-shadow: 0 0 10px currentColor;
}
.rune .pts {
    position: absolute;
    top: 2px;
    right: 4px;
    font-size: 10px;
    color: var(--dim);
    font-variant-numeric: tabular-nums;
}
.rune .elem {
    position: absolute;
    bottom: 1px;
    font-size: 10px;
    line-height: 1;
    opacity: .85;
}

/* Spent by the word currently being built: still visible, clearly not yours. */
.rune.used {
    opacity: .3;
    background: #10131a;
}
.rune.used .letter { color: var(--dim); text-shadow: none; }

.rune.pressed { transform: scale(.9); background: var(--active); }

.rune.gilded { box-shadow: inset 0 0 0 1px rgba(255, 215, 0, .35); }

/* A wildcard has no letter of its own until you pick one. */
.rune.wild .letter {
    background: linear-gradient(135deg, #ff3c28, #ffd700 35%, #00f0ff 65%, #b450ff);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    text-shadow: none;
}

/* Short play area — a small phone, or a big one with the browser chrome up.
   Shave the fixed rows so the sprite and the log keep a usable share. */
@container app (max-height: 720px) {
    .rune { height: 50px; }
    .btn { min-height: 38px; padding: 6px 8px; }
    .btn.primary { font-size: 14px; }
    .spellrow .btn { min-height: 38px; font-size: 10.5px; }
    .wordbox { min-height: 44px; }
    .typed { font-size: 19px; }
    .playerbar { padding: 3px 7px; }
    .log { min-height: 40px; font-size: 11px; }
    :root { --gap: 5px; }
}

/* --- the word being built ------------------------------------------------- */

.wordbox {
    border: 1px solid var(--line);
    background: var(--panel);
    padding: 5px 8px;
    flex: 0 0 auto;
    min-height: 52px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 2px;
}

.wordline {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 26px;
}

.typed {
    font-size: 22px;
    font-weight: 700;
    letter-spacing: 0.16em;
    color: var(--white);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: clip;
    flex: 1 1 auto;
    min-width: 0;
}
.typed .caret {
    color: var(--gold);
    animation: blink 1s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

.verdict { font-size: 12px; text-align: right; flex: 0 0 auto; line-height: 1.15; }
.verdict .dmg { display: block; color: var(--gold); font-weight: 700; font-size: 14px; }

/* A word can trigger more effects than fit on one phone-width line. The row
   scrolls, and the fade on the right edge says so — without it the last
   trigger just looks truncated. */
.triggers {
    font-size: 10.5px;
    color: var(--amber);
    white-space: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, #000 88%, transparent 100%);
    mask-image: linear-gradient(90deg, #000 88%, transparent 100%);
}
.triggers::-webkit-scrollbar { display: none; }

/* --- combat log ----------------------------------------------------------- */

/* The log takes whatever the sprite and the controls leave over — on a tall
   phone that is a comfortable scrollback, on a short one it collapses to a few
   lines and scrolls. */
.log {
    flex: 1 1 auto;
    min-height: 48px;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    font-size: 11.5px;
    line-height: 1.4;
    color: var(--txt);
    background: var(--panel);
    border: 1px solid var(--line);
    padding: 5px 7px;
    scrollbar-width: thin;
}
.log div { margin-bottom: 1px; }
.log div:last-child { color: var(--white); }

/* --- floating combat text ------------------------------------------------- */

.floaters {
    position: absolute;
    inset: 0;
    pointer-events: none;
    overflow: hidden;
    z-index: 40;
}

.floater {
    position: absolute;
    left: 50%;
    font-size: 15px;
    font-weight: 700;
    white-space: nowrap;
    text-shadow: 0 0 10px currentColor, 0 1px 3px #000;
    animation: rise 1.2s ease-out forwards;
}

@keyframes rise {
    0%   { transform: translate(-50%, 0) scale(.8); opacity: 0; }
    18%  { transform: translate(-50%, -8px) scale(1.08); opacity: 1; }
    100% { transform: translate(-50%, -54px) scale(1); opacity: 0; }
}

/* --- monster stage -------------------------------------------------------- */

.stage {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: 3px;
    border: 1px solid var(--line);
    background: linear-gradient(180deg, #1a1420 0%, var(--panel) 70%);
    padding: 5px 8px 6px;
    position: relative;
    overflow: hidden;
}

.stage .who { display: flex; align-items: baseline; gap: 6px; font-size: 13px; }
.stage .who .name { color: var(--fire); font-weight: 700; }
.stage .who .title { color: var(--dim); font-size: 10.5px; }

.intent {
    font-size: 11px;
    color: var(--amber);
    border: 1px dashed rgba(255, 170, 0, .4);
    padding: 2px 6px;
    text-align: center;
}
.intent .nm { font-weight: 700; }

.stage.hit { animation: shake .22s linear; }
@keyframes shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-5px); }
    75% { transform: translateX(5px); }
}

/* --- spells --------------------------------------------------------------- */

.spellrow { display: flex; gap: 4px; flex: 0 0 auto; }
.spellrow .btn {
    flex: 1 1 0;
    min-width: 0;
    flex-direction: column;
    gap: 1px;
    font-size: 11px;
    padding: 5px 2px;
    min-height: 42px;
}
.spellrow .btn .cost { font-size: 10px; color: var(--dim); }
.spellrow .btn.ready { border-color: currentColor; }

/* --- player strip --------------------------------------------------------- */

.playerbar {
    display: flex;
    gap: 8px;
    align-items: center;
    flex: 0 0 auto;
    background: var(--panel-2);
    border: 1px solid var(--line);
    padding: 4px 7px;
    font-size: 11.5px;
}
.playerbar .col { display: flex; flex-direction: column; gap: 1px; }
.playerbar .col.grow { flex: 1 1 auto; min-width: 0; }
.playerbar .chips { margin-left: auto; text-align: right; font-size: 11px; white-space: nowrap; }

/* --- modal ---------------------------------------------------------------- */

.modal-back {
    /* Absolute, not fixed: the modal belongs to the game column. On a phone
       that is the whole viewport anyway, but on a desktop browser a fixed
       overlay would spill across the empty space either side. */
    position: absolute;
    inset: 0;
    background: rgba(6, 8, 12, .84);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    z-index: 60;
}

.modal {
    width: 100%;
    max-width: 420px;
    max-height: 84dvh;
    overflow-y: auto;
    background: var(--panel);
    border: 1px solid var(--line);
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.modal h2 { font-size: 15px; color: var(--gold); font-weight: 700; }

/* A–Z picker for spending a Prismatic Star. */
.azgrid {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 4px;
}
.azgrid .btn { min-height: 44px; padding: 4px; font-size: 16px; font-weight: 700; }
.azgrid .btn.off { font-weight: 400; }

/* --- menu ----------------------------------------------------------------- */

.menu-item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    width: 100%;
    padding: 11px 12px;
    min-height: 56px;
    text-align: left;
    justify-content: center;
}
.menu-item .t { font-size: 14.5px; color: var(--white); font-weight: 700; }
.menu-item .d { font-size: 11.5px; color: var(--dim); }

/* The "add to home screen" offer: deliberately quieter than a mode button, so
   it reads as an aside under the menu rather than a fifth thing to play. */
.install-row {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    width: 100%;
    padding: 7px 10px;
    min-height: 0;
    background: transparent;
    border-style: dashed;
}
.install-row .t { font-size: 12px; font-weight: 700; }
.install-row .d { font-size: 10.5px; color: var(--dim); }

/* Numbered steps in the iOS instructions. */
.step {
    display: flex;
    align-items: baseline;
    gap: 7px;
    padding: 2px 0;
}
.step .n {
    flex: 0 0 auto;
    color: var(--gold);
    font-weight: 700;
}

/* --- map ------------------------------------------------------------------ */

.maprow {
    display: flex;
    gap: 4px;
    justify-content: center;
    align-items: stretch;
    flex-wrap: wrap;
}
.mapnode {
    flex: 1 1 0;
    min-width: 46px;
    border: 1px solid var(--line);
    background: var(--panel);
    padding: 5px 2px;
    text-align: center;
    font-size: 10px;
    color: var(--dim);
    line-height: 1.25;
}
.mapnode .ic { display: block; font-size: 17px; line-height: 1.1; }
.mapnode.done { opacity: .38; }
.mapnode.here {
    border-color: var(--gold);
    color: var(--gold);
    background: #241f07;
    box-shadow: 0 0 12px rgba(255, 215, 0, .18);
}

/* --- blitz ---------------------------------------------------------------- */

.timerbar { height: 6px; background: #12151d; border: 1px solid var(--line); flex: 0 0 auto; }
.timerbar > i { display: block; height: 100%; background: var(--fire); transition: width .1s linear; }
.timerbar.warn > i { background: var(--gold); }

.bigstat { display: flex; gap: 10px; align-items: baseline; justify-content: center; flex-wrap: wrap; }
.bigstat .v { font-size: 26px; font-weight: 700; color: var(--gold); font-variant-numeric: tabular-nums; }
.bigstat .l { font-size: 11px; color: var(--dim); }

/* Blitz scoring feed — newest at the top, fading down the list. */
.feed {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 4px 2px;
}
.feed-row {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 13px;
    border-bottom: 1px solid #191d27;
    padding-bottom: 2px;
}
.feed-row .w { color: var(--white); letter-spacing: .08em; flex: 1 1 auto; }
.feed-row .p { color: var(--gold); font-weight: 700; font-variant-numeric: tabular-nums; }
.feed-row .t { color: var(--emerald); font-size: 11px; font-variant-numeric: tabular-nums; }

/* --- misc ----------------------------------------------------------------- */

/* Takes the leftover height and centres in it, without drawing a box around
   the emptiness the way a .panel would. */
.fillplain {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

/* The stage's victory conditions, stated rather than implied. */
.rules { display: flex; flex-wrap: wrap; gap: 5px; justify-content: center; flex: 0 0 auto; }
.rule-chip {
    border: 1px dashed var(--line);
    color: var(--dim);
    padding: 2px 8px;
    font-size: 11px;
}

/* Words already banked in a puzzle stage. */
.found { display: flex; flex-wrap: wrap; gap: 5px; justify-content: center; }
.found-chip {
    border: 1px solid var(--emerald);
    color: var(--emerald);
    background: #0d2416;
    padding: 3px 8px;
    font-size: 13px;
    letter-spacing: .1em;
}
/* A slot still to fill, so the goal is visible before you have found anything. */
.found-chip.empty {
    border-style: dashed;
    border-color: var(--line);
    color: var(--line);
    background: none;
}

.kv { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; }
.kv .k { color: var(--dim); }

.relic-line { display: flex; gap: 8px; align-items: flex-start; font-size: 12px; }
.relic-line .ic { font-size: 17px; flex: 0 0 auto; line-height: 1.2; }

.boot {
    display: flex;
    flex-direction: column;
    gap: 10px;
    align-items: center;
    justify-content: center;
    flex: 1 1 auto;
    text-align: center;
    color: var(--dim);
    font-size: 12px;
}
