Files
2026-06-10 18:07:01 +07:00

1129 lines
32 KiB
HTML

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebGIS Project Universe</title>
<link rel="preconnect" href="https://unpkg.com">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
:root {
--bg: #040914;
--surface: rgba(8, 18, 34, .78);
--surface-strong: rgba(12, 27, 48, .96);
--line: rgba(181, 217, 234, .18);
--line-strong: rgba(224, 244, 255, .38);
--text: #f6fbff;
--muted: #a7b8c7;
--cyan: #56d7ff;
--mint: #35d3a4;
--violet: #a98bff;
--amber: #f2bd62;
--rose: #e78baa;
--ice: #eaf7fb;
--teal-deep: #145f68;
--steel: #8fa8bd;
--champagne: #f0d28a;
--copper: #d5824f;
--sapphire: #2e6bd8;
--plum: #6d55bd;
--shadow: 0 30px 96px rgba(0, 0, 0, .58);
--glass: linear-gradient(135deg, rgba(255,255,255,.13), rgba(86,215,255,.055) 34%, rgba(255,255,255,.045) 50%, rgba(242,189,98,.075));
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
min-height: 100vh;
color: var(--text);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background:
radial-gradient(circle at 74% -18%, rgba(242, 189, 98, .13), transparent 32%),
radial-gradient(circle at 16% 8%, rgba(86, 215, 255, .10), transparent 28%),
linear-gradient(118deg, #020511 0%, #06101f 42%, #102337 72%, #314454 100%);
overflow-x: hidden;
}
::selection {
color: #08111d;
background: #e8c16d;
}
#mapPreview {
position: fixed;
inset: 0;
z-index: 0;
filter: grayscale(.35) contrast(1.18) brightness(.82) saturate(.72);
opacity: .98;
transform: scale(1.018);
background: #02040b;
transform-origin: center;
}
.atmosphere,
.aurora,
.beam,
.map-light,
.metallic-flow,
.lux,
.scanlines,
.map-depth {
position: fixed;
inset: 0;
pointer-events: none;
}
.atmosphere {
z-index: 1;
background:
linear-gradient(112deg, rgba(0,0,0,.76) 0%, rgba(3,8,15,.54) 32%, rgba(14,35,51,.20) 62%, rgba(225,241,246,.10) 100%),
radial-gradient(ellipse at 74% 28%, rgba(242,189,98,.10), transparent 24%),
radial-gradient(ellipse at 48% 78%, rgba(53,211,164,.08), transparent 34%),
radial-gradient(ellipse at 18% 24%, rgba(86,215,255,.085), transparent 30%),
linear-gradient(180deg, rgba(0,0,0,.05), rgba(0,0,0,.42));
}
.aurora {
z-index: 2;
background:
linear-gradient(116deg, transparent 0%, rgba(242,189,98,.10) 36%, transparent 58%),
linear-gradient(28deg, transparent 0%, rgba(53,211,164,.085) 48%, transparent 70%),
linear-gradient(72deg, transparent 0%, rgba(169,139,255,.065) 52%, transparent 74%);
filter: blur(20px);
opacity: .22;
transform: translate3d(0,0,0) scale(1.06);
will-change: transform, opacity;
animation: auroraFlow 12s ease-in-out infinite;
}
.map-light {
z-index: 2;
background:
linear-gradient(118deg, rgba(0,0,0,.46) 0%, transparent 38%, rgba(238,246,247,.070) 70%, rgba(238,246,247,.13) 100%),
radial-gradient(ellipse at 86% 20%, rgba(255,255,255,.11), transparent 24%),
radial-gradient(ellipse at 78% 72%, rgba(242,189,98,.065), transparent 30%);
background-size: 100% 100%, 120% 120%, 130% 130%;
background-position: 0 0, 0 0, 0 0;
mix-blend-mode: screen;
opacity: .55;
will-change: background-position, opacity;
animation: mapLightMove 10s ease-in-out infinite;
}
.metallic-flow {
z-index: 3;
background:
conic-gradient(from 210deg at 76% 18%, transparent 0deg, rgba(242,189,98,.18) 44deg, rgba(86,215,255,.16) 78deg, transparent 118deg),
conic-gradient(from 28deg at 22% 78%, transparent 0deg, rgba(53,211,164,.14) 38deg, rgba(169,139,255,.11) 72deg, transparent 116deg),
linear-gradient(105deg, transparent 0%, rgba(255,255,255,.050) 46%, transparent 63%);
filter: blur(22px);
mix-blend-mode: screen;
opacity: .34;
transform: translate3d(0,0,0) scale(1.04);
will-change: transform, opacity;
animation: metallicFlow 11s ease-in-out infinite;
}
.map-depth {
z-index: 2;
background:
linear-gradient(90deg, rgba(0,0,0,.72), transparent 38%, rgba(255,255,255,.06)),
linear-gradient(180deg, rgba(0,0,0,.22), transparent 42%, rgba(0,0,0,.64));
opacity: .82;
}
.beam {
z-index: 3;
overflow: hidden;
}
.beam::before,
.beam::after {
content: "";
position: absolute;
top: -22%;
width: 28vw;
height: 150vh;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.16), rgba(242,189,98,.09), transparent);
filter: blur(20px);
opacity: .16;
transform: rotate(22deg) translate3d(-34vw, 0, 0);
will-change: transform;
animation: beamSweep 10.5s cubic-bezier(.4, 0, .2, 1) infinite;
}
.beam::after {
width: 16vw;
background: linear-gradient(90deg, transparent, rgba(86,215,255,.080), transparent);
animation-duration: 14s;
animation-delay: -4s;
}
.lux {
z-index: 3;
background:
radial-gradient(ellipse at 75% 30%, rgba(255,255,255,.16), transparent 22%),
radial-gradient(ellipse at 34% 74%, rgba(242,189,98,.070), transparent 28%),
linear-gradient(126deg, transparent 0%, rgba(255,255,255,.052) 48%, transparent 64%);
mix-blend-mode: soft-light;
opacity: .34;
transform: translate3d(0,0,0);
will-change: opacity, transform;
animation: luxuryGlow 7.5s ease-in-out infinite;
}
.scanlines {
z-index: 4;
background:
linear-gradient(rgba(255,255,255,.022) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,.018) 1px, transparent 1px);
background-size: 96px 96px, 96px 96px;
mask-image: linear-gradient(to bottom, transparent, #000 14%, #000 86%, transparent);
opacity: .035;
transform: translate3d(0,0,0);
will-change: transform, background-position;
animation: gridDrift 14s linear infinite;
}
@keyframes gridDrift {
from { background-position: 0 0, 0 0; }
to { background-position: 96px 96px, 96px 96px; }
}
@keyframes auroraFlow {
0%, 100% { transform: translate3d(-1.5%, -1%, 0) scale(1.06) rotate(0deg); opacity: .82; }
50% { transform: translate3d(1.5%, 1%, 0) scale(1.1) rotate(1deg); opacity: 1; }
}
@keyframes mapLightMove {
0%, 100% { background-position: 0 0, 0 0, 0 0; opacity: .72; }
50% { background-position: 0 0, 4% -3%, -5% 4%; opacity: .92; }
}
@keyframes metallicFlow {
0%, 100% { transform: translate3d(-1%, -1%, 0) scale(1.04) rotate(0deg); opacity: .62; }
50% { transform: translate3d(1.5%, 1%, 0) scale(1.08) rotate(1.2deg); opacity: .86; }
}
@keyframes beamSweep {
0% { transform: rotate(22deg) translate3d(-42vw, 0, 0); opacity: 0; }
18% { opacity: .34; }
56% { opacity: .22; }
100% { transform: rotate(22deg) translate3d(126vw, 0, 0); opacity: 0; }
}
@keyframes luxuryGlow {
0%, 100% { opacity: .54; transform: scale(1); }
50% { opacity: .86; transform: scale(1.035); }
}
.shell {
position: relative;
z-index: 5;
width: min(1220px, calc(100% - 36px));
margin: 0 auto;
padding: 24px 0 38px;
}
.shell::before {
content: "";
position: fixed;
left: 18px;
right: 18px;
top: 18px;
bottom: 18px;
border: 1px solid rgba(255,255,255,.055);
border-radius: 24px;
pointer-events: none;
box-shadow: inset 0 0 80px rgba(127,215,255,.035);
}
.topbar {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
padding: 12px;
border: 1px solid rgba(223, 251, 255, .16);
border-radius: 16px;
background:
linear-gradient(120deg, rgba(255,255,255,.14), rgba(86,215,255,.070) 32%, rgba(255,255,255,.045) 50%, rgba(242,189,98,.105)),
rgba(4, 8, 15, .58);
box-shadow: 0 20px 72px rgba(0, 0, 0, .38), inset 0 1px 0 rgba(255,255,255,.13);
backdrop-filter: blur(22px) saturate(1.22);
overflow: hidden;
}
.topbar::after {
content: "";
position: absolute;
left: 18px;
right: 18px;
bottom: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(127,215,255,.44), rgba(232,193,109,.46), transparent);
animation: headerLine 6s ease-in-out infinite;
}
.brand {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
font-weight: 900;
}
.brand-mark {
position: relative;
display: grid;
place-items: center;
width: 46px;
height: 46px;
border: 1px solid rgba(241, 247, 251, .34);
border-radius: 10px;
color: var(--cyan);
background: linear-gradient(145deg, rgba(86,215,255,.20), rgba(242,189,98,.14), rgba(53,211,164,.12));
box-shadow: 0 18px 34px rgba(0,0,0,.30), inset 0 0 18px rgba(255, 255, 255, .08);
}
.brand-mark::after {
content: "";
position: absolute;
inset: -5px;
border: 1px solid rgba(255, 255, 255, .12);
border-radius: 14px;
animation: pulseRing 2.8s ease-in-out infinite;
}
@keyframes pulseRing {
50% { transform: scale(1.08); opacity: .45; }
}
@keyframes headerLine {
0%, 100% { opacity: .28; transform: scaleX(.72); }
50% { opacity: .95; transform: scaleX(1); }
}
.brand-text {
display: grid;
line-height: 1.1;
letter-spacing: .01em;
}
.brand-text span {
margin-top: 5px;
color: var(--muted);
font-size: 12px;
font-weight: 760;
}
.nav-actions {
display: flex;
align-items: center;
gap: 10px;
}
.nav-link,
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 9px;
min-height: 42px;
padding: 0 15px;
border: 1px solid var(--line);
border-radius: 10px;
color: var(--text);
background: linear-gradient(135deg, rgba(255,255,255,.08), rgba(255,255,255,.035));
text-decoration: none;
font-size: 13px;
font-weight: 860;
position: relative;
overflow: hidden;
box-shadow: inset 0 1px 0 rgba(255,255,255,.08);
transition: transform .18s ease, border-color .18s ease, background .18s ease, box-shadow .18s ease, color .18s ease;
}
.nav-link::before,
.btn::before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(110deg, transparent 0%, rgba(255,255,255,.22) 42%, rgba(86,215,255,.13) 50%, rgba(242,189,98,.13) 60%, transparent 76%);
transform: translateX(-115%);
transition: transform .55s ease;
}
.nav-link:hover::before,
.btn:hover::before {
transform: translateX(115%);
}
.btn {
border-color: rgba(241, 247, 251, .46);
background: linear-gradient(135deg, rgba(86,215,255,.26), rgba(242,189,98,.22) 44%, rgba(53,211,164,.16));
box-shadow: 0 18px 38px rgba(0,0,0,.30), inset 0 1px 0 rgba(255,255,255,.18);
}
.nav-link:hover,
.btn:hover {
transform: translateY(-2px);
border-color: var(--line-strong);
color: #ffffff;
background: linear-gradient(135deg, rgba(86,215,255,.20), rgba(242,189,98,.16), rgba(53,211,164,.13));
box-shadow: 0 20px 42px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,255,255,.20);
}
.hero {
display: grid;
grid-template-columns: minmax(0, 1.08fr) minmax(360px, .76fr);
gap: 26px;
align-items: center;
min-height: calc(100vh - 120px);
padding: 42px 0 24px;
}
.hero-copy {
position: relative;
padding: 26px 0;
}
.hero-copy::before {
content: "";
position: absolute;
left: -36px;
top: 18px;
width: 220px;
height: 220px;
border-radius: 999px;
background: radial-gradient(circle, rgba(86,215,255,.12), rgba(242,189,98,.09) 35%, rgba(169,139,255,.06) 52%, transparent 68%);
filter: blur(8px);
opacity: .66;
z-index: -1;
animation: luxuryGlow 6.6s ease-in-out infinite;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 10px;
margin-bottom: 20px;
color: var(--mint);
font-size: 13px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: .08em;
}
.eyebrow::before {
content: "";
width: 42px;
height: 2px;
background: linear-gradient(90deg, var(--amber), var(--cyan), var(--mint), var(--violet));
box-shadow: 0 0 18px rgba(216, 186, 120, .18);
}
h1 {
max-width: 790px;
margin: 0;
font-size: clamp(48px, 7.6vw, 104px);
line-height: .88;
letter-spacing: 0;
text-wrap: balance;
text-shadow: 0 24px 70px rgba(0,0,0,.52);
}
.gradient-text {
color: transparent;
background: linear-gradient(100deg, #ffffff 4%, #ffd783 22%, #56d7ff 48%, #35d3a4 70%, #ffffff 96%);
background-size: 240% 100%;
-webkit-background-clip: text;
background-clip: text;
animation: textLight 5.4s ease-in-out infinite;
}
@keyframes textLight {
50% { background-position: 100% 0; }
}
.lead {
max-width: 690px;
margin: 24px 0 0;
color: #c7d6e5;
font-size: clamp(16px, 1.8vw, 21px);
line-height: 1.75;
font-weight: 560;
}
.feature-strip {
display: flex;
flex-wrap: wrap;
gap: 9px;
margin-top: 24px;
max-width: 720px;
}
.feature-strip span {
display: inline-flex;
align-items: center;
min-height: 34px;
padding: 0 12px;
border: 1px solid rgba(223,251,255,.15);
border-radius: 999px;
color: #dce8ef;
background:
linear-gradient(135deg, rgba(255,255,255,.095), rgba(86,215,255,.070), rgba(242,189,98,.055)),
rgba(3, 8, 16, .44);
box-shadow: inset 0 1px 0 rgba(255,255,255,.08), 0 12px 26px rgba(0,0,0,.18);
backdrop-filter: blur(12px);
font-size: 12px;
font-weight: 820;
}
.hero-actions {
display: flex;
flex-wrap: wrap;
gap: 11px;
margin-top: 30px;
}
.signal-row {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 11px;
max-width: 650px;
margin-top: 34px;
}
.signal {
min-height: 92px;
padding: 15px;
border: 1px solid rgba(223,251,255,.14);
border-radius: 12px;
background:
linear-gradient(145deg, rgba(255,255,255,.110), rgba(8,17,33,.58) 56%, rgba(86,215,255,.075), rgba(242,189,98,.085)),
rgba(4, 8, 15, .42);
box-shadow: inset 0 1px 0 rgba(255,255,255,.10), 0 18px 44px rgba(0,0,0,.22);
backdrop-filter: blur(16px) saturate(1.2);
transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.signal:hover {
transform: translateY(-3px);
border-color: rgba(241,247,251,.30);
box-shadow: inset 0 1px 0 rgba(255,255,255,.14), 0 22px 54px rgba(0,0,0,.30);
}
.signal strong {
display: block;
font-size: 28px;
line-height: 1;
color: transparent;
background: linear-gradient(110deg, #fff, var(--amber), var(--cyan));
-webkit-background-clip: text;
background-clip: text;
}
.signal span {
display: block;
margin-top: 10px;
color: var(--muted);
font-size: 11px;
font-weight: 900;
text-transform: uppercase;
}
.project-panel {
position: relative;
padding: 13px;
border: 1px solid rgba(223,251,255,.22);
border-radius: 20px;
background:
radial-gradient(circle at 18% 0%, rgba(86,215,255,.18), transparent 30%),
radial-gradient(circle at 100% 18%, rgba(242,189,98,.16), transparent 34%),
radial-gradient(circle at 70% 100%, rgba(53,211,164,.14), transparent 30%),
radial-gradient(circle at 28% 92%, rgba(169,139,255,.10), transparent 28%),
linear-gradient(145deg, rgba(21, 32, 44, .94), rgba(7, 13, 22, .88) 58%, rgba(2, 5, 10, .92));
box-shadow: var(--shadow), inset 0 1px 0 rgba(255,255,255,.12);
backdrop-filter: blur(26px) saturate(1.28);
overflow: hidden;
}
.project-panel::after {
content: "";
position: absolute;
inset: -20% -55%;
background: linear-gradient(105deg, transparent 35%, rgba(255,255,255,.11) 48%, rgba(232,193,109,.08) 53%, transparent 66%);
transform: translateX(-35%) rotate(3deg);
animation: panelSheen 9s ease-in-out infinite;
pointer-events: none;
}
.project-panel::before {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(135deg, rgba(255,255,255,.62), rgba(86,215,255,.26) 22%, rgba(242,189,98,.25) 42%, rgba(53,211,164,.15) 60%, rgba(255,255,255,.26));
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
mask-composite: exclude;
pointer-events: none;
}
.panel-head {
position: relative;
z-index: 1;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
padding: 10px 10px 15px;
border-bottom: 1px solid var(--line);
}
.panel-head h2 {
margin: 0;
font-size: 17px;
line-height: 1.2;
}
.panel-head span {
color: var(--cyan);
font-size: 11px;
font-weight: 950;
text-transform: uppercase;
letter-spacing: .08em;
white-space: nowrap;
}
.project-list {
position: relative;
z-index: 1;
display: grid;
gap: 10px;
margin-top: 10px;
}
@keyframes panelSheen {
0%, 100% { transform: translateX(-35%) rotate(3deg); opacity: 0; }
35% { opacity: .85; }
62% { transform: translateX(35%) rotate(3deg); opacity: .10; }
}
.project-card {
position: relative;
display: grid;
grid-template-columns: 44px minmax(0, 1fr) auto;
gap: 12px;
align-items: center;
min-height: 88px;
padding: 12px;
border: 1px solid rgba(148, 223, 255, .12);
border-radius: 12px;
color: var(--text);
background:
linear-gradient(135deg, rgba(255,255,255,.096), rgba(86,215,255,.048) 32%, rgba(255,255,255,.034) 54%, rgba(242,189,98,.060)),
rgba(4, 10, 22, .46);
text-decoration: none;
overflow: hidden;
box-shadow: inset 0 1px 0 rgba(255,255,255,.07);
transition: transform .2s ease, border-color .2s ease, background .2s ease, box-shadow .2s ease;
}
.project-card::before {
content: "";
position: absolute;
left: 0;
top: 12px;
bottom: 12px;
width: 3px;
border-radius: 0 99px 99px 0;
background: linear-gradient(180deg, var(--champagne), var(--steel));
box-shadow: 0 0 18px rgba(216,186,120,.20);
opacity: .82;
}
.project-card::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(100deg, transparent 0%, rgba(255,255,255,.18) 42%, rgba(86,215,255,.11) 50%, rgba(242,189,98,.11) 60%, transparent 76%);
transform: translateX(-110%);
transition: transform .55s ease;
}
.project-card:hover {
transform: translateX(5px) scale(1.01);
border-color: rgba(223, 251, 255, .38);
background:
linear-gradient(135deg, rgba(255,255,255,.14), rgba(86,215,255,.12) 26%, rgba(242,189,98,.11) 46%, rgba(53,211,164,.090)),
rgba(6, 15, 30, .60);
box-shadow: 0 18px 46px rgba(0, 0, 0, .36), inset 0 1px 0 rgba(255,255,255,.12);
}
.project-card:hover::after {
transform: translateX(110%);
}
.project-no {
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 10px;
color: var(--cyan);
background: linear-gradient(145deg, rgba(255,255,255,.17), rgba(86,215,255,.14), rgba(242,189,98,.11), rgba(53,211,164,.09));
border: 1px solid rgba(223,251,255,.22);
font-weight: 950;
font-size: 14px;
box-shadow: inset 0 1px 0 rgba(255,255,255,.12), 0 12px 24px rgba(0,0,0,.20);
}
.project-copy {
min-width: 0;
}
.project-copy strong {
display: block;
font-size: 15px;
line-height: 1.25;
}
.project-copy small {
display: block;
margin-top: 6px;
color: var(--muted);
font-size: 12px;
line-height: 1.48;
font-weight: 640;
}
.badge {
justify-self: end;
padding: 8px 10px;
border: 1px solid rgba(53, 211, 164, .32);
border-radius: 10px;
color: var(--mint);
background: linear-gradient(135deg, rgba(53,211,164,.19), rgba(86,215,255,.075), rgba(255,255,255,.055));
font-size: 11px;
font-weight: 950;
text-transform: uppercase;
white-space: nowrap;
}
.badge.admin {
color: var(--amber);
border-color: rgba(242, 189, 98, .36);
background: linear-gradient(135deg, rgba(242,189,98,.19), rgba(231,139,170,.11));
}
.project-card.admin .project-no {
color: var(--amber);
border-color: rgba(242, 189, 98, .34);
background: linear-gradient(145deg, rgba(242, 189, 98, .22), rgba(231,139,170,.12), rgba(255,255,255,.08));
}
.project-card.admin::before {
background: linear-gradient(180deg, var(--amber), var(--rose), var(--copper));
box-shadow: 0 0 18px rgba(242,189,98,.36);
}
.project-card:nth-child(1)::before {
background: linear-gradient(180deg, #f2bd62, #d5824f);
}
.project-card:nth-child(2)::before {
background: linear-gradient(180deg, #35d3a4, #145f68);
}
.project-card:nth-child(3)::before {
background: linear-gradient(180deg, #56d7ff, #2e6bd8);
}
.project-card:nth-child(4)::before {
background: linear-gradient(180deg, #a98bff, #e78baa);
}
.panel-foot {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-top: 12px;
padding: 12px;
border: 1px solid rgba(223,251,255,.12);
border-radius: 14px;
color: var(--muted);
background:
linear-gradient(135deg, rgba(86,215,255,.070), rgba(242,189,98,.060), rgba(53,211,164,.045)),
rgba(1, 5, 12, .40);
font-size: 12px;
line-height: 1.45;
}
.panel-foot strong {
display: block;
color: var(--text);
font-size: 13px;
margin-bottom: 2px;
}
.panel-foot a {
flex: 0 0 auto;
color: #ffd783;
text-decoration: none;
font-weight: 900;
}
.leaflet-control-attribution {
display: none;
}
.route-flow {
stroke-linecap: round;
stroke-linejoin: round;
filter: drop-shadow(0 0 6px rgba(86, 215, 255, .38));
animation: routeDash 3.8s linear infinite;
}
.route-flow-alt {
filter: drop-shadow(0 0 6px rgba(242, 189, 98, .30));
animation-duration: 5.4s;
animation-direction: reverse;
}
.map-node {
filter: drop-shadow(0 0 8px rgba(255, 255, 255, .36));
animation: nodePulse 2.8s ease-in-out infinite;
transform-origin: center;
}
.map-node:nth-of-type(2n) {
animation-delay: -.8s;
}
.map-pulse {
animation: pulseRingMap 3.6s ease-out infinite;
transform-origin: center;
filter: drop-shadow(0 0 8px rgba(242, 189, 98, .20));
}
@keyframes routeDash {
from { stroke-dashoffset: 0; }
to { stroke-dashoffset: -42; }
}
@keyframes nodePulse {
0%, 100% { opacity: .64; stroke-width: 1; }
50% { opacity: 1; stroke-width: 2.25; }
}
@keyframes pulseRingMap {
0% { opacity: .42; stroke-width: 1; }
70% { opacity: .10; stroke-width: 3; }
100% { opacity: 0; stroke-width: 4; }
}
@media (max-width: 960px) {
.shell {
width: min(100% - 24px, 760px);
}
.topbar {
align-items: flex-start;
flex-direction: column;
}
.nav-actions {
width: 100%;
flex-wrap: wrap;
}
.nav-link,
.btn {
flex: 1 1 170px;
}
.hero {
grid-template-columns: 1fr;
min-height: 0;
padding-top: 34px;
}
}
@media (max-width: 620px) {
.shell {
width: min(100% - 18px, 480px);
padding-top: 14px;
}
.brand-mark {
width: 42px;
height: 42px;
}
h1 {
font-size: 48px;
}
.lead {
font-size: 15px;
}
.signal-row {
grid-template-columns: 1fr;
}
.feature-strip span {
flex: 1 1 100%;
}
.project-panel {
padding: 10px;
}
.panel-head {
align-items: flex-start;
flex-direction: column;
gap: 8px;
}
.project-card {
grid-template-columns: 40px minmax(0, 1fr);
}
.project-no {
width: 40px;
height: 40px;
}
.badge {
grid-column: 2;
justify-self: start;
}
.panel-foot {
align-items: flex-start;
flex-direction: column;
}
}
</style>
</head>
<body>
<div id="mapPreview" aria-hidden="true"></div>
<div class="atmosphere" aria-hidden="true"></div>
<div class="aurora" aria-hidden="true"></div>
<div class="map-light" aria-hidden="true"></div>
<div class="map-depth" aria-hidden="true"></div>
<div class="metallic-flow" aria-hidden="true"></div>
<div class="beam" aria-hidden="true"></div>
<div class="lux" aria-hidden="true"></div>
<div class="scanlines" aria-hidden="true"></div>
<main class="shell">
<header class="topbar">
<div class="brand">
<div class="brand-mark" aria-hidden="true">
<svg width="25" height="25" viewBox="0 0 24 24" fill="none">
<path d="M12 21s7-5.2 7-11.2A7 7 0 0 0 5 9.8C5 15.8 12 21 12 21Z" stroke="currentColor" stroke-width="2"/>
<path d="M12 12.2a2.4 2.4 0 1 0 0-4.8 2.4 2.4 0 0 0 0 4.8Z" stroke="currentColor" stroke-width="2"/>
</svg>
</div>
<div class="brand-text">
WebGIS Project Universe
<span>Integrated spatial systems portfolio</span>
</div>
</div>
<nav class="nav-actions" aria-label="Navigasi utama">
<a class="nav-link" href="manual-book.html">Manual Book</a>
<a class="nav-link" href="#projects">Daftar Project</a>
<a class="btn" href="05/webgis_uas_FIXED_v2/webgis_uas_FIXED/login.php">Login Project 05</a>
</nav>
</header>
<section class="hero">
<div class="hero-copy">
<div class="eyebrow">Pontianak spatial command center</div>
<h1><span class="gradient-text">WebGIS</span> dalam satu ruang kendali.</h1>
<p class="lead">
Lima project digabung menjadi satu pintu akses: empat modul publik untuk eksplorasi peta, digitasi, dan visualisasi tematik, serta satu sistem admin dengan autentikasi.
</p>
<div class="feature-strip" aria-label="Fitur landing">
<span>Animated dark map</span>
<span>4 modul publik</span>
<span>1 akses admin</span>
<span>Manual book tersedia</span>
</div>
<div class="hero-actions">
<a class="btn" href="#projects">Pilih Project</a>
<a class="nav-link" href="manual-book.html">Baca Manual Book</a>
</div>
<div class="signal-row" aria-label="Ringkasan sistem">
<div class="signal">
<strong>04</strong>
<span>Project publik</span>
</div>
<div class="signal">
<strong>01</strong>
<span>Project admin</span>
</div>
<div class="signal">
<strong>05</strong>
<span>Total folder</span>
</div>
</div>
</div>
<aside class="project-panel" id="projects" aria-label="Daftar project">
<div class="panel-head">
<h2>Launchpad Project</h2>
<span>Public + Secure</span>
</div>
<nav class="project-list">
<a class="project-card" href="01/index.php">
<span class="project-no">01</span>
<span class="project-copy">
<strong>GIS SPBU Dasar</strong>
<small>Pemetaan titik SPBU dengan marker, status, dan data lokasi.</small>
</span>
<span class="badge">Tanpa login</span>
</a>
<a class="project-card" href="02/index.html">
<span class="project-no">02</span>
<span class="project-copy">
<strong>Digitasi Jalan, Parsil, Kerusakan</strong>
<small>Modul penggambaran objek spasial dan pencarian data peta.</small>
</span>
<span class="badge">Tanpa login</span>
</a>
<a class="project-card" href="03/Choropleth.html">
<span class="project-no">03</span>
<span class="project-copy">
<strong>Choropleth Kota Pontianak</strong>
<small>Visualisasi tematik wilayah memakai gradasi nilai.</small>
</span>
<span class="badge">Tanpa login</span>
</a>
<a class="project-card" href="04/index.php">
<span class="project-no">04</span>
<span class="project-copy">
<strong>GIS SPBU Pro</strong>
<small>Versi SPBU lanjutan dengan popup dan UI peta yang lebih rapi.</small>
</span>
<span class="badge">Tanpa login</span>
</a>
<a class="project-card admin" href="05/webgis_uas_FIXED_v2/webgis_uas_FIXED/login.php">
<span class="project-no">05</span>
<span class="project-copy">
<strong>Sistem WebGIS UAS</strong>
<small>Dashboard administrasi, laporan, data penduduk, dan modul internal.</small>
</span>
<span class="badge admin">Login</span>
</a>
</nav>
<div class="panel-foot">
<div>
<strong>Butuh panduan penggunaan?</strong>
Buka manual book untuk alur akses dan ringkasan fungsi setiap project.
</div>
<a href="manual-book.html">Manual Book</a>
</div>
</aside>
</section>
</main>
<script>
const preview = L.map('mapPreview', {
zoomControl: false,
dragging: false,
scrollWheelZoom: false,
doubleClickZoom: false,
boxZoom: false,
keyboard: false,
attributionControl: false
}).setView([-0.0263, 109.3425], 13);
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
maxZoom: 20,
subdomains: 'abcd'
}).addTo(preview);
const routes = [
[[-0.052, 109.314], [-0.036, 109.336], [-0.018, 109.354], [-0.003, 109.376]],
[[-0.066, 109.358], [-0.038, 109.352], [-0.014, 109.336], [0.008, 109.329]]
];
routes.forEach((route, index) => {
L.polyline(route, {
color: index ? '#f2bd62' : '#56d7ff',
weight: index ? 1.8 : 2.1,
opacity: index ? .50 : .58,
dashArray: index ? '10 12' : '8 12',
className: index ? 'route-flow route-flow-alt' : 'route-flow'
}).addTo(preview);
});
[
[-0.022, 109.342, '#56d7ff'],
[-0.041, 109.333, '#35d3a4'],
[-0.008, 109.365, '#a98bff'],
[-0.057, 109.355, '#f2bd62'],
[-0.029, 109.318, '#e78baa']
].forEach(([lat, lng, color]) => {
L.circleMarker([lat, lng], {
radius: 14,
color,
weight: 1,
opacity: .24,
fillOpacity: 0,
className: 'map-pulse'
}).addTo(preview);
L.circleMarker([lat, lng], {
radius: 5.6,
color: 'rgba(255,255,255,.78)',
weight: 1.3,
fillColor: color,
fillOpacity: .72,
className: 'map-node'
}).addTo(preview);
});
const driftSteps = [
[18, -10],
[-10, 16],
[-16, -8],
[8, 2]
];
let driftIndex = 0;
setInterval(() => {
const [x, y] = driftSteps[driftIndex % driftSteps.length];
preview.panBy([x, y], { animate: true, duration: 3.8, easeLinearity: .18 });
driftIndex += 1;
}, 5200);
</script>
</body>
</html>