/* The hub: navigation rail, title bar, game cards, and the about/featured aside.
 *
 * Three zones, and each one earns its space. The rail is the only navigation
 * that survives into a game, so it is also the way back out. The card grid is
 * the product. The aside explains what the product is for — copy that used to
 * sit in a disclosure on the host page, where nobody opened it.
 */

/* ============ Navigation rail ============ */
.rail {
    display: flex; flex-direction: column; align-items: stretch;
    gap: 4px;
    width: clamp(78px, 7vw, 96px);
    flex-shrink: 0;
    padding: 14px 8px;
    background: color-mix(in srgb, var(--surface) 74%, transparent);
    border-right: 1px solid var(--line);
    overflow: hidden;
}
.rail-brand { display: grid; place-items: center; padding: 4px 0 12px; }
.rail-mark { display: block; width: clamp(38px, 3.4vw, 44px); aspect-ratio: 1; }
.rail-mark svg { width: 100%; height: 100%; display: block; }
.rail-nav { display: flex; flex-direction: column; gap: 3px; min-height: 0; }

.rail-btn {
    appearance: none; font-family: 'Nunito', sans-serif;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
    width: 100%; min-height: 58px; padding: 9px 4px;
    border: 1px solid transparent; border-radius: 14px;
    background: transparent; color: var(--ink-dim);
    font-size: 12px; font-weight: 800; line-height: 1.15; text-align: center;
    cursor: pointer;
    transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.rail-btn .ico { width: 22px; height: 22px; }
.rail-btn:hover { background: var(--surface-2); color: var(--ink); }
.rail-btn[aria-pressed="true"] {
    background: color-mix(in srgb, var(--primary) 22%, transparent);
    border-color: color-mix(in srgb, var(--primary) 46%, transparent);
    color: var(--ink);
}
.rail-btn[aria-pressed="true"] .ico { color: var(--primary-ink); }

/* ============ Title + access bar ============ */
.access-bar {
    z-index: 40; flex-shrink: 0;
    display: flex; align-items: center; gap: 14px 20px; flex-wrap: wrap;
}
.brand { margin-right: auto; min-width: 0; }
.brand-title {
    font-size: clamp(24px, min(3.9vw, calc(5.2 * var(--vh))), 44px); font-weight: 600;
    line-height: 1.03; letter-spacing: -0.03em; color: var(--ink);
}
.brand-eyebrow {
    font-family: 'Fredoka', sans-serif; font-weight: 600;
    font-size: clamp(14px, min(1.6vw, calc(2.3 * var(--vh))), 20px); color: var(--primary-ink);
    letter-spacing: -0.01em; margin-top: 1px;
}
.brand-sub { color: var(--ink-dim); font-weight: 600; font-size: 14.5px; margin-top: 5px; }
@media (max-height: 780px) { .brand-sub { display: none; } }

/* Inside a game the title is a label, not a headline: the screen belongs to the
   game, and every row the header keeps is a row the dice do not get. */
body:not(.on-hub) .brand { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
body:not(.on-hub) .brand-title { font-size: clamp(17px, 1.8vw, 21px); }
body:not(.on-hub) .brand-eyebrow { font-size: 14px; margin-top: 0; }
body:not(.on-hub) .brand-sub { display: none; }
body.is-tight .brand-sub { display: none; }
body.is-tight .brand-title { font-size: clamp(20px, 2.4vw, 26px); }
body.is-tighter .brand { display: flex; align-items: baseline; gap: 10px; }
body.is-tighter .brand-title { font-size: 18px; }
body.is-tighter .brand-eyebrow { font-size: 13px; margin-top: 0; }

.access-group {
    display: flex; align-items: center; gap: 5px;
    background: var(--surface); border: 1px solid var(--line);
    border-radius: 16px; padding: 5px;
    box-shadow: var(--shadow-sm);
}
/* On the hub the access choice is the first decision, so it gets its own full
   row under the title, with the four tabs sharing it equally — the way the mock
   lays it out. In a game it folds back up beside the title, where the tabs go
   back to being as wide as their labels. */
@media (min-width: 920px) {
    body.on-hub:not(.is-tight) .access-group { order: 1; flex: 1 1 100%; }
    body.on-hub:not(.is-tight) .chip { flex: 1 1 0; }
}

.chip {
    appearance: none; font-family: 'Nunito', sans-serif;
    display: inline-flex; align-items: center; justify-content: center; gap: 9px;
    flex: 0 0 auto; min-width: 0;
    border: 1px solid transparent; background: transparent;
    color: var(--ink-dim);
    padding: 10px 20px; border-radius: 12px;
    font-size: 15px; font-weight: 800;
    cursor: pointer; min-height: 46px;
    transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.chip .ico { width: 20px; height: 20px; }
.chip span { white-space: nowrap; }

/* App controls: quieter than the access tabs, and never competing with them. */
.bar-tools { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.tool-btn {
    appearance: none; font-family: 'Nunito', sans-serif;
    display: inline-flex; align-items: center; gap: 8px;
    min-height: 46px; padding: 10px 16px;
    border: 1px solid var(--line); border-radius: 13px;
    background: var(--surface); color: var(--ink-dim);
    font-size: 14.5px; font-weight: 800; white-space: nowrap; cursor: pointer;
    transition: border-color .18s ease, color .18s ease, background .18s ease;
}
.tool-btn .ico { width: 19px; height: 19px; }
.tool-btn:hover { border-color: var(--primary-ink); color: var(--ink); background: var(--surface-2); }
.tool-btn[aria-pressed="true"] { border-color: color-mix(in srgb, var(--primary) 50%, transparent); color: var(--ink); }
.chip:hover { color: var(--ink); background: var(--surface-2); }
.chip[aria-pressed="true"] {
    background: color-mix(in srgb, var(--primary) 22%, transparent);
    border-color: color-mix(in srgb, var(--primary) 50%, transparent);
    color: var(--ink);
}
/* Each access method keeps one colour everywhere it appears. */
#mode-pointer .ico { color: var(--touch); }
#mode-switch .ico { color: var(--switch); }
#mode-dwell .ico { color: var(--gaze); }
#speak-toggle .ico { color: var(--ink-dim); }
#speak-toggle[aria-pressed="true"] { background: color-mix(in srgb, var(--speak) 18%, transparent); border-color: color-mix(in srgb, var(--speak) 46%, transparent); }
#speak-toggle[aria-pressed="true"] .ico { color: var(--speak); }

/* ============ Hub layout ============ */
/* Beats `.screen.active { display: flex }` — same specificity, later file. */
.hub.screen.active {
    display: grid;
    grid-template-columns: minmax(0, 1fr) clamp(258px, 21vw, 322px);
    gap: clamp(14px, 1.5vw, 24px);
    align-items: stretch;
}
.hub-main { display: flex; flex-direction: column; gap: clamp(10px, 1.2vw, 16px); min-width: 0; min-height: 0; }
.hub-aside {
    display: flex; flex-direction: column; gap: 12px;
    min-height: 0; overflow: hidden;
    container-type: size;
}

/* ============ Game cards ============ */
.game-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(198px, 1fr));
    grid-auto-rows: 1fr;
    gap: clamp(10px, 1.1vw, 16px);
    align-content: start;
}

.game-card {
    appearance: none; font-family: 'Nunito', sans-serif;
    position: relative; text-align: left;
    display: flex; flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden; padding: 0;
    cursor: pointer; color: var(--ink);
    /* Its own query container: how much of a card is shown — art, description,
       access strip — depends on how tall the grid row made it, and that is a
       question only the card itself can answer. */
    container-type: size;
    min-height: min(clamp(140px, calc(19 * var(--vh)), 220px), 100cqh);
    max-height: min(clamp(180px, calc(30 * var(--vh)), 288px), 100cqh);
    transition: transform .24s cubic-bezier(.34,1.56,.64,1), border-color .24s ease, box-shadow .24s ease;
}
.game-card:hover {
    transform: translateY(-4px);
    border-color: color-mix(in srgb, var(--tone, var(--primary)) 60%, transparent);
    box-shadow: var(--shadow-md);
}
.game-card:active { transform: translateY(-1px); }
.game-card[data-tone="sky"] { --tone: var(--sky); }
.game-card[data-tone="coral"] { --tone: var(--coral); }
.game-card[data-tone="mint"] { --tone: var(--mint); }
.game-card[data-tone="sun"] { --tone: var(--sun); }

.card-art { position: relative; flex: 1 1 0; min-height: 0; overflow: hidden; }
.card-art .art { display: block; width: 100%; height: 100%; }
/* The illustrations carry their own dark palettes, so a short fade is what
   settles them onto the card instead of a hard edge. */
.card-art::after {
    content: ''; position: absolute; inset: auto 0 0 0; height: 26%;
    background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--surface) 92%, transparent));
}

.card-body { flex: 0 0 auto; padding: 11px 14px 0; display: flex; flex-direction: column; gap: 4px; }
.card-title {
    font-family: 'Fredoka', sans-serif; font-weight: 600;
    font-size: clamp(17px, calc(2.4 * var(--vh)), 21px); line-height: 1.15;
    letter-spacing: -0.015em;
}
/* Two lines are reserved whether or not the sentence needs both, so the titles
   sit on the same baseline right across a row. Without the floor, a one-line
   description gave its card a taller illustration and pushed its title down. */
.card-desc {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
    min-height: 2.84em;
    font-size: 13px; font-weight: 600; color: var(--ink-dim); line-height: 1.42;
}

/* Every game plays every way — that promise is the product, so it is on every
   card rather than in a footnote. Decorative: the aside says it in words. */
.card-modes { display: flex; gap: 6px; padding: 8px 14px 11px; flex-shrink: 0; }
.card-mode {
    width: 26px; height: 26px; border-radius: 9px;
    display: grid; place-items: center;
    background: var(--surface-2); border: 1px solid var(--line);
}
.card-mode .ico { width: 15px; height: 15px; }
.card-mode.touch { color: var(--touch); }
.card-mode.switch { color: var(--switch); }
.card-mode.gaze { color: var(--gaze); }
.card-mode.speak { color: var(--speak); }

/* Graded by the card's own height. Order matters: the access strip is a promise
   the aside repeats in words, the description restates the title, and the
   artwork is the identity — so they go in that order. */
@container (max-height: 204px) { .card-modes { display: none; } }
@container (max-height: 168px) { .card-desc { display: none; } .card-body { padding: 10px 13px; } }
@container (max-height: 128px) { .card-title { font-size: 16px; } .card-body { padding: 8px 12px; } }
@container (max-height: 92px) { .card-art { display: none; } .card-body { flex: 1; justify-content: center; } }

/* Favourites and recents: the same card, laid along the row. */
.hub-recent {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
    gap: 10px; flex-shrink: 0;
}
.card-compact {
    container-type: normal;
    flex-direction: row; align-items: center; gap: 12px;
    min-height: 56px; max-height: none; padding-right: 12px;
}
.card-compact .card-art { flex: 0 0 56px; align-self: stretch; }
.card-compact .card-art::after { display: none; }
.card-compact .card-body { padding: 8px 0; justify-content: center; }
.card-compact .card-title { font-size: 16.5px; }
.card-compact .card-desc, .card-compact .card-modes { display: none; }
body.is-tight .hub-recent { display: none; }

/* ============ Aside: about + featured ============ */
.aside-card {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--radius); padding: 15px 17px;
    overflow: hidden; min-height: 0;
}
.about-card { flex: 0 0 auto; }
.featured-card { flex: 0 0 auto; }
.aside-title {
    font-family: 'Fredoka', sans-serif; font-weight: 600; font-size: 18px;
    letter-spacing: -0.015em; color: var(--ink);
}
.aside-lede { font-size: 13px; font-weight: 600; color: var(--ink-dim); line-height: 1.5; margin-top: 6px; }
.aside-list { list-style: none; display: flex; flex-direction: column; gap: 12px; margin-top: 13px; }
.aside-item { display: flex; gap: 12px; align-items: flex-start; }
.aside-ico {
    width: 34px; height: 34px; border-radius: 11px; flex-shrink: 0;
    display: grid; place-items: center; border: 1px solid var(--line-strong);
}
.aside-ico .ico { width: 18px; height: 18px; }
.aside-ico.touch { color: var(--touch); background: color-mix(in srgb, var(--touch) 14%, transparent); }
.aside-ico.switch { color: var(--switch); background: color-mix(in srgb, var(--switch) 14%, transparent); }
.aside-ico.gaze { color: var(--gaze); background: color-mix(in srgb, var(--gaze) 14%, transparent); }
.aside-text { min-width: 0; }
.aside-text b { display: block; font-family: 'Fredoka', sans-serif; font-weight: 600; font-size: 14.5px; letter-spacing: -0.01em; }
.aside-text > span {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
    font-size: 12.5px; font-weight: 600; color: var(--ink-dim); line-height: 1.45; margin-top: 2px;
}

.featured-card { display: flex; flex-direction: column; min-height: 0; }
.featured-eyebrow {
    font-size: 11.5px; font-weight: 800; letter-spacing: 0.11em; text-transform: uppercase;
    color: var(--primary-ink);
}
.featured-art {
    position: relative; margin-top: 10px; border-radius: 14px; overflow: hidden;
    aspect-ratio: 16 / 9; flex-shrink: 0;
}
.featured-art .art { display: block; width: 100%; height: 100%; }
.featured-play {
    position: absolute; inset: 0; display: grid; place-items: center;
    color: #fff;
}
.featured-play span {
    width: 46px; height: 46px; border-radius: 50%;
    display: grid; place-items: center;
    background: rgba(10, 8, 24, 0.42);
    border: 2px solid rgba(255, 255, 255, 0.85);
    backdrop-filter: blur(2px);
}
.featured-play .ico { width: 18px; height: 18px; margin-left: 2px; }
.featured-name { font-family: 'Fredoka', sans-serif; font-weight: 600; font-size: 18px; margin-top: 10px; letter-spacing: -0.015em; }
.featured-desc {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
    font-size: 12.5px; font-weight: 600; color: var(--ink-dim); line-height: 1.45; margin-top: 4px;
}
.featured-cta {
    appearance: none; font-family: 'Fredoka', sans-serif;
    margin-top: 14px; width: 100%; min-height: 52px;
    display: inline-flex; align-items: center; justify-content: center; gap: 10px;
    border: none; border-radius: 14px;
    background: linear-gradient(135deg, var(--primary), var(--primary-strong));
    color: #fff; font-size: 17px; font-weight: 600; cursor: pointer;
    box-shadow: var(--shadow-sm);
    transition: transform .18s ease, filter .18s ease;
}
.featured-cta .ico { width: 17px; height: 17px; }
.featured-cta:hover { transform: translateY(-2px); filter: brightness(1.08); }

/* The aside gives way from the bottom of the priority list up. */
@container (max-height: 720px) { .aside-lede { display: none; } }
@container (max-height: 620px) { .about-card .aside-text > span { display: none; } .aside-list { gap: 9px; } }
@container (max-height: 520px) { .about-card { display: none; } }
@container (max-height: 380px) { .featured-desc { display: none; } }
@container (max-height: 270px) { .featured-art { display: none; } }

/* ============ Density steps ============ */
body.is-tight .rail { width: clamp(66px, 5.6vw, 76px); padding: 10px 6px; }
body.is-tight .rail-btn { min-height: 48px; font-size: 11px; gap: 3px; padding: 6px 3px; }
body.is-tight .rail-brand { padding: 0 0 8px; }
body.is-tight .rail-mark { width: 32px; }
body.is-tight .chip { padding: 8px 14px; }
body.is-tighter .rail { width: 56px; }
body.is-tighter .rail-label { display: none; }
body.is-tighter .rail-btn { min-height: 44px; }
body.is-tighter .rail-brand { display: none; }
body.is-tighter .access-group { padding: 3px; border-radius: 12px; }
body.is-tighter .chip { min-height: 44px; padding: 6px 12px; font-size: 14px; }
body.is-tight .tool-btn { min-height: 44px; padding: 8px 12px; }
body.is-tighter .tool-label { display: none; }

/* ============ Narrow layouts ============ */
@media (max-width: 1100px) {
    .hub.screen.active { grid-template-columns: minmax(0, 1fr); }
    .hub-aside { display: none; }
}
@media (max-width: 860px) {
    .chip span { display: none; }
    .chip { padding: 10px 14px; }
    .tool-label { display: none; }
    .tool-btn { padding: 10px 13px; }
}
/* On a phone the game heading wraps to two rows, and the second row is spent on
   a word. The star says the same thing; the button keeps its aria-label. */
@media (max-width: 620px) {
    .fav-label { display: none; }
    .fav-btn { padding: 10px 14px; }
}
@media (max-width: 720px) {
    /* Rows are assigned explicitly, not by source order: the rail hides itself
       inside a game (below), and auto-placement would then slide the stage up
       into the auto-sized row and leave the 1fr row empty — a play surface a
       third of the height it should be. */
    .app { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
    .rail { grid-row: 1; }
    .stage { grid-row: 2; }
    /* On a phone the rail can only be a horizontal bar, and a bar costs a row of
       the screen. On the hub that row buys category filtering; inside a game it
       buys a second way to do what the Back button already does, at the price of
       the room the dice need. So it stands down until the player is back at the
       hub. */
    body:not(.on-hub) .rail { display: none; }
    .rail {
        width: 100%; flex-direction: row; align-items: center; flex-wrap: wrap;
        border-right: none; border-bottom: 1px solid var(--line);
        padding: 8px 10px; gap: 4px;
    }
    .rail-brand { padding: 0 8px 0 2px; }
    .rail-mark { width: 32px; }
    .rail-nav { flex-direction: row; flex-wrap: wrap; flex: 1; min-width: 0; }
    .rail-btn { min-height: 44px; min-width: 46px; width: auto; padding: 6px 8px; }
    .rail-label { display: none; }
    .brand-sub { display: none; }
    .grid2 { grid-template-columns: 1fr; }
    .game-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}
