<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Pigsty - 开箱即用的开源 PostgreSQL RDS">
    <title>Pigsty {{ version }}</title>
    <style>
        /* ========================================
           CSS Variables & Design Tokens
           ======================================== */
        :root {
            /* Colors */
            --color-primary: #0f4c81;
            --color-primary-dark: #0a3a64;
            --color-primary-light: #1a6bb5;
            --color-accent: #00b894;
            --color-accent-hover: #00a187;

            /* Neutral */
            --color-bg: #f8fafc;
            --color-surface: #ffffff;
            --color-border: #e2e8f0;
            --color-text: #1e293b;
            --color-text-secondary: #64748b;
            --color-text-muted: #94a3b8;

            /* Status Colors */
            --color-success: #10b981;
            --color-warning: #f59e0b;
            --color-info: #3b82f6;

            /* Typography */
            --font-sans: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            --font-mono: "SF Mono", "Fira Code", "Fira Mono", Menlo, Monaco, Consolas, monospace;

            /* Spacing */
            --space-xs: 0.25rem;
            --space-sm: 0.5rem;
            --space-md: 1rem;
            --space-lg: 1.5rem;
            --space-xl: 2rem;
            --space-2xl: 3rem;
            --space-3xl: 4rem;

            /* Border Radius */
            --radius-sm: 4px;
            --radius-md: 8px;
            --radius-lg: 12px;
            --radius-xl: 16px;

            /* Shadows */
            --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
            --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
            --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
            --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);

            /* Transitions */
            --transition-fast: 150ms ease;
            --transition-base: 200ms ease;
            --transition-slow: 300ms ease;
        }

        /* ========================================
           Reset & Base Styles
           ======================================== */
        *, *::before, *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        html {
            scroll-behavior: smooth;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
        }

        body {
            font-family: var(--font-sans);
            font-size: 16px;
            line-height: 1.6;
            color: var(--color-text);
            background-color: var(--color-bg);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
        }

        .main-content {
            flex: 1 0 auto;
            display: flex;
            flex-direction: column;
            background: linear-gradient(180deg, #f0f9ff 0%, #e8f4f8 100%);
        }

        .footer {
            flex-shrink: 0;
        }

        a {
            color: inherit;
            text-decoration: none;
            transition: color var(--transition-fast);
        }

        img {
            max-width: 100%;
            height: auto;
            display: block;
        }

        /* ========================================
           Layout
           ======================================== */
        .container {
            width: 100%;
            max-width: 1200px;
            margin: 0 auto;
            padding: 0 var(--space-lg);
        }

        /* ========================================
           Header & Navigation
           ======================================== */
        .header {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            z-index: 1000;
            background: rgba(255, 255, 255, 0.85);
            backdrop-filter: blur(12px);
            -webkit-backdrop-filter: blur(12px);
            border-bottom: 1px solid var(--color-border);
            transition: box-shadow var(--transition-base);
        }

        .header.scrolled {
            box-shadow: var(--shadow-md);
        }

        .header-inner {
            display: flex;
            align-items: center;
            justify-content: space-between;
            height: 64px;
        }

        .logo {
            display: flex;
            align-items: center;
            gap: var(--space-sm);
            font-weight: 700;
            font-size: 1.25rem;
            color: var(--color-primary);
            letter-spacing: 0.05em;
        }

        .logo-icon {
            width: 28px;
            height: 28px;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .logo-icon svg {
            width: 100%;
            height: 100%;
        }

        .nav {
            display: flex;
            align-items: center;
            gap: var(--space-xl);
        }

        .nav-link {
            font-size: 0.9375rem;
            font-weight: 500;
            color: var(--color-text-secondary);
            padding: var(--space-sm) 0;
            position: relative;
        }

        .nav-link:hover {
            color: var(--color-primary);
        }

        .nav-link::after {
            content: '';
            position: absolute;
            bottom: 0;
            left: 0;
            width: 0;
            height: 2px;
            background: var(--color-primary);
            transition: width var(--transition-base);
        }

        .nav-link:hover::after {
            width: 100%;
        }

        .nav-cta {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: var(--space-sm);
            padding: var(--space-sm) var(--space-lg);
            width: 150px;
            background: var(--color-primary);
            color: white;
            font-weight: 500;
            font-size: 0.9375rem;
            border-radius: var(--radius-md);
            transition: background var(--transition-fast), transform var(--transition-fast);
        }

        .nav-cta:hover {
            background: var(--color-primary-dark);
            transform: translateY(-1px);
        }

        .lang-switch {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 36px;
            height: 36px;
            color: var(--color-text-secondary);
            transition: all var(--transition-fast);
        }

        .lang-switch:hover {
            color: var(--color-primary);
        }

        .lang-switch svg {
            width: 20px;
            height: 20px;
        }

        .mobile-menu-btn {
            display: none;
            flex-direction: column;
            gap: 5px;
            padding: var(--space-sm);
            background: none;
            border: none;
            cursor: pointer;
        }

        .mobile-menu-btn span {
            display: block;
            width: 24px;
            height: 2px;
            background: var(--color-text);
            transition: var(--transition-fast);
        }

        /* ========================================
           Hero Section
           ======================================== */
        .hero {
            padding-top: calc(64px + var(--space-3xl));
            padding-bottom: var(--space-2xl);
            position: relative;
            overflow: hidden;
        }

        .hero::before {
            content: '';
            position: absolute;
            top: -50%;
            right: -10%;
            width: 600px;
            height: 600px;
            background: radial-gradient(circle, rgba(15, 76, 129, 0.05) 0%, transparent 70%);
            pointer-events: none;
        }

        .hero-content {
            text-align: center;
            position: relative;
            z-index: 1;
        }

        .hero-badge {
            display: inline-flex;
            align-items: center;
            gap: var(--space-sm);
            padding: var(--space-xs) var(--space-md);
            background: var(--color-surface);
            border: 1px solid var(--color-border);
            border-radius: 9999px;
            font-size: 0.875rem;
            color: var(--color-text-secondary);
            margin-bottom: var(--space-lg);
        }

        .hero-badge-version {
            display: inline-block;
            padding: 2px 8px;
            background: var(--color-primary);
            color: white;
            border-radius: 9999px;
            font-weight: 600;
            font-size: 0.75rem;
            text-decoration: none;
            transition: background var(--transition-fast);
        }

        .hero-badge-version:hover {
            background: var(--color-primary-dark);
        }

        .hero-title {
            font-size: clamp(2.5rem, 6vw, 4rem);
            font-weight: 800;
            line-height: 1.1;
            color: var(--color-text);
            margin-bottom: var(--space-md);
            letter-spacing: -0.02em;
        }

        .hero-title-accent {
            background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }

        .hero-title strong {
            color: var(--color-primary);
            font-weight: 800;
        }

        .hero-subtitle {
            font-size: 1.25rem;
            color: var(--color-text-secondary);
            max-width: 600px;
            margin: 0 auto var(--space-xl);
            line-height: 1.6;
        }

        .hero-acronym {
            font-size: 1rem;
            color: var(--color-text-muted);
            margin-bottom: var(--space-xl);
            letter-spacing: 0.05em;
        }

        .hero-acronym strong {
            color: var(--color-primary);
            font-weight: 700;
        }

        .hero-actions {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: var(--space-md);
            flex-wrap: wrap;
        }

        .btn {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: var(--space-sm);
            padding: 1rem 2rem;
            width: 180px;
            min-height: 56px;
            font-size: 1.2rem;
            font-weight: 600;
            border-radius: var(--radius-md);
            transition: all var(--transition-fast);
            cursor: pointer;
            border: none;
        }

        .btn-primary {
            background: var(--color-primary);
            color: white;
            box-shadow: 0 4px 14px 0 rgba(15, 76, 129, 0.3);
        }

        .btn-primary:hover {
            background: var(--color-primary-dark);
            transform: translateY(-2px);
            box-shadow: 0 6px 20px 0 rgba(15, 76, 129, 0.4);
        }

        .btn-secondary {
            background: var(--color-surface);
            color: var(--color-text);
            border: 1px solid var(--color-border);
        }

        .btn-secondary:hover {
            background: var(--color-bg);
            border-color: var(--color-primary);
            color: var(--color-primary);
        }

        .btn-icon {
            width: 20px;
            height: 20px;
        }

        /* ========================================
           Topology Matrix Section
           ======================================== */
        .topology-section {
            padding: var(--space-lg) 0 var(--space-xl);
            position: relative;
            overflow: hidden;
            flex: 1 0 auto;
        }

        .topology-section::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-image: radial-gradient(circle at 2px 2px, rgba(15, 76, 129, 0.03) 1px, transparent 0);
            background-size: 48px 48px;
            pointer-events: none;
        }

        .topology-toggle {
            display: block;
            margin: 0 auto var(--space-md);
            background: transparent;
            border: none;
            cursor: pointer;
            padding: var(--space-xs);
            opacity: 0.3;
            transition: opacity var(--transition-fast), transform var(--transition-fast);
        }

        .topology-toggle:hover {
            opacity: 0.6;
        }

        .topology-toggle-icon {
            width: 20px;
            height: 20px;
            color: var(--color-text-secondary);
            transition: transform var(--transition-base);
            transform: rotate(180deg);
        }

        .topology-toggle.collapsed .topology-toggle-icon {
            transform: rotate(0deg);
        }

        .topology-wrapper {
            position: relative;
            z-index: 1;
            overflow-x: auto;
            -webkit-overflow-scrolling: touch;
            max-height: 2000px;
            transition: max-height var(--transition-slow), opacity var(--transition-slow);
            opacity: 1;
        }

        .topology-wrapper.collapsed {
            max-height: 0;
            opacity: 0;
            overflow: hidden;
        }

        .topology-table {
            width: 100%;
            border-collapse: separate;
            border-spacing: 0;
            background: rgba(255, 255, 255, 0.6);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            border-radius: var(--radius-lg);
            overflow: hidden;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
        }

        .topology-table thead {
            background: linear-gradient(180deg, rgba(15, 76, 129, 0.1) 0%, rgba(15, 76, 129, 0.05) 100%);
        }

        .topology-table th {
            padding: var(--space-lg) var(--space-md);
            text-align: center;
            font-weight: 600;
            font-size: 0.875rem;
            color: var(--color-primary);
            text-transform: uppercase;
            letter-spacing: 0.05em;
            border-bottom: 2px solid rgba(15, 76, 129, 0.2);
            position: relative;
            cursor: pointer;
            transition: all var(--transition-base);
        }

        .topology-table th:hover {
            background: rgba(15, 76, 129, 0.15);
            color: var(--color-primary-dark);
        }

        .topology-table th::after {
            content: '';
            position: absolute;
            bottom: -2px;
            left: 0;
            right: 0;
            height: 2px;
            background: var(--color-primary);
            transform: scaleX(0);
            transition: transform var(--transition-base);
        }

        .topology-table th:hover::after {
            transform: scaleX(1);
        }

        .topology-table tbody tr {
            transition: all var(--transition-fast);
        }

        .topology-table tbody tr:hover {
            background: rgba(15, 76, 129, 0.08);
        }

        .topology-table td {
            padding: var(--space-md);
            text-align: center;
            border-bottom: 1px solid rgba(15, 76, 129, 0.08);
            position: relative;
            transition: all var(--transition-fast);
        }

        .topology-cell {
            display: inline-flex;
            align-items: center;
            gap: 0.25rem;
            padding: 0.25rem 0.75rem;
            background: rgba(241, 245, 249, 0.8);
            border: 1px solid rgba(226, 232, 240, 0.8);
            border-radius: 9999px;
            font-size: 0.8125rem;
            font-weight: 500;
            color: var(--color-text-secondary);
            transition: all var(--transition-fast);
            text-decoration: none;
            white-space: nowrap;
        }

        .topology-cell:hover {
            background: rgba(15, 76, 129, 0.1);
            border-color: var(--color-primary);
            color: var(--color-primary);
            transform: translateY(-1px) scale(1.02);
            box-shadow: 0 2px 8px rgba(15, 76, 129, 0.15);
        }

        /* Cluster-specific colors for PGSQL - PostgreSQL Blue tones */
        .topology-cell.pg-cluster {
            font-weight: 600;
            letter-spacing: 0.01em;
        }

        .topology-cell.pg-cluster:hover {
            transform: translateY(-1px) scale(1.05);
            box-shadow: 0 3px 12px rgba(0, 0, 0, 0.12);
        }

        .topology-cell.empty {
            background: transparent;
            border-color: transparent;
            color: var(--color-text-muted);
            cursor: default;
        }

        .topology-cell.empty:hover {
            transform: none;
            box-shadow: none;
        }

        .topology-ip {
            font-family: var(--font-mono);
            font-size: 0.875rem;
            font-weight: 500;
            color: var(--color-text);
        }

        /* Column highlight effect */
        .topology-table th.highlight,
        .topology-table td.highlight {
            background: rgba(15, 76, 129, 0.12);
        }

        /* Row highlight effect */
        .topology-table tr.highlight {
            background: rgba(15, 76, 129, 0.1);
        }

        /* ========================================
           Services Section
           ======================================== */
        .section {
            padding: var(--space-lg) 0;
        }

        .section-header {
            text-align: center;
            margin-bottom: var(--space-2xl);
        }

        .section-label {
            display: inline-block;
            padding: var(--space-xs) var(--space-md);
            background: rgba(15, 76, 129, 0.1);
            color: var(--color-primary);
            font-size: 0.75rem;
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: 0.1em;
            border-radius: var(--radius-sm);
            margin-bottom: var(--space-md);
        }

        .section-title {
            font-size: 2rem;
            font-weight: 700;
            color: var(--color-text);
            margin-bottom: var(--space-sm);
        }

        .section-desc {
            font-size: 1.0625rem;
            color: var(--color-text-secondary);
            max-width: 600px;
            margin: 0 auto;
        }

        /* Service Cards - Bootstrap-style responsive grid */
        .services-grid {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: var(--space-lg);
        }

        .service-card {
            background: var(--color-surface);
            border: 1px solid var(--color-border);
            border-radius: var(--radius-lg);
            padding: var(--space-lg) var(--space-xl);
            min-height: 100px;
            transition: all var(--transition-base);
            position: relative;
            overflow: hidden;
            display: flex;
            align-items: flex-start;
            gap: var(--space-md);
        }

        .service-card::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 3px;
            background: linear-gradient(90deg, var(--color-primary), var(--color-primary-light));
            opacity: 0;
            transition: opacity var(--transition-base);
        }

        .service-card:hover {
            border-color: var(--color-primary);
            box-shadow: var(--shadow-lg);
            transform: translateY(-4px);
        }

        .service-card:hover::before {
            opacity: 1;
        }

        .service-icon {
            width: 44px;
            height: 44px;
            background: linear-gradient(135deg, rgba(15, 76, 129, 0.1) 0%, rgba(15, 76, 129, 0.05) 100%);
            border-radius: var(--radius-md);
            display: flex;
            align-items: center;
            justify-content: center;
            flex-shrink: 0;
            color: var(--color-primary);
        }

        .service-icon svg {
            width: 22px;
            height: 22px;
            stroke: currentColor;
            stroke-width: 2;
            fill: none;
        }

        .service-content {
            flex: 1;
            min-width: 0;
        }

        .service-name {
            font-size: 1rem;
            font-weight: 600;
            color: var(--color-text);
            margin-bottom: var(--space-xs);
            line-height: 1.3;
        }

        .service-desc {
            font-size: 0.8125rem;
            color: var(--color-text-muted);
            line-height: 1.4;
        }

        .service-arrow {
            position: absolute;
            top: var(--space-lg);
            right: var(--space-lg);
            width: 20px;
            height: 20px;
            color: var(--color-text-muted);
            opacity: 0;
            transform: translateX(-8px);
            transition: all var(--transition-base);
        }

        .service-card:hover .service-arrow {
            opacity: 1;
            transform: translateX(0);
        }

        /* ========================================
           Footer
           ======================================== */
        .footer {
            background: #0a0e14;
            color: white;
            padding: 4rem 0 0;
            position: relative;
            border-top: 1px solid rgba(255, 255, 255, 0.06);
        }

        .footer::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-image:
                radial-gradient(circle at 1px 1px, rgba(255, 255, 255, 0.025) 1px, transparent 0);
            background-size: 40px 40px;
            pointer-events: none;
        }

        .footer-grid {
            display: grid;
            grid-template-columns: 1.8fr repeat(4, 1fr);
            gap: 3.5rem;
            padding-bottom: 3rem;
            position: relative;
        }

        .footer-brand {
            max-width: 360px;
            padding-right: 2rem;
        }

        .footer-logo {
            display: flex;
            align-items: center;
            gap: 0.875rem;
            margin-bottom: 1.5rem;
            transition: opacity 0.2s ease;
        }

        .footer-logo:hover {
            opacity: 0.8;
        }

        .footer-logo-icon {
            width: 36px;
            height: 36px;
            display: flex;
            align-items: center;
            justify-content: center;
            filter: drop-shadow(0 0 8px rgba(65, 158, 219, 0.3));
        }

        .footer-logo-icon svg {
            width: 100%;
            height: 100%;
        }

        .footer-logo-text {
            font-weight: 600;
            font-size: 1.375rem;
            letter-spacing: -0.01em;
            color: #fff;
        }

        .footer-desc {
            color: #9ca3af;
            font-size: 0.875rem;
            line-height: 1.7;
            margin-bottom: 0.75rem;
        }

        .footer-desc:last-child {
            margin-bottom: 0;
        }

        .footer-desc strong {
            color: #d1d5db;
            font-weight: 500;
        }

        .footer-slogan {
            margin-top: 1rem;
            color: #9ca3af;
            font-size: 0.875rem;
            font-weight: 400;
            line-height: 1.6;
            white-space: nowrap;
        }

        .footer-slogan .highlight-blue {
            color: #7ab7e0;
            font-weight: 500;
        }

        .footer-slogan .highlight-orange {
            color: #f4a261;
            font-weight: 500;
        }

        .footer-section-title {
            font-weight: 600;
            font-size: 0.75rem;
            text-transform: uppercase;
            letter-spacing: 0.08em;
            margin-bottom: 1.25rem;
            color: #6b7280;
        }

        .footer-links {
            list-style: none;
        }

        .footer-links li {
            margin-bottom: 0.75rem;
        }

        .footer-links a {
            color: #9ca3af;
            font-size: 0.9375rem;
            font-weight: 400;
            transition: color 0.15s ease;
            display: inline-block;
        }

        .footer-links a:hover {
            color: #fff;
        }

        .footer-bottom {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 1.75rem 0;
            border-top: 1px solid rgba(255, 255, 255, 0.06);
            font-size: 0.8125rem;
            color: #6b7280;
        }

        .footer-right {
            display: flex;
            align-items: center;
            gap: 1.5rem;
        }

        .footer-lang {
            display: flex;
            align-items: center;
            gap: 0.75rem;
        }

        .footer-lang a {
            font-size: 0.75rem;
            color: #6b7280;
            transition: color 0.2s ease;
        }

        .footer-lang a:hover {
            color: #419edb;
        }

        .footer-lang span {
            color: #6b7280;
            font-size: 0.75rem;
        }

        .footer-social {
            display: flex;
            gap: 0.5rem;
        }

        .footer-social a {
            width: 36px;
            height: 36px;
            background: rgba(255, 255, 255, 0.04);
            border-radius: 6px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #6b7280;
            transition: all 0.2s ease;
        }

        .footer-social a:hover {
            background: rgba(15, 76, 129, 0.15);
            color: #419edb;
        }

        /* ========================================
           Responsive Design - Bootstrap-style
           ======================================== */
        @media (max-width: 992px) {
            .services-grid {
                grid-template-columns: repeat(2, 1fr);
            }
        }

        @media (max-width: 768px) {
            .nav {
                display: none;
            }

            .mobile-menu-btn {
                display: flex;
            }

            .hero {
                padding-top: calc(64px + var(--space-2xl));
                padding-bottom: var(--space-xl);
            }

            .hero-title {
                font-size: 2rem;
            }

            .hero-subtitle {
                font-size: 1.0625rem;
            }

            .hero-actions {
                flex-direction: column;
            }

            .btn {
                width: 100%;
                max-width: 280px;
            }

            .topology-section {
                padding: var(--space-2xl) 0;
            }

            .topology-table th,
            .topology-table td {
                padding: var(--space-sm);
                font-size: 0.75rem;
            }

            .topology-cell {
                padding: 0.15rem 0.5rem;
                font-size: 0.6875rem;
            }

            .topology-ip {
                font-size: 0.75rem;
            }

            .section {
                padding: var(--space-xl) 0;
            }

            .section-title {
                font-size: 1.5rem;
            }

            .services-grid {
                grid-template-columns: repeat(2, 1fr);
                gap: var(--space-md);
            }

            .service-card {
                padding: var(--space-md);
            }

            .footer-grid {
                grid-template-columns: 1fr;
                gap: var(--space-2xl);
            }

            .footer-brand {
                max-width: 100%;
            }

            .footer-bottom {
                flex-direction: column;
                gap: var(--space-md);
                text-align: center;
            }
        }

        @media (max-width: 480px) {
            .container {
                padding: 0 var(--space-md);
            }

            .services-grid {
                grid-template-columns: 1fr;
            }

            .service-card {
                padding: var(--space-md) var(--space-lg);
            }
        }

        /* ========================================
           Animations
           ======================================== */
        @keyframes fadeInUp {
            from {
                opacity: 0;
                transform: translateY(20px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .animate-fade-in-up {
            animation: fadeInUp 0.6s ease forwards;
        }

        .animate-delay-1 { animation-delay: 0.1s; }
        .animate-delay-2 { animation-delay: 0.2s; }
        .animate-delay-3 { animation-delay: 0.3s; }

        /* ========================================
           Utility Classes
           ======================================== */
        .sr-only {
            position: absolute;
            width: 1px;
            height: 1px;
            padding: 0;
            margin: -1px;
            overflow: hidden;
            clip: rect(0, 0, 0, 0);
            white-space: nowrap;
            border: 0;
        }
    </style>
</head>
<body>
    <main class="main-content">
    <!-- Header -->
    <header class="header" id="header">
        <div class="container">
            <div class="header-inner">
                <a href="/" class="logo">
                    <span class="logo-icon">
                        <svg viewBox="24 15 25 27" xmlns="http://www.w3.org/2000/svg">
                            <g fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width=".75">
                                <path d="M31.73 28.43l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#bbb" fill-opacity=".95"/>
                                <path d="M31.73 36.74l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#de372c" fill-opacity=".85"/>
                                <path d="M38.93 32.61l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#424242" fill-opacity=".9"/>
                                <path d="M38.93 24.28l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#ffa269" fill-opacity=".9"/>
                                <path d="M31.73 20.15l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#419edb" fill-opacity=".9"/>
                                <path d="M24.52 24.28l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#2f6793" fill-opacity=".9"/>
                                <path d="M24.55 32.6l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#53ac79" fill-opacity=".9"/>
                            </g>
                        </svg>
                    </span>
                    <span>PIGSTY</span>
                </a>
                <nav class="nav">
                    <a href="https://pigsty.cc/docs" class="nav-link">文档</a>
                    <a href="https://github.com/pgsty/pigsty" class="nav-link" aria-label="GitHub">
                        <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" style="vertical-align: middle;">
                            <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
                        </svg>
                    </a>
                    <a href="/" class="lang-switch" title="Switch to English">
                        <svg viewBox="0 0 640 512">
                            <path fill="currentColor" d="M0 128C0 92.7 28.7 64 64 64H256h48 16H576c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H320 304 256 64c-35.3 0-64-28.7-64-64V128zm320 0V384H576V128H320zM178.3 175.9c-3.2-7.2-10.4-11.9-18.3-11.9s-15.1 4.7-18.3 11.9l-64 144c-4.5 10.1 .1 21.9 10.2 26.4s21.9-.1 26.4-10.2l8.9-20.1h73.6l8.9 20.1c4.5 10.1 16.3 14.6 26.4 10.2s14.6-16.3 10.2-26.4l-64-144zM160 233.2L179 276H141l19-42.8zM448 164c11 0 20 9 20 20v4h44 16c11 0 20 9 20 20s-9 20-20 20h-2l-1.6 4.5c-8.9 24.4-22.4 46.6-39.6 65.4c.9 .6 1.8 1.1 2.7 1.6l18.9 11.3c9.5 5.7 12.5 18 6.9 27.4s-18 12.5-27.4 6.9l-18.9-11.3c-4.5-2.7-8.8-5.5-13.1-8.5c-10.6 7.5-21.9 14-34 19.4l-3.6 1.6c-10.1 4.5-21.9-.1-26.4-10.2s.1-21.9 10.2-26.4l3.6-1.6c6.4-2.9 12.6-6.1 18.5-9.8l-12.2-12.2c-7.8-7.8-7.8-20.5 0-28.3s20.5-7.8 28.3 0l14.6 14.6 .5 .5c12.4-13.1 22.5-28.3 29.8-45H448 376c-11 0-20-9-20-20s9-20 20-20h52v-4c0-11 9-20 20-20z"/>
                        </svg>
                    </a>
                    <a href="/ui/d/pigsty" class="nav-cta">
                        <svg class="btn-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path d="M3 3v18h18"/>
                            <path d="M18.7 8l-5.1 5.2-2.8-2.7L7 14.3"/>
                        </svg>
                        监控面板
                    </a>
                </nav>
                <button class="mobile-menu-btn" aria-label="Toggle menu">
                    <span></span>
                    <span></span>
                    <span></span>
                </button>
            </div>
        </div>
    </header>

    <!-- Hero Section -->
    <section class="hero">
        <div class="container">
            <div class="hero-content">
                <div class="hero-badge animate-fade-in-up">
                    <span>开源 PG RDS / DBaaS</span>
                    <a href="https://vonng.com/pigsty/v4.0/" class="hero-badge-version" target="_blank" rel="noopener">{{ version }}</a>
                </div>
                <h1 class="hero-title animate-fade-in-up animate-delay-1">
                    <span class="hero-title-accent">PostgreSQL</span> <strong>i</strong>n <strong>G</strong>reat <strong>STY</strong>le
                </h1>
                <p class="hero-subtitle animate-fade-in-up animate-delay-2">
                    开箱即用、本地优先的开源 PostgreSQL 数据库发行版
                </p>
                <div class="hero-actions animate-fade-in-up animate-delay-3">
                    <a href="/ui/d/pigsty" class="btn btn-primary">
                        <svg class="btn-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <rect x="3" y="3" width="7" height="7"/>
                            <rect x="14" y="3" width="7" height="7"/>
                            <rect x="14" y="14" width="7" height="7"/>
                            <rect x="3" y="14" width="7" height="7"/>
                        </svg>
                        监控面板
                    </a>
                    <a href="https://pigsty.cc/docs" class="btn btn-secondary">
                        <svg class="btn-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
                        </svg>
                        参阅文档
                    </a>
                </div>
            </div>
        </div>
    </section>

    <!-- Services Section -->
    <section class="section">
        <div class="container">
            <div class="services-grid">
{% for svc in (infra_services | default([])) + (infra_extra_services | default([])) %}
                <a href="{{ svc.url }}" class="service-card" title="{{ svc.desc_cn | default(svc.desc) | default('') }}">
                    <div class="service-icon">
{% set icon = svc.icon | default('link') %}
{% if icon == 'metrics' %}
                        <svg viewBox="0 0 24 24"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>
{% elif icon == 'logs' %}
                        <svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
{% elif icon == 'traces' %}
                        <svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
{% elif icon == 'discovery' %}
                        <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
{% elif icon == 'target' %}
                        <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>
{% elif icon == 'alert' %}
                        <svg viewBox="0 0 24 24"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><circle cx="12" cy="17" r="0.5" fill="currentColor"/></svg>
{% elif icon == 'alertmgr' %}
                        <svg viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/><line x1="2" y1="2" x2="22" y2="22"/></svg>
{% elif icon == 'database' %}
                        <svg viewBox="0 0 24 24"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>
{% elif icon == 'file' %}
                        <svg viewBox="0 0 24 24"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
{% elif icon == 'activity' %}
                        <svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
{% elif icon == 'lock' %}
                        <svg viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
{% elif icon == 'package' %}
                        <svg viewBox="0 0 24 24"><path d="M16.5 9.4l-9-5.19M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>
{% elif icon == 'search' %}
                        <svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
{% elif icon == 'code' %}
                        <svg viewBox="0 0 24 24"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
{% elif icon == 'jupyter' %}
                        <svg viewBox="0 0 24 24"><circle cx="4.5" cy="4.5" r="2"/><circle cx="19.5" cy="19.5" r="2"/><circle cx="19.5" cy="4.5" r="1.5" fill="currentColor"/><ellipse cx="12" cy="12" rx="9" ry="5" transform="rotate(-30 12 12)"/></svg>
{% elif icon == 'claude' %}
                        <svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M12 2L9.5 9.5L2 12l7.5 2.5L12 22l2.5-7.5L22 12l-7.5-2.5L12 2z"/></svg>
{% else %}
                        <svg viewBox="0 0 24 24"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
{% endif %}
                    </div>
                    <div class="service-content">
                        <h3 class="service-name">{{ svc.name_cn | default(svc.name) | default('Service') }}</h3>
                        <p class="service-desc">{{ svc.desc_cn | default(svc.desc) | default('') }}</p>
                    </div>
                    <svg class="service-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M5 12h14M12 5l7 7-7 7"/>
                    </svg>
                </a>
{% endfor %}
            </div>
        </div>
    </section>

    <!-- Topology Matrix Section -->
    <section class="topology-section">
        <div class="container">
            <button class="topology-toggle" id="topologyToggle" aria-label="Toggle topology matrix">
                <svg class="topology-toggle-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <polyline points="6 9 12 15 18 9"></polyline>
                </svg>
            </button>
            <div class="topology-wrapper" id="topologyWrapper">
                <table class="topology-table" id="topologyTable">
                    <thead>
                        <tr>
                            <th data-col="node">
                                <a href="/ui/d/node-overview" style="color: inherit; text-decoration: none;">NODE</a>
                            </th>
                            <th data-col="pgsql">
                                <a href="/ui/d/pgsql-overview" style="color: inherit; text-decoration: none;">PGSQL</a>
                            </th>
                            <th data-col="infra">
                                <a href="/ui/d/infra-overview" style="color: inherit; text-decoration: none;">INFRA</a>
                            </th>
                            <th data-col="etcd">
                                <a href="/ui/d/etcd-overview" style="color: inherit; text-decoration: none;">ETCD</a>
                            </th>
                            <th data-col="minio">
                                <a href="/ui/d/minio-overview" style="color: inherit; text-decoration: none;">MINIO</a>
                            </th>
                        </tr>
                    </thead>
                    <tbody>
{% set nodes_list = [] %}
{% set pg_clusters = {} %}
{% set seen_hosts = {} %}
{% for host in groups['all'] | default([]) | sort %}
{%   if host not in seen_hosts %}
{%     set _ = seen_hosts.update({host: true}) %}
{%     set node_info = hostvars[host] | default({}) %}
{%     set pg_cluster = node_info.pg_cluster | default('') %}
{%     set pg_seq = node_info.pg_seq | default('') %}
{%     if pg_cluster and pg_cluster not in pg_clusters %}
{%       set _ = pg_clusters.update({pg_cluster: pg_clusters | length}) %}
{%     endif %}
{%     set _ = nodes_list.append({
          'ip': host,
          'nodename': node_info.nodename | default(host),
          'pg_cluster': pg_cluster,
          'pg_seq': pg_seq,
          'infra_seq': node_info.infra_seq | default(''),
          'etcd_seq': node_info.etcd_seq | default(''),
          'minio_cluster': node_info.minio_cluster | default(''),
          'minio_seq': node_info.minio_seq | default('')
        }) %}
{%   endif %}
{% endfor %}
{% for node in nodes_list | sort(attribute='ip') %}
                        <tr data-ip="{{ node.ip }}">
                            <td>
                                <a href="/ui/d/node-instance?var-id={{ node.ip }}" class="topology-cell">
                                    {{ node.ip }}
                                </a>
                            </td>
                            <td data-col="pgsql">
{%   if node.pg_cluster and node.pg_seq %}
{%     set cluster_id = pg_clusters.get(node.pg_cluster, 0) %}
                                <a href="/ui/d/pgsql-instance?var-ins={{ node.pg_cluster }}-{{ node.pg_seq }}" class="topology-cell pg-cluster" data-cluster="{{ node.pg_cluster }}" data-cluster-id="{{ cluster_id }}">
                                    {{ node.pg_cluster }}-{{ node.pg_seq }}
                                </a>
{%   else %}
                                <span class="topology-cell empty">-</span>
{%   endif %}
                            </td>
                            <td data-col="infra">
{%   if node.infra_seq %}
                                <a href="/ui/d/infra-overview" class="topology-cell">
                                    infra-{{ node.infra_seq }}
                                </a>
{%   else %}
                                <span class="topology-cell empty">-</span>
{%   endif %}
                            </td>
                            <td data-col="etcd">
{%   if node.etcd_seq %}
                                <a href="/ui/d/etcd-overview" class="topology-cell">
                                    etcd-{{ node.etcd_seq }}
                                </a>
{%   else %}
                                <span class="topology-cell empty">-</span>
{%   endif %}
                            </td>
                            <td data-col="minio">
{%   if node.minio_cluster and node.minio_seq %}
                                <a href="/ui/d/minio-instance?var-ins={{ node.minio_cluster }}-{{ node.minio_seq }}" class="topology-cell">
                                    {{ node.minio_cluster }}-{{ node.minio_seq }}
                                </a>
{%   else %}
                                <span class="topology-cell empty">-</span>
{%   endif %}
                            </td>
                        </tr>
{% endfor %}
                    </tbody>
                </table>
            </div>
        </div>
    </section>
    </main>

    <!-- Footer -->
    <footer class="footer">
        <div class="container">
            <div class="footer-grid">
                <div class="footer-brand">
                    <a href="https://pigsty.cc" class="footer-logo">
                        <span class="footer-logo-icon">
                            <svg viewBox="24 15 25 27" xmlns="http://www.w3.org/2000/svg">
                                <g fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width=".75">
                                    <path d="M31.73 28.43l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#bbb" fill-opacity=".95"/>
                                    <path d="M31.73 36.74l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#de372c" fill-opacity=".85"/>
                                    <path d="M38.93 32.61l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#424242" fill-opacity=".9"/>
                                    <path d="M38.93 24.28l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#ffa269" fill-opacity=".9"/>
                                    <path d="M31.73 20.15l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#419edb" fill-opacity=".9"/>
                                    <path d="M24.52 24.28l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#2f6793" fill-opacity=".9"/>
                                    <path d="M24.55 32.6l2.4-4.15h4.8l2.4 4.15-2.4 4.15h-4.8z" fill="#53ac79" fill-opacity=".9"/>
                                </g>
                            </svg>
                        </span>
                        <span class="footer-logo-text">PIGSTY</span>
                    </a>
                    <p class="footer-desc">
                        <strong>P</strong>ostgres <strong>I</strong>n <strong>G</strong>reat <strong>STY</strong>le
                    </p>
                    <p class="footer-desc">
                        开箱即用，本地优先<br> 开源免费的 PostgreSQL 发行版
                    </p>
                    <p class="footer-slogan">
                        助您用 <span class="highlight-blue">好数据库</span>，<span class="highlight-orange">用好</span> 数据库
                    </p>
                </div>
                <div class="footer-section">
                    <h4 class="footer-section-title">链接</h4>
                    <ul class="footer-links">
                        <li><a href="/ui/d/pgsql-overview">PGSQL</a></li>
                        <li><a href="/ui/d/infra-overview">INFRA</a></li>
                        <li><a href="/ui/d/nginx-instance">NGINX</a></li>
                        <li><a href="/alertmgr/#/alerts">ALERT</a></li>
                    </ul>
                </div>
                <div class="footer-section">
                    <h4 class="footer-section-title">监控</h4>
                    <ul class="footer-links">
                        <li><a href="/vmetrics">VictoriaMetrics</a></li>
                        <li><a href="/vlogs">VictoriaLogs</a></li>
                        <li><a href="/vtraces">VictoriaTraces</a></li>
                        <li><a href="/vmalert">VictoriaAlert</a></li>
                    </ul>
                </div>
                <div class="footer-section">
                    <h4 class="footer-section-title">资源</h4>
                    <ul class="footer-links">
                        <li><a href="https://pigsty.io">pigsty.io</a></li>
                        <li><a href="https://pigsty.cc">pigsty.cc</a></li>
                        <li><a href="https://pigsty.cc/blog">技术博客</a></li>
                        <li><a href="https://pigsty.cc/price">服务订阅</a></li>
                    </ul>
                </div>
                <div class="footer-section">
                    <h4 class="footer-section-title">社区</h4>
                    <ul class="footer-links">
                        <li><a href="https://github.com/pgsty/pigsty">GitHub</a></li>
                        <li><a href="https://github.com/pgsty/pigsty/discussions">在线论坛</a></li>
                        <li><a href="https://github.com/pgsty/pigsty/issues">问题反馈</a></li>
                        <li><a href="https://vonng.com/">关于作者</a></li>
                    </ul>
                </div>
            </div>
            <div class="footer-bottom">
                <span>Pigsty {{ version }} &copy; 2018-2026 冯若航</span>
                <div class="footer-right">
                    <div class="footer-lang">
                        <a href="/">English</a>
                        <span>/</span>
                        <a href="/zh">中文</a>
                    </div>
                    <div class="footer-social">
                        <a href="https://github.com/pgsty/pigsty" aria-label="GitHub">
                            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
                                <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
                            </svg>
                        </a>
                    </div>
                </div>
            </div>
        </div>
    </footer>

    <!-- Scripts -->
    <script>
        // Header scroll effect
        (function() {
            const header = document.getElementById('header');
            window.addEventListener('scroll', function() {
                if (window.pageYOffset > 10) {
                    header.classList.add('scrolled');
                } else {
                    header.classList.remove('scrolled');
                }
            }, { passive: true });
        })();

        // Intersection Observer for animations
        (function() {
            const observerOptions = {
                threshold: 0.1,
                rootMargin: '0px 0px -50px 0px'
            };

            const observer = new IntersectionObserver(function(entries) {
                entries.forEach(function(entry) {
                    if (entry.isIntersecting) {
                        entry.target.style.opacity = '1';
                        entry.target.style.transform = 'translateY(0)';
                    }
                });
            }, observerOptions);

            document.querySelectorAll('.service-card').forEach(function(el) {
                el.style.opacity = '0';
                el.style.transform = 'translateY(20px)';
                el.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
                observer.observe(el);
            });
        })();

        // Topology Matrix Interactive Highlight
        (function() {
            const table = document.getElementById('topologyTable');
            if (!table) return;

            const thead = table.querySelector('thead');
            const tbody = table.querySelector('tbody');

            // Generate PostgreSQL-themed blue colors for each cluster
            function hashStringToColor(str) {
                if (!str) return null;

                // Simple hash function
                let hash = 0;
                for (let i = 0; i < str.length; i++) {
                    hash = str.charCodeAt(i) + ((hash << 5) - hash);
                    hash = hash & hash; // Convert to 32bit integer
                }

                // PostgreSQL Slonik blue color palette
                // Base: #336791 (PostgreSQL official blue)
                // Generate subtle variations in the blue spectrum
                const baseHue = 210; // PostgreSQL blue
                const hueVariation = (Math.abs(hash % 30)) - 15; // -15 to +15
                const hue = baseHue + hueVariation; // 195-225 (cyan to blue range)

                const saturation = 35 + (Math.abs(hash >> 8) % 20); // 35-55% (low saturation, muted)
                const lightness = 86 + (Math.abs(hash >> 16) % 7);  // 86-93% (very light, pastel)

                return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
            }

            // Apply cluster colors to PGSQL cells
            const pgCells = tbody.querySelectorAll('.pg-cluster');
            const clusterColors = {};

            pgCells.forEach(function(cell) {
                const cluster = cell.getAttribute('data-cluster');
                if (cluster) {
                    if (!clusterColors[cluster]) {
                        clusterColors[cluster] = hashStringToColor(cluster);
                    }
                    const color = clusterColors[cluster];
                    if (color) {
                        cell.style.backgroundColor = color;

                        // Extract HSL values for border color
                        const hsl = color.match(/hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)/);
                        if (hsl) {
                            const h = hsl[1];
                            const s = Math.min(parseInt(hsl[2]) + 15, 70); // Slightly more saturated border
                            const l = Math.max(parseInt(hsl[3]) - 20, 60); // Darker border
                            cell.style.borderColor = `hsl(${h}, ${s}%, ${l}%)`;

                            // Text color - darker blue for contrast
                            cell.style.color = `hsl(${h}, ${Math.min(s + 20, 80)}%, 35%)`;
                        }
                    }
                }
            });

            // Column hover effect
            const thElements = thead.querySelectorAll('th[data-col]');
            thElements.forEach(function(th) {
                const colName = th.getAttribute('data-col');

                th.addEventListener('mouseenter', function() {
                    // Highlight all cells in this column
                    const cells = tbody.querySelectorAll('td[data-col="' + colName + '"]');
                    cells.forEach(function(cell) {
                        cell.classList.add('highlight');
                    });
                    th.classList.add('highlight');
                });

                th.addEventListener('mouseleave', function() {
                    // Remove highlight from all cells in this column
                    const cells = tbody.querySelectorAll('td[data-col="' + colName + '"]');
                    cells.forEach(function(cell) {
                        cell.classList.remove('highlight');
                    });
                    th.classList.remove('highlight');
                });
            });

            // Row hover effect
            const rows = tbody.querySelectorAll('tr');
            rows.forEach(function(row) {
                row.addEventListener('mouseenter', function() {
                    row.classList.add('highlight');
                });

                row.addEventListener('mouseleave', function() {
                    row.classList.remove('highlight');
                });
            });

            // Cell hover effect - highlight both row and column
            const cells = tbody.querySelectorAll('td[data-col]');
            cells.forEach(function(cell) {
                const colName = cell.getAttribute('data-col');

                cell.addEventListener('mouseenter', function() {
                    const row = cell.closest('tr');

                    // Highlight the column header
                    const th = thead.querySelector('th[data-col="' + colName + '"]');
                    if (th) {
                        th.classList.add('highlight');
                    }

                    // Highlight all cells in this column
                    const colCells = tbody.querySelectorAll('td[data-col="' + colName + '"]');
                    colCells.forEach(function(c) {
                        c.classList.add('highlight');
                    });

                    // Highlight the entire row
                    row.classList.add('highlight');
                });

                cell.addEventListener('mouseleave', function() {
                    const row = cell.closest('tr');

                    // Remove highlight from column header
                    const th = thead.querySelector('th[data-col="' + colName + '"]');
                    if (th) {
                        th.classList.remove('highlight');
                    }

                    // Remove highlight from all cells in this column
                    const colCells = tbody.querySelectorAll('td[data-col="' + colName + '"]');
                    colCells.forEach(function(c) {
                        c.classList.remove('highlight');
                    });

                    // Remove highlight from the row
                    row.classList.remove('highlight');
                });
            });
        })();

        // Topology Matrix Toggle
        (function() {
            const toggleBtn = document.getElementById('topologyToggle');
            const wrapper = document.getElementById('topologyWrapper');

            if (!toggleBtn || !wrapper) return;

            toggleBtn.addEventListener('click', function() {
                toggleBtn.classList.toggle('collapsed');
                wrapper.classList.toggle('collapsed');
            });
        })();
    </script>
</body>
</html>
