Files
webgis-sig/index.html
T

1405 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 — Sistem Informasi Geografis | Portofolio Tugas</title>
<meta
name="description"
content="Portofolio proyek WebGIS mata kuliah Sistem Informasi Geografis. Kumpulan aplikasi pemetaan interaktif berbasis web menggunakan Leaflet.js, PHP, dan MySQL."
/>
<link
rel="preconnect"
href="https://fonts.googleapis.com"
/>
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossorigin
/>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=Space+Grotesk:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"
/>
<style>
/* ===== CSS RESET & BASE ===== */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--clr-bg: #f0f4f8;
--clr-surface: #e8eef5;
--clr-card: #ffffff;
--clr-border: rgba(99, 102, 241, 0.15);
--clr-primary: #4f46e5;
--clr-secondary: #7c3aed;
--clr-accent: #059669;
--clr-warn: #ea580c;
--clr-text: #1e293b;
--clr-muted: #64748b;
--clr-white: #ffffff;
--grad-primary: linear-gradient(135deg, #4f46e5, #7c3aed);
--grad-card: linear-gradient(145deg, #ffffff, #f8fafc);
--shadow-glow: 0 0 40px rgba(79, 70, 229, 0.1);
--shadow-card: 0 4px 24px rgba(15, 23, 42, 0.08);
--radius: 16px;
--radius-sm: 10px;
--transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
html {
scroll-behavior: smooth;
}
body {
font-family: "Inter", sans-serif;
background-color: var(--clr-bg);
color: var(--clr-text);
line-height: 1.6;
overflow-x: hidden;
}
/* ===== SCROLLBAR ===== */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: var(--clr-bg);
}
::-webkit-scrollbar-thumb {
background: var(--clr-primary);
border-radius: 3px;
}
/* ===== CANVAS BG ===== */
#bg-canvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none;
opacity: 0.5;
}
/* ===== LAYOUT ===== */
.wrapper {
position: relative;
z-index: 1;
max-width: 1200px;
margin: 0 auto;
padding: 0 24px;
}
/* ===== NAVBAR ===== */
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
padding: 16px 0;
background: rgba(240, 244, 248, 0.75);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(99, 102, 241, 0.12);
transition: var(--transition);
}
.navbar.scrolled {
padding: 12px 0;
background: rgba(240, 244, 248, 0.97);
box-shadow: 0 2px 20px rgba(15, 23, 42, 0.08);
}
.nav-inner {
max-width: 1200px;
margin: 0 auto;
padding: 0 24px;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-logo {
display: flex;
align-items: center;
gap: 10px;
text-decoration: none;
}
.nav-logo-icon {
width: 38px;
height: 38px;
border-radius: 10px;
background: var(--grad-primary);
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
color: #fff;
box-shadow: 0 0 16px rgba(56, 189, 248, 0.4);
flex-shrink: 0;
}
.nav-logo-text {
font-family: "Space Grotesk", sans-serif;
font-weight: 700;
font-size: 1.1rem;
color: var(--clr-text);
line-height: 1.2;
}
.nav-logo-text span {
display: block;
font-size: 0.7rem;
font-weight: 400;
color: var(--clr-primary);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.nav-badge {
font-size: 0.75rem;
padding: 5px 14px;
border-radius: 50px;
background: rgba(79, 70, 229, 0.08);
border: 1px solid rgba(79, 70, 229, 0.25);
color: var(--clr-primary);
font-weight: 500;
letter-spacing: 0.02em;
}
/* ===== HERO ===== */
.hero {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 120px 24px 80px;
position: relative;
overflow: hidden;
}
.hero::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 900px;
height: 900px;
background: radial-gradient(
circle,
rgba(79, 70, 229, 0.07) 0%,
rgba(124, 58, 237, 0.04) 40%,
transparent 70%
);
pointer-events: none;
}
.hero-badge {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 0.8rem;
padding: 7px 18px;
border-radius: 50px;
background: rgba(79, 70, 229, 0.08);
border: 1px solid rgba(79, 70, 229, 0.22);
color: var(--clr-primary);
font-weight: 500;
letter-spacing: 0.06em;
text-transform: uppercase;
margin-bottom: 28px;
animation: fadeSlideDown 0.8s ease both;
}
.hero-badge i {
font-size: 0.75rem;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.4;
}
}
.hero-title {
font-family: "Space Grotesk", sans-serif;
font-size: clamp(2.4rem, 6vw, 4.2rem);
font-weight: 800;
line-height: 1.1;
letter-spacing: -0.03em;
color: var(--clr-text);
margin-bottom: 20px;
animation: fadeSlideDown 0.8s 0.1s ease both;
}
.hero-title .gradient-text {
background: var(--grad-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-sub {
font-size: 1.1rem;
color: var(--clr-muted);
max-width: 560px;
margin: 0 auto 40px;
line-height: 1.7;
animation: fadeSlideDown 0.8s 0.2s ease both;
}
.hero-stats {
display: flex;
align-items: center;
justify-content: center;
gap: 40px;
margin-bottom: 56px;
animation: fadeSlideDown 0.8s 0.3s ease both;
}
.hero-stat {
text-align: center;
}
.hero-stat-num {
font-family: "Space Grotesk", sans-serif;
font-size: 2rem;
font-weight: 700;
background: var(--grad-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
line-height: 1;
margin-bottom: 4px;
}
.hero-stat-label {
font-size: 0.78rem;
color: var(--clr-muted);
text-transform: uppercase;
letter-spacing: 0.06em;
}
.hero-divider {
width: 1px;
height: 40px;
background: var(--clr-border);
}
.hero-cta {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 14px 32px;
border-radius: 50px;
background: var(--grad-primary);
color: #fff;
font-weight: 600;
font-size: 0.95rem;
text-decoration: none;
box-shadow: 0 8px 24px rgba(79, 70, 229, 0.35);
transition: var(--transition);
animation: fadeSlideDown 0.8s 0.4s ease both;
}
.hero-cta:hover {
transform: translateY(-2px);
box-shadow: 0 12px 36px rgba(79, 70, 229, 0.5);
}
/* ===== SCROLL INDICATOR ===== */
.scroll-indicator {
position: absolute;
bottom: 32px;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
color: var(--clr-muted);
font-size: 0.75rem;
letter-spacing: 0.06em;
text-transform: uppercase;
animation: fadeSlideDown 0.8s 0.8s ease both;
}
.scroll-indicator .mouse {
width: 22px;
height: 34px;
border: 2px solid rgba(79, 70, 229, 0.3);
border-radius: 11px;
display: flex;
justify-content: center;
padding-top: 6px;
}
.scroll-indicator .wheel {
width: 3px;
height: 6px;
background: var(--clr-primary);
border-radius: 2px;
animation: scrollDown 1.6s infinite;
}
@keyframes scrollDown {
0% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(10px);
}
}
/* ===== SECTION ===== */
.section {
padding: 100px 0;
}
.section-label {
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--clr-primary);
margin-bottom: 12px;
}
.section-title {
font-family: "Space Grotesk", sans-serif;
font-size: clamp(1.8rem, 4vw, 2.6rem);
font-weight: 700;
color: var(--clr-text);
line-height: 1.2;
letter-spacing: -0.02em;
margin-bottom: 16px;
}
.section-desc {
font-size: 1rem;
color: var(--clr-muted);
max-width: 520px;
line-height: 1.7;
}
.section-header {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 56px;
gap: 24px;
flex-wrap: wrap;
}
/* ===== PROJECT CARDS ===== */
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
gap: 28px;
}
.project-card {
position: relative;
background: var(--grad-card);
border: 1px solid var(--clr-border);
border-radius: var(--radius);
overflow: hidden;
text-decoration: none;
color: var(--clr-text);
transition: var(--transition);
display: flex;
flex-direction: column;
box-shadow: var(--shadow-card);
animation: fadeSlideUp 0.6s ease both;
}
.project-card:hover {
transform: translateY(-6px);
border-color: rgba(79, 70, 229, 0.35);
box-shadow:
0 16px 48px rgba(15, 23, 42, 0.14),
0 0 0 1px rgba(79, 70, 229, 0.1);
}
.project-card:hover .card-arrow {
transform: translate(4px, -4px);
opacity: 1;
}
.project-card:hover .card-icon {
transform: scale(1.08);
}
.card-header {
padding: 28px 28px 20px;
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
}
.card-icon {
width: 56px;
height: 56px;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.4rem;
transition: var(--transition);
flex-shrink: 0;
}
.card-num {
font-family: "Space Grotesk", sans-serif;
font-size: 0.75rem;
font-weight: 600;
color: var(--clr-muted);
letter-spacing: 0.08em;
background: rgba(15, 23, 42, 0.04);
border: 1px solid rgba(15, 23, 42, 0.1);
border-radius: 50px;
padding: 4px 12px;
}
.card-arrow {
position: absolute;
top: 24px;
right: 24px;
color: var(--clr-primary);
font-size: 1rem;
opacity: 0.5;
transition: var(--transition);
}
.card-body {
padding: 0 28px 28px;
flex: 1;
display: flex;
flex-direction: column;
}
.card-title {
font-family: "Space Grotesk", sans-serif;
font-size: 1.25rem;
font-weight: 700;
color: var(--clr-text);
margin-bottom: 10px;
letter-spacing: -0.01em;
line-height: 1.3;
}
.card-desc {
font-size: 0.88rem;
color: var(--clr-muted);
line-height: 1.65;
margin-bottom: 20px;
flex: 1;
}
.card-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 24px;
}
.tag {
font-size: 0.72rem;
font-weight: 500;
padding: 4px 12px;
border-radius: 50px;
letter-spacing: 0.02em;
}
.tag-blue {
background: #eff6ff;
color: #1d4ed8;
border: 1px solid #bfdbfe;
}
.tag-purple {
background: #f5f3ff;
color: #6d28d9;
border: 1px solid #ddd6fe;
}
.tag-green {
background: #ecfdf5;
color: #065f46;
border: 1px solid #a7f3d0;
}
.tag-orange {
background: #fff7ed;
color: #9a3412;
border: 1px solid #fed7aa;
}
.tag-pink {
background: #fdf2f8;
color: #9d174d;
border: 1px solid #fbcfe8;
}
.card-footer {
border-top: 1px solid var(--clr-border);
padding: 18px 28px;
display: flex;
align-items: center;
justify-content: space-between;
}
.card-link-btn {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 0.85rem;
font-weight: 600;
color: var(--clr-primary);
transition: var(--transition);
}
.project-card:hover .card-link-btn {
gap: 12px;
}
.card-status {
display: flex;
align-items: center;
gap: 6px;
font-size: 0.75rem;
}
.status-dot {
width: 7px;
height: 7px;
border-radius: 50%;
animation: statusPulse 2s infinite;
}
.status-dot.active {
background: var(--clr-accent);
box-shadow: 0 0 8px var(--clr-accent);
}
.status-dot.pending {
background: var(--clr-warn);
box-shadow: 0 0 8px var(--clr-warn);
}
.status-dot.inactive {
background: var(--clr-muted);
}
@keyframes statusPulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.4;
}
}
/* ===== CARD ACCENT LINES ===== */
.card-glow-line {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
border-radius: var(--radius) var(--radius) 0 0;
}
/* ===== COMING SOON CARD ===== */
.card-overlay {
position: absolute;
inset: 0;
background: rgba(240, 244, 248, 0.75);
backdrop-filter: blur(3px);
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--radius);
}
.coming-soon-badge {
font-size: 0.8rem;
font-weight: 600;
padding: 8px 20px;
border-radius: 50px;
background: #fff7ed;
border: 1px solid #fed7aa;
color: var(--clr-warn);
letter-spacing: 0.06em;
text-transform: uppercase;
box-shadow: 0 2px 8px rgba(234, 88, 12, 0.15);
}
/* ===== TECH STACK SECTION ===== */
.tech-strip {
padding: 60px 0;
border-top: 1px solid var(--clr-border);
border-bottom: 1px solid var(--clr-border);
}
.tech-strip-label {
text-align: center;
font-size: 0.75rem;
font-weight: 500;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--clr-muted);
margin-bottom: 32px;
}
.tech-items {
display: flex;
align-items: center;
justify-content: center;
gap: 40px;
flex-wrap: wrap;
}
.tech-item {
display: flex;
align-items: center;
gap: 10px;
color: var(--clr-muted);
font-size: 0.9rem;
font-weight: 500;
transition: var(--transition);
}
.tech-item:hover {
color: var(--clr-text);
}
.tech-item i {
font-size: 1.4rem;
transition: var(--transition);
}
/* ===== FOOTER ===== */
.footer {
padding: 60px 0 40px;
border-top: 1px solid var(--clr-border);
}
.footer-inner {
text-align: center;
}
.footer-logo {
font-family: "Space Grotesk", sans-serif;
font-size: 1.4rem;
font-weight: 700;
color: var(--clr-text);
margin-bottom: 12px;
}
.footer-logo span {
background: var(--grad-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.footer-course {
font-size: 0.85rem;
color: var(--clr-muted);
margin-bottom: 32px;
}
.footer-divider {
width: 60px;
height: 1px;
background: var(--clr-border);
margin: 32px auto;
}
.footer-copy {
font-size: 0.8rem;
color: var(--clr-muted);
}
.footer-copy a {
color: var(--clr-primary);
text-decoration: none;
}
/* ===== ANIMATIONS ===== */
@keyframes fadeSlideDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeSlideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.reveal {
opacity: 0;
transform: translateY(30px);
transition:
opacity 0.7s ease,
transform 0.7s ease;
}
.reveal.visible {
opacity: 1;
transform: translateY(0);
}
/* ===== RESPONSIVE ===== */
@media (max-width: 640px) {
.hero-stats {
gap: 24px;
}
.projects-grid {
grid-template-columns: 1fr;
}
.section-header {
flex-direction: column;
align-items: flex-start;
}
}
</style>
</head>
<body>
<!-- ===== CANVAS BACKGROUND ===== -->
<canvas id="bg-canvas"></canvas>
<!-- ===== NAVBAR ===== -->
<nav
class="navbar"
id="navbar"
>
<div class="nav-inner">
<a
href="#"
class="nav-logo"
id="logo-link"
>
<div class="nav-logo-icon">
<i class="fa-solid fa-map-location-dot"></i>
</div>
<div class="nav-logo-text">
WebGIS
<span>Sistem Informasi Geografis</span>
</div>
</a>
<div class="nav-badge">
<i class="fa-solid fa-graduation-cap"></i> &nbsp;Portofolio Tugas
</div>
</div>
</nav>
<!-- ===== HERO ===== -->
<section
class="hero"
id="home"
>
<div class="wrapper">
<div
class="hero-badge"
id="hero-badge"
>
<i class="fa-solid fa-circle"></i>
Sistem Informasi Geografis
</div>
<h1
class="hero-title"
id="hero-title"
>
Portofolio Proyek<br />
<span class="gradient-text">WebGIS Interaktif</span>
</h1>
<p
class="hero-sub"
id="hero-sub"
>
Kumpulan tugas pemetaan berbasis web menggunakan teknologi geospasial
modern — dari peta jalan, persil, hingga visualisasi data sosial.
</p>
<div id="student-card" style="
display: inline-flex;
align-items: center;
gap: 14px;
background: rgba(79, 70, 229, 0.07);
border: 1px solid rgba(79, 70, 229, 0.2);
border-radius: 14px;
padding: 12px 22px;
margin-top: 8px;
margin-bottom: 4px;
">
<div style="width: 42px; height: 42px; border-radius: 50%; background: var(--grad-primary); display: flex; align-items: center; justify-content: center; font-size: 1.2rem;">👤</div>
<div style="text-align: left;">
<div style="font-weight: 700; font-size: 0.95rem; color: var(--clr-text);">Syariffullah</div>
<div style="font-size: 0.78rem; color: var(--clr-muted); font-family: 'Space Grotesk', monospace; letter-spacing: 0.03em;">NIM: D1041231018</div>
</div>
<div style="width: 1px; height: 32px; background: rgba(79,70,229,0.2);"></div>
<div style="font-size: 0.78rem; color: var(--clr-muted); text-align: left;">
<div><i class="fa-solid fa-graduation-cap" style="color: var(--clr-primary);"></i> Sistem Informasi Geografis</div>
<div style="margin-top:2px;"><i class="fa-solid fa-microchip" style="color: var(--clr-secondary);"></i> Teknik Informatika</div>
</div>
</div>
<div
class="hero-stats"
id="hero-stats"
>
<div class="hero-stat">
<div
class="hero-stat-num"
id="stat-projects"
>
4
</div>
<div class="hero-stat-label">Proyek</div>
</div>
<div class="hero-divider"></div>
<div class="hero-stat">
<div class="hero-stat-num">GIS</div>
<div class="hero-stat-label">Mata Kuliah</div>
</div>
<div class="hero-divider"></div>
<div class="hero-stat">
<div class="hero-stat-num">Web</div>
<div class="hero-stat-label">Platform</div>
</div>
</div>
<a
href="#projects"
class="hero-cta"
id="hero-cta"
>
<i class="fa-solid fa-layer-group"></i>
Lihat Semua Proyek
</a>
<div class="scroll-indicator">
<div class="mouse"><div class="wheel"></div></div>
<span>Scroll</span>
</div>
</div>
</section>
<!-- ===== TECH STRIP ===== -->
<div class="tech-strip">
<div class="wrapper">
<p class="tech-strip-label">Teknologi yang Digunakan</p>
<div class="tech-items">
<div class="tech-item">
<i
class="fa-solid fa-map"
style="color: #4f46e5"
></i>
Leaflet.js
</div>
<div class="tech-item">
<i
class="fa-brands fa-php"
style="color: #7c3aed"
></i>
PHP
</div>
<div class="tech-item">
<i
class="fa-solid fa-database"
style="color: #059669"
></i>
MySQL
</div>
<div class="tech-item">
<i
class="fa-brands fa-html5"
style="color: #ea580c"
></i>
HTML / CSS / JS
</div>
<div class="tech-item">
<i
class="fa-solid fa-globe"
style="color: #db2777"
></i>
GeoJSON
</div>
<div class="tech-item">
<i
class="fa-solid fa-server"
style="color: #b45309"
></i>
XAMPP
</div>
</div>
</div>
</div>
<!-- ===== PROJECTS ===== -->
<section
class="section"
id="projects"
>
<div class="wrapper">
<div class="section-header reveal">
<div>
<p class="section-label">
<i class="fa-solid fa-folder-open"></i> &nbsp;Daftar Proyek
</p>
<h2 class="section-title">Semua Tugas WebGIS</h2>
<p class="section-desc">
Klik pada kartu proyek untuk membuka aplikasi pemetaan interaktif.
</p>
</div>
</div>
<div class="projects-grid">
<!-- ===== PROJECT 1: POVERTY ===== -->
<a
href="/webgis/poverty/index.php"
class="project-card"
id="card-poverty"
style="animation-delay: 0.05s"
>
<div
class="card-glow-line"
style="background: linear-gradient(90deg, #38bdf8, #818cf8)"
></div>
<div class="card-arrow">
<i class="fa-solid fa-arrow-up-right"></i>
</div>
<div class="card-header">
<div
class="card-icon"
style="
background: rgba(56, 189, 248, 0.12);
border: 1px solid rgba(56, 189, 248, 0.2);
"
>
🏠
</div>
<span class="card-num">01</span>
</div>
<div class="card-body">
<h3 class="card-title">
Pemetaan Kemiskinan<br />Kota Pontianak
</h3>
<p class="card-desc">
Aplikasi WebGIS untuk memetakan sebaran penduduk miskin, rumah
ibadah, dan log bantuan sosial di Kota Pontianak. Dilengkapi
manajemen data spasial berbasis peran.
</p>
<div class="card-tags">
<span class="tag tag-blue">Leaflet.js</span>
<span class="tag tag-purple">PHP</span>
<span class="tag tag-green">MySQL Spatial</span>
<span class="tag tag-orange">GeoJSON</span>
<span class="tag tag-pink">Role-based</span>
</div>
</div>
<div class="card-footer">
<span class="card-link-btn">
Buka Proyek <i class="fa-solid fa-arrow-right"></i>
</span>
<div class="card-status">
<div class="status-dot active"></div>
<span style="color: var(--clr-muted); font-size: 0.75rem"
>Aktif</span
>
</div>
</div>
</a>
<!-- ===== PROJECT 2: JALAN ===== -->
<a
href="/webgis/jalan/index.php"
class="project-card"
id="card-jalan"
style="animation-delay: 0.1s"
>
<div
class="card-glow-line"
style="background: linear-gradient(90deg, #818cf8, #f472b6)"
></div>
<div class="card-arrow">
<i class="fa-solid fa-arrow-up-right"></i>
</div>
<div class="card-header">
<div
class="card-icon"
style="
background: rgba(129, 140, 248, 0.12);
border: 1px solid rgba(129, 140, 248, 0.2);
"
>
🛣️
</div>
<span class="card-num">02</span>
</div>
<div class="card-body">
<h3 class="card-title">Pemetaan Jaringan<br />Jalan</h3>
<p class="card-desc">
Visualisasi dan analisis jaringan jalan menggunakan data
spasial. Mencakup klasifikasi jalan, kondisi, dan data atribut
terkait infrastruktur jalan.
</p>
<div class="card-tags">
<span class="tag tag-purple">WebGIS</span>
<span class="tag tag-blue">Shapefile</span>
<span class="tag tag-green">Road Network</span>
</div>
</div>
<div class="card-footer">
<span class="card-link-btn">
Buka Proyek <i class="fa-solid fa-arrow-right"></i>
</span>
<div class="card-status">
<div class="status-dot active"></div>
<span style="color: var(--clr-muted); font-size: 0.75rem"
>Aktif</span
>
</div>
</div>
</a>
<!-- ===== PROJECT 3: PARSIL ===== -->
<a
href="/webgis/parsil/index.php"
class="project-card"
id="card-parsil"
style="animation-delay: 0.15s"
>
<div
class="card-glow-line"
style="background: linear-gradient(90deg, #34d399, #38bdf8)"
></div>
<div class="card-arrow">
<i class="fa-solid fa-arrow-up-right"></i>
</div>
<div class="card-header">
<div
class="card-icon"
style="
background: rgba(52, 211, 153, 0.12);
border: 1px solid rgba(52, 211, 153, 0.2);
"
>
🗺️
</div>
<span class="card-num">03</span>
</div>
<div class="card-body">
<h3 class="card-title">Pemetaan Bidang<br />Tanah (Parsil)</h3>
<p class="card-desc">
Sistem informasi geografis untuk pemetaan bidang tanah,
kepemilikan lahan, dan informasi pertanahan berbasis web yang
terintegrasi dengan data spasial.
</p>
<div class="card-tags">
<span class="tag tag-green">Land Parcel</span>
<span class="tag tag-blue">Polygon</span>
<span class="tag tag-orange">Cadastral</span>
</div>
</div>
<div class="card-footer">
<span class="card-link-btn">
Buka Proyek <i class="fa-solid fa-arrow-right"></i>
</span>
<div class="card-status">
<div class="status-dot active"></div>
<span style="color: var(--clr-muted); font-size: 0.75rem"
>Aktif</span
>
</div>
</div>
</a>
<!-- ===== PROJECT 4: SPBU ===== -->
<a
href="/webgis/spbu/index.php"
class="project-card"
id="card-spbu"
style="animation-delay: 0.2s"
>
<div
class="card-glow-line"
style="background: linear-gradient(90deg, #fb923c, #facc15)"
></div>
<div class="card-arrow">
<i class="fa-solid fa-arrow-up-right"></i>
</div>
<div class="card-header">
<div
class="card-icon"
style="
background: rgba(251, 146, 60, 0.12);
border: 1px solid rgba(251, 146, 60, 0.2);
"
>
</div>
<span class="card-num">04</span>
</div>
<div class="card-body">
<h3 class="card-title">Pemetaan Lokasi<br />SPBU</h3>
<p class="card-desc">
Peta interaktif untuk menampilkan sebaran Stasiun Pengisian
Bahan Bakar Umum (SPBU) beserta informasi detail, analisis
jangkauan, dan fasilitas yang tersedia.
</p>
<div class="card-tags">
<span class="tag tag-orange">POI Mapping</span>
<span class="tag tag-blue">Leaflet.js</span>
<span class="tag tag-pink">Radius Analysis</span>
</div>
</div>
<div class="card-footer">
<span class="card-link-btn">
Buka Proyek <i class="fa-solid fa-arrow-right"></i>
</span>
<div class="card-status">
<div class="status-dot active"></div>
<span style="color: var(--clr-muted); font-size: 0.75rem"
>Aktif</span
>
</div>
</div>
</a>
</div>
</div>
</section>
<!-- ===== FOOTER ===== -->
<footer class="footer">
<div class="wrapper">
<div class="footer-inner">
<div class="footer-logo">Web<span>GIS</span></div>
<div class="footer-course">
Mata Kuliah Sistem Informasi Geografis &nbsp;·&nbsp; Portofolio
Tugas Mahasiswa
</div>
<div
style="
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
flex-wrap: wrap;
"
>
<span style="font-size: 0.8rem; color: var(--clr-muted)">
<i
class="fa-solid fa-map-location-dot"
style="color: var(--clr-primary)"
></i>
&nbsp;Leaflet.js
</span>
<span style="color: var(--clr-border)">|</span>
<span style="font-size: 0.8rem; color: var(--clr-muted)">
<i
class="fa-solid fa-database"
style="color: var(--clr-accent)"
></i>
&nbsp;MySQL Spatial
</span>
<span style="color: var(--clr-border)">|</span>
<span style="font-size: 0.8rem; color: var(--clr-muted)">
<i
class="fa-brands fa-php"
style="color: var(--clr-secondary)"
></i>
&nbsp;PHP / XAMPP
</span>
</div>
<div class="footer-divider"></div>
<p style="font-size: 0.9rem; color: var(--clr-text); font-weight: 600; margin-bottom: 6px;">
Syariffullah &nbsp;<span style="font-family: 'Space Grotesk', monospace; color: var(--clr-primary); font-size: 0.82rem; background: rgba(79,70,229,0.08); padding: 2px 8px; border-radius: 6px;">D1041231018</span>
</p>
<p class="footer-copy">
&copy; 2025 WebGIS Portfolio &nbsp;&middot;&nbsp; Sistem Informasi Geografis
</p>
</div>
</div>
</footer>
<!-- ===== SCRIPTS ===== -->
<script>
/* ----- CANVAS PARTICLE / GRID BG ----- */
(function () {
const canvas = document.getElementById("bg-canvas");
const ctx = canvas.getContext("2d");
let w, h, animId;
const DOTS = [];
const LINES = [];
const COUNT = 60;
function resize() {
w = canvas.width = window.innerWidth;
h = canvas.height = window.innerHeight;
}
resize();
window.addEventListener("resize", resize);
// Grid lines
function drawGrid() {
ctx.strokeStyle = "rgba(79,70,229,0.06)";
ctx.lineWidth = 1;
const step = 60;
for (let x = 0; x < w; x += step) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
ctx.stroke();
}
for (let y = 0; y < h; y += step) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(w, y);
ctx.stroke();
}
}
// Particles
for (let i = 0; i < COUNT; i++) {
DOTS.push({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
vx: (Math.random() - 0.5) * 0.3,
vy: (Math.random() - 0.5) * 0.3,
r: Math.random() * 2 + 0.5,
a: Math.random(),
});
}
function draw() {
ctx.clearRect(0, 0, w, h);
drawGrid();
// Move and draw dots
DOTS.forEach((d) => {
d.x += d.vx;
d.y += d.vy;
if (d.x < 0) d.x = w;
if (d.x > w) d.x = 0;
if (d.y < 0) d.y = h;
if (d.y > h) d.y = 0;
ctx.beginPath();
ctx.arc(d.x, d.y, d.r, 0, Math.PI * 2);
ctx.fillStyle = `rgba(79,70,229,${d.a * 0.4})`;
ctx.fill();
});
// Connect nearby dots
for (let i = 0; i < DOTS.length; i++) {
for (let j = i + 1; j < DOTS.length; j++) {
const dx = DOTS[i].x - DOTS[j].x;
const dy = DOTS[i].y - DOTS[j].y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 120) {
ctx.beginPath();
ctx.moveTo(DOTS[i].x, DOTS[i].y);
ctx.lineTo(DOTS[j].x, DOTS[j].y);
ctx.strokeStyle = `rgba(79,70,229,${0.12 * (1 - dist / 120)})`;
ctx.lineWidth = 0.8;
ctx.stroke();
}
}
}
animId = requestAnimationFrame(draw);
}
draw();
})();
/* ----- NAVBAR SCROLL ----- */
const navbar = document.getElementById("navbar");
window.addEventListener("scroll", () => {
navbar.classList.toggle("scrolled", window.scrollY > 40);
});
/* ----- SCROLL REVEAL ----- */
const revealEls = document.querySelectorAll(".reveal");
const revealObs = new IntersectionObserver(
(entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
e.target.classList.add("visible");
revealObs.unobserve(e.target);
}
});
},
{ threshold: 0.15 },
);
revealEls.forEach((el) => revealObs.observe(el));
/* ----- CARD REVEAL ----- */
const cardEls = document.querySelectorAll(".project-card");
const cardObs = new IntersectionObserver(
(entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
e.target.style.opacity = "1";
e.target.style.transform = "translateY(0)";
cardObs.unobserve(e.target);
}
});
},
{ threshold: 0.1 },
);
cardEls.forEach((card) => {
card.style.opacity = "0";
card.style.transform = "translateY(30px)";
card.style.transition = "opacity 0.6s ease, transform 0.6s ease";
cardObs.observe(card);
});
/* ----- SMOOTH HERO CTA ----- */
document.getElementById("hero-cta").addEventListener("click", (e) => {
e.preventDefault();
document
.getElementById("projects")
.scrollIntoView({ behavior: "smooth" });
});
</script>
</body>
</html>