/*
 * Public marketing surfaces: the homepage (layouts/marketing.php,
 * public/home.php) and the login split page (layouts/auth.php).
 * Kept in its own file rather than growing components.css/layout.css,
 * since nothing here is reused by the authenticated app shell.
 *
 * Phase 13 "Weblium-inspired editorial redesign" — see
 * .claude/skills/evoting-design/SKILL.md Sections 1-9 for the full
 * rationale. The short version: fewer boxed cards, bolder full-bleed
 * color blocking using the SAME --primary/--visual-theme tokens every
 * other page already uses (so all 32 color x visual combinations still
 * work), an oversized one-headline-per-page display size, and an arch-
 * shaped illustration container as the one recurring signature shape
 * instead of another rounded rectangle.
 *
 * Colors only ever come from tokens.css custom properties (the two
 * fixed "battle" accents on the candidate showcase reuse --warning/
 * --danger rather than inventing new ones). Every animation is
 * automatically neutralised by tokens.css's global
 * `prefers-reduced-motion: reduce` rule.
 */

/* ---------- Page frame ----------
 * Wraps the marketing homepage and the login page in a centered, rounded,
 * shadowed "application window" — never applied to the authenticated app
 * shell (layouts/app.php) or the public result pages (layouts/public.php).
 *
 * The fixed-position decorative layers (.theme-background/.theme-overlay,
 * Phase 11) are re-scoped to `absolute` here so they clip to the frame's
 * rounded corners via overflow:hidden instead of spanning the real
 * viewport edge-to-edge — a page-frame-only override; every OTHER layout
 * that still uses those two classes (app.php, public.php) keeps the
 * original fixed, viewport-wide behavior visual-themes.css defines.
 */
body.has-page-frame {
    background: var(--background);
}
.page-frame {
    position: relative;
    width: 100%;
    margin: 0;
    border-radius: 0;
    overflow: hidden;
    box-shadow: none;
    background: var(--background);
}
.page-frame .theme-background,
.page-frame .theme-overlay {
    position: absolute;
}
.page-frame .marketing-shell,
.page-frame .auth-split {
    min-height: 100vh;
}

/* =====================================================================
   DEFAULT EDITORIAL DESIGN MODE (Phase 13 third correction)
   =====================================================================
   Scoped to theme_key="blue" (ThemeKey::DEFAULT) AND visual_theme=
   "minimal" (VisualThemeKey::DEFAULT) at once — the exact combination a
   school sees before anyone has ever opened Admin > Settings >
   Appearance. Re-maps the SAME custom properties every rule below
   already consumes (--primary, --background, --text, --border,
   --radius-*, --shadow-*, --text-4xl, --text-display) instead of
   duplicating every component's CSS — the whole page inherits an
   off-white/near-black/coral, flat-and-bordered editorial language for
   free, everywhere those tokens are already used correctly.

   The instant an admin picks ANY other combination — a different color
   preset, a different visual theme, or both — this selector stops
   matching and their own choice renders exactly as it already does
   today (ThemeService, the 8 color presets, the 4 visual themes,
   persistence, and the appearance preview are all completely untouched
   — nothing here is PHP, a migration, or a change to the theme
   mechanism, only a CSS re-skin of one specific attribute combination).
   A generic "Minimal" default for every OTHER color preset still exists
   further down this file (body[data-visual-theme="minimal"] .hero),
   unchanged from before — this block only goes further for the one
   combination that is the app's actual untouched default.

   Phase 14: the core token remap itself (palette/radius/shadow/type-scale)
   now lives in tokens.css, not here — tokens.css loads on EVERY layout
   (app.php included), so the internal/admin shell inherits the exact same
   editorial language automatically. Only the marketing/login-specific
   STRUCTURAL rules (container width, hero, stat-mosaic, cta-band, auth
   panel) stay in this file, since those classes only exist on these pages.
   ===================================================================== */
body[data-color-theme="blue"][data-visual-theme="minimal"] .marketing-container,
body[data-color-theme="blue"][data-visual-theme="minimal"] .marketing-container-wide {
    max-width: 1440px;
}
body[data-color-theme="blue"][data-visual-theme="minimal"] .marketing-section {
    padding: 6rem var(--space-5);
}
body[data-color-theme="blue"][data-visual-theme="minimal"] .hero {
    background: var(--background);
    color: var(--text);
    padding-top: 4rem;
}
body[data-color-theme="blue"][data-visual-theme="minimal"] .hero-title,
body[data-color-theme="blue"][data-visual-theme="minimal"] .section-heading,
body[data-color-theme="blue"][data-visual-theme="minimal"] .editorial-split h2,
body[data-color-theme="blue"][data-visual-theme="minimal"] .candidate-case-body h3,
body[data-color-theme="blue"][data-visual-theme="minimal"] .cta-band h2,
body[data-color-theme="blue"][data-visual-theme="minimal"] .auth-panel h2 {
    letter-spacing: -0.03em;
}
body[data-color-theme="blue"][data-visual-theme="minimal"] .auth-panel h2 {
    font-size: var(--text-4xl);
    line-height: 1.08;
}
body[data-color-theme="blue"][data-visual-theme="minimal"] .auth-panel p { font-size: var(--text-base); }
body[data-color-theme="blue"][data-visual-theme="minimal"] .auth-form-card { box-shadow: none; border: 1px solid var(--border); }
body[data-color-theme="blue"][data-visual-theme="minimal"] .form-control { border-color: var(--border); }
body[data-color-theme="blue"][data-visual-theme="minimal"] .site-header {
    background: var(--background);
    border-bottom: 1px solid var(--border);
    box-shadow: none;
}

/* Statistics: a divided row of big numbers, not a color-block mosaic. */
body[data-color-theme="blue"][data-visual-theme="minimal"] .stat-mosaic {
    background: var(--surface);
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}
body[data-color-theme="blue"][data-visual-theme="minimal"] .stat-mosaic-cell {
    background: none;
    color: var(--text);
    border-left: 1px solid var(--border);
}
body[data-color-theme="blue"][data-visual-theme="minimal"] .stat-mosaic-cell:first-child { border-left: none; }
body[data-color-theme="blue"][data-visual-theme="minimal"] .stat-mosaic-number { color: var(--primary); }
body[data-color-theme="blue"][data-visual-theme="minimal"] .stat-mosaic-cell.is-solid .stat-mosaic-number { color: var(--primary); }
@media (max-width: 767px) {
    body[data-color-theme="blue"][data-visual-theme="minimal"] .stat-mosaic-cell:nth-child(2n) { border-left: none; }
}

/* CTA band: flat solid coral, no gradient. */
body[data-color-theme="blue"][data-visual-theme="minimal"] .cta-band {
    background: var(--primary);
}

/* ---------- Site header / nav ---------- */
.site-header {
    position: sticky;
    top: 0;
    z-index: 40;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-3) var(--space-5);
}
.site-header-brand {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}
.site-header-brand .brand-mark {
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--radius-md);
    background: var(--primary);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: var(--font-bold);
    flex-shrink: 0;
}
.site-header-brand-text { font-weight: var(--font-semibold); font-size: var(--text-sm); line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.site-header-brand-text small { display: block; color: var(--text-muted); font-weight: var(--font-normal); font-size: var(--text-xs); }

.site-nav {
    display: none;
    align-items: center;
    gap: var(--space-1);
}
.site-nav-link {
    padding: 0.5rem 0.85rem;
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    color: var(--text-secondary);
}
.site-nav-link:hover { background: var(--border-subtle); color: var(--text); text-decoration: none; }
.site-nav-link.is-active { color: var(--primary); font-weight: var(--font-semibold); }

.site-header-actions { display: flex; align-items: center; gap: var(--space-2); }
.nav-toggle-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    background: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    color: var(--text);
    cursor: pointer;
}
.nav-toggle-btn:hover { background: var(--border-subtle); }

.mobile-nav-panel {
    display: none;
    flex-direction: column;
    gap: 2px;
    padding: var(--space-2) var(--space-4) var(--space-4);
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}
.mobile-nav-panel.is-open { display: flex; }
.mobile-nav-panel .site-nav-link { display: block; padding: 0.65rem var(--space-3); }

@media (min-width: 900px) {
    .site-nav { display: flex; }
    .nav-toggle-btn { display: none; }
    .mobile-nav-panel { display: none !important; }
}

/* ---------- Marketing shell / sections ---------- */
.marketing-shell { position: relative; z-index: 1; min-height: 100vh; display: flex; flex-direction: column; }
.marketing-main { flex: 1; }
.marketing-section { padding: var(--space-12) var(--space-5); background: var(--background); }
.marketing-container { max-width: 1180px; margin: 0 auto; width: 100%; }
.marketing-container-wide { max-width: 1320px; margin: 0 auto; width: 100%; }
@media (max-width: 639px) {
    .marketing-section { padding: var(--space-9) var(--space-4); }
}

/* Section eyebrow — used sparingly (hero + at most one section heading
   per page), never as a habitual label above every block. */
.section-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--primary);
    font-weight: var(--font-semibold);
    font-size: var(--text-sm);
    letter-spacing: 0.02em;
    margin-bottom: var(--space-3);
}
.section-heading { font-size: var(--text-4xl); line-height: 1.15; margin-bottom: var(--space-3); max-width: 20ch; }
.section-lead { color: var(--text-secondary); font-size: var(--text-lg); max-width: 60ch; margin-bottom: 0; }
.section-intro { margin-bottom: var(--space-9); }
.section-intro.is-centered { text-align: center; margin-left: auto; margin-right: auto; }
.section-intro.is-centered .section-heading,
.section-intro.is-centered .section-lead { margin-left: auto; margin-right: auto; }

/* ---------- Hero (bold full-bleed color block) ---------- */
.hero {
    position: relative;
    overflow: hidden;
    padding: var(--space-12) var(--space-5) var(--space-10);
    background: linear-gradient(160deg, var(--primary) 0%, var(--primary-active) 100%);
    color: #fff;
}
body[data-visual-theme="merah-putih"] .hero { background: linear-gradient(160deg, var(--danger) 0%, var(--danger-text) 100%); }

/* Minimal is the app's DEFAULT visual theme (VisualThemeKey::DEFAULT) —
   the combination shown before any admin ever opens Settings. Per the
   Design Bible (Section 9: "the safest, highest-contrast, most
   data-forward mode... when in doubt, remove decoration") its hero drops
   the saturated gradient entirely for a light/warm surface, dark text and
   ONE accent color — the editorial-reference character this theme is
   meant to default to. Batik/Merah-Putih/Nusantara keep the bold
   gradient hero above; that decorative intensity is THEIR identity, not
   a default every school sees on day one. */
body[data-visual-theme="minimal"] .hero {
    background: var(--surface-cream);
    color: var(--text);
}
body[data-visual-theme="minimal"] .hero-eyebrow { color: var(--primary); }
body[data-visual-theme="minimal"] .hero-period { color: var(--text-muted); }
body[data-visual-theme="minimal"] .hero-title { color: var(--text); }
body[data-visual-theme="minimal"] .hero-title .accent { color: var(--primary); }
body[data-visual-theme="minimal"] .hero-lead { color: var(--text-secondary); }
body[data-visual-theme="minimal"] .hero-actions .btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; }
body[data-visual-theme="minimal"] .hero-actions .btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); color: #fff; }
body[data-visual-theme="minimal"] .hero-actions .btn-outline { border-color: var(--border); color: var(--text); }
body[data-visual-theme="minimal"] .hero-actions .btn-outline:hover { background: var(--surface-alt); }
body[data-visual-theme="minimal"] .collage-item { border-color: var(--surface); box-shadow: var(--shadow-md); }
body[data-visual-theme="minimal"] .hero-art-dot { background: var(--primary-soft); }

/* ---------- Hero: optional admin-uploaded background photo (Admin >
   Pengaturan > Tampilan) — replaces the active theme's own gradient/
   surface with a real image + dark scrim, always white text for contrast
   regardless of which of the 8x4 theme combinations is active. Every
   selector below repeats the exact body[data-visual-theme=...] prefix of
   the rule it must outrank, at equal specificity, and is placed after all
   of them in source order, so it always wins the cascade regardless of
   which theme is active. ---------- */
.hero-bg-image { position: absolute; inset: 0; z-index: 0; }
.hero-bg-image img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero-bg-scrim { position: absolute; inset: 0; z-index: 0; background: linear-gradient(160deg, rgba(10,14,26,.88) 0%, rgba(10,14,26,.6) 55%, rgba(10,14,26,.4) 100%); }
.hero.has-custom-bg,
body[data-visual-theme="merah-putih"] .hero.has-custom-bg,
body[data-visual-theme="minimal"] .hero.has-custom-bg {
    background: none;
    color: #fff;
}
.hero.has-custom-bg .hero-eyebrow,
body[data-visual-theme="minimal"] .hero.has-custom-bg .hero-eyebrow { color: #fff; opacity: .92; }
.hero.has-custom-bg .hero-period,
body[data-visual-theme="minimal"] .hero.has-custom-bg .hero-period { color: rgba(255,255,255,.85); }
.hero.has-custom-bg .hero-title,
body[data-visual-theme="minimal"] .hero.has-custom-bg .hero-title { color: #fff; }
.hero.has-custom-bg .hero-title .accent,
body[data-visual-theme="minimal"] .hero.has-custom-bg .hero-title .accent { color: var(--primary-soft); }
.hero.has-custom-bg .hero-lead,
body[data-visual-theme="minimal"] .hero.has-custom-bg .hero-lead { color: rgba(255,255,255,.88); }
.hero.has-custom-bg .hero-actions .btn-outline,
body[data-visual-theme="minimal"] .hero.has-custom-bg .hero-actions .btn-outline { border-color: rgba(255,255,255,.6); color: #fff; }
.hero.has-custom-bg .hero-actions .btn-outline:hover,
body[data-visual-theme="minimal"] .hero.has-custom-bg .hero-actions .btn-outline:hover { background: rgba(255,255,255,.14); }

/* Same gradient block treatment as .hero, reused for the candidate detail
   page's visi/misi/program band — kept as its own class (not just reusing
   .hero) since it never carries the hero's grid/padding assumptions. */
.gradient-band {
    background: linear-gradient(160deg, var(--primary) 0%, var(--primary-active) 100%);
    color: #fff;
}
.gradient-band h1, .gradient-band h2, .gradient-band h3, .gradient-band h4 { color: #fff; }
body[data-visual-theme="merah-putih"] .gradient-band { background: linear-gradient(160deg, var(--danger) 0%, var(--danger-text) 100%); }
body[data-visual-theme="minimal"] .gradient-band { background: var(--surface-cream); color: var(--text); }
body[data-visual-theme="minimal"] .gradient-band h1,
body[data-visual-theme="minimal"] .gradient-band h2,
body[data-visual-theme="minimal"] .gradient-band h3,
body[data-visual-theme="minimal"] .gradient-band h4 { color: var(--text); }
.gradient-band .text-muted { color: rgba(255, 255, 255, 0.82); }
body[data-visual-theme="minimal"] .gradient-band .text-muted { color: var(--text-secondary); }

.hero-decoration { position: absolute; inset: 0; overflow: hidden; }
.hero-grid {
    position: relative;
    z-index: 1;
    max-width: 1180px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    gap: var(--space-9);
    align-items: center;
}
@media (max-width: 899px) {
    .hero-grid { grid-template-columns: 1fr; }
    /* width:100% is required here, not just max-width: .hero-visual's own
       content is entirely position:absolute (the collage tiles), which
       contributes no intrinsic size — as a grid item with margin:0 auto
       (auto-margin alignment forces fit-content sizing instead of
       stretch), that intrinsic-width-less box would otherwise resolve to
       0 and collapse the whole collage. A real bug found and fixed
       verifying this at the 768px breakpoint. */
    .hero-visual { width: 100%; max-width: 26rem; margin: 0 auto; }
}
.hero-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: rgba(255, 255, 255, 0.82);
    font-weight: var(--font-semibold);
    font-size: var(--text-sm);
    letter-spacing: 0.03em;
    margin-bottom: var(--space-3);
}
.hero-title {
    font-size: var(--text-display);
    font-weight: var(--font-bold);
    line-height: 1.05;
    /* Softened rather than full white — the .accent phrase below is the
       one part of the headline meant to read as "the point," at full
       white. A tinted --primary-soft accent tested too close to white on
       a dark gradient to register as a highlight at all (every preset's
       *-soft token is a pale, near-white tint by design); inverting which
       phrase is "quiet" is a theme-preset-proof fix that needs no new
       color token. */
    color: rgba(255, 255, 255, 0.78);
    margin-bottom: var(--space-5);
    letter-spacing: -0.01em;
}
.hero-title .accent { color: #fff; }
/* Bagian 23 — the one extra line the winner-showcase hero needs (academic
   year, between the eyebrow and the headline); same muted-white treatment
   as .hero-eyebrow, never a new color token. */
.hero-period { color: rgba(255, 255, 255, 0.78); font-size: var(--text-sm); margin: 0 0 var(--space-3); }
.hero-lead { color: rgba(255, 255, 255, 0.88); font-size: var(--text-lg); line-height: var(--leading-relaxed); margin-bottom: var(--space-7); max-width: 42ch; }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.hero-actions .btn-primary { background: #fff; border-color: #fff; color: var(--primary); }
.hero-actions .btn-primary:hover { background: rgba(255, 255, 255, 0.9); border-color: rgba(255, 255, 255, 0.9); color: var(--primary); }
body[data-visual-theme="merah-putih"] .hero-actions .btn-primary { color: var(--danger-text); }
.hero-actions .btn-outline { border-color: rgba(255, 255, 255, 0.55); color: #fff; }
.hero-actions .btn-outline:hover { background: rgba(255, 255, 255, 0.12); }

/* ---------- Hero visual: candidate photo collage ----------
 * Phase 13 correction — the hero's right column used to be a single
 * illustrated ballot box; it is now an editorial collage built entirely
 * from REAL published-pair campaign photos ($candidatePairs, home.php),
 * count-aware (1/2/3/4+) and never hard-coding how many candidates exist.
 * Every tile reuses the app's one signature "arch" radius (see the
 * now-removed .hero-art comment this replaced) at varying sizes rather
 * than inventing a second container shape, and a plain white edge
 * (var(--surface)) keeps each photo readable whether it's sitting on a
 * saturated gradient (Batik/Merah-Putih/Nusantara) or the Minimal
 * default's cream surface.
 */
.hero-visual { position: relative; }
.hero-visual-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    aspect-ratio: 4 / 3;
    border-radius: 28px;
    border: 1px dashed rgba(255, 255, 255, 0.4);
    color: rgba(255, 255, 255, 0.85);
    text-align: center;
    padding: var(--space-6);
}
body[data-visual-theme="minimal"] .hero-visual-empty { border-color: var(--border); color: var(--text-muted); }
.hero-visual-empty .icon svg { width: 2rem; height: 2rem; }
.hero-visual-empty p { margin: 0; font-size: var(--text-sm); }

.candidate-collage { position: relative; min-height: 380px; }
.collage-item {
    position: absolute;
    overflow: hidden;
    border-radius: 120px 120px 24px 24px;
    border: 5px solid var(--surface);
    box-shadow: var(--shadow-lg);
    margin: 0;
    transition: transform var(--duration-base) var(--ease-standard), box-shadow var(--duration-base) var(--ease-standard);
}
.collage-item img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---------- Candidate photo motion (Phase 14) ----------
 * A slow, bounded, alternating float on the homepage hero collage only —
 * this IS the page's one "characteristic moment" per the Design Bible
 * (Section 10: the hero is where a bit more energy is earned; the voting
 * flow's own candidate cards deliberately stay calm and are untouched
 * here). Hover/focus everywhere a candidate photo appears gets a small,
 * consistent scale — that part is shared with .candidate-case-media and
 * the internal .candidate-card-photo below. `prefers-reduced-motion` is
 * handled for free: tokens.css's global `*,*::before,*::after` rule
 * already forces every animation to 1 iteration at ~0 duration, so the
 * float keyframe below collapses to a single static frame automatically
 * — nothing extra needed here.
 */
@keyframes candidate-float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-10px); }
}
.candidate-collage .collage-item { animation: candidate-float 6s var(--ease-standard) infinite; }
.candidate-collage .collage-item:nth-child(2) { animation-delay: -3s; }
.candidate-collage .collage-item:nth-child(3) { animation-delay: -1.5s; }
.candidate-collage.count-many .collage-item:nth-child(even) { animation-delay: -4s; }
.collage-item:hover,
.collage-item:focus-within {
    /* animation: none, not animation-play-state: paused — a paused CSS
       animation still "owns" the transform property at its last
       interpolated value, which would silently out-rank this rule's own
       transform regardless of specificity. Removing the animation
       outright hands transform back to the normal cascade. */
    animation: none;
    transform: scale(1.035) translateY(-4px);
    box-shadow: var(--shadow-lg), 0 0 0 3px var(--primary-soft);
}
.collage-fallback {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--primary-soft);
    color: var(--primary);
    font-weight: var(--font-bold);
    font-size: var(--text-3xl);
}
.collage-number {
    position: absolute;
    bottom: var(--space-2);
    left: var(--space-2);
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 50%;
    background: var(--surface);
    color: var(--primary);
    font-weight: var(--font-bold);
    font-size: var(--text-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: var(--shadow-sm);
}
.hero-art-dot {
    position: absolute;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.5);
}

/* One photo: a single large centered tile. */
.candidate-collage.count-1 { min-height: 420px; }
.candidate-collage.count-1 .collage-item { top: 4%; left: 15%; width: 70%; height: 92%; }

/* Two photos: an uneven pair with a deliberate overlap, echoing the
   reference's "one bigger, one smaller, offset" composition. */
.candidate-collage.count-2 .collage-item:nth-child(1) { top: 0; left: 2%; width: 58%; height: 76%; z-index: 1; }
.candidate-collage.count-2 .collage-item:nth-child(2) { bottom: 0; right: 2%; width: 48%; height: 64%; z-index: 2; }

/* Three photos: one tall anchor + two smaller stacked opposite it. */
.candidate-collage.count-3 .collage-item:nth-child(1) { top: 2%; left: 0; width: 46%; height: 96%; z-index: 1; }
.candidate-collage.count-3 .collage-item:nth-child(2) { top: 0; right: 4%; width: 40%; height: 44%; z-index: 2; }
.candidate-collage.count-3 .collage-item:nth-child(3) { bottom: 0; right: 0; width: 44%; height: 48%; z-index: 2; }

/* Four or more: falls back to a responsive wrapping flow (never fixed
   absolute slots) so it stays correct for any candidate count. */
.candidate-collage.count-many {
    display: flex;
    flex-wrap: wrap;
    align-content: flex-start;
    gap: var(--space-4);
    min-height: 0;
}
.candidate-collage.count-many .collage-item {
    /* relative, not static: still a normal flex item, but stays the
       positioning context for its own .collage-number badge (a real bug
       once, found via a mismatched-badge-position check on mobile —
       static would let the badge escape to the whole collage's corner
       instead of each tile's own). */
    position: relative;
    width: calc(50% - var(--space-2));
    aspect-ratio: 3 / 4;
}
.candidate-collage.count-many .collage-item:nth-child(odd) { transform: translateY(-10px); }
.candidate-collage.count-many .collage-item:nth-child(even) { transform: translateY(10px); }

@media (max-width: 899px) {
    .candidate-collage:not(.count-many) { min-height: 340px; }
    .candidate-collage.count-1 .collage-item { left: 10%; width: 80%; }
}
@media (max-width: 479px) {
    .candidate-collage.count-2,
    .candidate-collage.count-3 {
        display: flex;
        flex-wrap: wrap;
        align-content: flex-start;
        gap: var(--space-4);
        min-height: 0;
    }
    .candidate-collage.count-2 .collage-item:nth-child(n),
    .candidate-collage.count-3 .collage-item:nth-child(n) {
        /* relative, not static — see the count-many comment above for why. */
        position: relative;
        width: calc(50% - var(--space-2));
        height: auto;
        top: auto; left: auto; right: auto; bottom: auto;
        aspect-ratio: 3 / 4;
        transform: none;
        z-index: auto;
    }
}

/* ---------- Candidate collage (Phase 17b correction) ----------
 * A full-bleed strip immediately below the hero — NOT overlapping it (no
 * negative margin, no z-index trick this time): touching, EQUAL-size
 * tiles edge to edge, uniform fixed height regardless of each banner's own
 * original aspect ratio (object-fit: cover does the cropping). Column
 * count is still DATA-driven (--collage-count, set inline from the real
 * candidate count), just laid out as a plain touching row/wrap instead of
 * a staggered composition. */
.candidate-collage-section { display: block; }
.candidate-collage-grid {
    display: grid;
    grid-template-columns: repeat(var(--collage-count, 2), 1fr);
    gap: 0;
}
@media (max-width: 1023px) {
    .candidate-collage-grid { grid-template-columns: repeat(min(var(--collage-count, 2), 3), 1fr); }
}
@media (max-width: 767px) {
    .candidate-collage-grid { grid-template-columns: repeat(min(var(--collage-count, 2), 2), 1fr); }
}
@media (max-width: 479px) {
    .candidate-collage-grid { grid-template-columns: 1fr; }
}
.candidate-collage-card {
    position: relative;
    display: block;
    height: clamp(260px, 32vw, 440px);
    overflow: hidden;
    background: var(--primary-soft);
    transition: transform var(--duration-base) var(--ease-standard), box-shadow var(--duration-base) var(--ease-standard);
}
.candidate-collage-card:hover,
.candidate-collage-card:focus-visible { transform: translateY(-4px); box-shadow: var(--shadow-lg); z-index: 1; }
.candidate-collage-card img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform var(--duration-slow) var(--ease-standard);
}
.candidate-collage-card:hover img { transform: scale(1.04); }
.candidate-collage-card-fallback {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--text-4xl);
    font-weight: var(--font-bold);
    color: var(--primary);
}
.candidate-collage-card-overlay {
    position: absolute;
    inset: auto 0 0 0;
    padding: var(--space-5) var(--space-4) var(--space-4);
    background: linear-gradient(to top, rgba(15, 23, 42, 0.85) 0%, rgba(15, 23, 42, 0.35) 60%, transparent 100%);
    color: #fff;
}
.candidate-collage-card-eyebrow { display: block; font-size: var(--text-xs); font-weight: var(--font-semibold); text-transform: uppercase; letter-spacing: .06em; opacity: 0.85; margin-bottom: 2px; }
.candidate-collage-card-number { display: block; font-size: var(--text-4xl); font-weight: var(--font-bold); line-height: 1; margin-bottom: var(--space-2); }
.candidate-collage-card-names { font-size: var(--text-base); font-weight: var(--font-bold); line-height: 1.25; }
.candidate-collage-empty { text-align: center; padding: var(--space-8); color: rgba(255, 255, 255, 0.85); background: var(--primary); }
body[data-visual-theme="minimal"] .candidate-collage-empty { color: var(--text-secondary); background: var(--surface-alt); }

/* ---------- Public candidate detail page (Phase 17: editorial redesign) ----------
 * A campaign profile, not a data-record page: the banner IS the hero
 * backdrop (never a small boxed thumbnail), name/number are set at the
 * same scale a page's one earned headline gets, and video/visi-misi reuse
 * the SAME editorial-split alternating pattern the homepage "Tentang"
 * section already established — no new generic card grid invented. */
.candidate-hero {
    position: relative;
    overflow: hidden;
    min-height: 62vh;
    display: flex;
    align-items: flex-end;
    background: linear-gradient(160deg, var(--primary) 0%, var(--primary-active) 100%);
}
body[data-visual-theme="merah-putih"] .candidate-hero { background: linear-gradient(160deg, var(--danger) 0%, var(--danger-text) 100%); }
body[data-visual-theme="minimal"] .candidate-hero { background: var(--surface-cream); }
.candidate-hero-bg { position: absolute; inset: 0; }
.candidate-hero-bg img { width: 100%; height: 100%; object-fit: cover; display: block; }
.candidate-hero-scrim {
    position: absolute;
    inset: 0;
    /* Strong and deep, not just a bottom fade: the name/number sit in a
       flex-end block that can run 150-200px tall, so the near-opaque zone
       has to cover that whole reading area regardless of how busy the
       banner photo itself is — a thin fade at the very edge isn't enough. */
    background: linear-gradient(to top, rgba(8, 10, 18, 0.96) 0%, rgba(8, 10, 18, 0.8) 40%, rgba(8, 10, 18, 0.45) 68%, rgba(8, 10, 18, 0.08) 100%);
}
/* No banner uploaded: fall back to the same corner-ornament treatment the
   auth panel uses, per visual theme — never a flat, empty color block. */
.candidate-hero .theme-panel-decoration { opacity: 0.5; }
.candidate-hero-content { position: relative; z-index: 2; padding: var(--space-10) var(--space-5) var(--space-9); color: #fff; }
body[data-visual-theme="minimal"] .candidate-hero.has-no-banner .candidate-hero-content { color: var(--text); }
.candidate-hero-back {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: inherit;
    opacity: 0.85;
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    margin-bottom: var(--space-6);
}
.candidate-hero-back:hover { opacity: 1; text-decoration: none; }
.candidate-hero-eyebrow {
    display: block;
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    opacity: 0.85;
    margin-bottom: var(--space-1);
}
.candidate-hero-number {
    display: block;
    font-size: var(--text-4xl);
    font-weight: var(--font-bold);
    line-height: 1;
    margin-bottom: var(--space-3);
}
.candidate-hero-name { font-size: var(--text-4xl); line-height: 1.1; color: #fff; margin: 0 0 var(--space-2); max-width: 22ch; }
body[data-visual-theme="minimal"] .candidate-hero.has-no-banner .candidate-hero-name { color: var(--text); }
.candidate-hero-role { font-size: var(--text-lg); opacity: 0.85; margin: 0; }
@media (max-width: 639px) { .candidate-hero { min-height: 52vh; } .candidate-hero-number { font-size: var(--text-3xl); } .candidate-hero-name { font-size: var(--text-3xl); } }

/* Profile strip: floats up over the hero's bottom edge (Section 10's "one
   continuous moment", same overlap technique as the homepage collage). */
.candidate-profile-strip { position: relative; margin-top: calc(-1 * var(--space-9)); z-index: 3; padding-bottom: var(--space-8); }
.candidate-profile-row { display: flex; align-items: flex-end; gap: var(--space-5); flex-wrap: wrap; }
.candidate-detail-photo {
    position: relative;
    flex-shrink: 0;
    width: 132px;
    height: 132px;
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--primary-soft);
    border: 4px solid var(--surface);
    box-shadow: var(--shadow-lg);
}
.candidate-detail-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.candidate-detail-photo-fallback {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--text-3xl);
    font-weight: var(--font-bold);
    color: var(--primary);
}
.candidate-profile-meta { display: flex; flex-direction: column; gap: var(--space-2); padding-bottom: var(--space-2); }
.candidate-profile-meta-row { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; font-size: var(--text-base); }
@media (max-width: 479px) { .candidate-profile-row { align-items: center; text-align: center; flex-direction: column; } }

/* Video + Visi/Misi reuse .editorial-split (Tentang section) directly —
   the video sits in the same "signature shape" frame the homepage
   illustration uses, so the two pages read as one product. */
.candidate-hero-video-frame {
    border-radius: 28px 160px 28px 160px;
    background: #000;
    overflow: hidden;
    aspect-ratio: 5 / 4;
    display: flex;
}
.candidate-hero-video-frame video { width: 100%; height: 100%; object-fit: contain; display: block; }
.candidate-vm-solo .editorial-split-text { max-width: 68ch; margin: 0 auto; }
.candidate-vm-label {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    letter-spacing: 0.04em;
    color: var(--primary);
    margin-bottom: var(--space-3);
}
.candidate-vm-label .icon svg { width: 1.1rem; height: 1.1rem; }
.candidate-vm-vision {
    font-size: var(--text-2xl);
    font-weight: var(--font-semibold);
    line-height: 1.4;
    color: var(--text);
    margin: 0;
    border-left: 4px solid var(--primary);
    padding-left: var(--space-5);
}
.candidate-vm-mission {
    font-size: var(--text-base);
    line-height: var(--leading-relaxed);
    color: var(--text-secondary);
    margin: 0;
    padding-left: var(--space-5);
}

/* ---------- Feature grid (icon + underline device, no card chrome) ---------- */
.feature-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-8) var(--space-6);
}
@media (max-width: 899px) { .feature-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 479px) { .feature-grid { grid-template-columns: 1fr; } }
.feature-card { text-align: left; }
.feature-icon {
    width: 2.75rem;
    height: 2.75rem;
    margin-bottom: var(--space-4);
    border-radius: var(--radius-md);
    background: var(--primary-soft);
    color: var(--primary);
    display: flex;
    align-items: center;
    justify-content: center;
}
.feature-icon svg { width: 1.4rem; height: 1.4rem; }
.feature-card h3 { font-size: var(--text-lg); margin-bottom: var(--space-2); }
.feature-card-bar { width: 2rem; height: 3px; background: var(--primary); border-radius: var(--radius-full); margin-bottom: var(--space-3); }
.feature-card p { color: var(--text-secondary); font-size: var(--text-sm); margin: 0; line-height: var(--leading-relaxed); }

/* ---------- Alternating editorial section (illustration + text) ---------- */
.editorial-split {
    display: grid;
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    gap: var(--space-9);
    align-items: center;
}
.editorial-split.is-reversed { grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); }
.editorial-split.is-reversed .editorial-split-art { order: 2; }
@media (max-width: 899px) {
    .editorial-split,
    .editorial-split.is-reversed { grid-template-columns: 1fr; }
    .editorial-split.is-reversed .editorial-split-art { order: 0; }
}
.editorial-split-art { position: relative; }
.editorial-split-art svg { width: 100%; height: auto; }
.editorial-split-art-shape {
    border-radius: 28px 160px 28px 160px;
    background: var(--primary-soft);
    overflow: hidden;
    aspect-ratio: 5 / 4;
    display: flex;
    align-items: center;
    justify-content: center;
}
.editorial-split h2 { font-size: var(--text-4xl); line-height: 1.15; margin-bottom: var(--space-4); }
.editorial-split p { color: var(--text-secondary); font-size: var(--text-base); line-height: var(--leading-relaxed); margin-bottom: var(--space-5); max-width: 56ch; }
.editorial-split-list { list-style: none; margin: 0 0 var(--space-6); padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.editorial-split-list li { display: flex; align-items: flex-start; gap: var(--space-3); font-size: var(--text-sm); color: var(--text-secondary); }
.editorial-split-list .icon { color: var(--primary); margin-top: 2px; }

/* ---------- Candidate showcase (editorial portfolio / case rows) ----------
 * Phase 13 second correction — replaces the earlier "battle" card grid
 * (SaaS-card-kit shaped: identical rounded cards, same shadow, VS badge)
 * with a portfolio/case-study presentation: one full-width alternating
 * row per candidate pair, reusing the SAME image+text alternation
 * already established by .editorial-split rather than inventing a
 * second grid mechanic.
 */
.candidate-showcase-header { max-width: 42rem; margin: 0 0 var(--space-8); }
.candidate-showcase-empty { color: var(--text-muted); }
.candidate-case-list { display: flex; flex-direction: column; gap: var(--space-11); }
.candidate-case {
    display: grid;
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    gap: var(--space-8);
    align-items: center;
}
.candidate-case.is-reversed { grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); }
.candidate-case.is-reversed .candidate-case-media { order: 2; }
@media (max-width: 899px) {
    .candidate-case,
    .candidate-case.is-reversed { grid-template-columns: 1fr; }
    .candidate-case.is-reversed .candidate-case-media { order: 0; }
}
.candidate-case-media {
    position: relative;
    aspect-ratio: 4 / 3;
    border-radius: 28px 160px 28px 160px;
    overflow: hidden;
    background: var(--primary-soft);
    transition: transform var(--duration-base) var(--ease-standard);
}
.candidate-case-media:hover,
.candidate-case-media:focus-within { transform: scale(1.02); }
.candidate-case-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.candidate-case-fallback {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--text-4xl);
    font-weight: var(--font-bold);
    color: var(--primary);
}
.candidate-case-number { display: inline-block; color: var(--primary); font-weight: var(--font-semibold); font-size: var(--text-sm); margin-bottom: var(--space-2); }
.candidate-case-body h3 { font-size: var(--text-3xl); line-height: 1.2; margin-bottom: var(--space-2); }
.candidate-case-role { color: var(--text-secondary); font-size: var(--text-sm); margin: 0 0 var(--space-4); }
.candidate-case-vision { color: var(--text-secondary); font-size: var(--text-base); line-height: var(--leading-relaxed); margin: 0; max-width: 56ch; }

/* ---------- Announcement / election status band ---------- */
.announce-band { padding: var(--space-8) var(--space-5); background: var(--surface-alt); border-top: 1px solid var(--border); }
.announce-band-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-4); }
.announce-band-text .badge { margin-bottom: var(--space-2); }
.announce-band-text h3 { font-size: var(--text-xl); margin: 0 0 2px; }
.announce-band-text p { color: var(--text-muted); font-size: var(--text-sm); margin: 0; }

/* ---------- CTA band ---------- */
.cta-band {
    background: var(--primary);
    color: #fff;
    text-align: center;
    padding: var(--space-11) var(--space-5);
}
body[data-visual-theme="merah-putih"] .cta-band { background: var(--danger); }
.cta-band h2 { color: #fff; font-size: var(--text-4xl); margin-bottom: var(--space-3); max-width: 24ch; margin-left: auto; margin-right: auto; }
.cta-band p { color: rgba(255, 255, 255, 0.85); font-size: var(--text-base); max-width: 50ch; margin: 0 auto var(--space-6); }
.cta-band .btn-primary { background: #fff; border-color: #fff; color: var(--primary); }
.cta-band .btn-primary:hover { background: rgba(255, 255, 255, 0.9); color: var(--primary); }
body[data-visual-theme="merah-putih"] .cta-band .btn-primary { color: var(--danger-text); }

/* ---------- Footer (multi-column, sophisticated) ---------- */
.site-footer {
    background: var(--sidebar-bg);
    color: var(--sidebar-text);
    padding: var(--space-10) var(--space-5) var(--space-6);
}
.site-footer-grid {
    max-width: 1180px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: minmax(0, 1.3fr) repeat(2, minmax(0, 1fr));
    gap: var(--space-8);
}
@media (max-width: 767px) {
    .site-footer-grid { grid-template-columns: 1fr; gap: var(--space-7); }
}
.site-footer-brand { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); }
.site-footer-tagline { color: var(--sidebar-text-strong); font-size: var(--text-sm); font-weight: var(--font-medium); }
.site-footer-desc { color: var(--sidebar-text); font-size: var(--text-sm); line-height: var(--leading-relaxed); max-width: 34ch; margin: 0; }
.site-footer-heading { color: var(--sidebar-text-strong); font-size: var(--text-xs); font-weight: var(--font-semibold); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: var(--space-4); }
.site-footer-links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.site-footer-links a { color: var(--sidebar-text); font-size: var(--text-sm); }
.site-footer-links a:hover { color: var(--sidebar-text-strong); text-decoration: none; }
.site-footer-contact { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.site-footer-contact li { display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--text-sm); color: var(--sidebar-text); }
.site-footer-contact .icon { color: var(--sidebar-text-strong); margin-top: 2px; flex-shrink: 0; }
.site-footer-bottom {
    max-width: 1180px;
    margin: var(--space-8) auto 0;
    padding-top: var(--space-5);
    border-top: 1px solid var(--sidebar-border);
    color: var(--sidebar-text);
    font-size: var(--text-xs);
    text-align: center;
}

/* ---------- Bold decorative panels (hero art + login panel) ----------
 * Deliberately NOT part of visual-themes.css's subtle body-wide
 * .theme-background mechanism (Phase 11, unmodified) — an additive
 * treatment used only inside a fixed-size decorative container.
 *
 * A CORNER/EDGE ornament, not a repeating tile: two opposite corners
 * (top-left + bottom-right, via ::before/::after — the second mirrored
 * 180° so the same asset's "dense" origin point lands on ITS corner
 * too) for Minimal/Batik/Nusantara, or a flowing ribbon along the top
 * and bottom edges for Merah Putih.
 */
.theme-panel-decoration {
    position: absolute;
    inset: 0;
    pointer-events: none;
    overflow: hidden;
}
.theme-panel-decoration::before,
.theme-panel-decoration::after {
    content: '';
    position: absolute;
    width: 260px;
    height: 260px;
    background-repeat: no-repeat;
}
.theme-panel-decoration::before { top: -24px; left: -24px; }
.theme-panel-decoration::after { bottom: -24px; right: -24px; transform: rotate(180deg); }

/* Minimal: colored line-icon corners on the (light) auth panel, white on
   the (saturated) hero-art gradient — same asset, tint depends on context. */
body[data-visual-theme="minimal"] .theme-panel-decoration::before,
body[data-visual-theme="minimal"] .theme-panel-decoration::after {
    -webkit-mask-image: url('../img/patterns/minimal-accents.svg');
    mask-image: url('../img/patterns/minimal-accents.svg');
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-size: 200px 200px;
    mask-size: 200px 200px;
    -webkit-mask-position: top left;
    mask-position: top left;
    background-color: var(--surface);
}
body[data-visual-theme="minimal"] .auth-panel .theme-panel-decoration::before,
body[data-visual-theme="minimal"] .auth-panel .theme-panel-decoration::after {
    background-color: var(--primary);
}

body[data-visual-theme="batik"] .theme-panel-decoration::before,
body[data-visual-theme="batik"] .theme-panel-decoration::after {
    background-color: var(--surface);
    -webkit-mask-image: url('../img/patterns/batik-corner.svg');
    mask-image: url('../img/patterns/batik-corner.svg');
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-size: 260px 260px;
    mask-size: 260px 260px;
}

body[data-visual-theme="nusantara"] .theme-panel-decoration::before,
body[data-visual-theme="nusantara"] .theme-panel-decoration::after {
    background-color: var(--surface);
    -webkit-mask-image: url('../img/patterns/nusantara-tumpal.svg');
    mask-image: url('../img/patterns/nusantara-tumpal.svg');
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-size: 180px 180px;
    mask-size: 180px 180px;
    -webkit-mask-position: top left;
    mask-position: top left;
}

/* Merah Putih: a flowing ribbon along the top and bottom edges instead of a
   corner box — its own fixed red/white identity, independent of the color
   theme, same as its body-wide pattern. */
body[data-visual-theme="merah-putih"] .theme-panel-decoration::before,
body[data-visual-theme="merah-putih"] .theme-panel-decoration::after {
    width: 100%;
    height: 150px;
    left: 0;
    background-image: url('../img/patterns/merah-putih-ribbon.svg');
    background-size: 100% 100%;
}
body[data-visual-theme="merah-putih"] .theme-panel-decoration::before { top: 0; }
body[data-visual-theme="merah-putih"] .theme-panel-decoration::after { bottom: 0; right: auto; transform: scaleY(-1); }

/* ---------- Login split page ----------
 * A genuine 50/50 split, not a centered card over a backdrop: LEFT is a
 * bold themed panel carrying the same visual language as the homepage
 * hero (arch-shaped illustration, per-visual-theme decoration); RIGHT is
 * a plain white panel holding the actual form. On mobile the left panel
 * collapses to a compact header band (not hidden) so the form stays
 * immediately reachable without a large decorative scroll first.
 */
.auth-split {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
}
@media (max-width: 899px) {
    .auth-split { grid-template-columns: 1fr; }
}
.auth-panel {
    position: relative;
    overflow: hidden;
    background: linear-gradient(165deg, var(--primary) 0%, var(--primary-active) 100%);
    color: #fff;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: var(--space-10);
}
body[data-visual-theme="minimal"] .auth-panel { background: var(--primary-soft); color: var(--text); }
body[data-visual-theme="merah-putih"] .auth-panel { background: #ffffff; color: var(--text); }
.auth-panel .theme-panel-decoration { opacity: 0.45; }
.auth-panel-content { position: relative; z-index: 1; max-width: 26rem; }
.auth-panel-brand { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-8); }
.auth-panel-brand .brand-mark { width: 2.5rem; height: 2.5rem; border-radius: var(--radius-md); background: rgba(255, 255, 255, 0.18); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: var(--font-bold); flex-shrink: 0; }
body[data-visual-theme="minimal"] .auth-panel-brand .brand-mark,
body[data-visual-theme="merah-putih"] .auth-panel-brand .brand-mark { background: var(--primary); color: #fff; }
.auth-panel h2 { font-size: var(--text-3xl); color: inherit; margin-bottom: var(--space-3); line-height: 1.15; }
.auth-panel p { font-size: var(--text-base); line-height: var(--leading-relaxed); opacity: 0.9; margin-bottom: 0; max-width: 32ch; }
.auth-panel-art { position: relative; z-index: 1; margin-top: var(--space-8); max-width: 12rem; }
.auth-panel-art svg { width: 100%; height: auto; }
.auth-panel-art img { width: 100%; height: auto; display: block; }
@media (max-width: 899px) {
    .auth-panel { padding: var(--space-6) var(--space-5); }
    .auth-panel-art { display: none; }
    .auth-panel-brand { margin-bottom: var(--space-4); }
    .auth-panel h2 { font-size: var(--text-xl); }
    .auth-panel p { display: none; }
}

/* ---------- Login panel: optional admin-uploaded background photo (same
   upload as the homepage hero, Admin > Pengaturan > Tampilan) — desktop
   only, matching the existing 899px breakpoint above where the panel
   already collapses to a compact header band on mobile; showing a full
   photo there wouldn't fit that band. ---------- */
.auth-panel-bg-image { position: absolute; inset: 0; z-index: 0; }
.auth-panel-bg-image img { width: 100%; height: 100%; object-fit: cover; display: block; }
.auth-panel-bg-scrim { position: absolute; inset: 0; z-index: 0; background: linear-gradient(165deg, rgba(10,14,26,.82) 0%, rgba(10,14,26,.55) 100%); }
.auth-panel.has-custom-bg,
body[data-visual-theme="minimal"] .auth-panel.has-custom-bg,
body[data-visual-theme="merah-putih"] .auth-panel.has-custom-bg {
    background: none;
    color: #fff;
}
@media (max-width: 899px) {
    .auth-panel-bg-image, .auth-panel-bg-scrim { display: none; }
    /* The image itself is hidden above (doesn't fit the collapsed header
       band), but .has-custom-bg's "background: none" still applies here —
       restore a visible fill so the band isn't blank white-on-white. */
    .auth-panel.has-custom-bg,
    body[data-visual-theme="minimal"] .auth-panel.has-custom-bg,
    body[data-visual-theme="merah-putih"] .auth-panel.has-custom-bg {
        background: linear-gradient(165deg, var(--primary) 0%, var(--primary-active) 100%);
    }
}

.auth-form-side {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-8) var(--space-6);
    background: var(--background);
}
.auth-form-card { width: 100%; max-width: 24rem; }

/* ---------- Input with icon (login form) ---------- */
.input-icon-group { position: relative; }
.input-icon-group .form-control { padding-left: 2.5rem; }
.input-icon-group .input-icon {
    position: absolute;
    left: 0.85rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
    pointer-events: none;
    display: flex;
}
.input-icon-group .input-icon svg { width: 1.05rem; height: 1.05rem; }
.password-toggle-btn {
    position: absolute;
    right: 0.5rem;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 0.35rem;
    display: flex;
    border-radius: var(--radius-sm);
}
.password-toggle-btn:hover { color: var(--text); background: var(--border-subtle); }
.password-toggle-btn svg { width: 1.1rem; height: 1.1rem; }
.input-icon-group.has-toggle .form-control { padding-right: 2.5rem; }
.password-toggle-btn .eye-closed { display: none; }
.password-toggle-btn.is-visible .eye-open { display: none; }
.password-toggle-btn.is-visible .eye-closed { display: block; }

/* ---------- Login: "forgot password" disclosure ----------
 * No password-reset feature exists (deliberately out of scope) — this is a
 * plain, static <details> note pointing the visitor at a human, not a form
 * post to anywhere.
 */
.login-forgot-note { text-align: center; margin-top: var(--space-4); }
.login-forgot-note summary {
    cursor: pointer;
    color: var(--primary);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    list-style: none;
}
.login-forgot-note summary::-webkit-details-marker { display: none; }
.login-forgot-note summary:hover { text-decoration: underline; }
.login-forgot-note[open] summary { margin-bottom: var(--space-2); }
.login-forgot-note p { margin: 0; font-size: var(--text-xs); color: var(--text-muted); }
.login-footer-text { text-align: center; margin-top: var(--space-5); padding-top: var(--space-4); border-top: 1px solid var(--border-subtle); color: var(--text-muted); font-size: var(--text-xs); }
