/* App detail page ----------------------------------------------------------- */

/* Hero card: icon + identity + action row, with the fact strip as the
   card's footer band (hairline-separated stat cells). */
.app-hero {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: 26px 26px 0;
    margin-bottom: 24px;
}

.app-hero-top {
    display: flex;
    align-items: flex-start;
    gap: 22px;
}

.app-hero-icon {
    width: 96px;
    height: 96px;
    flex: none;
    border-radius: 24px;
    border: 1px solid var(--border);
    object-fit: cover;
    background: var(--surface-2);
    box-shadow: var(--shadow-card);
}

.app-hero-info {
    min-width: 0;
}

.app-hero-info .cat-chip {
    margin-bottom: 8px;
}

.app-hero h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.02em;
}

.app-hero-dev {
    font-size: 14px;
    color: var(--text-muted);
    margin-top: 3px;
}

.app-hero-actions {
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
    margin-top: 22px;
}

/* Disclosure under the CTA: the download is the PremiBox host app, not the
   game binary. Muted, small, with the info glyph — clear but not alarming, so
   the download is transparent (never a deceptive-download flag). */
.install-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-top: 14px;
    max-width: 640px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-muted);
}
.install-note svg {
    width: 16px;
    height: 16px;
    flex: none;
    margin-top: 1px;
    color: var(--accent, currentColor);
}

/* Quiet secondary links next to the main CTA (official store listing,
   affiliate share); deliberately muted so they never compete with it. */
.store-link {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-muted);
    transition: color 120ms ease;
}

.store-link:hover {
    color: var(--text);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.store-link svg {
    width: 14px;
    height: 14px;
    flex: none;
}

/* Fact strip: compact stat cells separated by hairlines, bleeding to the
   hero card's edges as its footer band. */
.fact-strip {
    display: flex;
    list-style: none;
    margin: 24px -26px 0;
    border-top: 1px solid var(--border);
    overflow-x: auto;
}

.fact-strip li {
    flex: 1;
    min-width: 106px;
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 13px 20px 15px;
    border-left: 1px solid var(--border);
}

.fact-strip li:first-child {
    border-left: 0;
}

.fact-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-muted);
    white-space: nowrap;
}

.fact-value {
    font-size: 14.5px;
    font-weight: 650;
    white-space: nowrap;
}

/* Safe / caution accents (e.g. whether root is required). */
.fact-value-ok {
    color: var(--green);
}

.fact-value-warn {
    color: var(--amber);
}

/* Mod features panel: ONE white card (same carded language as the hero) with
   a premium amber accent — never a card-on-tinted-panel, which muddied the
   surfaces. A thin amber rule tops the card; a soft amber-washed header banner
   carries the filled icon badge + title; the features sit in a hairline grid
   (1px gaps reveal the --border underneath — no nested boxes) so every row is
   flush on the same surface. A name-only legacy entry (.mod-item-bare) centres
   its badge on the name, keeping the old clean checklist look. */
.mod-panel {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-raised);
    overflow: hidden;
    margin-bottom: 24px;
}

/* Premium accent rule across the very top of the card. */
.mod-panel::before {
    content: "";
    position: absolute;
    inset: 0 0 auto;
    height: 3px;
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--amber) 45%, transparent),
        var(--amber) 45%,
        color-mix(in srgb, var(--amber) 45%, transparent));
}

.mod-panel-head {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 19px 22px;
    /* Faint amber wash so the header reads as a banner without a second card. */
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--amber) 9%, var(--surface)),
        var(--surface));
    border-bottom: 1px solid var(--border);
}

.mod-panel-icon {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    border-radius: 12px;
    background: var(--amber);
    /* The glyph borrows the surface colour, so it stays legible on the amber
       badge in either theme (white on light, near-black on dark amber). */
    color: var(--surface);
    box-shadow: 0 2px 8px color-mix(in srgb, var(--amber) 40%, transparent);
}

.mod-panel-icon svg {
    width: 22px;
    height: 22px;
}

.mod-panel-title {
    margin: 0;
    font-size: 15.5px;
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.2;
}

.mod-panel-sub {
    margin: 3px 0 0;
    font-size: 12.5px;
    line-height: 1.4;
    color: var(--text-muted);
}

/* "Cảm ơn sự đóng góp của …" — the credit line of the mod list, naming the
   members who put diamonds on a request for this game. Diamond CYAN, the
   wallet's colour in the app, so the honour reads as "bought with diamonds" at
   a glance and never as one more amber feature row. ONE wrapping row — label,
   the names as face chips, the (?) at the far end (operator 06/10: keep it
   small, the list below is what the panel is for). It is a <details>: the
   paragraph saying what this is stays folded until the (?) is pressed. */
.mod-thanks {
    --gem: #0e8ea8;
    position: relative;
    background: linear-gradient(90deg, color-mix(in srgb, var(--gem) 9%, var(--surface)), var(--surface) 70%);
    border-bottom: 1px solid var(--border);
    overflow: hidden;
}

[data-theme="dark"] .mod-thanks {
    --gem: #35d0e8;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .mod-thanks {
        --gem: #35d0e8;
    }
}

/* A slow glint across the band — the only motion on the panel. */
.mod-thanks::after {
    content: "";
    position: absolute;
    inset: 0 auto 0 -40%;
    width: 30%;
    background: linear-gradient(100deg, transparent, color-mix(in srgb, var(--gem) 14%, transparent), transparent);
    transform: skewX(-18deg);
    animation: mod-thanks-glint 7s ease-in-out 1.2s infinite;
    pointer-events: none;
}

@keyframes mod-thanks-glint {
    0%, 70% { left: -40%; }
    100% { left: 130%; }
}

/* The whole row is the <summary>, but only the (?) toggles it: the pointer is
   taken off the row and handed back to the (?) alone. A click there bubbles
   to the summary, which is what opens the <details>. The keyboard still
   reaches it — the summary keeps its focus and its Enter. */
.mod-thanks-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 7px 10px;
    padding: 10px 14px 10px 22px;
    list-style: none;
    cursor: default;
    pointer-events: none;
}

.mod-thanks-row::-webkit-details-marker {
    display: none;
}

.mod-thanks-row:focus-visible {
    outline: 2px solid var(--gem);
    outline-offset: -2px;
}

.mod-thanks-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--gem);
}

.mod-thanks-label svg {
    flex: none;
    width: 15px;
    height: 15px;
    filter: drop-shadow(0 0 4px color-mix(in srgb, var(--gem) 55%, transparent));
}

.mod-thanks-names {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    min-width: 0;
}

.mod-thanks-person {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 100%;
    padding: 2px 10px 2px 2px;
    background: color-mix(in srgb, var(--gem) 9%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--gem) 32%, var(--border));
    border-radius: 999px;
}

.mod-thanks-face {
    flex: none;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    object-fit: cover;
    background: color-mix(in srgb, var(--gem) 22%, var(--surface-2));
}

.mod-thanks-initial {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 10.5px;
    font-weight: 700;
    color: var(--gem);
}

.mod-thanks-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12.5px;
    font-weight: 650;
    color: var(--text);
}

.mod-thanks-help {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    margin-left: auto;
    border-radius: 50%;
    color: var(--text-muted);
    cursor: pointer;
    pointer-events: auto;
    transition: color 140ms ease, background-color 140ms ease;
}

.mod-thanks-help svg {
    width: 18px;
    height: 18px;
}

.mod-thanks-help:hover,
.mod-thanks[open] .mod-thanks-help {
    color: var(--gem);
    background: color-mix(in srgb, var(--gem) 12%, transparent);
}

/* Narrow: the label already fills its own line, so the (?) joins it on the
   right and the names drop to the next — rather than the (?) wrapping to the
   end of the names, away from the heading it explains. */
@media (max-width: 560px) {
    .mod-thanks-label {
        flex: 1 1 auto;
        white-space: normal;
    }

    .mod-thanks-help {
        order: 1;
    }

    .mod-thanks-names {
        order: 2;
        flex-basis: 100%;
    }
}

/* The folded explanation: one idea per line, each led by a small two-facet
   gem — light top, deep bottom, the same stone as the badge — instead of a
   paragraph to wade through (operator 06/10). A cyan rule down the left marks
   it as the band's footnote, not a second card. */
.mod-thanks-about {
    list-style: none;
    display: grid;
    gap: 7px;
    margin: 0 22px 12px;
    padding: 10px 14px 10px 12px;
    max-width: 760px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-muted);
    background: color-mix(in srgb, var(--gem) 5%, var(--surface-2));
    border-left: 2px solid color-mix(in srgb, var(--gem) 60%, var(--border));
    border-radius: var(--radius-sm);
}

.mod-thanks-about li {
    position: relative;
    padding-left: 18px;
}

.mod-thanks-about li::before {
    content: "";
    position: absolute;
    left: 1px;
    top: 0.5em;
    width: 9px;
    height: 9px;
    clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--gem) 55%, #fff) 0 50%,
        color-mix(in srgb, var(--gem) 80%, #000) 50% 100%);
}

@media (prefers-reduced-motion: reduce) {
    .mod-thanks::after {
        animation: none;
        display: none;
    }
}

/* Hairline grid: the 1px gap lets the list's own --border background show
   through as clean dividers, so cells share the card surface with no boxes. */
.mod-list {
    list-style: none;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1px;
    background: var(--border);
}

/* An odd last feature spans the full width, so a half-row of bare divider
   colour never shows. */
.mod-item:last-child:nth-child(odd) {
    grid-column: 1 / -1;
}

.mod-item {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 16px 22px;
    background: var(--surface);
    transition: background-color 140ms ease;
}

.mod-item:hover {
    background: color-mix(in srgb, var(--amber) 5%, var(--surface));
}

/* Check badge sits inline with the name — one tidy row, no separate column, so
   the description and note below run the full width of the cell. */
.mod-item-head {
    display: flex;
    align-items: center;
    gap: 10px;
}

.mod-item-check {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--amber);
    color: var(--surface);
    box-shadow: 0 1px 3px color-mix(in srgb, var(--amber) 45%, transparent);
}

.mod-item-check svg {
    width: 14px;
    height: 14px;
}

.mod-item-name {
    margin: 0;
    min-width: 0;
    font-size: 14.5px;
    font-weight: 650;
    line-height: 1.35;
}

/* Risk badge. Follows the same lesson as the caution callout below: the
   saturated colour is a TINT, not a fill. A row of solid red pills down a
   feature list reads as "this page is broken", which is the opposite of the
   message — the warning has to be legible without making a working product
   look alarming. So: warm/red ink on a barely-tinted surface, hairline frame,
   and the icon carries the only strong colour.

   Two levels only, matching the MOD menu inside the app, because a scale the
   reader has to learn is a scale they will guess at. */
.mod-item-risk {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-left: auto;
    padding: 3px 9px 3px 7px;
    font-size: 11px;
    font-weight: 650;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    white-space: nowrap;
    border-radius: 999px;
}

.mod-item-risk svg {
    width: 12px;
    height: 12px;
}

.mod-item-risk-warn {
    color: color-mix(in srgb, var(--amber) 45%, var(--text-muted));
    background: color-mix(in srgb, var(--amber) 8%, var(--surface-2));
    border: 1px solid color-mix(in srgb, var(--amber) 22%, var(--border));
}

.mod-item-risk-warn svg {
    color: color-mix(in srgb, var(--amber) 80%, var(--text-muted));
}

.mod-item-risk-danger {
    color: color-mix(in srgb, var(--danger) 55%, var(--text-muted));
    background: color-mix(in srgb, var(--danger) 9%, var(--surface-2));
    border: 1px solid color-mix(in srgb, var(--danger) 26%, var(--border));
}

.mod-item-risk-danger svg {
    color: color-mix(in srgb, var(--danger) 85%, var(--text-muted));
}

/* Narrow cells: the badge stops sharing a line with the name rather than
   squeezing it to two or three words per line. */
@media (max-width: 560px) {
    .mod-item-head {
        flex-wrap: wrap;
    }

    .mod-item-risk {
        margin-left: 34px;
    }
}

.mod-item-desc {
    margin: 0;
    font-size: 13px;
    line-height: 1.55;
    color: var(--text-muted);
}

/* Caution callout: reads as an aside, never as body copy — and never as a
   yellow block. Raw --amber as text on --amber-soft was glare on the dark
   skins (#fbbf24 bold on brown, seven times down one panel), so amber is only
   a TINT here: warm muted ink, a surface barely warmed, a hairline frame, and
   the one saturated mark left is the icon and a softened left rule. */
.mod-item-note {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    margin: 4px 0 0;
    padding: 9px 12px;
    font-size: 12.5px;
    line-height: 1.5;
    font-weight: 400;
    color: color-mix(in srgb, var(--amber) 35%, var(--text-muted));
    background: color-mix(in srgb, var(--amber) 6%, var(--surface-2));
    border: 1px solid color-mix(in srgb, var(--amber) 16%, var(--border));
    border-left: 2px solid color-mix(in srgb, var(--amber) 50%, var(--border));
    border-radius: var(--radius-sm);
}

.mod-item-note svg {
    width: 14px;
    height: 14px;
    flex: none;
    margin-top: 2px;
    color: color-mix(in srgb, var(--amber) 75%, var(--text-muted));
}

/* Screenshots + description sit on card surfaces so every app-section
   shares the same carded language as the file list / info table. */
.app-section .screenshot-strip {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    padding: 16px 16px 12px;
}

.app-section .prose {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    padding: 24px 26px;
    /* Card spans the section like the other surfaces; the text keeps a
       readable measure inside it. */
    max-width: none;
}

.app-section .prose > * {
    max-width: 760px;
}

/* Repeat of the primary download CTA at the end of the description.
   The app name makes the label long, so let it wrap on narrow screens. */
.content-download {
    margin-top: 22px;
}

.content-download .btn {
    max-width: 100%;
    white-space: normal;
    flex-wrap: wrap;
    text-align: center;
}

/* Downloadable file variants ---------------------------------------------------------- */

.file-list {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
}

.file-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 12px 18px;
    flex-wrap: wrap;
}

.file-row + .file-row {
    border-top: 1px solid var(--border);
}

.file-info {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.file-name {
    font-weight: 600;
    font-size: 13.5px;
    overflow-wrap: anywhere;
}

.file-meta {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    font-size: 12.5px;
    color: var(--text-muted);
}

.file-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 10px;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--green);
    background: var(--green-soft);
    border-radius: 999px;
    white-space: nowrap;
}

/* Guides on the app detail page --------------------------------------------------------- */

.guide-list {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}

.guide-item {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 13px 18px;
    transition: background-color 140ms ease;
}

.guide-item + .guide-item {
    border-top: 1px solid var(--border);
}

.guide-item:hover {
    background: var(--surface-2);
}

.guide-item:hover .guide-title {
    color: var(--accent);
}

.guide-title {
    font-weight: 600;
    transition: color 140ms ease;
}

.guide-summary {
    font-size: 13px;
    color: var(--text-muted);
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.guide-date {
    font-size: 12px;
    color: var(--text-muted);
}

.guide-more {
    display: inline-flex;
    margin-top: 12px;
}

/* Information table ------------------------------------------------------------------- */

.info-table-wrap {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    overflow-x: auto;
}

.info-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13.5px;
}

.info-table th {
    width: 200px;
    text-align: left;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-muted);
    padding: 12px 18px;
    vertical-align: middle;
    white-space: nowrap;
}

.info-table td {
    padding: 12px 18px;
    font-weight: 550;
    overflow-wrap: anywhere;
}

.info-table td a {
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 3px;
    transition: color 120ms ease;
}

.info-table td a:hover {
    color: var(--accent-hover);
}

.info-table tr + tr th,
.info-table tr + tr td {
    border-top: 1px solid var(--border);
}

/* Screenshot lightbox — full-screen photo overlay; the black/white chrome
   is deliberately theme-independent (allowed overlay exception). ----------- */

.lightbox {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.88);
}

.lightbox-image {
    max-width: 90vw;
    max-height: 90vh;
    border-radius: var(--radius);
    background: var(--surface);
    /* Horizontal drag drives prev/next (handled in JS via Pointer Events, so it
       works with mouse, touch and pen); let the browser keep vertical pan and
       pinch-zoom. transform is toggled during a drag. */
    touch-action: pan-y pinch-zoom;
    will-change: transform;
    transition: transform 180ms ease;
    user-select: none;
    -webkit-user-select: none;
    -webkit-user-drag: none;
}

/* Grab affordance only when there is more than one screenshot to swipe. */
.lightbox-image.is-swipeable {
    cursor: grab;
}

/* Active drag follows the pointer exactly — no easing lag. */
.lightbox-image.is-swiping {
    transition: none;
    cursor: grabbing;
}

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

.lightbox-close,
.lightbox-nav {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    cursor: pointer;
    transition: background-color 140ms ease;
}

.lightbox-close:hover,
.lightbox-nav:hover {
    background: rgba(255, 255, 255, 0.24);
}

.lightbox-close:focus-visible,
.lightbox-nav:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}

.lightbox-close svg,
.lightbox-nav svg {
    width: 20px;
    height: 20px;
}

.lightbox-close {
    position: absolute;
    top: 16px;
    right: 16px;
}

.lightbox-nav {
    position: absolute;
    top: 50%;
    margin-top: -20px;
}

.lightbox-prev {
    left: 16px;
}

.lightbox-next {
    right: 16px;
}

.lightbox-counter {
    position: absolute;
    bottom: 16px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 13px;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.85);
}

/* Video tile — the first item of the screenshot strip (app-video.js). The
   poster is 16:9 (9:16 for a Short), so the tile carries the ratio itself and
   is centred against the taller screenshots rather than stretched to them.
   Its width is capped to leave the next screenshot peeking in, which is what
   tells a phone user the strip scrolls. The play badge and the label sit ON a
   photo, so they are the theme-independent overlay exception: black/white,
   not tokens — a token would lose contrast against the picture. ----------- */

.video-thumb {
    position: relative;
    flex: none;
    align-self: center;
    display: block;
    width: min(533px, calc(100% - 40px));
    aspect-ratio: 16 / 9;
    scroll-snap-align: start;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-2);
    overflow: hidden;
    cursor: pointer;
    transition: border-color 140ms ease, box-shadow 140ms ease;
}

.video-thumb.is-vertical {
    width: auto;
    height: 300px;
    aspect-ratio: 9 / 16;
}

.video-thumb:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-card);
}

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

.video-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.video-thumb-play {
    position: absolute;
    top: 50%;
    left: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    margin: -32px 0 0 -32px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.62);
    color: #fff;
    box-shadow: 0 4px 18px rgba(0, 0, 0, 0.35);
    transition: transform 160ms ease, background-color 160ms ease;
}

.video-thumb-play svg {
    width: 28px;
    height: 28px;
    margin-left: 3px; /* optical centre of a triangle */
}

.video-thumb:hover .video-thumb-play,
.video-thumb:focus-visible .video-thumb-play {
    transform: scale(1.08);
    background: rgba(0, 0, 0, 0.78);
}

.video-thumb-label {
    position: absolute;
    left: 10px;
    bottom: 10px;
    padding: 4px 10px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.62);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
}

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

/* The player: a black full-screen overlay (same exception as the lightbox)
   holding the YouTube frame at the largest 16:9 (or 9:16) box the viewport
   fits. dvh where supported, so a phone's collapsing address bar never pushes
   the picture's edge off screen. */

.video-player {
    position: fixed;
    inset: 0;
    z-index: 120;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #000;
}

.video-player-frame {
    position: relative;
    width: min(100vw, calc(100vh * 16 / 9));
    width: min(100vw, calc(100dvh * 16 / 9));
    aspect-ratio: 16 / 9;
}

.video-player.is-vertical .video-player-frame {
    width: min(100vw, calc(100vh * 9 / 16));
    width: min(100vw, calc(100dvh * 9 / 16));
    aspect-ratio: 9 / 16;
}

.video-player-frame iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

.video-player-close {
    position: absolute;
    top: 14px;
    right: 14px;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 0;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    cursor: pointer;
    transition: background-color 140ms ease;
}

.video-player-close:hover {
    background: rgba(255, 255, 255, 0.24);
}

.video-player-close:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}

.video-player-close svg {
    width: 20px;
    height: 20px;
}

/* Modal content — the dialog scaffold (.modal-overlay/.modal-panel/
   .modal-head/.modal-close/.modal-body) lives in core.css; only the content
   styles a dialog fills itself with are defined here. Used by the "Install
   with PremiBox" explainer (partials/premibox-install-modal.php), which
   inherited them from the license-key gate this file was written for. ----- */

.modal-sub {
    font-size: 13.5px;
    color: var(--text-muted);
    line-height: 1.55;
    margin-bottom: 14px;
}

.modal-tabs {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 4px;
    padding: 3px;
    margin-bottom: 16px;
    background: var(--surface-2);
    border-radius: var(--radius-sm);
}

.modal-tab {
    padding: 6px 10px;
    font: inherit;
    font-size: 13px;
    font-weight: 550;
    color: var(--text-muted);
    background: none;
    border: 0;
    border-radius: 7px;
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease;
}

.modal-tab.is-active {
    color: var(--text);
    background: var(--surface);
    box-shadow: inset 0 0 0 1px var(--border), var(--shadow-card);
}

.modal-feature-list {
    list-style: none;
    display: grid;
    gap: 8px;
    margin-bottom: 16px;
}

.modal-feature-list li {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 13.5px;
}

.modal-feature-list svg {
    width: 16px;
    height: 16px;
    flex: none;
    margin-top: 1px;
    color: var(--green);
}

.modal-note {
    margin-top: 12px;
    font-size: 12.5px;
    color: var(--text-muted);
    text-align: center;
}

/* Responsive ---------------------------------------------------------------- */

@media (max-width: 900px) {
    .info-table th {
        width: 140px;
    }
}

@media (max-width: 640px) {
    .app-hero {
        padding: 20px 20px 0;
    }

    .app-hero-top {
        gap: 15px;
    }

    .app-hero-icon {
        width: 72px;
        height: 72px;
        border-radius: 18px;
    }

    .app-hero h1 {
        font-size: 21px;
    }

    .app-hero-actions {
        gap: 14px;
    }

    .app-hero-actions .btn {
        width: 100%;
    }

    /* Two-column grid instead of a horizontally-scrolling flex row: long
       values ("Directly on device", "Not required") were forcing the strip
       wider than the card and spilling off-screen. Cells now wrap in place. */
    .fact-strip {
        margin: 20px -20px 0;
        display: grid;
        grid-template-columns: 1fr 1fr;
        overflow-x: visible;
    }

    .fact-strip li {
        min-width: 0;
        padding: 11px 16px 13px;
        border-top: 1px solid var(--border);
    }

    /* Column rail on the right cells; first row needs no top rail (the strip's
       own border-top already draws it). */
    .fact-strip li:nth-child(odd) {
        border-left: 0;
    }

    .fact-strip li:nth-child(1),
    .fact-strip li:nth-child(2) {
        border-top: 0;
    }

    /* Let long values wrap rather than force the cell wider. */
    .fact-strip .fact-value {
        white-space: normal;
    }

    /* Mod features: one column on phones, tighter padding. The hairline grid
       still draws the row dividers from its 1px gap. */
    .mod-list {
        grid-template-columns: 1fr;
    }

    .mod-panel-head {
        padding: 16px 18px;
    }

    .mod-item {
        padding: 15px 18px;
    }

    .screenshot-thumb img {
        height: 220px;
    }

    /* 16:9 at the screenshots' 220px would be 391px — wider than a phone's
       strip; the width cap takes over and the tile simply sits shorter. */
    .video-thumb {
        width: min(391px, calc(100% - 40px));
    }

    .video-thumb.is-vertical {
        width: auto;
        height: 220px;
    }

    .video-thumb-play {
        width: 54px;
        height: 54px;
        margin: -27px 0 0 -27px;
    }

    .video-thumb-play svg {
        width: 24px;
        height: 24px;
    }

    .app-section .prose {
        padding: 18px;
    }

    .modal-overlay {
        padding: 20px 12px;
    }
}

@media (max-width: 460px) {
    .lightbox-nav {
        top: auto;
        bottom: 12px;
        margin-top: 0;
    }

    .lightbox-counter {
        bottom: 22px;
    }
}
