feat: rewrite as Vite + Globe.GL with side-panel + zoom photos

This commit is contained in:
Nuri Softworks
2026-08-08 16:06:12 +00:00
parent b472a6670b
commit 7d0cff306c
11 changed files with 1677 additions and 382 deletions

403
src/styles.css Normal file
View File

@@ -0,0 +1,403 @@
:root {
--bg-0: #0a0e16;
--bg-1: #111723;
--bg-2: #182132;
--ink-0: #f4ecd8;
--ink-1: #d8d2c2;
--ink-2: #8d9bb1;
--ink-3: #5d6a82;
--accent: #d6a05c;
--accent-2: #4ea8de;
--accent-3: #c97b63;
--border: rgba(255, 255, 255, 0.08);
--shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
--radius: 14px;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body {
height: 100%;
background: radial-gradient(circle at 20% 10%, #1a2335 0%, var(--bg-0) 60%);
color: var(--ink-0);
font-family: 'Inter', system-ui, -apple-system, sans-serif;
font-size: 15px;
line-height: 1.55;
overflow: hidden;
}
#app {
display: flex;
flex-direction: column;
height: 100vh;
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 22px;
border-bottom: 1px solid var(--border);
background: rgba(10, 14, 22, 0.78);
backdrop-filter: blur(8px);
z-index: 5;
}
.brand {
display: flex;
gap: 14px;
align-items: center;
}
.brand-mark {
width: 38px;
height: 38px;
border-radius: 50%;
background:
radial-gradient(circle at 35% 35%, #4ea8de 0%, transparent 60%),
radial-gradient(circle at 60% 65%, #d6a05c 0%, transparent 60%),
linear-gradient(135deg, #0a0e16, #1a2335);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.15), 0 6px 18px rgba(0, 0, 0, 0.5);
}
.brand-text h1 {
font-family: 'Fraunces', Georgia, serif;
font-weight: 600;
font-size: 20px;
letter-spacing: -0.01em;
}
.brand-text p {
font-size: 12.5px;
color: var(--ink-2);
margin-top: 2px;
}
.topbar-actions {
display: flex;
gap: 14px;
align-items: center;
}
.btn-ghost {
background: transparent;
color: var(--ink-1);
border: 1px solid var(--border);
padding: 7px 14px;
border-radius: 999px;
font: inherit;
font-size: 13px;
cursor: pointer;
transition: background 0.15s, border-color 0.15s;
}
.btn-ghost:hover {
background: rgba(255, 255, 255, 0.06);
border-color: rgba(255, 255, 255, 0.18);
}
.hint {
font-size: 12.5px;
color: var(--ink-3);
}
.layout {
flex: 1;
display: grid;
grid-template-columns: 1fr 420px;
gap: 0;
min-height: 0;
}
.globe-wrap {
position: relative;
background: radial-gradient(ellipse at center, #0d1322 0%, #050811 80%);
min-height: 0;
overflow: hidden;
}
#globe {
position: absolute;
inset: 0;
}
.legend {
position: absolute;
left: 18px;
bottom: 18px;
background: rgba(10, 14, 22, 0.7);
border: 1px solid var(--border);
border-radius: 10px;
padding: 10px 14px;
font-size: 12.5px;
color: var(--ink-1);
pointer-events: none;
z-index: 4;
}
.legend-row {
display: flex;
align-items: center;
gap: 8px;
}
.legend-sub {
margin-top: 4px;
color: var(--ink-3);
font-size: 11.5px;
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: radial-gradient(circle, var(--accent) 0%, var(--accent-3) 70%);
box-shadow: 0 0 12px var(--accent);
}
.panel {
background: linear-gradient(180deg, var(--bg-1) 0%, var(--bg-0) 100%);
border-left: 1px solid var(--border);
overflow-y: auto;
padding: 26px 26px 36px;
}
.panel::-webkit-scrollbar {
width: 6px;
}
.panel::-webkit-scrollbar-thumb {
background: var(--ink-3);
border-radius: 3px;
}
.panel-empty {
text-align: center;
padding-top: 36px;
}
.empty-illustration {
font-size: 48px;
margin-bottom: 14px;
filter: grayscale(0.2);
}
.panel-empty h2 {
font-family: 'Fraunces', Georgia, serif;
font-weight: 600;
font-size: 22px;
margin-bottom: 10px;
}
.panel-empty p {
color: var(--ink-2);
font-size: 13.5px;
max-width: 320px;
margin: 0 auto 22px;
}
.country-list {
list-style: none;
text-align: left;
border-top: 1px solid var(--border);
padding-top: 14px;
}
.country-list li {
display: flex;
justify-content: space-between;
padding: 8px 0;
font-size: 13px;
color: var(--ink-1);
border-bottom: 1px dashed var(--border);
}
.country-list li .num {
color: var(--accent);
font-variant-numeric: tabular-nums;
}
/* selected panel */
.card-head {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 12px;
margin-bottom: 16px;
}
.card-head .city {
font-size: 12px;
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: 4px;
}
.card-head h2 {
font-family: 'Fraunces', Georgia, serif;
font-weight: 600;
font-size: 24px;
line-height: 1.2;
}
.card-head .country {
font-size: 13px;
color: var(--ink-2);
margin-top: 4px;
}
.children-badge {
background: var(--bg-2);
border: 1px solid var(--border);
border-radius: 10px;
padding: 10px 14px;
text-align: center;
min-width: 88px;
}
.children-badge .num {
font-family: 'Fraunces', Georgia, serif;
font-weight: 800;
font-size: 26px;
color: var(--accent);
line-height: 1;
}
.children-badge .label {
font-size: 11px;
color: var(--ink-2);
text-transform: uppercase;
letter-spacing: 0.06em;
margin-top: 4px;
}
.stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
margin-bottom: 20px;
}
.stat {
background: var(--bg-2);
border: 1px solid var(--border);
border-radius: 10px;
padding: 10px 12px;
}
.stat .label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--ink-2);
}
.stat .value {
font-size: 14px;
margin-top: 4px;
color: var(--ink-0);
}
.bio {
font-size: 14px;
line-height: 1.65;
color: var(--ink-1);
margin-bottom: 22px;
}
.bio::first-letter {
font-family: 'Fraunces', Georgia, serif;
font-weight: 600;
font-size: 28px;
float: left;
line-height: 0.9;
padding: 4px 8px 0 0;
color: var(--accent);
}
.photos {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.photo {
position: relative;
border-radius: 10px;
overflow: hidden;
background: var(--bg-2);
aspect-ratio: 4 / 3;
cursor: zoom-in;
border: 1px solid var(--border);
}
.photo img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.4s ease;
display: block;
}
.photo:hover img {
transform: scale(1.06);
}
.photo .caption {
position: absolute;
left: 8px;
bottom: 8px;
font-size: 10.5px;
background: rgba(0, 0, 0, 0.6);
color: var(--ink-0);
padding: 3px 7px;
border-radius: 6px;
letter-spacing: 0.04em;
}
.lightbox {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.85);
display: none;
align-items: center;
justify-content: center;
z-index: 50;
cursor: zoom-out;
}
.lightbox.open {
display: flex;
}
.lightbox img {
max-width: 92vw;
max-height: 92vh;
border-radius: 8px;
box-shadow: var(--shadow);
}
.footbar {
border-top: 1px solid var(--border);
padding: 10px 22px;
font-size: 12px;
color: var(--ink-3);
text-align: center;
background: rgba(10, 14, 22, 0.7);
}
@media (max-width: 860px) {
.layout {
grid-template-columns: 1fr;
grid-template-rows: 1fr 360px;
}
.panel {
border-left: none;
border-top: 1px solid var(--border);
}
.hint { display: none; }
}