Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 693e41af0a | |||
| 00cc837fa1 | |||
| fc338e8473 | |||
| 125828a566 | |||
| 5186195930 | |||
| 783ac833f2 | |||
| 82a386e3bb | |||
| b6c9bbbefe |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 984 KiB |
|
After Width: | Height: | Size: 209 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 192 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 337 KiB |
|
After Width: | Height: | Size: 131 KiB |
@@ -5,6 +5,31 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Group 03 – HarusSelesaiKP | Informatika UNTAN</title>
|
||||
|
||||
<!--
|
||||
Anti-flicker safety net: body.lang-pending is hidden via style.css
|
||||
until localization.js (loaded sync below) applies the correct
|
||||
language on DOMContentLoaded and removes the class.
|
||||
This inline fallback ensures the rule exists even if style.css
|
||||
hasn't parsed yet.
|
||||
-->
|
||||
<style id="lang-pending-style">
|
||||
body.lang-pending {
|
||||
visibility: hidden
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--
|
||||
localization.js MUST be loaded synchronously (no defer/async)
|
||||
so the IIFE inside it can set window.__currentLang before
|
||||
the browser renders any text. This is the ONLY intentional
|
||||
render-blocking script.
|
||||
-->
|
||||
<script src="localization.js"></script>
|
||||
|
||||
<!-- Preload hero image — eliminates LCP delay -->
|
||||
<link rel="preload" href="images/hero.webp" as="image" type="image/webp" />
|
||||
|
||||
<link rel="stylesheet" href="../../assets/global/output.css" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
@@ -12,297 +37,17 @@
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:ital,wght@0,100..900;1,100..900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"
|
||||
rel="stylesheet" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
|
||||
<style>
|
||||
/* ===== MOBILE NAV STABILITY FIX =====
|
||||
Prevents logo bar from collapsing/shifting on Android when
|
||||
address bar hides/shows and triggers viewport reflow.
|
||||
===================================== */
|
||||
#nav-wrapper {
|
||||
position: fixed !important;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 50;
|
||||
backface-visibility: hidden;
|
||||
-webkit-backface-visibility: hidden;
|
||||
will-change: box-shadow;
|
||||
}
|
||||
|
||||
#nav-wrapper>div:first-child {
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
#nav-wrapper>div:first-child {
|
||||
min-height: 0 !important;
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
|
||||
#pengumuman-bar {
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
#pengumuman-bar.is-stuck {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip Link Styling */
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
left: 0;
|
||||
background: #003150;
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
z-index: 100;
|
||||
transition: top 0.3s;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
/* Focus visible for better keyboard navigation */
|
||||
:focus-visible {
|
||||
outline: 3px solid #feb401;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Mega Menu Styles */
|
||||
.hero-pagination .swiper-pagination-bullet {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
display: inline-block;
|
||||
margin: 0 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.hero-pagination .swiper-pagination-bullet-active {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
width: 24px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mega-panel {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition:
|
||||
opacity 0.2s ease,
|
||||
visibility 0.2s ease;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.mega-panel.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Overlay transition */
|
||||
#mega-overlay {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition:
|
||||
opacity 0.2s ease,
|
||||
visibility 0.2s ease;
|
||||
}
|
||||
|
||||
#mega-overlay.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.mega-link-card {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
background: white;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.mega-link-card:hover {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.mega-link-icon {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-menu-btn.active {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #1e40af;
|
||||
}
|
||||
|
||||
.nav-menu-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
/* Mega menu positioning - no gap */
|
||||
#main-nav {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#mega-menu-container {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#mega-menu-container .mega-panel {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* Keep pointer bridge under nav to prevent hover loss */
|
||||
.nav-menu-item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-menu-item::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -12px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
/* ===== Luco Chatbot Animations ===== */
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
transform: translateX(100px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
transform: translateY(10px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lucoPulse {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
#luco-container {
|
||||
animation: slideInRight 0.5s ease-out forwards;
|
||||
}
|
||||
|
||||
#luco-bubble {
|
||||
animation: fadeInUp 0.3s ease-out forwards;
|
||||
width: min(18rem, calc(100vw - 2rem));
|
||||
}
|
||||
|
||||
#luco-btn {
|
||||
animation: lucoPulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
#luco-btn:hover {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
/* Typing indicator */
|
||||
.typing-indicator span {
|
||||
display: inline-block;
|
||||
animation: bounce 1.4s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.typing-indicator span:nth-child(1) {
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.typing-indicator span:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.typing-indicator span:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: translateY(-6px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Luco speech bubble arrow */
|
||||
#luco-bubble .bubble-arrow {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
bottom: -6px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: white;
|
||||
transform: rotate(45deg);
|
||||
border-right: 1px solid #e2e8f0;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
</style>
|
||||
<!-- Font Awesome loaded async — non-render-blocking -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
||||
media="print" onload="this.media='all'" />
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
|
||||
</noscript>
|
||||
</head>
|
||||
|
||||
<body class="text-slate-800 antialiased bg-white selection:bg-untan-yellow selection:text-black">
|
||||
|
||||
<body class="lang-pending text-slate-800 antialiased bg-white selection:bg-untan-yellow selection:text-black">
|
||||
<a href="#main-content" class="skip-link" data-i18n="skip_to_content">Langsung ke Konten Utama</a>
|
||||
|
||||
<div id="nav-wrapper"
|
||||
@@ -351,8 +96,9 @@
|
||||
<div class="pl-5 border-l border-white/20 flex items-center gap-3">
|
||||
<button
|
||||
class="language-toggle-btn text-xs hover:text-untan-yellow transition-colors flex items-center gap-1">
|
||||
<span class="font-bold">ID</span>
|
||||
<span class="text-white/40">|</span> EN
|
||||
<span class="lang-id font-bold">ID</span>
|
||||
<span class="text-white/40">|</span>
|
||||
<span class="lang-en opacity-50 font-normal">EN</span>
|
||||
</button>
|
||||
<button
|
||||
class="w-8 h-8 flex items-center justify-center rounded-sm hover:bg-white/10 transition-colors"
|
||||
@@ -1133,9 +879,9 @@
|
||||
<button
|
||||
class="language-toggle-btn px-2 py-1 rounded-sm border border-white/20 text-xs text-white hover:bg-white/10 transition-colors flex items-center gap-2"
|
||||
aria-label="Ganti bahasa">
|
||||
<span class="font-bold">ID</span>
|
||||
<span class="lang-id font-bold">ID</span>
|
||||
<span class="text-white/40" aria-hidden="true">|</span>
|
||||
EN
|
||||
<span class="lang-en opacity-50 font-normal">EN</span>
|
||||
</button>
|
||||
<button
|
||||
class="w-8 h-8 rounded-sm bg-white/10 hover:bg-white/20 flex items-center justify-center text-white transition-colors"
|
||||
@@ -1411,7 +1157,8 @@
|
||||
<!-- LIVING COST SECTION -->
|
||||
<section class="living-cost-section">
|
||||
<div class="cost-banner-wrapper">
|
||||
<img src="images/biaya-hidup.png" alt="biaya hidup" class="cost-banner-img" />
|
||||
<img src="images/biaya-hidup.jpg" alt="Ilustrasi biaya hidup mahasiswa di Pontianak"
|
||||
class="cost-banner-img" loading="lazy" decoding="async" width="1200" height="800" />
|
||||
<div class="cost-banner-overlay"></div>
|
||||
</div>
|
||||
|
||||
@@ -1464,7 +1211,7 @@
|
||||
|
||||
<div class="flex flex-col sm:flex-row items-center justify-center gap-4 sm:gap-6 mb-8 text-left">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/id/0/03/Lambang_Universitas_Tanjungpura.png"
|
||||
class="h-16 sm:h-20 shrink-0" alt="Logo UNTAN" />
|
||||
class="h-16 sm:h-20 shrink-0" alt="Logo UNTAN" loading="lazy" decoding="async" />
|
||||
<div
|
||||
class="border-t sm:border-t-0 sm:border-l-2 border-white/30 pt-4 sm:pt-0 sm:pl-5 py-1 flex flex-col justify-center leading-tight">
|
||||
<h2 class="text-2xl sm:text-3xl font-bold font-sans tracking-widest uppercase mb-1">
|
||||
@@ -1499,7 +1246,8 @@
|
||||
|
||||
<div class="w-full relative z-0 mt-4 pointer-events-none flex justify-center">
|
||||
<img src="../../assets/global/footer.png" alt="Footer Illustration"
|
||||
class="w-full h-auto object-cover object-bottom" style="object-position: bottom" />
|
||||
class="w-full h-auto object-cover object-bottom" style="object-position: bottom" loading="lazy"
|
||||
decoding="async" />
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1616,7 +1364,8 @@
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script src="../../assets/global/localization.js"></script>
|
||||
<!-- localization.js is now loaded in <head> synchronously for anti-flicker -->
|
||||
<!-- See the <head> section for its <script> tag -->
|
||||
|
||||
<!-- JavaScript Kelompok HarusSelesaiKP -->
|
||||
<!-- JavaScript Facilities Section -->
|
||||
@@ -1671,7 +1420,7 @@
|
||||
desc: "Fasilitas olahraga outdoor dan indoor lengkap untuk menjaga kebugaran dan menyalurkan bakat.",
|
||||
},
|
||||
{
|
||||
imgSrc: "images/masjid-muhtadin.png",
|
||||
imgSrc: "images/masjid-muhtadin.jpeg",
|
||||
icon: "icons/tabler-mosque.svg",
|
||||
titleKey: "skp_fac_title_masjid",
|
||||
descKey: "skp_fac_desc_masjid",
|
||||
@@ -1687,7 +1436,7 @@
|
||||
desc: "Fasilitas laboratorium modern UNTAN yang mendukung riset, praktikum, dan inovasi mahasiswa.",
|
||||
},
|
||||
{
|
||||
imgSrc: "images/audit-untan.png",
|
||||
imgSrc: "images/audit-untan.webp",
|
||||
icon: "icons/building-home.svg",
|
||||
titleKey: "skp_fac_title_audit",
|
||||
descKey: "skp_fac_desc_audit",
|
||||
@@ -1710,10 +1459,10 @@
|
||||
|
||||
cardsHtmlHTML += `
|
||||
<div class="facility-card">
|
||||
<img src="${item.imgSrc}" alt="${displayTitle}" class="card-image" />
|
||||
<img src="${item.imgSrc}" alt="${displayTitle}" class="card-image" loading="lazy" decoding="async" />
|
||||
<div class="card-body">
|
||||
<span class="card-icon-badge">
|
||||
<img src="${item.icon}" alt="" class="card-icon" />
|
||||
<img src="${item.icon}" alt="" class="card-icon" loading="lazy" decoding="async" />
|
||||
</span>
|
||||
<h3 class="card-title">${displayTitle}</h3>
|
||||
<p class="card-desc">${displayDesc}</p>
|
||||
@@ -1759,7 +1508,7 @@
|
||||
const directoriesData = [
|
||||
{
|
||||
category: "Organisasi",
|
||||
image: "images/bem-untan.png",
|
||||
image: "images/bem-untan.webp",
|
||||
titleKey: "skp_campus_dir_bem_title",
|
||||
descKey: "skp_campus_dir_bem_desc",
|
||||
},
|
||||
@@ -1771,25 +1520,25 @@
|
||||
},
|
||||
{
|
||||
category: "Organisasi",
|
||||
image: "images/hmif-untan.png",
|
||||
image: "images/hmif-untan.webp",
|
||||
titleKey: "skp_campus_dir_hmif_title",
|
||||
descKey: "skp_campus_dir_hmif_desc",
|
||||
},
|
||||
{
|
||||
category: "Organisasi",
|
||||
image: "images/cafatifa-untan.png",
|
||||
image: "images/cafatifa-untan.webp",
|
||||
titleKey: "skp_campus_dir_cafa_title",
|
||||
descKey: "skp_campus_dir_cafa_desc",
|
||||
},
|
||||
{
|
||||
category: "UKM",
|
||||
image: "images/mapala-untan.png",
|
||||
image: "images/mapala-untan.webp",
|
||||
titleKey: "skp_campus_dir_mapala_title",
|
||||
descKey: "skp_campus_dir_mapala_desc",
|
||||
},
|
||||
{
|
||||
category: "UKM",
|
||||
image: "images/lisma-untan.png",
|
||||
image: "images/lisma-untan.webp",
|
||||
titleKey: "skp_campus_dir_lisma_title",
|
||||
descKey: "skp_campus_dir_lisma_desc",
|
||||
},
|
||||
@@ -1851,7 +1600,7 @@
|
||||
return `
|
||||
<div class="directory-card">
|
||||
<div class="img-wrapper">
|
||||
<img src="${dir.image}" alt="${displayTitle}" />
|
||||
<img src="${dir.image}" alt="${displayTitle}" loading="lazy" decoding="async" />
|
||||
</div>
|
||||
<div class="card-info">
|
||||
<h4>${displayTitle}</h4>
|
||||
@@ -1920,13 +1669,13 @@
|
||||
|
||||
const destinationsData = [
|
||||
{
|
||||
image: "images/khatulistiwa.png",
|
||||
image: "images/khatulistiwa.webp",
|
||||
nameKey: "skp_dest_khatulistiwa_name",
|
||||
descKey: "skp_dest_khatulistiwa_desc",
|
||||
href: "https://maps.app.goo.gl/BUw4GWfSSDxvMCKA6",
|
||||
},
|
||||
{
|
||||
image: "images/waterfront.png",
|
||||
image: "images/waterfront.webp",
|
||||
nameKey: "skp_dest_waterfront_name",
|
||||
descKey: "skp_dest_waterfront_desc",
|
||||
href: "https://maps.app.goo.gl/2uo4jwNZtGTsvogU6",
|
||||
@@ -1938,7 +1687,7 @@
|
||||
href: "https://maps.app.goo.gl/dfLJPdFJMK9ifja66",
|
||||
},
|
||||
{
|
||||
image: "images/rumah-radank.png",
|
||||
image: "images/rumah-radank.webp",
|
||||
nameKey: "skp_dest_radank_name",
|
||||
descKey: "skp_dest_radank_desc",
|
||||
href: "https://maps.app.goo.gl/UqBchR3zimQSdTqW6",
|
||||
@@ -1950,13 +1699,13 @@
|
||||
href: "https://maps.app.goo.gl/2SWXaJB3D9VXx9Kg7",
|
||||
},
|
||||
{
|
||||
image: "images/amalzone.png",
|
||||
image: "images/amalzone.jpg",
|
||||
nameKey: "skp_dest_amalzone_name",
|
||||
descKey: "skp_dest_amalzone_desc",
|
||||
href: "https://maps.app.goo.gl/386expVMhHgMAmXA9",
|
||||
},
|
||||
{
|
||||
image: "images/hutan-mangrove.png",
|
||||
image: "images/hutan-mangrove.jpg",
|
||||
nameKey: "skp_dest_mangrove_name",
|
||||
descKey: "skp_dest_mangrove_desc",
|
||||
href: "#",
|
||||
@@ -2015,14 +1764,14 @@
|
||||
const cuisinesData = [
|
||||
{
|
||||
category: "RM",
|
||||
image: "images/gebuk-sambalado.png",
|
||||
image: "images/gebuk-sambalado.webp",
|
||||
name: "Gebuk Sambalado",
|
||||
descKey: "skp_cul_gebuk_desc",
|
||||
href: "https://maps.app.goo.gl/J1xCUCqVyTqPStG6A",
|
||||
},
|
||||
{
|
||||
category: "RM",
|
||||
image: "images/kota-sari.png",
|
||||
image: "images/kota-sari.webp",
|
||||
name: "Kota Sari",
|
||||
descKey: "skp_cul_sari_desc",
|
||||
href: "https://maps.app.goo.gl/Tx5Rbt6TxCtFuiGdA",
|
||||
@@ -2043,21 +1792,21 @@
|
||||
},
|
||||
{
|
||||
category: "RM",
|
||||
image: "images/ayam-melepar.png",
|
||||
image: "images/ayam-melepar.jpg",
|
||||
name: "Ayam Melepar",
|
||||
descKey: "skp_cul_melepar_desc",
|
||||
href: "https://maps.app.goo.gl/DRbCvAS7EPAbxjvM6",
|
||||
},
|
||||
{
|
||||
category: "RM",
|
||||
image: "images/geprek-umi.png",
|
||||
image: "images/geprek-umi.jpg",
|
||||
name: "Geprek Umi",
|
||||
descKey: "skp_cul_umi_desc",
|
||||
href: "https://maps.app.goo.gl/HV8X9qyLkjT7rULC7",
|
||||
},
|
||||
{
|
||||
category: "CS",
|
||||
image: "images/cw-coffee.png",
|
||||
image: "images/cw-coffee.jpg",
|
||||
name: "CW Coffee",
|
||||
descKey: "skp_cul_cw_desc",
|
||||
href: "https://maps.app.goo.gl/xRJn6eM93PonZFfWA",
|
||||
@@ -2071,7 +1820,7 @@
|
||||
},
|
||||
{
|
||||
category: "CS",
|
||||
image: "images/toko-kami.png",
|
||||
image: "images/toko-kami.jpg",
|
||||
name: "Toko Kami",
|
||||
descKey: "skp_cul_kami_desc",
|
||||
href: "https://maps.app.goo.gl/Nc9TX7aSQAJNr7es7",
|
||||
@@ -2085,14 +1834,14 @@
|
||||
},
|
||||
{
|
||||
category: "CS",
|
||||
image: "images/weng-coffee.png",
|
||||
image: "images/weng-coffee.jpg",
|
||||
name: "Weng Coffee",
|
||||
descKey: "skp_cul_weng_desc",
|
||||
href: "https://maps.app.goo.gl/EW3C4fjh4ZEpiRf18",
|
||||
},
|
||||
{
|
||||
category: "CS",
|
||||
image: "images/tropic-coffee.png",
|
||||
image: "images/tropic.jpg",
|
||||
name: "Tropic Coffee",
|
||||
descKey: "skp_cul_tropic_desc",
|
||||
href: "https://maps.app.goo.gl/b3P9QJMk4mBPHoQM9",
|
||||
@@ -2139,7 +1888,7 @@
|
||||
|
||||
return `
|
||||
<div class="culinary-card">
|
||||
<img src="${item.image}" alt="${item.name}" class="card-thumb" />
|
||||
<img src="${item.image}" alt="${item.name}" class="card-thumb" loading="lazy" decoding="async" />
|
||||
<div class="card-body">
|
||||
<h3>${item.name}</h3>
|
||||
<p>${displayDesc}</p>
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
/**
|
||||
* localization.js — HarusSelesaiKP
|
||||
*
|
||||
* ANTI-FLICKER STRATEGY:
|
||||
* 1. IIFE `initLanguage()` runs SYNCHRONOUSLY (no defer/async) to set
|
||||
* `window.__currentLang` before the browser paints any content.
|
||||
* 2. HTML body carries `lang-pending` class; body is hidden via CSS until
|
||||
* `applyTranslations()` fires on DOMContentLoaded and removes the class.
|
||||
* 3. `languageChanged` CustomEvent signals dynamic sections (Facilities,
|
||||
* Campus, Culinary, LivingCost) to re-render in the new language.
|
||||
*/
|
||||
|
||||
// ─── 1. Determine language SYNCHRONOUSLY (before first paint) ────────────────
|
||||
(function initLanguage() {
|
||||
try {
|
||||
const saved = localStorage.getItem("lang");
|
||||
const browserLang = navigator.language?.startsWith("id") ? "id" : "en";
|
||||
window.__currentLang = saved || browserLang;
|
||||
} catch (e) {
|
||||
window.__currentLang = "id";
|
||||
}
|
||||
// Stamp <html lang> immediately so screen readers get the right language
|
||||
document.documentElement.lang = window.__currentLang;
|
||||
})();
|
||||
|
||||
// ─── 2. Translation data ─────────────────────────────────────────────────────
|
||||
const translations = {
|
||||
id: {
|
||||
// Kelompok HarusSelesaiKP (skp)
|
||||
@@ -330,3 +356,76 @@ const translations = {
|
||||
skp_cost_type_transport: "Transport",
|
||||
},
|
||||
};
|
||||
|
||||
// ─── 3. Public translation helper ────────────────────────────────────────────
|
||||
/**
|
||||
* t(key) — returns translated string for the active language.
|
||||
* Falls back to Indonesian if key is missing in the active language.
|
||||
*/
|
||||
function t(key) {
|
||||
const lang = window.__currentLang || "id";
|
||||
return translations[lang]?.[key] ?? translations["id"]?.[key] ?? key;
|
||||
}
|
||||
|
||||
// ─── 4. Apply translations to all [data-i18n] elements ───────────────────────
|
||||
function applyTranslations(lang) {
|
||||
window.__currentLang = lang;
|
||||
document.documentElement.lang = lang;
|
||||
|
||||
try {
|
||||
localStorage.setItem("lang", lang);
|
||||
} catch (e) {
|
||||
// localStorage may be blocked (private mode, etc.) — fail silently
|
||||
}
|
||||
|
||||
document.querySelectorAll("[data-i18n]").forEach((el) => {
|
||||
const key = el.dataset.i18n;
|
||||
const text = t(key);
|
||||
if (text && text !== key) {
|
||||
el.textContent = text;
|
||||
}
|
||||
});
|
||||
|
||||
// Signal dynamic components to re-render
|
||||
document.dispatchEvent(new CustomEvent("languageChanged"));
|
||||
}
|
||||
|
||||
// ─── 5. Toggle language (called by UI buttons) ───────────────────────────────
|
||||
function toggleLanguage() {
|
||||
const next = window.__currentLang === "id" ? "en" : "id";
|
||||
applyTranslations(next);
|
||||
updateLanguageButtonUI(next);
|
||||
}
|
||||
|
||||
function updateLanguageButtonUI(lang) {
|
||||
document.querySelectorAll(".language-toggle-btn").forEach((btn) => {
|
||||
const idSpan = btn.querySelector(".lang-id");
|
||||
const enSpan = btn.querySelector(".lang-en");
|
||||
if (idSpan && enSpan) {
|
||||
if (lang === "id") {
|
||||
idSpan.className = "lang-id font-bold text-white";
|
||||
enSpan.className = "lang-en font-normal opacity-50";
|
||||
} else {
|
||||
idSpan.className = "lang-id font-normal opacity-50";
|
||||
enSpan.className = "lang-en font-bold text-white";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ─── 6. Boot on DOMContentLoaded ─────────────────────────────────────────────
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Apply translations to all static [data-i18n] nodes
|
||||
applyTranslations(window.__currentLang);
|
||||
|
||||
// Sync language button label
|
||||
updateLanguageButtonUI(window.__currentLang);
|
||||
|
||||
// Wire up toggle buttons
|
||||
document.querySelectorAll(".language-toggle-btn").forEach((btn) => {
|
||||
btn.addEventListener("click", toggleLanguage);
|
||||
});
|
||||
|
||||
// ── Reveal body: remove anti-flicker class after translations applied ──
|
||||
document.body.classList.remove("lang-pending");
|
||||
});
|
||||
|
||||
@@ -119,7 +119,7 @@ body {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.article-body>*+* {
|
||||
.article-body > * + * {
|
||||
margin-top: 1.1em;
|
||||
}
|
||||
|
||||
@@ -252,7 +252,8 @@ body {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
padding-block: 4px;
|
||||
padding-inline: 8px;
|
||||
background-color: #ffffff;
|
||||
color: #1f2937;
|
||||
border-radius: 8px;
|
||||
@@ -268,8 +269,8 @@ body {
|
||||
}
|
||||
|
||||
.map-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -299,10 +300,12 @@ body {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
background: linear-gradient(270deg,
|
||||
rgba(0, 0, 0, 0) 21.76%,
|
||||
rgba(0, 0, 0, 0.4) 63.31%,
|
||||
rgba(0, 49, 80, 0.72) 78.24%);
|
||||
background: linear-gradient(
|
||||
270deg,
|
||||
rgba(0, 0, 0, 0) 21.76%,
|
||||
rgba(0, 0, 0, 0.4) 63.31%,
|
||||
rgba(0, 49, 80, 0.72) 78.24%
|
||||
);
|
||||
}
|
||||
|
||||
/* Judul Hero */
|
||||
@@ -375,11 +378,11 @@ body {
|
||||
|
||||
.about-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
padding: 20px 12px;
|
||||
|
||||
--box-size: 300px;
|
||||
--box-size: 150px;
|
||||
|
||||
.about-image-wrapper {
|
||||
position: relative;
|
||||
@@ -414,17 +417,17 @@ body {
|
||||
|
||||
.about-title {
|
||||
color: var(--brand-navy);
|
||||
font-size: 1.25rem;
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.05em;
|
||||
}
|
||||
|
||||
.about-description {
|
||||
color: var(--text-soft);
|
||||
font-size: 0.75rem;
|
||||
font-size: 8px;
|
||||
font-weight: 500;
|
||||
|
||||
p+p {
|
||||
p + p {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
@@ -438,7 +441,7 @@ body {
|
||||
color: var(--brand-navy);
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
font-size: 0.75rem;
|
||||
font-size: 7px;
|
||||
|
||||
.link-icon {
|
||||
margin-bottom: 2px;
|
||||
@@ -456,9 +459,18 @@ body {
|
||||
--box-size: 240px;
|
||||
|
||||
.about-content {
|
||||
.about-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.about-description {
|
||||
font-size: 0.75rem;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.about-link {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,7 +557,7 @@ body {
|
||||
/* Layouting Grid Kartu */
|
||||
.facilities-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -598,16 +610,15 @@ body {
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 0.875rem;
|
||||
font-size: 10px;
|
||||
font-weight: 900;
|
||||
color: var(--brand-navy);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 0.75rem;
|
||||
font-size: 7px;
|
||||
color: var(--text-soft);
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,6 +641,14 @@ body {
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,7 +729,6 @@ body {
|
||||
/* Layout Wrapper */
|
||||
.campus-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -718,9 +736,8 @@ body {
|
||||
/* Styling Tombol Kategori (Kiri) */
|
||||
.category-list-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
|
||||
.category-btn {
|
||||
display: flex;
|
||||
@@ -781,8 +798,8 @@ body {
|
||||
}
|
||||
|
||||
.img-wrapper {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -806,15 +823,14 @@ body {
|
||||
h4 {
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.05em;
|
||||
font-size: 1rem;
|
||||
font-size: 10px;
|
||||
color: var(--brand-navy);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.75rem;
|
||||
font-size: 7px;
|
||||
color: var(--text-soft);
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -837,11 +853,26 @@ body {
|
||||
.category-list-container {
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
min-width: 25%;
|
||||
}
|
||||
|
||||
.category-list-container .category-btn {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.directory-list-container {
|
||||
.directory-card {
|
||||
.card-info {
|
||||
h4 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
@@ -953,10 +984,12 @@ body {
|
||||
.pontianak-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(0deg,
|
||||
rgba(0, 0, 0, 0.9) -14.48%,
|
||||
rgba(0, 0, 0, 0.4) 42.76%,
|
||||
rgba(0, 0, 0, 0) 100%);
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.9) -14.48%,
|
||||
rgba(0, 0, 0, 0.4) 42.76%,
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -1076,8 +1109,8 @@ body {
|
||||
/* Layout Grid Destinasi */
|
||||
.destination-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
gap: 24px;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -1087,17 +1120,24 @@ body {
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
border-radius: 8px;
|
||||
height: 450px;
|
||||
height: 300px;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
/* Gradasi Hitam di Atas Background */
|
||||
.card-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(0deg,
|
||||
rgba(0, 0, 0, 0.8) 0%,
|
||||
rgba(0, 0, 0, 0.2) 50%,
|
||||
rgba(0, 0, 0, 0) 100%);
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.8) 0%,
|
||||
rgba(0, 0, 0, 0.2) 50%,
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -1110,20 +1150,19 @@ body {
|
||||
height: 100%;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
padding-bottom: 32px;
|
||||
padding-bottom: 24px;
|
||||
max-width: 288px;
|
||||
|
||||
.card-name {
|
||||
color: #ffffff;
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.025em;
|
||||
font-size: 1rem;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
color: #d1d5db;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.25rem;
|
||||
font-size: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1133,11 +1172,20 @@ body {
|
||||
|
||||
.destination-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.destination-card .card-content .card-name {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.destination-card .card-content .card-desc {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.destination-card {
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
@@ -1163,6 +1211,10 @@ body {
|
||||
.destination-title {
|
||||
font-size: 2.25rem;
|
||||
}
|
||||
|
||||
.destination-card {
|
||||
height: 450px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
@@ -1234,7 +1286,7 @@ body {
|
||||
/* Grid Layout Kartu */
|
||||
.culinary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 24px;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1249,6 +1301,11 @@ body {
|
||||
border-radius: 12px;
|
||||
background-color: #ffffff;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.card-thumb {
|
||||
width: 100%;
|
||||
@@ -1270,11 +1327,10 @@ body {
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.75rem;
|
||||
font-size: 9px;
|
||||
font-weight: 500;
|
||||
color: var(--text-soft);
|
||||
line-height: 1.25rem;
|
||||
min-height: 60px;
|
||||
min-height: 26px;
|
||||
}
|
||||
|
||||
/* Modifikasi Khusus MapButton di dalam Kartu Kuliner */
|
||||
@@ -1332,7 +1388,6 @@ body {
|
||||
|
||||
.living-cost-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
/* Wrapper Gambar Kiri */
|
||||
@@ -1351,10 +1406,12 @@ body {
|
||||
.cost-banner-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(0deg,
|
||||
#012338 0%,
|
||||
rgba(1, 35, 56, 0.4) 55%,
|
||||
rgba(1, 35, 56, 0) 100%);
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
#012338 0%,
|
||||
rgba(1, 35, 56, 0.4) 55%,
|
||||
rgba(1, 35, 56, 0) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1371,14 +1428,13 @@ body {
|
||||
color: #ffffff;
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.05em;
|
||||
font-size: 1.25rem;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.cost-desc {
|
||||
color: #ffffff;
|
||||
font-size: 0.75rem;
|
||||
font-size: 8px;
|
||||
font-weight: 500;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.cost-note {
|
||||
@@ -1407,6 +1463,12 @@ body {
|
||||
color: #ffffff;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
width: 75px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
/* Target styling tag svg di dalam kartu */
|
||||
svg {
|
||||
@@ -1418,19 +1480,19 @@ body {
|
||||
.cost-type {
|
||||
text-transform: uppercase;
|
||||
color: #9ca3af;
|
||||
font-size: 0.75rem;
|
||||
font-size: 8px;
|
||||
margin: 4px 0 0 0;
|
||||
}
|
||||
|
||||
.cost-price {
|
||||
font-weight: 900;
|
||||
font-size: 0.875rem;
|
||||
font-size: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.cost-period {
|
||||
color: #9ca3af;
|
||||
font-size: 0.875rem;
|
||||
font-size: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -1440,17 +1502,21 @@ body {
|
||||
.cost-info-panel {
|
||||
padding: 32px;
|
||||
|
||||
.cost-desc {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.cost-item-card {
|
||||
.cost-type {
|
||||
font-size: 0.875rem;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.cost-price {
|
||||
font-size: 1rem;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.cost-period {
|
||||
font-size: 1rem;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1468,10 +1534,12 @@ body {
|
||||
|
||||
/* Perubahan Arah Gradasi: Mengabur ke arah kiri panel Navy */
|
||||
.cost-banner-overlay {
|
||||
background: linear-gradient(270deg,
|
||||
#012338 0%,
|
||||
rgba(0, 0, 0, 0.4) 65%,
|
||||
rgba(0, 0, 0, 0) 78%);
|
||||
background: linear-gradient(
|
||||
270deg,
|
||||
#012338 0%,
|
||||
rgba(0, 0, 0, 0.4) 65%,
|
||||
rgba(0, 0, 0, 0) 78%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1491,6 +1559,18 @@ body {
|
||||
flex: none;
|
||||
width: 130px;
|
||||
padding: 16px;
|
||||
|
||||
.cost-type {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.cost-price {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.cost-period {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1523,4 +1603,301 @@ body {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
ANTI-FLICKER — localization.js sets window.__currentLang
|
||||
synchronously; body stays hidden until DOMContentLoaded
|
||||
fires applyTranslations() and removes this class.
|
||||
============================================================ */
|
||||
body.lang-pending {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
MOVED FROM index.html <style> TAG
|
||||
(Originally inline; moved here so the browser can cache it)
|
||||
============================================================ */
|
||||
|
||||
/* ===== MOBILE NAV STABILITY FIX =====
|
||||
Prevents logo bar from collapsing/shifting on Android when
|
||||
address bar hides/shows and triggers viewport reflow.
|
||||
===================================== */
|
||||
#nav-wrapper {
|
||||
position: fixed !important;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 50;
|
||||
backface-visibility: hidden;
|
||||
-webkit-backface-visibility: hidden;
|
||||
will-change: box-shadow;
|
||||
}
|
||||
|
||||
#nav-wrapper > div:first-child {
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
#nav-wrapper > div:first-child {
|
||||
min-height: 0 !important;
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
|
||||
#pengumuman-bar {
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
#pengumuman-bar.is-stuck {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip Link Styling */
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
left: 0;
|
||||
background: #003150;
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
z-index: 100;
|
||||
transition: top 0.3s;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
/* Focus visible for better keyboard navigation */
|
||||
:focus-visible {
|
||||
outline: 3px solid #feb401;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Mega Menu Styles */
|
||||
.hero-pagination .swiper-pagination-bullet {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
display: inline-block;
|
||||
margin: 0 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.hero-pagination .swiper-pagination-bullet-active {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
width: 24px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mega-panel {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition:
|
||||
opacity 0.2s ease,
|
||||
visibility 0.2s ease;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.mega-panel.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Overlay transition */
|
||||
#mega-overlay {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition:
|
||||
opacity 0.2s ease,
|
||||
visibility 0.2s ease;
|
||||
}
|
||||
|
||||
#mega-overlay.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.mega-link-card {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
background: white;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.mega-link-card:hover {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.mega-link-icon {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-menu-btn.active {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #1e40af;
|
||||
}
|
||||
|
||||
.nav-menu-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
/* Mega menu positioning - no gap */
|
||||
#main-nav {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#mega-menu-container {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#mega-menu-container .mega-panel {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* Keep pointer bridge under nav to prevent hover loss */
|
||||
.nav-menu-item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-menu-item::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -12px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
/* ===== Luco Chatbot Animations ===== */
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
transform: translateX(100px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
transform: translateY(10px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lucoPulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
#luco-container {
|
||||
animation: slideInRight 0.5s ease-out forwards;
|
||||
}
|
||||
|
||||
#luco-bubble {
|
||||
animation: fadeInUp 0.3s ease-out forwards;
|
||||
width: min(18rem, calc(100vw - 2rem));
|
||||
}
|
||||
|
||||
#luco-btn {
|
||||
animation: lucoPulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
#luco-btn:hover {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
/* Typing indicator */
|
||||
.typing-indicator span {
|
||||
display: inline-block;
|
||||
animation: bounce 1.4s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.typing-indicator span:nth-child(1) {
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.typing-indicator span:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.typing-indicator span:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: translateY(-6px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Luco speech bubble arrow */
|
||||
#luco-bubble .bubble-arrow {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
bottom: -6px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: white;
|
||||
transform: rotate(45deg);
|
||||
border-right: 1px solid #e2e8f0;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||