/* ============================================================
   app-page.css — the shared "app" page (PlusReal first; Lunaria and Lumine next)
   Loaded after home.css, which supplies the primitives these pages build on:
   hm-hero, hm-section, hm-title, hm-text, hm-btn, hm-rows / hm-row.
   Behaviour (video facade, store facts, side-scrolling strips, the picker)
   is assets/js/app-page.js. Every class is prefixed ap-; every colour,
   size and space comes from variables.css (section 23 for this page type).

   Page outline, in order — each section is optional:
     .hm-hero .ap-hero        category, H1, lead, store badge + Discord, store facts
     #what   .ap-split        title + intro | hm-rows with one icon each
     #see    .ap-video        click-to-play cover, then an .ap-scroller of screenshots
     #loop   .ap-steps        numbered cycle
     #games  .ap-picker       an .ap-scroller of options + one .ap-panel per option
     #points .ap-split        intro note | token rows, then an .ap-more link
             .ap-closing      dark closing call to action
   ============================================================ */

/* style.css forces every <p> to 18px / 28px !important below 768px; keep ours on the type scale. */
@media (max-width: 767px) {
    p.ap-builder-subtitle,
    p.ap-areas-hint {
        font-size: var(--text-label-size) !important;
        line-height: var(--text-label-line) !important;
    }

    p.ap-step-text,
    p.ap-note,
    p.ap-panel-meta,
    p.ap-builder-desc,
    p.ap-card-text,
    p.ap-feature-text,
    p.ap-closing-text {
        font-size: var(--text-body-size) !important;
        line-height: var(--text-body-line) !important;
    }
}

/* Body copy in full-strength ink: --ink-muted is 4.49:1 on white and 3.3:1 on --surface-alt. */
.ap-page .hm-section .hm-text {
    color: var(--ink);
}

/* ---- 1. Hero: category, title, lead, actions, store facts ---- */
/* The app's Google Play category, in a pill with the icon Google Play uses for it, under the app name. */
.ap-hero-category {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    justify-self: start;  /* the pill hugs its text inside the hero grid */
    margin: 0;            /* the title's own bottom margin spaces it */
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--app-hero-rule);
    border-radius: var(--radius-pill);
    color: var(--home-hero-subtitle-ink);
}

.ap-hero-category .material-symbols-outlined {
    font-size: var(--app-fact-icon-size);
}

/* Text on the left; from 992px the app icon sits on the right, its top a set distance below the top of the app name
   (so it stays put whether the name takes one line or two). Below 992px (tablets and phones): a smaller icon to the
   left of the app name and the category. */
.ap-hero-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 992px) {
    .ap-hero-grid {
        grid-template-columns: minmax(0, 1fr) auto;
        gap: var(--space-10);
        align-items: center;
    }

    .ap-hero-media {
        display: block;
        width: var(--app-hero-media-width);
        max-width: 100%;
        height: auto;
        align-self: start;
        margin-top: var(--app-hero-media-offset);
        border-radius: var(--app-hero-media-radius);
    }
}

/* The app name's bottom margin (style.css sets it per breakpoint): tighter while the name fits on one line (1600px
   and up), and 15px on tablets and up to 575px, as on desktop. 576-767px keeps style.css's value. */
@media (min-width: 1600px) {
    .slide.ap-hero .inner h1.hm-hero-title {
        margin-bottom: var(--app-hero-title-gap-wide);
    }
}

@media (min-width: 768px) and (max-width: 991px), (max-width: 575px) {
    .slide.ap-hero .inner h1.hm-hero-title {
        margin-bottom: var(--app-hero-title-gap-md);
    }
}

/* Below 992px: the text column dissolves into the grid (display: contents) so the icon can take the first column of
   the category and title rows only, level with the top of the category; the lead, actions and store facts run the
   full width below, as before. */
@media (max-width: 991px) {
    .ap-hero-grid {
        --ap-hero-icon: var(--app-hero-media-size-md);
        grid-template-columns: var(--ap-hero-icon) minmax(0, 1fr);
        column-gap: var(--app-hero-media-gap-md);  /* tablets: more room beside the larger icon */
    }

    .ap-hero-grid > div {
        display: contents;
    }

    .ap-hero-grid > div > * {
        grid-column: 1 / -1;
    }

    .ap-hero-grid .ap-hero-category,
    .ap-hero-grid .hm-hero-title {
        grid-column: 2;
    }

    .ap-hero-media {
        display: block;
        grid-column: 1;
        grid-row: 1 / span 2;
        align-self: start;
        margin-top: var(--app-hero-media-offset-md);
        width: var(--ap-hero-icon);
        height: var(--ap-hero-icon);
        border-radius: var(--app-hero-media-radius);
    }
}

@media (max-width: 767px) {
    .ap-hero-grid {
        --ap-hero-icon: var(--app-hero-media-size-sm);
        column-gap: var(--space-4);
    }

    .ap-hero-media {
        margin-top: 0;
    }
}

/* the smallest phones: the app name goes back to two lines */
@media (max-width: 560px) {
    .ap-hero-grid {
        --ap-hero-icon: var(--app-hero-media-size-xs);
    }
}

.ap-hero .hm-hero-subtitle {
    color: var(--white);
    margin-top: var(--space-4);
}

.ap-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4) var(--space-6);
    margin-top: var(--space-6);
}

.ap-store-badge {
    display: inline-flex;
    border-radius: var(--radius-sm);
}

.ap-store-badge img {
    display: block;
    width: auto;
    height: var(--app-badge-height);
}

.ap-store-badge:focus-visible,
.ap-discord:focus-visible,
.ap-community:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 3px;
}

/* Community link (Discord, Instagram…): a quiet text link with the brand mark, white on the dark hero and closing.
   .ap-discord is the PlusReal name for the same link. */
.ap-discord,
.ap-discord:visited,
.ap-discord:hover,
.ap-discord:active,
.ap-discord:focus,
.ap-community,
.ap-community:visited,
.ap-community:hover,
.ap-community:active,
.ap-community:focus {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--white);
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    font-weight: var(--weight-semibold);
    text-decoration: none;
}

.ap-discord .fab,
.ap-community .fab {
    font-size: var(--app-discord-icon-size);
}

@media (hover: hover) {
    .ap-discord:hover span,
    .ap-community:hover span {
        text-decoration: underline;
        text-underline-offset: 3px;
    }
}

.ap-facts {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-5);
    max-width: var(--app-facts-width);
    margin: var(--space-8) 0 0;
    padding: var(--space-5) 0 0;
    border-top: 1px solid var(--app-hero-rule);
}

/* Downloads and release year: side by side; from 576px each column as wide as its content, a wide gap between. */
@media (max-width: 575px) {
    .ap-actions {
        flex-wrap: nowrap;
        gap: var(--space-4);
    }

    .ap-store-badge {
        flex: none;
    }

    .ap-discord,
    .ap-community {
        min-width: 0;
    }
}

@media (min-width: 576px) {
    .ap-facts {
        grid-template-columns: repeat(2, auto);
        justify-content: start;
        column-gap: var(--space-10);
    }
}

.ap-fact {
    display: flex;
    flex-direction: column-reverse;  /* the label reads first to a screen reader, the value shows on top */
    gap: var(--space-1);
    margin: 0;
}

.ap-fact dt {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--home-hero-subtitle-ink);
    font-weight: var(--weight-semibold);
}

.ap-fact dt .material-symbols-outlined {
    font-size: var(--app-fact-icon-size);
}

.ap-fact dd {
    margin: 0;
    white-space: nowrap;
    font-family: var(--font-display);
    font-weight: var(--weight-bold);
    font-size: var(--text-display-sm-size);
    line-height: var(--text-display-sm-line);
    color: var(--white);
}

/* ---- 2. What it is: title + intro on one side, three icon rows on the other ---- */
.ap-split {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-6);
}

@media (min-width: 992px) {
    .ap-split {
        grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
        gap: var(--space-10);
        align-items: start;
    }
}

/* The rows are information, not links: transparent, and no hover colour. */
.ap-rows.hm-rows {
    background: transparent;
}

.ap-rows .hm-row {
    align-items: flex-start;
}

/* a one-ink SVG icon (white on transparent) used as a mask and filled with the accent, in the 40px row-icon box */
.ap-row-mask {
    flex: none;
    width: var(--home-row-icon-size);
    height: var(--home-row-icon-size);
    background-color: var(--accent);
    -webkit-mask: var(--ap-icon) center / var(--app-row-mask-size) no-repeat;
    mask: var(--ap-icon) center / var(--app-row-mask-size) no-repeat;
}

.ap-row-symbol {
    flex: none;
    width: var(--home-row-icon-size);
    font-size: var(--app-row-symbol-size);
    line-height: var(--home-row-icon-size);
    color: var(--accent);
    text-align: center;
}

/* the filled version of a Material Symbol (the page loads the FILL axis 0..1) */
.ap-row-symbol--fill {
    font-variation-settings: 'FILL' 1;
}

/* filled and heavier, for a symbol that looks lighter than the icons beside it (the page loads wght 400..700) */
.ap-row-symbol--fill.ap-row-symbol--heavy {
    font-variation-settings: 'FILL' 1, 'wght' 700;
}

/* a full-colour icon drawn at the symbols' visual size, centred in the same 40px column */
.hm-row-icon.ap-row-icon--small {
    width: var(--app-row-mask-size);
    height: var(--app-row-mask-size);
    margin: calc((var(--home-row-icon-size) - var(--app-row-mask-size)) / 2);
}

@media (hover: hover) {
    .ap-rows .hm-row:hover .hm-row-title {
        color: var(--ink);
    }
}

/* ---- 3. See it in action: click-to-play video, then the screenshot strip ---- */
.ap-video {
    position: relative;
    display: block;
    overflow: hidden;
    margin-top: var(--space-6);
    border-radius: var(--radius-lg);
    background: var(--gray-dark);
    aspect-ratio: 16 / 9;
}

.ap-video img,
.ap-video iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
    object-fit: cover;
}

.ap-video-play {
    position: absolute;
    top: 50%;
    left: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--app-video-play-size);
    height: var(--app-video-play-size);
    border-radius: var(--radius-pill);
    background: var(--accent);
    color: var(--white);
    transform: translate(-50%, -50%);
    transition: transform 0.3s ease-in-out;
}

.ap-video-play .material-symbols-outlined {
    font-size: calc(var(--app-video-play-size) / 2);
}

.ap-video:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

@media (hover: hover) {
    .ap-video:hover .ap-video-play {
        transform: translate(-50%, -50%) scale(1.08);
    }
}

@media (prefers-reduced-motion: reduce) {
    .ap-video-play {
        transition: none;
    }
}

.ap-shots {
    margin-top: var(--space-5);
}

.ap-shots img {
    display: block;
    width: auto;  /* the width follows each image's own proportions */
    height: var(--app-shot-height);
    border-radius: var(--radius-md);
}

.ap-shot {
    display: block;
    border-radius: var(--radius-md);
    cursor: zoom-in;
}

.ap-shot:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ---- Image viewer (app-page.js): opens the images of a strip marked data-ap-viewer, one at a time ---- */
html.ap-viewer-open {
    overflow: hidden;  /* the page behind stays still */
}

.ap-viewer {
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    margin: 0;
    padding: var(--space-5);
    border: 0;
    background: var(--app-viewer-backdrop);
    color: var(--white);
}

.ap-viewer::backdrop {
    background: transparent;
}

.ap-viewer[open] {
    display: flex;
    align-items: center;
    justify-content: center;
}

.ap-viewer-img {
    display: block;
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: var(--radius-md);
    user-select: none;
    touch-action: pan-y;  /* sideways swipes go to the viewer */
}

.ap-viewer-btn {
    position: absolute;
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--app-viewer-button-size);
    height: var(--app-viewer-button-size);
    padding: 0;
    border: 0;
    border-radius: var(--radius-pill);
    background: var(--header-surface);
    color: var(--white);
    cursor: pointer;
    transition: background-color 0.3s ease-in-out;
}

/* style.css gives buttons a pink border on focus/active; these have none */
.ap-viewer-btn:focus,
.ap-viewer-btn:active {
    border: 0;
    outline: none;
}

.ap-viewer-btn:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 2px;
}

@media (hover: hover) {
    .ap-viewer-btn:hover {
        background: var(--accent);
    }
}

.ap-viewer-btn[hidden] {
    display: none;
}

.ap-viewer-close {
    top: var(--space-4);
    right: var(--space-4);
}

.ap-viewer-prev,
.ap-viewer-next {
    top: 50%;
    transform: translateY(-50%);
}

.ap-viewer-prev {
    left: var(--space-4);
}

.ap-viewer-next {
    right: var(--space-4);
}

.ap-viewer-count {
    position: absolute;
    bottom: var(--space-4);
    left: 50%;
    transform: translateX(-50%);
    font-family: var(--font-sans);
    font-size: var(--text-label-size);
    line-height: var(--text-label-line);
    font-weight: var(--weight-semibold);
    color: var(--home-hero-subtitle-ink);
    font-variant-numeric: tabular-nums;
}

/* room at the sides for the arrows, at the top for the close button and at the bottom for the count */
@media (min-width: 768px) {
    .ap-viewer {
        padding: var(--space-10) calc(var(--app-viewer-button-size) + var(--space-8));
    }
}

@media (max-width: 767px) {
    .ap-viewer {
        padding: var(--space-12) var(--space-4);
    }

    .ap-viewer-prev,
    .ap-viewer-next {
        top: auto;
        bottom: var(--space-3);
        transform: none;
    }
}

/* ---- Side-scrolling strip (screenshots, picker options) ----
   Markup: <div class="ap-scroller"><ul class="ap-track">…</ul></div>. app-page.js adds the mouse drag, and per strip
   either previous / next buttons in another element (data-ap-arrows) or a progress bar (data-ap-progress). Touch
   screens swipe natively. Below 992px the track runs edge to edge; its inset lives inside the scroller (padding %
   resolves against the container), so the first item lines up with the page text. */
.ap-scroller {
    position: relative;
}

/* A section heading with controls at the end of its row (the strip's previous / next buttons). */
.ap-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-4);
}

.ap-head-nav {
    display: flex;
    flex: none;
    gap: var(--space-2);
}

.ap-head-nav[hidden] {
    display: none;
}

/* previous / next: a chevron in a thin circle, no fill */
.ap-arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--app-strip-button-size);
    height: var(--app-strip-button-size);
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: transparent;
    color: var(--ink);
    cursor: pointer;
    transition: color 0.3s ease-in-out, border-color 0.3s ease-in-out, opacity 0.3s ease-in-out;
}

.ap-arrow--next svg {
    transform: scaleX(-1);
}

/* style.css gives buttons a pink border on focus/active */
.ap-arrow:focus,
.ap-arrow:active {
    border-color: var(--border);
    outline: none;
}

.ap-arrow:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.ap-arrow:disabled {
    opacity: 0.35;
    cursor: default;
}

@media (hover: hover) {
    .ap-arrow:not(:disabled):hover {
        border-color: var(--accent);
        color: var(--accent);
    }
}

/* how much of the strip has been seen: a thin track with an accent fill */
.ap-progress {
    height: var(--app-strip-progress-height);
    margin-top: var(--space-4);
    overflow: hidden;
    border-radius: var(--radius-pill);
    background: var(--border);
}

.ap-progress[hidden] {
    display: none;
}

.ap-progress-fill {
    width: 0;
    height: 100%;
    border-radius: var(--radius-pill);
    background: var(--accent);
}

.ap-track {
    position: relative;  /* items' offsetLeft is measured from the track (app-page.js) */
    display: flex;
    gap: var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
    overflow-x: auto;
    scrollbar-width: none;
    overscroll-behavior-x: contain;
}

.ap-track::-webkit-scrollbar {
    display: none;
}

.ap-track > li {
    flex: none;
}

.ap-track:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

@media (max-width: 991px) {
    .ap-track {
        width: 100vw;
        margin-left: calc(50% - 50vw);
        padding-left: calc(50vw - 50%);
        padding-right: calc(50vw - 50%);
    }
}

@media (hover: hover) and (pointer: fine) {
    .ap-track {
        cursor: grab;
    }

    .ap-track--dragging {
        cursor: grabbing;
        user-select: none;
    }

    .ap-track--dragging > li {
        pointer-events: none;
    }
}

/* ---- 4. The loop: numbered steps, the number large in Bricolage ---- */
.ap-steps {
    display: grid;
    gap: var(--space-5);
    margin: var(--space-6) 0 0;
    padding: 0;
    list-style: none;
    counter-reset: ap-step;
}

@media (min-width: 576px) {
    .ap-steps {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (min-width: 992px) {
    .ap-steps {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

/* three phases: one column below 992px, three from 992px */
@media (min-width: 576px) and (max-width: 991px) {
    .ap-steps--3 {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (min-width: 992px) {
    .ap-steps--3 {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

.ap-step {
    padding: var(--space-6);
    border-radius: var(--radius-lg);
    background: var(--surface);
    counter-increment: ap-step;
}

/* on a white section the cards take the alternate surface, so they still read as cards */
.hm-section:not(.hm-section--alt) .ap-step {
    background: var(--surface-alt);
}

.ap-step::before {
    content: counter(ap-step, decimal-leading-zero);
    display: block;
    margin-bottom: var(--space-4);
    font-family: var(--font-display);
    font-weight: var(--weight-extrabold);
    font-size: var(--text-display-xl-size);
    line-height: var(--text-display-xl-line);
    color: var(--accent);
}

.ap-step-title {
    font-family: var(--font-display);
    font-weight: var(--weight-bold);
    font-size: var(--text-display-sm-size);
    line-height: var(--text-display-sm-line);
    color: var(--ink);
    margin: 0 0 var(--space-2);
}

.ap-step-text {
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    color: var(--ink);
    margin: 0;
}

/* a step's illustration under its text: a dark block flush with the card's sides and bottom (the card's rounded corners
   clip it), the image whole at its own proportions; pushed to the bottom so the blocks of a row line up */
.ap-step:has(.ap-step-media) {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.ap-step-media {
    margin: auto calc(-1 * var(--space-6)) calc(-1 * var(--space-6));
    padding: var(--space-5) 0;  /* air above and below only: the image keeps the block's full width */
    background: var(--gray-dark);
}

/* at least this much space between the text and the block */
.ap-step:has(.ap-step-media) .ap-step-text {
    margin-bottom: var(--space-5);
}

.ap-step-media img {
    display: block;
    width: 100%;
    height: auto;
}

/* an illustration drawn smaller inside its canvas: shown larger without changing the block (it grows into the padding) */
.ap-step-media img.ap-step-media-zoom {
    transform: scale(var(--app-step-art-zoom));
}

/* ---- 5. Picker: a strip of options; the chosen one opens its panel below ----
   Markup: .ap-picker > .ap-scroller > .ap-track > li > button.ap-option[aria-controls]
           .ap-picker > .ap-panel[id] (one per option; app-page.js shows the chosen one, all show without JS) */
.ap-picker .ap-scroller {
    margin-top: var(--space-6);
}

/* The chosen option's ring (3px outline + 2px offset) is drawn outside the cover, and the scrolling track clips
   whatever overflows it: room for the ring on every side. From 992px the track has no side inset, so it widens by
   the same amount on each side to keep the covers aligned with the page text. */
.ap-picker .ap-track {
    padding-top: var(--app-option-ring-room);
    padding-bottom: var(--app-option-ring-room);
}

@media (min-width: 992px) {
    .ap-picker .ap-track {
        margin-left: calc(-1 * var(--app-option-ring-room));
        margin-right: calc(-1 * var(--app-option-ring-room));
        padding-left: var(--app-option-ring-room);
        padding-right: var(--app-option-ring-room);
    }
}

.ap-option {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    width: var(--app-option-width);
    padding: 0;
    border: 0;
    background: none;
    color: var(--ink-muted);
    font-family: var(--font-sans);
    font-size: var(--text-label-size);
    line-height: var(--text-label-line);
    font-weight: var(--weight-semibold);
    text-align: left;
    cursor: pointer;
    transition: color 0.3s ease-in-out;
}

.ap-option img,
.ap-option-art {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: var(--app-cover-ratio);
    object-fit: cover;
    border-radius: var(--radius-md);
    outline: 3px solid transparent;
    outline-offset: 2px;
    transition: outline-color 0.3s ease-in-out;
}

/* style.css gives buttons a pink border on focus/active; these have none. */
.ap-option:focus,
.ap-option:active {
    border: 0;
    outline: none;
}

.ap-option:focus-visible img,
.ap-option:focus-visible .ap-option-art {
    outline-color: var(--ink-muted);
}

.ap-option[aria-pressed="true"] {
    color: var(--accent);
}

.ap-option[aria-pressed="true"] img,
.ap-option[aria-pressed="true"] .ap-option-art {
    outline-color: var(--accent);
}

@media (hover: hover) {
    .ap-option:hover {
        color: var(--accent);
    }
}

.ap-panel {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-6);
    margin-top: var(--space-8);
}

.ap-panel[hidden] {
    display: none;
}

@media (min-width: 768px) {
    .ap-panel {
        grid-template-columns: var(--app-panel-cover-width) minmax(0, 1fr);
        gap: var(--space-8);
        align-items: start;
    }
}

/* the cover holds the poster and, for a version that adds something (power-ups, a timer), a tag on its corner */
.ap-panel-cover {
    position: relative;
}

.ap-tag {
    position: absolute;
    top: var(--app-tag-offset);
    left: var(--app-tag-offset);
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--app-tag-size);
    height: var(--app-tag-size);
    border-radius: var(--radius-md);
    background: var(--accent);
    color: var(--white);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
    animation: ap-tag-in 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) 0.1s both;  /* pops in each time its version is chosen */
}

.ap-tag[hidden] {
    display: none;
}

.ap-tag .material-symbols-outlined {
    font-size: var(--app-tag-icon-size);
    font-variation-settings: 'FILL' 1;
}

@keyframes ap-tag-in {
    from {
        opacity: 0;
        transform: scale(0.5) rotate(-12deg);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .ap-tag {
        animation: none;
    }
}

.ap-panel-cover img {
    display: block;
    width: 100%;
    max-width: var(--app-panel-cover-width);
    height: auto;
    aspect-ratio: var(--app-cover-ratio);
    object-fit: cover;
    border-radius: var(--radius-lg);
}

.ap-modes {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: var(--space-4) 0 0;
    padding: 0;
    list-style: none;
}

/* version pills: the chosen one filled with the accent, the others outlined */
.ap-mode {
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: transparent;
    color: var(--ink);
    font-family: var(--font-sans);
    font-size: var(--text-label-size);
    line-height: var(--text-label-line);
    font-weight: var(--weight-semibold);
    cursor: pointer;
    transition: background-color 0.3s ease-in-out, color 0.3s ease-in-out, border-color 0.3s ease-in-out;
}

/* style.css gives buttons a pink border on focus/active */
.ap-mode:focus,
.ap-mode:active {
    border-color: var(--border);
    outline: none;
}

.ap-mode:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.ap-mode[aria-pressed="true"],
.ap-mode[aria-pressed="true"]:focus,
.ap-mode[aria-pressed="true"]:active {
    border-color: var(--accent);
    background: var(--accent);
    color: var(--white);
}

@media (hover: hover) {
    .ap-mode[aria-pressed="false"]:hover {
        border-color: var(--accent);
        color: var(--accent);
    }
}

.ap-panel-name {
    font-family: var(--font-display);
    font-weight: var(--weight-bold);
    font-size: var(--text-display-md-size);
    line-height: var(--text-display-md-line);
    color: var(--ink);
    margin: 0;
}

.ap-howto-label {
    color: var(--ink-muted);
    margin: var(--space-6) 0 var(--space-3);
}

.ap-howto {
    display: grid;
    gap: var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.ap-howto li {
    display: grid;
    grid-template-columns: var(--app-howto-icon-width) minmax(0, 1fr);
    gap: var(--space-4);
    align-items: center;
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    color: var(--ink);
}

/* the game's own instruction card for that step (in the page language) */
.ap-howto img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--radius-md);
}

/* desktop: the text beside each card stays a short line (two lines are fine) */
@media (min-width: 992px) {
    .ap-howto li span {
        max-width: var(--app-howto-text-width);
    }
}

/* below 992px: the card above its text; tablets up to a set width, phones the full width of the page */
@media (max-width: 991px) {
    .ap-howto li {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-2);
    }

    .ap-howto img {
        max-width: var(--app-howto-icon-width-sm);
    }
}

@media (max-width: 767px) {
    .ap-howto img,
    .ap-panel-cover img {
        max-width: none;
    }
}

/* An option's art drawn in CSS: a one-colour symbol (a white-on-transparent PNG used as a mask) in --ap-art-ink on a
   box of --ap-art-bg. Set the three per option: style="--ap-art-mask: url(…); --ap-art-ink: …; --ap-art-bg: …". */
.ap-option-art {
    position: relative;
    background: var(--ap-art-bg);
}

.ap-option-art::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--ap-art-ink);
    -webkit-mask: var(--ap-art-mask) center / contain no-repeat;
    mask: var(--ap-art-mask) center / contain no-repeat;
}

/* Square options (signs, palettes): square thumbnails and cover; images shown whole. */
.ap-picker--square {
    --app-cover-ratio: 1 / 1;
    --app-option-width: var(--app-option-width-square);
}

.ap-picker--square .ap-option img,
.ap-picker--square .ap-panel-cover img {
    object-fit: contain;
}

/* small: a dozen small options that fit in one row on a wide screen (signs); caption-size labels.
   Same gap between options as any other strip (16px). */
.ap-picker--small {
    --app-option-width: var(--app-option-width-small);
}

.ap-picker--small .ap-option {
    font-size: var(--text-caption-size);
    line-height: var(--text-caption-line);
}

/* wide screens (the 970px container): the options share the whole row, edge to edge, with no scrolling */
@media (min-width: 1200px) {
    .ap-picker--small .ap-track > li {
        flex: 1 1 0;
        min-width: 0;
    }

    .ap-picker--small .ap-option {
        width: 100%;
    }
}

/* compact: a short set of smaller square options that wraps onto new lines instead of scrolling sideways, so every
   option stays in view (palettes, beside another column) */
.ap-picker--compact {
    --app-option-width: var(--app-option-width-compact);
}

.ap-picker--compact .ap-track {
    flex-wrap: wrap;
    overflow: visible;
    width: auto;
    margin-left: calc(-1 * var(--app-option-ring-room));  /* the ring's room, so the first option lines up with the text */
    margin-right: calc(-1 * var(--app-option-ring-room));
    padding-left: var(--app-option-ring-room);
    padding-right: var(--app-option-ring-room);
    cursor: auto;
}

/* groups: a compact picker split into titled groups (palettes by family), each a wrapping row of small thumbnails;
   the names live in the chosen panel and in each thumbnail's tooltip and screen-reader label */
.ap-picker--groups {
    --app-option-width: var(--app-option-width-group);
}

.ap-group + .ap-group {
    margin-top: var(--space-4);
}

.ap-group-label {
    color: var(--ink-muted);
    margin: var(--space-5) 0 var(--space-2);
}

.ap-picker--groups .ap-track {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, var(--app-option-width)));  /* six per row; thumbnails shrink if needed */
    gap: var(--space-3);
}

.ap-picker--groups .ap-option {
    width: 100%;
    gap: 0;
}

/* the palette sits a little inside its box, so the ring (same size) frames it with some air */
.ap-picker--groups .ap-option img {
    box-sizing: border-box;
    padding: var(--app-option-group-inset);
    object-fit: contain;
}

/* ---- Phone mockup: an abstract app screen (blocks and lines, no real text) recoloured by --pm-* custom properties.
   A picker option with data-ap-vars="--pm-…: …;" sets them on the picker's [data-ap-vars-target] when chosen. ---- */
.ap-phone {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--app-phone-gap);
    width: 100%;
    max-width: var(--app-phone-width);
    aspect-ratio: 10 / 16;
    padding: var(--app-phone-pad-top) var(--app-phone-pad) var(--app-phone-pad);
    overflow: hidden;
    border: var(--app-phone-frame) solid var(--gray-dark);
    border-radius: var(--app-phone-radius);
    background: var(--white);
    box-shadow: var(--shadow);
}

/* the palette's screen colour, from the middle of the title card down; white above it */
.ap-phone-fill {
    position: absolute;
    inset: calc(var(--app-phone-pad-top) + var(--app-phone-title-height) / 2) 0 0;
    background: var(--pm-bg);
}

.ap-phone > :not(.ap-phone-fill) {
    position: relative;  /* above the fill */
}

.ap-phone,
.ap-phone * {
    transition: background-color 0.25s ease-out, border-color 0.25s ease-out;
}

@media (prefers-reduced-motion: reduce) {
    .ap-phone,
    .ap-phone * {
        transition: none;
    }
}

/* :where() keeps this at zero specificity, so the flex containers below (bubble) keep their display */
:where(.ap-phone) span {
    display: block;
}

.ap-phone-title {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    height: var(--app-phone-title-height);
    border-radius: var(--radius-md);
    background: var(--pm-title-bg);
}

.ap-phone-title span {
    width: 46%;
    height: 12px;
    border-radius: var(--radius-pill);
    background: var(--pm-title);
}

/* the card and the button: a narrower column on the right; the date block hangs off the card's left side */
.ap-phone-main {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--app-phone-gap);
    flex: 1;
    margin-left: var(--app-phone-date-width);
}

/* a day tab: flush with the card's top and left edge, 40% of its height, a short line for the date */
.ap-phone-date {
    position: absolute;
    top: 0;
    right: 100%;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    width: var(--app-phone-date-width);
    height: calc((100% - var(--app-phone-gap) - var(--app-phone-button-height)) * 0.4);
    padding-top: 14px;
    border-radius: var(--radius-md) 0 0 var(--radius-md);
    background: var(--pm-date-bg);
}

.ap-phone-date span {
    width: 50%;
    height: 5px;
    border-radius: var(--radius-pill);
    background: var(--pm-date);
}

.ap-phone-card {
    display: flex;
    flex-direction: column;
    gap: 9%;
    flex: 1;
    padding: 9% 7%;
    border-radius: 0 var(--radius-md) var(--radius-md) var(--radius-md);  /* square where the day tab meets it */
    background: var(--pm-card);
}

.ap-phone-guide {
    display: flex;
    align-items: center;
    gap: 8px;
}

.ap-phone-avatar {
    flex: none;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--pm-avatar);
}

/* a speech bubble: its square corner at the top left, toward the avatar */
.ap-phone-bubble {
    display: flex;
    align-items: center;
    width: 52%;
    height: 18px;
    padding: 0 8px;
    border-radius: 0 10px 10px 10px;
    background: var(--pm-bubble-bg);
}

.ap-phone-bubble span {
    width: 80%;
    height: 4px;
    border-radius: var(--radius-pill);
    background: var(--pm-bubble);
}

.ap-phone-lines {
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.ap-phone-lines span {
    width: var(--w);
    height: 5px;
    border-radius: var(--radius-pill);
    background: var(--pm-lines);
    opacity: 0.85;
}

.ap-phone-button {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    height: var(--app-phone-button-height);
    border-radius: var(--radius-pill);
    background: var(--pm-button-bg);
}

.ap-phone-button span {
    width: 38%;
    height: 6px;
    border-radius: var(--radius-pill);
    background: var(--pm-button);
}

/* the picker's preview column (phone + palette name): as wide as the phone; centred on phones and tablets */
.ap-panels {
    width: 100%;
    max-width: var(--app-phone-width);
    margin-top: var(--space-5);  /* level with the first group's label beside it */
}

@media (max-width: 991px) {
    .ap-panels {
        margin-inline: auto;
    }
}

/* side: the section's title and text head the options column; the preview column is centred on it vertically.
   Below 992px the column dissolves (display: contents) so the order reads title, text, preview, options. */
@media (min-width: 992px) {
    .ap-picker--side {
        align-items: center;
    }

    .ap-picker--side .ap-panels {
        position: relative;
        margin-top: 0;
    }

    /* the palette name hangs under the phone without adding to its column, so the phone itself is what is centred */
    .ap-picker--side .ap-panels .ap-panel {
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
    }
}

@media (max-width: 991px) {
    .ap-picker--side {
        row-gap: 0;
    }

    .ap-picker-side {
        display: contents;
    }

    .ap-picker-side > .hm-title,
    .ap-picker-side > .hm-text {
        order: -1;
    }
}

/* A panel with a wide cover: landscape art (the signs' illustrations) at its own proportions, given more room. */
@media (min-width: 768px) {
    .ap-panel--wide {
        grid-template-columns: var(--app-panel-wide-width) minmax(0, 1fr);
    }
}

.ap-panel--wide .ap-panel-cover img,
.ap-picker--square .ap-panel--wide .ap-panel-cover img {
    max-width: var(--app-panel-wide-width);
    aspect-ratio: auto;
    object-fit: initial;
}

@media (max-width: 767px) {
    .ap-panel--wide .ap-panel-cover img,
    .ap-picker--square .ap-panel--wide .ap-panel-cover img {
        max-width: none;
    }
}

/* A panel whose content stacks under its image at every width (narrow columns). */
.ap-panel--stack {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
    margin-top: var(--space-5);
}

@media (min-width: 768px) {
    .ap-panel--stack {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-4);
    }
}

.ap-panel--stack .ap-panel-cover img {
    max-width: var(--app-panel-stack-width);
}

/* short lines under a panel's name (dates, a description); a comfortable reading width */
.ap-panel-meta {
    max-width: var(--app-panel-text-width);
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    color: var(--ink);
    margin: var(--space-2) 0 0;
}

.ap-panel-meta--muted {
    color: var(--ink-muted);
}

/* ---- Cards: a grid of short items, each an image or icon, a name and a line of text (voices, features) ---- */
.ap-cards {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-5);
    margin: var(--space-6) 0 0;
    padding: 0;
    list-style: none;
}

@media (min-width: 576px) {
    .ap-cards {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* four short cards (voices): two per row from the smallest phone, four from 992px */
.ap-cards--4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 992px) {
    .ap-cards--4 {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

@media (max-width: 575px) {
    .ap-cards--4 {
        gap: var(--space-3);
    }

    .ap-cards--4 .ap-card {
        padding: var(--space-3);
        gap: var(--space-2);
    }
}

.ap-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-5);
    border-radius: var(--radius-lg);
    background: var(--surface-alt);
}

.hm-section--alt .ap-card {
    background: var(--surface);
}

.ap-card img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: var(--radius-md);
}

/* an icon instead of an illustration (an <img>, at the row-icon size) */
.ap-card .ap-card-icon {
    width: var(--home-row-icon-size);
    height: var(--home-row-icon-size);
    aspect-ratio: auto;
    object-fit: contain;
    border-radius: 0;
}

.ap-card .ap-row-symbol {
    width: auto;
    text-align: left;
}

.ap-card-title {
    font-family: var(--font-display);
    font-weight: var(--weight-bold);
    font-size: var(--text-display-sm-size);
    line-height: var(--text-display-sm-line);
    color: var(--ink);
    margin: 0;
}

.ap-card-text {
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    color: var(--ink);
    margin: 0;
}

/* a card with an image on its right: the text on the left; the image in a dark block as tall as the card, flush with its
   top, right and bottom (the card's rounded corners clip it), shown whole at its own proportions */
.ap-card--media {
    flex-direction: row;
    gap: 0;
    min-height: var(--app-card-media-min-height);
    padding: 0;
    overflow: hidden;
}

.ap-card-body {
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: var(--space-3);
    min-width: 0;
    padding: var(--space-5);
}

.ap-card-media {
    display: flex;
    flex: 0 0 var(--app-card-media-width);
    align-items: center;
    justify-content: center;
    background: var(--gray-dark);
}

.ap-card .ap-card-media img {
    width: 100%;
    height: 100%;
    aspect-ratio: auto;
    object-fit: contain;
    border-radius: 0;
}

/* desktop: the image a little smaller inside its block, with air above and below */
@media (min-width: 992px) {
    .ap-card-media {
        padding: var(--space-6) 0;
    }
}

/* ---- Band: a dark section (--gray-dark) headed by a logo, with a grid of features (Lunaria Premium) ---- */
/* an optional artwork over the dark ground (style="--ap-band-art: url(…)"): centred and scaled to cover the band at
   any size, cropped at the sides or top and bottom as the band's proportions change */
.ap-band {
    background: var(--ap-band-art, none) center / cover no-repeat, var(--gray-dark);
}

.ap-band-title {
    margin: 0;
}

.ap-band-logo {
    display: block;
    width: auto;
    height: var(--app-band-logo-height);
}

.ap-band-logo-ink {
    fill: var(--white);
}

.ap-band-logo-moon {
    fill: var(--token-points);  /* the logo's own yellow (#ffbe00) */
}

.ap-features {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-5);
    margin: var(--space-8) 0 0;
    padding: 0;
    list-style: none;
}

@media (min-width: 992px) {
    .ap-features {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

.ap-feature {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-5);
    border: 1px solid var(--app-hero-rule);
    border-radius: var(--radius-lg);
}

.ap-feature-icon {
    width: var(--app-feature-icon-size);
    height: var(--app-feature-icon-size);
    font-size: var(--app-feature-icon-size);
    line-height: 1;
    color: var(--white);
    margin-bottom: var(--space-2);
}

.ap-feature-title {
    font-family: var(--font-sans);
    font-size: var(--text-body-lg-size);
    line-height: var(--text-body-lg-line);
    font-weight: var(--weight-semibold);
    color: var(--white);
    margin: 0;
}

.ap-feature-text {
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    color: var(--gray-light);
    margin: 0;
}

/* style.css forces every <p> to 18px / 28px !important below 768px; a one-sentence feature keeps the body-lg size */
@media (max-width: 767px) {
    p.ap-feature-title {
        font-size: var(--text-body-lg-size) !important;
        line-height: var(--text-body-lg-line) !important;
    }
}

@media (max-width: 575px) {
    .ap-features {
        gap: var(--space-3);
    }

    .ap-feature {
        padding: var(--space-4);
    }
}

/* ---- Builder: one pick per pair of traits makes a result, shown on a dark card; a grid of every result below
   (Lumine: four traits make one of the 16 crystals). The two are linked: a pick updates the card and the grid, a grid
   tile sets the four picks. Markup:
     .ap-builder[data-ap-builder][data-ap-hero="…{CODE}…"][data-ap-letters="…{CODE}…"]
       .ap-builder-pick > .ap-traits > .ap-trait[data-ap-trait] > .ap-trait-option[data-value] (badge + name)
       .ap-builder-card > img[data-ap-builder-hero], img[data-ap-builder-letters], .ap-builder-text[data-ap-code] x n,
                          button[data-ap-shuffle]
     .ap-type-grid > li > button.ap-type-tile[data-ap-code]
   app-page.js joins the chosen values (in group order) into the code. */
.ap-builder {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-8);
    margin-top: var(--space-6);
}

/* side by side from 1200px; narrower, the options would squeeze their names, so the card goes under them */
@media (min-width: 1200px) {
    .ap-builder {
        grid-template-columns: minmax(0, 1fr) var(--app-builder-card-width);
        gap: var(--space-8);
        align-items: start;
    }
}

.ap-traits {
    display: grid;
    gap: var(--space-6);
    margin: 0;
    padding: 0;
    list-style: none;
}

.ap-trait-label {
    margin: var(--space-2) 0 0;  /* under its two options, so the first row's top lines up with the card's */
    color: var(--ink-muted);
}

/* the two options of a trait: two equal columns, so every trait's options line up */
.ap-trait-options {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
}

/* an option: a rounded rectangle with the value's icon, its name and its letter. The other option: a white rectangle,
   the icon in one solid muted colour. The chosen one: the result card's black, the icon in its own blue and white
   (drawn for a dark ground). */
.ap-trait-option {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: var(--space-3) var(--space-5);
    border: 0;
    border-radius: var(--radius-md);
    background: var(--white);
    color: var(--ink-muted);
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    font-weight: var(--weight-semibold);
    text-align: left;
    cursor: pointer;
    transition: background-color 0.3s ease-in-out, color 0.3s ease-in-out;
}

/* style.css gives buttons a pink border on focus/active */
.ap-trait-option:focus,
.ap-trait-option:active {
    border: 0;
    outline: none;
}

/* .ap-trait-options in front: Bootstrap's button:focus:not(:focus-visible) would otherwise win */
.ap-trait-options .ap-trait-option:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* the icon's shape as a mask: not chosen, filled with the text's grey (--ink-muted, one solid colour); chosen, the
   <img> inside shows the icon in its own colours */
.ap-trait-icon {
    display: block;
    flex: none;
    width: var(--app-trait-icon-size);
    height: var(--app-trait-icon-size);
    background-color: var(--ink-muted);
    -webkit-mask: var(--ap-icon) center / contain no-repeat;
    mask: var(--ap-icon) center / contain no-repeat;
    transition: background-color 0.3s ease-in-out;
}

.ap-trait-icon img {
    display: block;
    width: 100%;
    height: 100%;
    opacity: 0;
    transition: opacity 0.3s ease-in-out;
}

.ap-trait-name {
    flex: 1;
    min-width: 0;
}

.ap-trait-letter {
    flex: none;
    font-family: var(--font-display);
    font-weight: var(--weight-bold);
    font-size: var(--text-body-lg-size);
    line-height: 1;
}

.ap-trait-option[aria-pressed="true"] {
    background: var(--app-builder-card-bg);
    color: var(--white);
}

.ap-trait-option[aria-pressed="true"] .ap-trait-icon {
    background-color: transparent;
}

.ap-trait-option[aria-pressed="true"] .ap-trait-icon img {
    opacity: 1;
}

@media (hover: hover) {
    .ap-trait-option[aria-pressed="false"]:hover {
        color: var(--ink);
    }

    .ap-trait-option[aria-pressed="false"]:hover .ap-trait-icon {
        background-color: var(--ink);
    }
}

/* below 1200px (the builder stacked: tablets and phones): the traits closer together, each label and Shuffle centred
   under the options */
@media (max-width: 1199px) {
    .ap-traits {
        gap: var(--space-4);
    }

    .ap-trait-label {
        text-align: center;
    }

    .ap-builder-pick {
        display: flex;
        flex-direction: column;
    }

    .ap-builder-pick .ap-builder-shuffle {
        align-self: center;
    }

    .ap-type-tile-letters {
        height: 90%;  /* the tiles are small here: bigger letters */
    }
}

/* phones: the icon top left, the letter top right and the name under them across the option, so a long single-word
   name (Translúcido) has the full width and the option can keep generous padding */
@media (max-width: 575px) {
    .ap-trait-option {
        display: grid;
        grid-template-columns: auto 1fr;
        grid-template-areas: "icon letter" "name name";
        align-items: center;
        gap: var(--space-2);
        padding: var(--space-3) var(--space-4);
        font-size: var(--text-label-size);
        line-height: var(--text-label-line);
    }

    .ap-trait-icon {
        grid-area: icon;
        width: var(--app-trait-icon-size-sm);
        height: var(--app-trait-icon-size-sm);
    }

    .ap-trait-letter {
        grid-area: letter;
        justify-self: end;
    }

    .ap-trait-name {
        grid-area: name;
    }
}

.ap-builder-pick .ap-builder-note {
    margin-top: var(--space-6);
    max-width: var(--app-panel-text-width);
}

/* the result: a dark card with the type's phrase, the crystal, its letters (an image), its name and description */
.ap-builder-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: var(--space-6);
    border-radius: var(--radius-lg);
    background: var(--app-builder-card-bg);
    color: var(--white);
    text-align: center;
}

/* below 1200px: under the traits, no wider than beside them */
@media (max-width: 1199px) {
    .ap-builder-card {
        width: 100%;
        max-width: var(--app-builder-card-width);
        margin-inline: auto;
    }
}

.ap-builder-hero {
    display: block;
    width: 100%;
    max-width: var(--app-builder-hero-width);
    height: auto;
}

.ap-builder-letters {
    display: block;
    width: auto;
    height: var(--app-builder-letters-height);
    margin-top: 0;  /* right under the crystal (the render's own air is the gap) */
}

.ap-builder-text[hidden] {
    display: none;
}

.ap-builder-subtitle {
    margin: 0;  /* the type's phrase heads the card; no gap to the crystal, whose render has its own air */
    font-family: var(--font-sans);
    font-size: var(--text-label-size);
    line-height: var(--text-label-line);
    font-weight: var(--weight-semibold);
    color: var(--home-hero-subtitle-ink);
}

.ap-builder-name {
    margin: var(--space-2) 0 0;
    font-family: var(--font-display);
    font-weight: var(--weight-bold);
    font-size: var(--text-display-sm-size);
    line-height: var(--text-display-sm-line);
    color: var(--white);
}

.ap-builder-desc {
    margin: var(--space-2) 0 0;
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    color: var(--home-hero-subtitle-ink);
}

/* Shuffle, under the traits: a white pill on the grey section, the accent on hover */
.ap-builder-shuffle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-5);
    padding: var(--space-2) var(--space-5);
    border: 0;
    border-radius: var(--radius-pill);
    background: var(--white);
    color: var(--ink);
    font-family: var(--font-sans);
    font-size: var(--text-label-size);
    line-height: var(--text-label-line);
    font-weight: var(--weight-semibold);
    cursor: pointer;
    transition: background-color 0.3s ease-in-out, color 0.3s ease-in-out;
}

.ap-builder-shuffle:focus,
.ap-builder-shuffle:active {
    border: 0;
    outline: none;
}

.ap-builder-pick .ap-builder-shuffle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.ap-builder-shuffle .material-symbols-outlined {
    font-size: var(--app-fact-icon-size);
}

@media (hover: hover) {
    .ap-builder-shuffle:hover {
        background: var(--accent);
        color: var(--white);
    }
}

/* every result, in a grid, opened by a link-styled button under the builder (.ap-more with a down arrow that turns up
   when open); each tile a close-up of the crystal under its letters, the chosen one ringed */
button.ap-builder-all {
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
}

button.ap-builder-all:focus,
button.ap-builder-all:active {
    border: 0;
    outline: none;
}

button.ap-builder-all:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

.ap-builder-all[aria-expanded="true"] .material-symbols-outlined {
    transform: rotate(180deg);
}

/* button.… so these outrank the .ap-more:hover rule (a sideways nudge) further down the file */
@media (hover: hover) {
    button.ap-builder-all:hover .material-symbols-outlined {
        transform: translateY(var(--space-1));
    }

    button.ap-builder-all[aria-expanded="true"]:hover .material-symbols-outlined {
        transform: rotate(180deg) translateY(var(--space-1));
    }
}

.ap-type-grid[hidden] {
    display: none;
}

.ap-type-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-4);
    margin: var(--space-5) 0 0;
    padding: 0;
    list-style: none;
}

@media (min-width: 768px) {
    .ap-type-grid {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

.ap-type-tile {
    position: relative;
    display: block;
    width: 100%;
    padding: 0;
    overflow: hidden;
    border: 0;
    border-radius: var(--radius-md);
    background: var(--gray-dark);
    aspect-ratio: var(--app-type-tile-ratio);
    outline: 3px solid transparent;
    outline-offset: 2px;
    cursor: pointer;
    transition: outline-color 0.3s ease-in-out;
}

.ap-type-tile:focus,
.ap-type-tile:active {
    border: 0;
}

/* .ap-type-grid in front: Bootstrap's button:focus:not(:focus-visible) would otherwise drop the ring after a click */
.ap-type-grid .ap-type-tile:focus-visible {
    outline: 3px solid var(--ink-muted);
}

.ap-type-grid .ap-type-tile[aria-pressed="true"] {
    outline: 3px solid var(--accent);
}

.ap-type-tile-bg {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.3s ease-in-out;
}

/* a light shade under the letters, so the light lettering reads on the brightest close-ups */
.ap-type-tile::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--app-type-tile-shade);
}

.ap-type-tile-letters {
    position: absolute;
    top: 50%;
    left: 50%;
    z-index: 1;
    width: auto;
    height: 70%;
    transform: translate(-50%, -50%);
}

@media (hover: hover) {
    .ap-type-tile:hover .ap-type-tile-bg {
        transform: scale(1.05);
    }
}

@media (prefers-reduced-motion: reduce) {
    .ap-type-tile-bg {
        transition: none;
    }
}

/* ---- A layered sample (Lumine: how a thought is built): a card with a tag and one row per layer; the first is free
   (its icon in the accent), the others locked (muted, with the Gems mark). The text is drawn as lines. ---- */
.ap-reading {
    max-width: var(--app-reading-width);
    margin-top: var(--space-6);
    padding: var(--space-5) var(--space-6);
    border-radius: var(--radius-lg);
    background: var(--surface-alt);
}

.ap-reading-tag {
    display: inline-block;
    margin: 0 0 var(--space-4);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    background: var(--accent);
    color: var(--white);
}

.ap-reading-layers {
    display: grid;
    gap: var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.ap-reading-layer {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
}

.ap-reading-icon {
    flex: none;
    font-size: var(--app-reading-icon-size);
    line-height: 1;
    color: var(--accent);
    font-variation-settings: 'FILL' 1;
}

.ap-reading-body {
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 0;
}

.ap-reading-name {
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    font-weight: var(--weight-semibold);
    color: var(--ink);
}

.ap-reading-lines {
    display: grid;
    gap: var(--space-2);
}

.ap-reading-lines span {
    display: block;
    width: var(--w, 100%);
    height: var(--app-reading-line-height);
    border-radius: var(--radius-pill);
    background: var(--ink-muted);
    opacity: 0.45;
}

.ap-reading-layer--locked .ap-reading-icon,
.ap-reading-layer--locked .ap-reading-name {
    color: var(--ink-muted);
}

.ap-reading-layer--locked .ap-reading-lines span {
    opacity: 0.2;
}

/* the free layer's topic (the area chosen beside the sample) and the locked layers' one-line description */
.ap-reading-topic {
    font-family: var(--font-sans);
    font-size: var(--text-label-size);
    line-height: var(--text-label-line);
    font-weight: var(--weight-semibold);
    color: var(--accent);
}

.ap-reading-what {
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    color: var(--ink);
}

.ap-reading-desc {
    font-family: var(--font-sans);
    font-size: var(--text-label-size);
    line-height: var(--text-label-line);
    color: var(--ink-muted);
}

.ap-reading-layer--locked .ap-reading-body {
    gap: 0;
}

/* the Gems mark on a locked layer (gemsIconColor.svg as a mask in --token-gem, as in the Gems section) */
.ap-reading-gems {
    flex: none;
    width: var(--app-reading-icon-size);
    height: var(--app-reading-icon-size);
    background-color: var(--token-gem);
    -webkit-mask: url("/assets/images/icons/gemsIconColor.svg") center / contain no-repeat;
    mask: url("/assets/images/icons/gemsIconColor.svg") center / contain no-repeat;
}

/* the sample beside a list that follows the crystal builder (the chosen type's growth areas): side by side from 992px */
.ap-thought {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-6);
    margin-top: var(--space-6);
}

/* from 992px the areas column is as tall as the sample beside it, and no wider than --app-areas-width */
@media (min-width: 992px) {
    .ap-thought {
        grid-template-columns: var(--app-reading-width) minmax(0, var(--app-areas-width));
        gap: var(--space-10);
        align-items: stretch;
    }
}

.ap-thought .ap-reading {
    margin-top: 0;
}

/* stacked (below 992px): the sample and the areas centred, as wide as the sample */
@media (max-width: 991px) {
    .ap-thought .ap-reading,
    .ap-areas {
        width: 100%;
        max-width: var(--app-reading-width);
        margin-inline: auto;
    }
}

.ap-areas {
    display: flex;
    flex-direction: column;
}

.ap-areas-label {
    margin: 0;
    color: var(--ink-muted);
}

.ap-areas-hint {
    margin: var(--space-1) 0 0;
    font-family: var(--font-sans);
    font-size: var(--text-label-size);
    line-height: var(--text-label-line);
    color: var(--ink-muted);
}

/* the areas: six blocks of one width, one above the other, sharing the column's height (the version pills' colours),
   one chosen; its name and pattern show in the sample's free layer */
.ap-areas-list {
    display: grid;
    grid-auto-rows: 1fr;
    flex: 1;
    gap: var(--space-2);
    margin: var(--space-3) 0 0;
}

.ap-areas-list[hidden] {
    display: none;
}

.ap-areas-list .ap-area {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: var(--app-area-min-height);
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-md);
    text-align: left;
}

/* an illustration on the left (transparent, no ground) and the coming-soon block on the right, their tops aligned.
   Phones: stacked, the illustration as wide as the block. Tablets: side by side. Desktop: the block pushed to the right,
   further from the illustration. */
.ap-next {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-6);
    align-items: start;
    margin-top: var(--space-6);
}

.ap-next-art,
.ap-next .ap-soon {
    width: 100%;
    max-width: var(--app-reading-width);
    margin-inline: auto;
}

.ap-next-art {
    display: block;
    height: auto;
}

.ap-next .ap-soon {
    margin-top: 0;
}

@media (min-width: 768px) {
    .ap-next {
        grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
    }

    .ap-next-art,
    .ap-next .ap-soon {
        margin-inline: 0;
    }
}

@media (min-width: 992px) {
    .ap-next {
        grid-template-columns: var(--app-next-art-width) minmax(0, 1fr);
        gap: var(--space-12);
    }

    .ap-next .ap-soon {
        justify-self: end;
    }
}

/* ---- Coming soon: the dashed "not yet" block of points.html (.pt-state--next): no fill, a dashed outline on the
   block and on its tag, the text in the normal ink ---- */
.ap-soon {
    max-width: var(--app-reading-width);
    margin-top: var(--space-6);
    padding: var(--space-5);
    border: 1.5px dashed var(--gray-medium);
    border-radius: var(--radius-md);
}

.ap-soon-tag {
    display: inline-block;
    margin: 0 0 var(--space-3);
    padding: var(--space-1) var(--space-3);
    border: 1.5px dashed var(--gray-medium);
    border-radius: var(--radius-pill);
    color: var(--ink);
}

.ap-soon-list {
    display: grid;
    gap: var(--space-3);
    margin: 0;
    padding: 0;
    list-style: none;
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    color: var(--ink);
}

/* each item with its icon (a Material Symbol, filled, in the accent) */
.ap-soon-list li {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
}

/* heavier, for a thin-lined symbol beside the solid ones (the page loads wght 400..700) */
.ap-soon-list .material-symbols-outlined.ap-soon-heavy {
    font-variation-settings: 'FILL' 1, 'wght' 600;
}

/* a one-ink SVG icon in the same place, as a mask in the accent (Lumine: the provisional prism) */
.ap-soon-mask {
    flex: none;
    width: var(--app-reading-icon-size);
    height: var(--text-body-line);
    background-color: var(--accent);
    -webkit-mask: var(--ap-icon) center / var(--app-reading-icon-size) no-repeat;
    mask: var(--ap-icon) center / var(--app-reading-icon-size) no-repeat;
}

.ap-soon-list .material-symbols-outlined {
    flex: none;
    font-size: var(--app-reading-icon-size);
    line-height: var(--text-body-line);
    color: var(--accent);
    font-variation-settings: 'FILL' 1;
}

/* a small dashed tag beside a row's title, for something not available yet (the coming-soon look of .ap-soon) */
.ap-row-tag {
    display: inline-block;
    margin-left: var(--space-2);
    padding: 0 var(--space-2);
    border: 1.5px dashed var(--gray-medium);
    border-radius: var(--radius-pill);
    color: var(--ink-muted);
    vertical-align: middle;
}

/* ---- 6. Token section: an intro note beside icon rows (the What-it-is pattern), then a link to the token's page ----
   .ap-note is the note of points.html (.pt-note): muted text set off by a thin rule on its left. */
.ap-note {
    margin: 0;
    padding-left: var(--space-4);
    border-left: 2px solid var(--border);
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    color: var(--ink-muted);
}

/* the token's own icon, larger, to the left of the intro note's grey rule (the section is about that token) */
.ap-lead {
    display: flex;
    align-items: flex-start;
    gap: var(--space-4);
}

.ap-lead-icon {
    flex: none;
    width: var(--app-lead-icon-size);
    height: var(--app-lead-icon-size);
}

/* Gems: the one-ink icon as a mask in --token-gem, so it follows the theme (as points.css draws it) */
.ap-lead-icon--gems {
    background-color: var(--token-gem);
    -webkit-mask: url("/assets/images/icons/gemsIconColor.svg") center / contain no-repeat;
    mask: url("/assets/images/icons/gemsIconColor.svg") center / contain no-repeat;
}

.ap-more,
.ap-more:visited {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    margin-top: var(--space-6);
    color: var(--accent);
    font-family: var(--font-sans);
    font-size: var(--text-body-size);
    line-height: var(--text-body-line);
    font-weight: var(--weight-semibold);
    text-decoration: none;
}

/* style.css turns a:hover / a:active orange; the link keeps its accent */
.ap-more:hover,
.ap-more:active,
.ap-more:focus {
    color: var(--accent);
}

.ap-more .material-symbols-outlined {
    font-size: var(--text-body-lg-size);
    transition: transform 0.3s ease-in-out;
}

@media (hover: hover) {
    .ap-more:hover span:first-child {
        text-decoration: underline;
        text-underline-offset: 3px;
    }

    .ap-more:hover .material-symbols-outlined {
        transform: translateX(var(--space-1));
    }
}

/* ---- 7. Closing: dark band, centred, the same actions as the hero ---- */
/* no space below: the band runs straight into the footer (also dark) */
.ap-closing {
    padding: var(--space-12) 0 0;
    background: var(--gray-dark);
    text-align: center;
}

.ap-closing-title {
    font-family: var(--font-display);
    font-weight: var(--weight-bold);
    font-size: var(--text-display-lg-size);
    line-height: var(--text-display-lg-line);
    color: var(--white);
    margin: 0;
}

.ap-closing-text {
    font-family: var(--font-sans);
    font-size: var(--text-body-lg-size);
    line-height: var(--text-body-lg-line);
    color: var(--home-hero-subtitle-ink);
    margin: var(--space-3) auto 0;
    max-width: 40ch;
}

.ap-closing .ap-actions {
    justify-content: center;
}

.ap-closing .ap-discord,
.ap-closing .ap-community {
    text-align: left;  /* the closing centres its text; a two-line link reads from its left edge */
}

@media (max-width: 767px) {
    .ap-closing {
        padding: var(--space-10) 0 0;
    }
}
