/* =========================================================================
   Zamzam Event Photo Service — Phase 7 stylesheet
   -------------------------------------------------------------------------
   Design constraints (per PHASES.md phase 7 + global UI rules):
   - Full RTL Persian layout.
   - Mobile-first, responsive breakpoints at 640 / 768 / 1024 px.
   - Colour palette: teal + slate + stone. NO indigo, NO blue.
   - Sticky footer: body is a flex column, footer gets margin-top: auto.
   - Touch-friendly: minimum 44px tap targets.
   - Subtle loading / error animations.
   - Uses the uploaded background.jpg as a fixed hero backdrop.
   ========================================================================= */

/* ----------------------------- Design tokens ---------------------------- */
:root {
    /* Brand palette (teal + slate + stone — NO blue/indigo) */
    --color-primary: #0d9488;        /* teal-600 */
    --color-primary-hover: #0f766e;  /* teal-700 */
    --color-primary-active: #115e54; /* teal-800 */
    --color-primary-soft: #ccfbf1;   /* teal-100 */
    --color-accent: #d97706;         /* amber-600 (warnings) */
    --color-success: #059669;        /* emerald-600 */
    --color-danger: #be123c;         /* rose-700 */
    --color-danger-soft: #ffe4e6;    /* rose-100 */

    --color-text: #1e293b;           /* slate-800 */
    --color-text-muted: #64748b;     /* slate-500 */
    --color-text-faint: #94a3b8;     /* slate-400 */

    --color-surface: #ffffff;
    --color-surface-alt: #f8fafc;    /* slate-50 */
    --color-surface-overlay: rgba(250, 250, 249, 0.92); /* stone-50/92 */
    --color-border: #e2e8f0;         /* slate-200 */
    --color-border-strong: #cbd5e1;  /* slate-300 */

    /* Hero background — the uploaded image, with a readable overlay */
    --bg-image: url('/static/img/background.jpg');
    --bg-overlay: linear-gradient(
        180deg,
        rgba(15, 23, 42, 0.55) 0%,
        rgba(15, 23, 42, 0.65) 40%,
        rgba(15, 23, 42, 0.78) 100%
    );

    /* Typography */
    --font-family: 'Vazirmatn', 'Segoe UI', Tahoma, 'Iranian Sans',
                   -apple-system, BlinkMacSystemFont, sans-serif;
    --font-size-base: 16px;
    --line-height-base: 1.6;

    /* Spacing scale */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.25rem;
    --space-6: 1.5rem;
    --space-8: 2rem;
    --space-10: 2.5rem;
    --space-12: 3rem;

    /* Radii */
    --radius-sm: 6px;
    --radius-md: 10px;
    --radius-lg: 16px;
    --radius-pill: 9999px;

    /* Shadows */
    --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.08);
    --shadow-md: 0 6px 18px rgba(15, 23, 42, 0.12);
    --shadow-lg: 0 18px 40px rgba(15, 23, 42, 0.22);

    /* Touch targets */
    --tap-min: 44px;

    /* Transitions */
    --t-fast: 120ms ease-out;
    --t-base: 200ms ease-out;
}

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

/* CRITICAL: The HTML `hidden` attribute is equivalent to `display: none` in
 * the UA stylesheet, but ANY author CSS rule that sets `display` (e.g.
 * `display: flex` on .captcha-loading, .photo-load-error) overrides it.
 * This universal rule restores the expected behaviour so that setting
 * `element.hidden = true` in JS actually hides the element, regardless of
 * any `display` value elsewhere. Without this, the captcha spinner and the
 * photo-load-error overlay would be permanently visible. */
[hidden] { display: none !important; }

html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    scroll-behavior: smooth;
}

body {
    margin: 0;
    min-height: 100vh;
    min-height: 100dvh;            /* mobile-aware viewport */
    font-family: var(--font-family);
    font-size: var(--font-size-base);
    line-height: var(--line-height-base);
    color: var(--color-text);
    /* Solid fallback colour only — the decorative background lives on the
       fixed pseudo-layer below (see body::before). `background-attachment:
       fixed` is broken/ignored on iOS Safari and behaves inconsistently
       inside iframes, whereas `position: fixed` paints identically on
       every browser and anchors to the FRAME's viewport when embedded. */
    background-color: #0f172a;      /* slate-900 fallback (behind bg image) */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Fixed backdrop — iOS-safe alternative to background-attachment: fixed.
   Sits behind all page content (z-index: -1) and always covers exactly the
   visible viewport — including inside the Construct 3 game's iframe, where
   it tracks the iframe's own size and resizes live. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background-color: #0f172a;
    background-image: var(--bg-overlay), var(--bg-image);
    background-size: cover;
    background-position: center center;
    background-repeat: no-repeat;
}

img { max-width: 100%; height: auto; display: block; }
button { font: inherit; cursor: pointer; }
a { color: inherit; text-decoration: none; }

/* ------------------------- Sticky-footer page shell --------------------- */
.page-shell {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}

.site-main {
    flex: 1 0 auto;
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: var(--space-6) var(--space-4) var(--space-8);
}

/* ------------------------------- Header --------------------------------- */
.site-header {
    flex: 0 0 auto;
    width: 100%;
    background: rgba(15, 23, 42, 0.55);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.header-inner {
    max-width: 1080px;
    margin: 0 auto;
    padding: var(--space-3) var(--space-4);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: #ffffff;
    min-height: var(--tap-min);
}

.brand-logo {
    width: 40px;
    height: 40px;
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.06);
    padding: 4px;
    object-fit: contain;
}

.brand-name {
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: 0.02em;
}

.header-tag {
    color: rgba(226, 232, 240, 0.85);
    font-size: 0.85rem;
    font-weight: 500;
}

@media (max-width: 480px) {
    .header-tag { display: none; }
    .brand-logo { width: 36px; height: 36px; }
}

/* ------------------------------- Footer --------------------------------- */
.site-footer {
    flex: 0 0 auto;
    width: 100%;
    background: rgba(15, 23, 42, 0.72);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    color: rgba(226, 232, 240, 0.8);
    /* Push to bottom on short pages; on long pages it flows naturally */
    margin-top: auto;
}

.footer-inner {
    max-width: 1080px;
    margin: 0 auto;
    padding: var(--space-4);
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.footer-text { margin: 0; font-size: 0.85rem; font-weight: 600; }
.footer-sub { margin: 0; font-size: 0.75rem; color: rgba(148, 163, 184, 0.85); }

/* -------------------------------- Hero ---------------------------------- */
.hero {
    width: 100%;
    max-width: 520px;
    display: flex;
    justify-content: center;
}

.hero-card {
    width: 100%;
    background: var(--color-surface-overlay);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-6);
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

@media (min-width: 640px) {
    .hero-card { padding: var(--space-8); }
}

@media (max-width: 480px) {
    .hero-card { padding: var(--space-5); border-radius: var(--radius-md); }
}

.hero-head {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}

.hero-logo {
    width: 56px;
    height: 56px;
    flex: 0 0 auto;
    border-radius: var(--radius-md);
    background: var(--color-primary-soft);
    padding: 6px;
    object-fit: contain;
}

.hero-titles { display: flex; flex-direction: column; gap: 2px; }

.hero-title {
    margin: 0;
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--color-text);
}

.hero-subtitle {
    margin: 0;
    font-size: 0.9rem;
    color: var(--color-text-muted);
}

/* ------------------------------- Messages ------------------------------- */
.message {
    border-radius: var(--radius-md);
    padding: var(--space-3) var(--space-4);
    font-size: 0.9rem;
    font-weight: 500;
    border: 1px solid transparent;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.message.is-error {
    background: var(--color-danger-soft);
    color: var(--color-danger);
    border-color: rgba(190, 18, 60, 0.2);
}

.message.is-success {
    background: #d1fae5;
    color: var(--color-success);
    border-color: rgba(5, 150, 105, 0.2);
}

.message.is-info {
    background: var(--color-primary-soft);
    color: var(--color-primary-active);
    border-color: rgba(13, 148, 136, 0.2);
}

.message[hidden] { display: none; }

/* ------------------------------- Form ----------------------------------- */
.retrieve-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.field { display: flex; flex-direction: column; gap: var(--space-2); }

.field-label {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-text);
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.field-hint {
    font-size: 0.75rem;
    font-weight: 400;
    color: var(--color-text-faint);
}

.field-input {
    width: 100%;
    min-height: var(--tap-min);
    padding: var(--space-3) var(--space-4);
    font-family: inherit;
    font-size: 1rem;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1.5px solid var(--color-border-strong);
    border-radius: var(--radius-md);
    transition: border-color var(--t-fast), box-shadow var(--t-fast);
    -webkit-appearance: none;
    appearance: none;
}

.field-input::placeholder { color: var(--color-text-faint); }

.field-input:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px rgba(13, 148, 136, 0.18);
}

.field-input.is-invalid {
    border-color: var(--color-danger);
    box-shadow: 0 0 0 3px rgba(190, 18, 60, 0.14);
}

.field-error {
    margin: 0;
    font-size: 0.78rem;
    color: var(--color-danger);
    font-weight: 500;
}
.field-error[hidden] { display: none; }

/* ------------------------------- Captcha -------------------------------- */
.captcha-row {
    display: flex;
    gap: var(--space-3);
    align-items: stretch;
}

.captcha-image-wrap {
    position: relative;
    flex: 0 0 220px;
    width: 220px;
    height: 80px;
    border-radius: var(--radius-md);
    overflow: hidden;
    border: 1.5px solid var(--color-border-strong);
    background: var(--color-surface-alt);
}

.captcha-image {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

.captcha-reload {
    position: absolute;
    top: 4px;
    inset-inline-end: 4px;       /* RTL-aware */
    width: 32px;
    height: 32px;
    border: none;
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.85);
    color: var(--color-text-muted);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background var(--t-fast), color var(--t-fast);
    padding: 0;
}

.captcha-reload:hover {
    background: var(--color-surface);
    color: var(--color-primary);
}

.captcha-reload:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.captcha-reload[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Loading indicator — placed OUTSIDE the captcha image box (below the
   captcha row) so it doesn't overlay the captcha text. Previously this
   was an absolute-positioned overlay inside .captcha-image-wrap, which
   covered the captcha image and appeared at top-left. Now it's a simple
   inline flex row with a small spinner + text. */
.captcha-loading {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: 0.8rem;
    color: var(--color-text-muted);
    padding: var(--space-1) 0;
}

.captcha-loading[hidden] { display: none; }

.captcha-loading .spinner {
    width: 16px;
    height: 16px;
    border-color: rgba(13, 148, 136, 0.25);
    border-top-color: var(--color-primary);
}

.captcha-input {
    flex: 1 1 auto;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-family: 'Menlo', 'Consolas', monospace;
}

@media (max-width: 480px) {
    .captcha-image-wrap { flex-basis: 180px; width: 180px; height: 72px; }
}

/* ------------------------------- Buttons -------------------------------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--tap-min);
    padding: var(--space-3) var(--space-5);
    font-family: inherit;
    font-size: 1rem;
    font-weight: 600;
    border-radius: var(--radius-md);
    border: 1.5px solid transparent;
    transition: background var(--t-fast), border-color var(--t-fast),
                color var(--t-fast), transform var(--t-fast);
    -webkit-appearance: none;
    appearance: none;
    text-decoration: none;
    cursor: pointer;
    user-select: none;
}

.btn:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.btn:active { transform: translateY(1px); }

.btn[disabled] {
    opacity: 0.6;
    cursor: not-allowed;
    transform: none;
}

.btn-primary {
    background: var(--color-primary);
    color: #ffffff;
}
.btn-primary:hover:not([disabled]) { background: var(--color-primary-hover); }
.btn-primary:active:not([disabled]) { background: var(--color-primary-active); }

.btn-secondary {
    background: var(--color-surface);
    color: var(--color-primary-active);
    border-color: var(--color-primary);
}
.btn-secondary:hover:not([disabled]) {
    background: var(--color-primary-soft);
}

.btn-ghost {
    background: transparent;
    color: var(--color-text-muted);
    border-color: var(--color-border-strong);
}
.btn-ghost:hover:not([disabled]) {
    background: var(--color-surface-alt);
    color: var(--color-text);
}

.btn-block { width: 100%; }

.btn-spinner,
.btn .spinner {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* ------------------------------ Spinner --------------------------------- */
.spinner {
    width: 18px;
    height: 18px;
    border: 2px solid rgba(255, 255, 255, 0.4);
    border-top-color: #ffffff;
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
    display: inline-block;
}

/* Note: .captcha-loading .spinner styles are defined above, next to the
   .captcha-loading rule itself (in the Captcha section). */

@keyframes spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .spinner { animation-duration: 1.6s; }
    .btn:active { transform: none; }
    html { scroll-behavior: auto; }
}

/* --------------------------- Photo result ------------------------------- */
.photo-result {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    animation: fade-in var(--t-base);
}

.photo-result[hidden] { display: none; }

.photo-result-head { display: flex; flex-direction: column; gap: 2px; }

.photo-result-title {
    margin: 0;
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--color-success);
}

.photo-result-sub {
    margin: 0;
    font-size: 0.85rem;
    color: var(--color-text-muted);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.photo-result-sub .dot { color: var(--color-text-faint); }

.photo-frame {
    position: relative;
    width: 100%;
    background: var(--color-surface-alt);
    border: 1.5px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    min-height: 200px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.photo-display {
    width: 100%;
    height: auto;
    max-height: 60vh;
    object-fit: contain;
    display: block;
}

.photo-load-error {
    position: absolute;
    inset: 0;
    background: var(--color-surface);
    color: var(--color-danger);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: var(--space-4);
    font-size: 0.9rem;
    font-weight: 500;
}

.photo-caption {
    margin: 0;
    padding: var(--space-3) var(--space-4);
    font-size: 0.88rem;
    line-height: 1.7;
    color: var(--color-text);
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    text-align: center;
    word-wrap: break-word;
    overflow-wrap: break-word;
}

.photo-actions {
    display: flex;
    gap: var(--space-3);
    flex-wrap: wrap;
}

.photo-actions .btn { flex: 1 1 auto; }
.photo-actions .btn-ghost { flex: 0 0 auto; }

.expiry-timer {
    margin: 0;
    font-size: 0.78rem;
    color: var(--color-text-faint);
    text-align: center;
}

/* ------------------------------ Animations ------------------------------ */
@keyframes fade-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}

@keyframes pulse-soft {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.55; }
}

.is-loading { animation: pulse-soft 1.2s ease-in-out infinite; }

/* --------------------------- Capture page -------------------------------- */
/* The capture page is an APP-LIKE screen: it must always fit its viewport
   with no page-level scrolling, whether that viewport is:
   - the whole browser window (standalone mode), or
   - the Construct 3 game's <iframe> (embedded mode), which can be any size
     and is resized live by the game / by rotating the device.

   HOW THE MODES WORK — frame-detect.js (a synchronous <head> script) adds
   classes to <html> before first paint (no FOUC):
   - html.on-capture  → the page shell locks to the viewport height and the
                        footer is hidden; the capture card stretches to
                        fill all remaining space ("fills the whole page").
   - html.iframe-mode → the page runs inside an iframe: the site header is
                        hidden too and the card's outer chrome (border,
                        radius, shadow) is removed so it fills the frame
                        edge-to-edge.
   The :has() variants below are a no-JS / extra-robustness fallback.

   DENSITY SYSTEM — frame-detect.js also sets data-density on <html>
   ("normal" → "compact" → "tight" → "tiny" → "ultra") from the CURRENT
   viewport height (and the player count), re-evaluating on every resize.
   The levels progressively shrink paddings, gaps, input/button heights and
   the camera's minimum height through the custom properties declared on
   .capture-card. Inside an iframe, window.innerHeight IS the iframe
   height, so the layout self-adjusts the moment the game resizes it.

   LAST-RESORT SAFETY — if the viewport is so small that even "ultra"
   doesn't fit, the card scrolls internally (overflow-y: auto) instead of
   clipping content. Nothing is ever unreachable. */

/* --- Page-shell modes (capture) ------------------------------------------ */

/* The footer is meaningless on the capture flow and only wastes space. */
.page-shell:has(.capture-page) .site-footer,
html.on-capture .site-footer { display: none; }

/* App-mode shell: lock the document to the viewport so the card can fill
   it and the page itself never scrolls. 100dvh is the dynamic viewport
   height (mobile URL-bar aware); inside an iframe it equals the iframe's
   height exactly. */
html.on-capture .page-shell {
    height: 100vh;      /* fallback: very old browsers */
    height: 100dvh;
    overflow: hidden;
}

html.on-capture .site-main {
    flex: 1 1 auto;
    min-height: 0;      /* allow children to shrink within the locked shell */
    justify-content: flex-start;
    padding: var(--space-3) var(--space-4) var(--space-4);
}

/* Embedded in an iframe: every pixel belongs to the game — drop the site
   header (~70px of vertical space) and the card's outer chrome. */
html.iframe-mode .site-header { display: none; }
html.iframe-mode .site-main { padding: 0; }
html.iframe-mode .capture-page { max-width: 1200px; }
html.iframe-mode .capture-card {
    border-radius: 0;
    border: none;
    box-shadow: none;
}

/* --- Capture page / card -------------------------------------------------- */

.capture-page {
    width: 100%;
    max-width: 680px;
    display: flex;
    justify-content: center;
    /* Center horizontally inside .site-main. `margin-inline: auto`
       reliably centers a flex item on the cross axis even when the
       parent's align-items is overridden elsewhere, and works with the
       max-width cap. */
    margin-inline: auto;
    /* Fill the available vertical space (site-main is locked to the
       viewport in on-capture mode) so the card stretches with resizes. */
    flex: 1 1 auto;
    min-height: 0;
}

/* Horizontal layout (fields right, camera left per RTL) kicks in on
   desktop widths OR comfortably wide landscape viewports. Inside an iframe
   these media queries evaluate against the FRAME's size, so a wide game
   iframe gets the 2-column layout and a narrow one stays vertical. */
@media (min-width: 768px), (orientation: landscape) and (min-width: 640px) {
    .capture-page { max-width: 1000px; }
}

.capture-card {
    /* Density-controlled dimensions (see data-density blocks below). */
    --cap-gap: var(--space-4);       /* gap between card sections */
    --head-logo: 48px;
    --head-title: 1.25rem;
    --head-sub: 0.85rem;
    --inp-h: 48px;                   /* input min-height (touch-friendly) */
    --inp-fs: 1rem;                  /* input font-size */
    --inp-pad: var(--space-3) var(--space-4);  /* input padding-block/inline */
    --label-fs: 0.9rem;              /* field label font-size */
    --legend-fs: 0.9rem;             /* player-group legend font-size */
    --pg-pad: var(--space-4);        /* player-group padding */
    --pg-gap: var(--space-3);        /* player-group inner gap */
    --cam-min: 150px;                /* camera frame minimum height */
    --btn-h: 48px;                   /* button min-height */

    width: 100%;
    background: var(--color-surface-overlay);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--cap-gap);
    /* Stretch to fill .capture-page (which fills the viewport), and allow
       shrinking below content size when the viewport is short. */
    flex: 1 1 auto;
    min-height: 0;
    /* Safety net: if even the most compact density can't fit (extremely
       small iframe), scroll INSIDE the card instead of clipping controls.
       overflow-x stays hidden so nothing drifts sideways. */
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: rgba(100, 116, 139, 0.5) transparent;
}

.capture-card::-webkit-scrollbar,
.capture-fields::-webkit-scrollbar { width: 8px; }
.capture-card::-webkit-scrollbar-thumb,
.capture-fields::-webkit-scrollbar-thumb {
    background: rgba(100, 116, 139, 0.45);
    border-radius: var(--radius-pill);
}
.capture-card::-webkit-scrollbar-track,
.capture-fields::-webkit-scrollbar-track { background: transparent; }

@media (min-width: 640px) {
    .capture-card { padding: var(--space-6); }
}

@media (max-width: 480px) {
    .capture-card { padding: var(--space-4); border-radius: var(--radius-md); }
}

.capture-head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex: 0 0 auto;
}

.capture-logo {
    width: var(--head-logo);
    height: var(--head-logo);
    flex: 0 0 auto;
    border-radius: var(--radius-md);
    background: var(--color-primary-soft);
    padding: 6px;
    object-fit: contain;
}

.capture-title {
    margin: 0;
    font-size: var(--head-title);
    font-weight: 700;
    color: var(--color-text);
}

.capture-subtitle {
    margin: 0;
    font-size: var(--head-sub);
    color: var(--color-text-muted);
}

/* ---- Capture body ---- */
/* Vertical (portrait / narrow) layout: the DOM order is camera-first
   (see capture.html), so the natural column flow reads: camera+actions
   on top, player fields below. The game usually pre-fills phone/name via
   query params, so the camera — the interactive centrepiece — gets the
   prime position and absorbs the remaining height. Normal flow (NOT
   column-reverse) keeps any extreme-viewport overflow extending DOWNWARD
   into the card's internal scroll area, where it stays reachable.
   Horizontal (desktop / wide landscape) layout: flex-row → player fields
   on the RIGHT (RTL start), camera+actions on the LEFT (order: -1 puts
   the fields back in front of the camera column despite the DOM order). */
.capture-body {
    display: flex;
    flex-direction: column;
    gap: var(--cap-gap);
    flex: 1 1 auto;
    min-height: 0;
}

@media (min-width: 768px), (orientation: landscape) and (min-width: 640px) {
    .capture-body {
        flex-direction: row;
        align-items: stretch;
        gap: var(--space-5);
    }
}

/* ---- Player fields ---- */
.capture-fields {
    display: flex;
    flex-direction: column;
    gap: var(--cap-gap);
    /* Vertical mode: content-sized and NEVER shrunk below its content
       (flex-shrink: 0) — the camera column absorbs all shrinking instead,
       because a video with object-fit: cover looks right at ANY height
       while clipped inputs/buttons do not. If even the camera's floor is
       exceeded, the card itself scrolls (reachable overflow). */
    flex: 0 0 auto;
    min-height: 0;
}

/* In horizontal mode the fields column takes a fixed width so the camera
   column absorbs the rest, and moves to the front (RIGHT side in RTL).
   Two stacked player groups can exceed a short landscape frame — the
   column scrolls internally rather than clipping. */
@media (min-width: 768px), (orientation: landscape) and (min-width: 640px) {
    .capture-fields {
        order: -1;              /* fields to the right (RTL) */
        flex: 0 0 340px;
        max-width: 380px;
        overflow-y: auto;
        scrollbar-width: thin;
        scrollbar-color: rgba(100, 116, 139, 0.5) transparent;
    }
}

/* Two-player mode: the two player fieldsets side-by-side whenever there
   is comfortable width (saves ~150px of vertical space). Reverted to a
   single column inside the narrow horizontal-mode fields column, where
   two groups would be ~160px wide each — too cramped for the inputs. */
.capture-fields--two {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--cap-gap);
}

@media (min-width: 520px) {
    .capture-fields--two { grid-template-columns: 1fr 1fr; }
}

@media (min-width: 768px), (orientation: landscape) and (min-width: 640px) {
    .capture-fields--two { grid-template-columns: 1fr; }
}

/* ---- Capture side (camera + actions) ---- */
.capture-side {
    display: flex;
    flex-direction: column;
    gap: var(--cap-gap);
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
}

/* Player group: a bordered fieldset grouping phone + name for one player.
   Visually distinguishes player 1 from player 2 in two-player mode. */
.player-group {
    margin: 0;
    padding: var(--pg-pad);
    border: 1.5px solid var(--color-border);
    border-radius: var(--radius-md);
    display: flex;
    flex-direction: column;
    gap: var(--pg-gap);
    background: var(--color-surface);
}

.player-group-legend {
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--color-primary);
    padding: 0 var(--space-2);
}

/* Density-aware inputs & buttons (scoped to the capture page only — the
   index page keeps the roomy global sizing). NOTE: the input's real
   height = line-height + padding-block, so the PADDING must shrink with
   the density too — a min-height alone can't override a roomy padding. */
.capture-page .field-input {
    min-height: var(--inp-h);
    font-size: var(--inp-fs);
    padding: var(--inp-pad);
}

.capture-page .field-label { font-size: var(--label-fs); }

.capture-page .player-group-legend { font-size: var(--legend-fs); }

.capture-page .btn {
    min-height: var(--btn-h);
    padding: var(--space-2) var(--space-4);
}

.camera-section {
    width: 100%;
    /* Absorb remaining vertical space so the camera grows/shrinks with the
       viewport. min-height: 0 lets it shrink below its content size. */
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.camera-frame {
    position: relative;
    width: 100%;
    /* Fill the available height in the camera section; never force the
       card taller than the viewport (the density-controlled --cam-min is
       the floor below which the card scrolls internally). */
    flex: 1 1 auto;
    min-height: var(--cam-min);
    background: #000;
    border-radius: var(--radius-md);
    overflow: hidden;
    border: 1.5px solid var(--color-border-strong);
}

.camera-frame video,
.camera-frame .preview-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.camera-frame video[hidden],
.camera-frame .preview-image[hidden] { display: none; }

.camera-placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    color: rgba(255, 255, 255, 0.5);
    font-size: 0.85rem;
    text-align: center;
    padding: var(--space-4);
}

.camera-placeholder svg {
    opacity: 0.4;
}

.camera-placeholder[hidden] { display: none; }

.camera-loading {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    display: flex;
    align-items: center;
    justify-content: center;
}

.camera-loading .spinner {
    width: 32px;
    height: 32px;
    border-color: rgba(255, 255, 255, 0.3);
    border-top-color: #ffffff;
}

.camera-loading[hidden] { display: none; }

.capture-actions {
    display: flex;
    flex-direction: column;
    gap: var(--cap-gap);
    flex: 0 0 auto;
}

.capture-row {
    display: flex;
    gap: var(--space-3);
}

.capture-row .btn { flex: 1 1 auto; }

.capture-row[hidden] { display: none; }

/* Compact status message on tight viewports. */
.capture-page .message { flex: 0 0 auto; }

/* ---- Density levels (set on <html> by frame-detect.js) -------------------
   Breakpoints are effective viewport heights (760 / 560 / 480 / 420 px),
   calibrated against the real rendered heights of every section.
   frame-detect.js subtracts ~220px from the real height in two-player
   mode (one extra player-group + gap), so the two-player page reaches the
   same densities ~220px earlier without duplicating rules. */

/* compact — slightly reduced rhythm */
html[data-density="compact"] .capture-card {
    --cap-gap: var(--space-3);
    --inp-h: 44px;
    --inp-pad: var(--space-2) var(--space-4);
    --cam-min: 130px;
    --btn-h: 44px;
}

/* tight — short viewports (e.g. half-screen landscape iframes) */
html[data-density="tight"] .capture-card {
    padding: var(--space-4);
    --cap-gap: var(--space-3);
    --head-logo: 40px;
    --head-title: 1.1rem;
    --head-sub: 0.8rem;
    --inp-h: 42px;
    --inp-fs: 0.95rem;
    --inp-pad: var(--space-2) var(--space-3);
    --label-fs: 0.82rem;
    --legend-fs: 0.82rem;
    --pg-pad: var(--space-3);
    --pg-gap: var(--space-2);
    --cam-min: 115px;
    --btn-h: 42px;
}

/* tiny — very short viewports: the tagline is dropped to save a line */
html[data-density="tiny"] .capture-card {
    padding: var(--space-3);
    --cap-gap: var(--space-2);
    --head-logo: 32px;
    --head-title: 1rem;
    --inp-h: 38px;
    --inp-fs: 0.9rem;
    --inp-pad: var(--space-1) var(--space-3);
    --label-fs: 0.78rem;
    --legend-fs: 0.78rem;
    --pg-pad: var(--space-2);
    --pg-gap: var(--space-2);
    --cam-min: 100px;
    --btn-h: 38px;
}

html[data-density="tiny"] .capture-subtitle { display: none; }

html[data-density="tiny"] .capture-page .message {
    padding: var(--space-2) var(--space-3);
    font-size: 0.85rem;
}

/* ultra — extreme squeeze (very small game overlays). Inputs stay ≥34px so
   they remain tappable; if even this doesn't fit, the card scrolls. */
html[data-density="ultra"] .capture-card {
    padding: var(--space-2);
    --cap-gap: var(--space-2);
    --head-logo: 26px;
    --head-title: 0.95rem;
    --inp-h: 34px;
    --inp-fs: 0.85rem;
    --inp-pad: 2px var(--space-2);
    --label-fs: 0.75rem;
    --legend-fs: 0.75rem;
    --pg-pad: var(--space-2);
    --pg-gap: var(--space-1);
    --cam-min: 70px;
    --btn-h: 34px;
}

html[data-density="ultra"] .capture-subtitle { display: none; }

html[data-density="ultra"] .capture-head { gap: var(--space-2); }

html[data-density="ultra"] .capture-page .message {
    padding: var(--space-1) var(--space-2);
    font-size: 0.8rem;
}

/* --------------------------- Print / a11y -------------------------------- */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

@media print {
    body { background: #ffffff; }
    body::before { display: none; }
    .site-header, .site-footer, .captcha-row, .retrieve-form { display: none; }
}
