/* ============================================================
   360° Tour — futuristic UI
   ============================================================ */

:root {
    --bg:        #070b14;
    --bg-2:      #0b1120;
    --panel:     rgba(19, 26, 44, .72);
    --panel-2:   rgba(12, 18, 33, .9);
    --line:      rgba(120, 160, 255, .16);
    --line-hot:  rgba(120, 190, 255, .45);
    --text:      #e9eefb;
    --muted:     #8b9ac0;
    --accent:    #4dd6ff;
    --accent-2:  #7b5cff;
    --danger:    #ff5470;
    --ok:        #2ee6a8;
    --radius:    16px;
    --ease:      cubic-bezier(.22, 1, .36, 1);
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
    margin: 0;
    min-height: 100vh;
    background: var(--bg);
    color: var(--text);
    font: 15px/1.6 "Inter", system-ui, "Segoe UI", Roboto, sans-serif;
    -webkit-font-smoothing: antialiased;
    letter-spacing: .01em;
    overflow-x: hidden;
}

/* Ambient aurora backdrop */
body::before {
    content: "";
    position: fixed;
    inset: -30vh -20vw;
    z-index: -2;
    background:
        radial-gradient(42vw 42vw at 12% 8%,  rgba(77, 214, 255, .16), transparent 62%),
        radial-gradient(38vw 38vw at 88% 22%, rgba(123, 92, 255, .16), transparent 60%),
        radial-gradient(46vw 46vw at 50% 108%, rgba(46, 230, 168, .10), transparent 62%);
    filter: blur(20px);
    animation: drift 26s var(--ease) infinite alternate;
}
/* Faint grid */
body::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image:
        linear-gradient(rgba(120, 160, 255, .045) 1px, transparent 1px),
        linear-gradient(90deg, rgba(120, 160, 255, .045) 1px, transparent 1px);
    background-size: 64px 64px;
    mask-image: radial-gradient(ellipse 90% 65% at 50% 30%, #000 30%, transparent 100%);
    -webkit-mask-image: radial-gradient(ellipse 90% 65% at 50% 30%, #000 30%, transparent 100%);
}

@keyframes drift {
    from { transform: translate3d(-2%, -1%, 0) scale(1); }
    to   { transform: translate3d(3%, 2%, 0) scale(1.12); }
}

a { color: var(--accent); text-decoration: none; }

.wrap { max-width: 1180px; margin: 0 auto; padding: 0 24px; }

/* ---------- Header ---------- */
.site-head {
    position: sticky;
    top: 0;
    z-index: 40;
    padding: 16px 0;
    margin-bottom: 34px;
    background: rgba(7, 11, 20, .62);
    backdrop-filter: blur(18px) saturate(150%);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    border-bottom: 1px solid var(--line);
}
.site-head .wrap { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.site-head h1 {
    margin: 0;
    font-size: 19px;
    font-weight: 650;
    letter-spacing: .04em;
    background: linear-gradient(96deg, #fff 10%, var(--accent) 55%, var(--accent-2) 95%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

/* ---------- Hero ---------- */
.hero { padding: 26px 0 40px; text-align: center; }
.hero .eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 14px;
    margin-bottom: 18px;
    border: 1px solid var(--line-hot);
    border-radius: 999px;
    background: rgba(77, 214, 255, .07);
    font-size: 12px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--accent);
}
.hero .eyebrow .dot {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 0 0 rgba(77, 214, 255, .6);
    animation: pulse 2.2s infinite;
}
@keyframes pulse {
    70%  { box-shadow: 0 0 0 9px rgba(77, 214, 255, 0); }
    100% { box-shadow: 0 0 0 0  rgba(77, 214, 255, 0); }
}
.hero h2 {
    margin: 0 0 12px;
    font-size: clamp(30px, 5.5vw, 54px);
    line-height: 1.08;
    font-weight: 700;
    letter-spacing: -.02em;
    background: linear-gradient(180deg, #fff 30%, rgba(233, 238, 251, .58) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}
.hero p { margin: 0 auto; max-width: 560px; color: var(--muted); }

/* ---------- Buttons ---------- */
.btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 17px;
    border: 1px solid var(--line);
    border-radius: 11px;
    background: var(--panel);
    color: var(--text);
    font: inherit;
    font-size: 14px;
    cursor: pointer;
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    transition: border-color .25s var(--ease), transform .25s var(--ease),
                box-shadow .25s var(--ease), background .25s var(--ease);
}
.btn:hover {
    border-color: var(--line-hot);
    transform: translateY(-1px);
    box-shadow: 0 8px 26px -12px rgba(77, 214, 255, .55);
}
.btn:active { transform: translateY(0) scale(.985); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn-primary {
    background: linear-gradient(96deg, var(--accent), var(--accent-2));
    border-color: transparent;
    color: #05131c;
    font-weight: 650;
}
.btn-primary:hover { box-shadow: 0 12px 34px -12px rgba(123, 92, 255, .8); }
.btn-ghost { background: rgba(255, 255, 255, .03); }
.btn-danger { background: rgba(255, 84, 112, .14); border-color: rgba(255, 84, 112, .4); color: #ff8ea1; }
.btn-danger:hover { background: rgba(255, 84, 112, .24); border-color: var(--danger); }
.btn-sm { padding: 6px 11px; font-size: 13px; border-radius: 9px; }
.btn.is-on { border-color: var(--accent); background: rgba(77, 214, 255, .16); color: #d7f6ff; }

/* ---------- Gallery ---------- */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 26px;
    padding-bottom: 30px;
}
.tile {
    position: relative;
    display: block;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--panel);
    overflow: hidden;
    color: inherit;
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    /* Visible by DEFAULT. The entrance animation supplies its own start state,
       so if it is throttled, disabled or never scheduled the tile still shows.
       Never make visibility depend on an animation completing. */
    opacity: 1;
    animation: rise .7s var(--ease) both;
    animation-delay: var(--d, 0s);
    transition: transform .45s var(--ease), border-color .45s var(--ease), box-shadow .45s var(--ease);
}
@keyframes rise {
    from { opacity: 0; transform: translateY(22px); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .tile { opacity: 1; transform: none; animation: none; }
    body::before { animation: none; }
}
.tile:hover {
    transform: translateY(-6px);
    border-color: var(--line-hot);
    box-shadow: 0 26px 60px -28px rgba(77, 214, 255, .6);
}
/* Rotating conic sheen on hover */
.tile::before {
    content: "";
    position: absolute;
    inset: -1px;
    border-radius: inherit;
    padding: 1px;
    background: conic-gradient(from var(--a, 0deg), transparent 0 62%, var(--accent) 78%, var(--accent-2) 88%, transparent 100%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    opacity: 0;
    transition: opacity .4s var(--ease);
    pointer-events: none;
}
.tile:hover::before { opacity: 1; animation: spin 3.4s linear infinite; }
@property --a { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
@keyframes spin { to { --a: 360deg; } }

.tile-media {
    position: relative; overflow: hidden; height: 190px;
    /* Shown while the image loads, and left behind if it never does. */
    background: linear-gradient(135deg, #131c31, #0c1424);
}
.tile-media img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
    transform: scale(1.02);
    transition: transform .8s var(--ease), filter .5s var(--ease);
}
/* If the file is missing/undecodable, hide the broken-image glyph and
   let the placeholder label below show through instead. */
.tile-media img.failed { opacity: 0; }
.tile-media .fallback {
    position: absolute; inset: 0;
    display: none;
    align-items: center; justify-content: center;
    gap: 8px;
    color: var(--muted); font-size: 13px; letter-spacing: .04em;
}
.tile-media img.failed + .fallback { display: flex; }
.tile:hover .tile-media img { transform: scale(1.12); filter: saturate(1.15) brightness(1.05); }
.tile-media::after {
    content: "";
    position: absolute; inset: 0;
    background: linear-gradient(180deg, transparent 42%, rgba(7, 11, 20, .88) 100%);
}
.badge {
    position: absolute; top: 12px; right: 12px; z-index: 2;
    display: inline-flex; align-items: center; gap: 6px;
    padding: 5px 11px;
    border: 1px solid var(--line-hot);
    border-radius: 999px;
    background: rgba(7, 11, 20, .6);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--accent);
}
.badge svg { width: 13px; height: 13px; animation: orbit 7s linear infinite; }
@keyframes orbit { to { transform: rotate(360deg); } }

.tile-body { position: relative; z-index: 2; padding: 16px 18px 20px; margin-top: -34px; }
.tile-body h3 { margin: 0 0 5px; font-size: 17px; font-weight: 620; letter-spacing: -.01em; }
.tile-body p {
    margin: 0; font-size: 13px; color: var(--muted);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.tile-cta {
    display: inline-flex; align-items: center; gap: 6px;
    margin-top: 12px; font-size: 13px; font-weight: 600; color: var(--accent);
    opacity: .75; transition: gap .3s var(--ease), opacity .3s var(--ease);
}
.tile:hover .tile-cta { gap: 11px; opacity: 1; }

.empty {
    border: 1px dashed var(--line);
    border-radius: var(--radius);
    padding: 74px 24px;
    text-align: center;
    background: var(--panel);
}

/* ---------- Cards & forms ---------- */
.card {
    position: relative;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 26px;
    margin-bottom: 28px;
    backdrop-filter: blur(16px) saturate(140%);
    -webkit-backdrop-filter: blur(16px) saturate(140%);
    box-shadow: 0 24px 60px -40px rgba(0, 0, 0, .9);
}
.card h1, .card h2 { margin-top: 0; letter-spacing: -.01em; }
.card h2 { font-size: 18px; }

label { display: block; margin-bottom: 16px; font-size: 13px; color: var(--muted); letter-spacing: .02em; }
label.check { display: flex; align-items: center; gap: 9px; }
input[type=text], input[type=password], input[type=number], input[type=file], textarea {
    display: block;
    width: 100%;
    margin-top: 7px;
    padding: 11px 13px;
    background: rgba(5, 9, 18, .75);
    border: 1px solid var(--line);
    border-radius: 11px;
    color: var(--text);
    font: inherit;
    transition: border-color .25s var(--ease), box-shadow .25s var(--ease), background .25s var(--ease);
}
input:focus, textarea:focus {
    outline: none;
    border-color: var(--accent);
    background: rgba(5, 9, 18, .95);
    box-shadow: 0 0 0 4px rgba(77, 214, 255, .12);
}
input[type=file] { padding: 9px 12px; cursor: pointer; }
label.check input { width: auto; margin: 0; accent-color: var(--accent); }
.preview { width: 100%; max-height: 280px; object-fit: cover; border-radius: 12px; margin-bottom: 20px; border: 1px solid var(--line); }

.page-narrow { display: flex; align-items: center; justify-content: center; min-height: 100vh; min-height: 100dvh; padding: 24px; }
.page-narrow .card { width: 100%; max-width: 430px; animation: rise .6s var(--ease) both; }
.steps { color: var(--muted); font-size: 14px; padding-left: 18px; }
.lead { color: var(--muted); margin-top: 0; }
.muted { color: var(--muted); }
.site-foot { padding: 48px 24px 64px; font-size: 13px; text-align: center; }
.built-by {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    margin: 0 0 8px;
    font-size: 14px;
    color: var(--text);
}
.built-by strong {
    font-weight: 620;
    background: linear-gradient(96deg, var(--accent), var(--accent-2));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}
.built-by .heart {
    width: 15px;
    height: 15px;
    fill: #ff4d6d;
    filter: drop-shadow(0 0 6px rgba(255, 77, 109, .55));
    transform-origin: center;
    animation: heartbeat 1.6s var(--ease) infinite;
}
@keyframes heartbeat {
    0%, 28%, 100% { transform: scale(1); }
    14%           { transform: scale(1.25); }
    21%           { transform: scale(1.1); }
}
@media (prefers-reduced-motion: reduce) {
    .built-by .heart { animation: none; }
}
.thanks { margin: 0; font-size: 13px; color: var(--muted); }
.thanks a {
    display: inline-flex; align-items: center; gap: 4px;
    color: var(--accent); font-weight: 600;
    border-bottom: 1px solid transparent;
    transition: border-color .25s var(--ease), color .25s var(--ease);
}
.thanks a:hover { color: #9fe9ff; border-bottom-color: currentColor; }
.thanks .ext { width: 12px; height: 12px; }

.alert {
    padding: 12px 16px; border-radius: 11px; margin-bottom: 20px; font-size: 14px;
    animation: rise .45s var(--ease) both;
}
.alert-ok  { background: rgba(46, 230, 168, .1); border: 1px solid rgba(46, 230, 168, .4); color: #92f4d3; }
.alert-err { background: rgba(255, 84, 112, .1); border: 1px solid rgba(255, 84, 112, .4); color: #ffa7b6; }

/* ---------- Table ---------- */
.table-scroll { overflow-x: auto; border-radius: 12px; }
.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th, .table td { padding: 12px 11px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: middle; }
.table tbody tr { transition: background .25s var(--ease); }
.table tbody tr:hover { background: rgba(77, 214, 255, .05); }
.table th { color: var(--muted); font-weight: 600; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; }
.thumb { width: 92px; height: 52px; object-fit: cover; border-radius: 8px; display: block; border: 1px solid var(--line); }
.actions { white-space: nowrap; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.inline { display: inline; }
.pill { padding: 4px 11px; border-radius: 999px; font-size: 11px; letter-spacing: .08em; text-transform: uppercase; }
.pill-on  { background: rgba(46, 230, 168, .15); color: #7cf0cd; border: 1px solid rgba(46, 230, 168, .35); }
.pill-off { background: rgba(139, 154, 192, .13); color: var(--muted); border: 1px solid var(--line); }

/* ============================================================
   Viewer
   ============================================================ */
/* 100vh is wrong on phones: it counts the space behind the browser's URL bar,
   so the dock ends up under it. dvh tracks the actually-visible viewport;
   the vh line stays first as the fallback for older browsers. */
.viewer-page { height: 100vh; height: 100dvh; display: flex; flex-direction: column; overflow: hidden; }
.viewer-page::after { display: none; }

.viewer-bar {
    position: relative;
    z-index: 20;
    display: flex; align-items: center; justify-content: space-between; gap: 14px;
    padding: 12px 20px;
    background: rgba(7, 11, 20, .62);
    backdrop-filter: blur(18px) saturate(150%);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    border-bottom: 1px solid var(--line);
}
.viewer-bar h2 { margin: 0; font-size: 16px; font-weight: 620; letter-spacing: -.01em; }
.viewer-bar .grp { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.stage { position: relative; flex: 1; min-height: 0; background: #04070e; }
#panorama { position: absolute; inset: 0; opacity: 0; transition: opacity .9s var(--ease); }
#panorama.ready { opacity: 1; }

/* Floating control dock */
.dock {
    position: absolute;
    left: 50%; bottom: 22px;
    transform: translateX(-50%) translateY(12px);
    z-index: 15;
    display: flex; align-items: center; gap: 8px;
    padding: 9px 12px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: rgba(11, 17, 32, .7);
    backdrop-filter: blur(20px) saturate(160%);
    -webkit-backdrop-filter: blur(20px) saturate(160%);
    box-shadow: 0 20px 50px -24px rgba(0, 0, 0, .95);
    opacity: 0;
    pointer-events: none;
    transition: opacity .5s var(--ease), transform .5s var(--ease);
    flex-wrap: wrap;
    justify-content: center;
    max-width: calc(100vw - 32px);
}
.dock.show { opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; }
.dock .sep { width: 1px; height: 22px; background: var(--line); }
.dock .speed { display: flex; align-items: center; gap: 8px; padding: 0 6px; font-size: 12px; color: var(--muted); }
.dock .speed b {
    min-width: 16px; text-align: center;
    color: var(--accent); font-variant-numeric: tabular-nums; font-weight: 600;
}
/* Play / pause is the primary control — make it read that way. */
.dock .playpause { min-width: 44px; justify-content: center; font-size: 12px; }
.dock .playpause.is-on {
    border-color: var(--accent);
    background: rgba(77, 214, 255, .16);
    color: #d7f6ff;
}
.dock input[type=range] {
    width: 92px; height: 3px; margin: 0; padding: 0;
    appearance: none; -webkit-appearance: none;
    background: linear-gradient(90deg, var(--accent), var(--accent-2));
    border: none; border-radius: 3px; cursor: pointer;
}
.dock input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 13px; height: 13px; border-radius: 50%;
    background: #fff; border: none; cursor: pointer;
    box-shadow: 0 0 10px rgba(77, 214, 255, .9);
}
.dock input[type=range]::-moz-range-thumb {
    width: 13px; height: 13px; border-radius: 50%;
    background: #fff; border: none; cursor: pointer;
}

/* ---------- Download / loading overlay ---------- */
.loader {
    position: absolute; inset: 0; z-index: 25;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px;
    background: radial-gradient(60% 60% at 50% 45%, rgba(19, 28, 51, .96), rgba(4, 7, 14, .99));
    transition: opacity .6s var(--ease), visibility .6s var(--ease);
}
.loader.hide { opacity: 0; visibility: hidden; }

.ring { position: relative; width: 148px; height: 148px; }
.ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.ring .track { fill: none; stroke: rgba(120, 160, 255, .14); stroke-width: 5; }
.ring .bar {
    fill: none; stroke: url(#ringGrad); stroke-width: 5; stroke-linecap: round;
    transition: stroke-dashoffset .25s linear;
    filter: drop-shadow(0 0 7px rgba(77, 214, 255, .75));
}
.ring .pct {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    font-variant-numeric: tabular-nums;
}
.ring .pct b { font-size: 33px; font-weight: 620; letter-spacing: -.02em; }
.ring .pct small { font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); margin-top: 3px; }

.loader-meta { text-align: center; }
.loader-meta .title { font-size: 15px; font-weight: 600; margin-bottom: 6px; }
.loader-meta .sub { font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.loader-meta .err { color: #ffa7b6; }

.viewer-caption {
    position: relative; z-index: 20;
    padding: 12px 20px;
    background: rgba(7, 11, 20, .62);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border-top: 1px solid var(--line);
    font-size: 13.5px; color: var(--muted);
}

/* ---------- Cinema mode ---------- */
.cinema-exit {
    position: absolute; top: 14px; right: 16px; z-index: 32;
    opacity: 0; pointer-events: none;
    transition: opacity .45s var(--ease);
}
body.cinema .viewer-bar,
body.cinema .dock,
body.cinema .hud,
body.cinema .viewer-caption,
body.cinema .pnlm-controls-container,
body.cinema .infopanel { opacity: 0 !important; pointer-events: none !important; }
body.cinema .viewer-bar,
body.cinema .viewer-caption { transform: translateY(-100%); }
body.cinema .viewer-caption { transform: translateY(100%); }
body.cinema .cinema-exit { opacity: .25; pointer-events: auto; }
body.cinema .cinema-exit:hover { opacity: 1; }
.viewer-bar, .viewer-caption {
    transition: opacity .45s var(--ease), transform .45s var(--ease);
}

/* ---------- HUD readout ---------- */
.hud {
    position: absolute; top: 14px; left: 16px; z-index: 15;
    display: flex; gap: 14px;
    padding: 7px 13px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: rgba(11, 17, 32, .62);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    font-size: 11.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--muted);
    font-variant-numeric: tabular-nums;
    opacity: 0; transform: translateY(-8px);
    transition: opacity .5s var(--ease), transform .5s var(--ease);
    pointer-events: none;
}
.hud.show { opacity: 1; transform: none; }
.hud b { color: var(--accent); font-weight: 600; }

/* ---------- Toast ---------- */
.toast {
    position: absolute; left: 50%; top: 22px; z-index: 30;
    transform: translate(-50%, -14px);
    padding: 9px 16px;
    border: 1px solid var(--line-hot);
    border-radius: 999px;
    background: rgba(11, 17, 32, .9);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    font-size: 13px;
    opacity: 0; pointer-events: none;
    transition: opacity .35s var(--ease), transform .35s var(--ease);
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* ---------- Info panel ---------- */
.infopanel {
    position: absolute; top: 0; right: 0; bottom: 0; z-index: 28;
    width: min(340px, 86vw);
    padding: 26px 24px;
    overflow-y: auto;
    border-left: 1px solid var(--line);
    background: rgba(8, 13, 24, .93);
    backdrop-filter: blur(24px) saturate(160%);
    -webkit-backdrop-filter: blur(24px) saturate(160%);
    transform: translateX(103%);
    transition: transform .5s var(--ease);
}
.infopanel.show { transform: none; }
.infopanel h3 { margin: 0 0 10px; font-size: 18px; }
.infopanel h4 { margin: 24px 0 10px; font-size: 12px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.infopanel p { color: var(--muted); font-size: 14px; }
.infopanel dl { display: grid; grid-template-columns: auto 1fr; gap: 7px 14px; margin: 18px 0 0; font-size: 13px; }
.infopanel dt { color: var(--muted); }
.infopanel dd { margin: 0; text-align: right; }
.keys { list-style: none; margin: 0 0 20px; padding: 0; font-size: 13px; color: var(--muted); }
.keys li { display: flex; align-items: center; gap: 8px; padding: 5px 0; }
kbd {
    display: inline-block; min-width: 22px; padding: 3px 6px;
    border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 6px;
    background: rgba(255, 255, 255, .05);
    font: 11px/1 ui-monospace, monospace; text-align: center; color: var(--text);
}

/* ---------- Hotspots ---------- */
.pnlm-hotspot.hs-scene { background-position: 0 -26px; }
.pnlm-tooltip span {
    background: rgba(11, 17, 32, .95) !important;
    border: 1px solid var(--line-hot) !important;
    border-radius: 9px !important;
    color: var(--text) !important;
    font: 13px/1.4 "Inter", system-ui, sans-serif !important;
    padding: 8px 12px !important;
    box-shadow: 0 14px 34px -18px #000 !important;
}
.pnlm-tooltip span::after { border-top-color: rgba(120, 190, 255, .45) !important; }

/* ---------- Admin: settings layout ---------- */
.wrap.wide { max-width: 1340px; }
.cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: 26px; align-items: start; }
.card h3 { margin: 24px 0 8px; font-size: 13px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0 16px; }
.hint { display: block; font-size: 11.5px; color: rgba(139, 154, 192, .75); margin-top: 2px; }
.small { font-size: 12.5px; }
.grp { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.edit-pano {
    width: 100%; height: 420px;
    border: 1px solid var(--line);
    border-radius: 12px;
    overflow: hidden;
    background: #04070e;
}
.picker-bar {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    margin-top: 14px;
}
.picker-bar .readout {
    font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums;
    letter-spacing: .04em; margin-right: auto;
}
.picker-bar .readout b { color: var(--accent); }

.table.spots input, .table.spots select {
    margin-top: 0; padding: 7px 9px; font-size: 13px; border-radius: 8px;
}
.table.spots td { padding: 7px 6px; }
.table.spots .sp-pitch, .table.spots .sp-yaw { width: 88px; }

.sticky-save {
    position: sticky; bottom: 0; z-index: 30;
    display: flex; gap: 10px; align-items: center;
    padding: 16px 0 22px;
    margin-top: 6px;
    background: linear-gradient(180deg, transparent, var(--bg) 42%);
}

/* Pannellum chrome tweaks */
.pnlm-load-box, .pnlm-lbox { display: none !important; }
.pnlm-controls { border-radius: 10px !important; overflow: hidden; }

/* ============================================================
   RESPONSIVE
   Measured on the real pages before writing any of this:
     375px viewer  - top bar wrapped to 103px tall, title crushed to 33px,
                     control dock wrapped to 162px tall
     375px admin   - 6-column table 527px wide inside a 273px scroller
     admin inputs  - 13px, which makes iOS zoom the page on focus
   Breakpoints: 900 (tablet) / 640 (phone) / 400 (small phone),
   plus a short-viewport block for landscape phones.
   ============================================================ */

/* ---- Base-layer fixes (apply at every width) ---- */

/* Nothing should ever be able to push the page sideways. */
html, body { max-width: 100%; overflow-x: hidden; }

/* The ambient backdrop is inset by viewport units; without this it counts
   towards scrollable area on some mobile browsers. */
body::before, body::after { pointer-events: none; }

/* Long filenames, URLs and titles must wrap rather than widen their box. */
.tile-body h3, .table td, .infopanel dd, .loader-meta .sub { overflow-wrap: anywhere; }

/* Respect the notch / home indicator. */
.site-head, .viewer-bar { padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); }

@media (max-width: 900px) {
    .wrap { padding: 0 18px; }
    .cols { grid-template-columns: 1fr; gap: 18px; }
    .edit-pano { height: 320px; }
}

/* ============================================================
   Phones
   ============================================================ */
@media (max-width: 640px) {

    /* ---- Global ---- */
    .wrap { padding: 0 14px; }
    .site-head { padding: 12px 0; margin-bottom: 22px; }
    .site-head .wrap { gap: 10px; }
    .site-head h1 { font-size: 16px; }
    .hero { padding: 14px 0 26px; }
    .hero h2 { font-size: clamp(26px, 8.5vw, 38px); }
    .hero p { font-size: 14px; }
    .card { padding: 18px 16px; border-radius: 14px; }
    .site-foot { padding: 34px 16px 48px; }

    /* Comfortable touch targets. */
    .btn { padding: 10px 15px; }
    .btn-sm { padding: 8px 12px; }

    /* ---- Gallery ---- */
    .grid { grid-template-columns: 1fr; gap: 18px; }
    .tile-media { height: 200px; }

    /* ---- Forms: 16px stops iOS zooming the page on focus ---- */
    input[type=text], input[type=password], input[type=number],
    input[type=url], input[type=file], textarea, select {
        font-size: 16px;
    }
    .table.spots input, .table.spots select { font-size: 16px; }
    .pair { grid-template-columns: 1fr; }

    /* ---- Admin table -> stacked cards (no sideways scrolling) ---- */
    .table-scroll { overflow-x: visible; }
    .table, .table tbody, .table tr, .table td { display: block; width: 100%; }
    .table thead { display: none; }
    .table tr {
        border: 1px solid var(--line);
        border-radius: 14px;
        background: rgba(255, 255, 255, .02);
        padding: 12px;
        margin-bottom: 14px;
    }
    .table td {
        border-bottom: 1px solid var(--line);
        padding: 9px 0;
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 14px;
        text-align: right;
    }
    .table td:last-child { border-bottom: 0; padding-bottom: 0; }
    /* Header text comes from data-label on each cell. */
    .table td::before {
        content: attr(data-label);
        flex: 0 0 auto;
        text-align: left;
        color: var(--muted);
        font-size: 11px;
        letter-spacing: .1em;
        text-transform: uppercase;
    }
    /* The preview cell is a full-width banner with no label. */
    .table td[data-label=""] { display: block; padding-top: 0; }
    .table td[data-label=""]::before { content: none; }
    .thumb { width: 100%; height: 160px; border-radius: 10px; }
    .actions { justify-content: flex-end; flex-wrap: wrap; gap: 8px; }
    .table td.actions::before { align-self: center; }

    /* Hotspot editor rows become cards too. */
    .table.spots td { padding: 7px 0; }
    .table.spots .sp-pitch, .table.spots .sp-yaw { width: 100%; }
    .table.spots input, .table.spots select { max-width: 62%; }

    .sticky-save { padding: 12px 0 16px; }
    .sticky-save .btn { flex: 1; justify-content: center; }

    /* ---- Viewer top bar: one row, title takes the slack ---- */
    .viewer-bar {
        flex-wrap: nowrap;
        gap: 8px;
        padding: 8px 12px;
        padding-top: calc(8px + env(safe-area-inset-top));
    }
    .viewer-bar .grp { flex-wrap: nowrap; gap: 6px; }
    .viewer-bar h2 {
        flex: 1 1 auto;
        min-width: 0;
        max-width: none;
        font-size: 14px;
        text-align: center;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    /* Icon-only buttons; these labels are what forced the bar to three rows. */
    .viewer-bar .btn { padding: 8px 10px; }
    .viewer-bar .lbl { display: none; }

    /* ---- Control dock: compact, at most two rows ----
       The dock was 4 rows tall on a phone. Cause: with left:50% and no right
       offset, an absolutely positioned box shrink-to-fits against only the
       space from its left edge to the viewport edge — half the screen, 188px
       at 375px — so it wrapped early. Anchoring both edges gives it the full
       width and lets the centring come from justify-content instead. */
    .dock {
        left: 10px;
        right: 10px;
        max-width: none;
        transform: translateY(12px);
        justify-content: center;
        gap: 6px;
        padding: 8px;
        bottom: calc(12px + env(safe-area-inset-bottom));
        border-radius: 18px;
    }
    .dock.show { transform: translateY(0); }
    .dock .btn-sm { padding: 9px 10px; min-width: 36px; justify-content: center; }
    .dock .sep { display: none; }
    .dock .speed { gap: 6px; padding: 0 2px; }
    .dock .speed span { display: none; }          /* the slider is self-evident */
    .dock input[type=range] { width: 76px; }
    /* Pinch-to-zoom is native on touch, so the +/- buttons only cost rows. */
    #zoomIn, #zoomOut { display: none; }

    /* The HUD is decorative and competes with the title for space. */
    .hud { display: none; }

    /* ---- Info panel: full width sheet ---- */
    .infopanel {
        width: 100%;
        max-width: 100%;
        padding: 22px 18px calc(22px + env(safe-area-inset-bottom));
    }
    .infopanel dl { grid-template-columns: auto 1fr; }
    .viewer-caption { font-size: 12.5px; padding: 10px 14px; }

    /* ---- Loader ---- */
    .ring { width: 118px; height: 118px; }
    .ring .pct b { font-size: 26px; }
    .loader { gap: 14px; padding: 0 18px; }
    .loader-meta .sub { font-size: 12px; }

    /* ---- Footer ---- */
    .built-by { font-size: 13px; }
    .thanks { font-size: 12px; line-height: 1.5; }
}

/* ============================================================
   Small phones
   ============================================================ */
@media (max-width: 400px) {
    .wrap { padding: 0 12px; }
    .dock { gap: 5px; padding: 7px; }
    .dock .btn-sm { padding: 8px 8px; min-width: 32px; }
    .dock .playpause { min-width: 40px; }
    .dock input[type=range] { width: 52px; }
    .dock .speed b { display: none; }
    /* Last thing to go on a 320px screen; the browser's own share still works. */
    #shareBtn { display: none; }
    .tile-media { height: 175px; }
    .card { padding: 16px 13px; }
    .table.spots input, .table.spots select { max-width: 58%; }
}

/* ============================================================
   Short viewports (landscape phones) — vertical space is the
   scarce resource here, not width.
   ============================================================ */
@media (max-height: 480px) and (orientation: landscape) {
    .viewer-bar { padding: 5px 10px; }
    .viewer-bar h2 { font-size: 13px; }
    .viewer-bar .btn { padding: 6px 9px; }
    .viewer-bar .lbl { display: none; }
    /* Same shrink-to-fit trap as portrait — a landscape phone is wide enough
       to miss the 640px block but still needs both edges anchored. */
    .dock {
        left: 10px; right: 10px; max-width: none;
        transform: translateY(10px); justify-content: center;
        bottom: 8px; padding: 5px 8px; gap: 5px;
    }
    .dock.show { transform: translateY(0); }
    .dock .btn-sm { padding: 7px 9px; }
    .dock .speed { display: none; }   /* speed is set less often than play/pause */
    .viewer-caption { display: none; }
    .hud { display: none; }
    .ring { width: 92px; height: 92px; }
    .ring .pct b { font-size: 21px; }
    .loader { flex-direction: row; gap: 20px; }
}

/* Coarse pointers get larger hit areas regardless of width. */
@media (pointer: coarse) {
    .btn { min-height: 40px; }
    .dock .btn-sm { min-height: 40px; }
    input[type=range] { height: 6px; }
    input[type=range]::-webkit-slider-thumb { width: 18px; height: 18px; }
    input[type=range]::-moz-range-thumb { width: 18px; height: 18px; }
}
