diff --git a/about.php b/about.php new file mode 100644 index 0000000..bc7c982 --- /dev/null +++ b/about.php @@ -0,0 +1,118 @@ + 'Institutional trust platform', 'summary' => 'A calm, premium first impression that feels credible to partners, institutions, and high-value guests.'], + ['title' => 'Curated service commerce', 'summary' => 'Packages are presented with editorial clarity and routed into a structured consultation path.'], + ['title' => 'Partnership integration system', 'summary' => 'Multiple partner categories can be invited into one operating ecosystem without clutter.'], + ['title' => 'Publication authority layer', 'summary' => 'Insight and updates reinforce operational maturity, not just marketing claims.'], + ['title' => 'Governance foundation', 'summary' => 'The site prepares future reporting, admin processes, and ecosystem accountability.'], +]; + +$operatingModel = [ + ['title' => 'Discovery', 'summary' => 'Brief collection, stakeholder mapping, and intent clarification.'], + ['title' => 'Curation', 'summary' => 'Program architecture, partner matching, and experience design.'], + ['title' => 'Delivery', 'summary' => 'On-ground hospitality, logistics, facilitation, and documentation.'], + ['title' => 'Governance', 'summary' => 'Reporting discipline, partner oversight, and institutional trust signals.'], +]; + +require __DIR__ . '/includes/header.php'; +?> +
+
+
+
+
+ About GBP +

An integrated ecosystem designed for trust, transformation, and operational clarity.

+

PT. Genitri Bregas Persada positions Genitri Ecco Wellness as a premium nature-based experience ecosystem—bridging eco wellness, hospitality, retreat design, MICE support, and partnership collaboration.

+
+
+
+ Tagline +

Nature-Based Experiential Tourism & Eco Wellness Services

+

The experience direction is intentionally calm, institutional, and editorial rather than marketplace-like or generic tourism-led.

+
+
+
+
+
+
+ +
+
+
+ Strategic positioning +

What makes the ecosystem distinct.

+

The site is designed to show how multiple disciplines can feel integrated without looking crowded.

+
+
+ +
+

+

+
+ +
+
+
+ +
+
+
+
+
+ Core objectives +

A website built to do real institutional work.

+
    + +
  • + +
+
+
+
+
+ Operating model +

A lightweight structure that can scale.

+
+ +
+ +

+
+ +
+
+
+
+
+
+ +
+
+
+
+ Values +

A premium tone supported by ethical grounding.

+
+
+ +
+

+

+
+ +
+
+
+
+ diff --git a/admin.php b/admin.php new file mode 100644 index 0000000..3c9e199 --- /dev/null +++ b/admin.php @@ -0,0 +1,169 @@ + 0)) { + $selectedInquiry = find_inquiry($selectedId, $selectedReference !== '' ? $selectedReference : null); +} + +if (!$selectedInquiry && $inquiries !== []) { + $selectedInquiry = $inquiries[0]; +} + +require __DIR__ . '/includes/header.php'; +?> +
+
+
+
+
+ Preview desk +

Qualified inquiry queue and detail view.

+

This admin-style page gives the first delivery an internal operational surface: a list of saved inquiries and a focused detail panel for review.

+
+
+
+ Queue snapshot +
+
+ Entries + +
+
+ Storage mode + +
+
+
+
+
+
+ + +
+ Preview mode: the queue is reading from the current browser session because the database connection is rejecting access right now. +
+ + +
+
+
+
+ Inquiry list +

Current queue

+
+ New inquiry +
+ +
+ Empty state +

No inquiries yet

+

Submit the first strategic brief to see the list and detail layout in action.

+ Open inquiry desk +
+ +
+ + + + + + + + + + + + + + + + + + + +
ReferenceContactInterestStatus
+ +
+
+ +
+
+
+
+
+
+ +
+ + +
+
+
+ diff --git a/assets/css/custom.css b/assets/css/custom.css index 789132e..4f7eed4 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,403 +1,770 @@ +:root { + --bg: #f4f6f8; + --surface: #ffffff; + --surface-muted: #f8fafb; + --surface-dark: #1c2733; + --text: #1c2733; + --muted: #5f6b76; + --border: #dde4ec; + --border-strong: #c7d0db; + --primary: #0066cc; + --primary-soft: #e8f1fb; + --accent: #00acc1; + --accent-soft: #e7f7fa; + --gold: #bfa100; + --gold-soft: #faf7e8; + --success-soft: #edf8f2; + --shadow-sm: 0 10px 24px rgba(28, 39, 51, 0.04); + --shadow-lg: 0 24px 60px rgba(28, 39, 51, 0.08); + --radius-sm: 10px; + --radius-md: 14px; + --radius-lg: 18px; + --spacing-xs: 0.375rem; + --spacing-sm: 0.75rem; + --spacing-md: 1rem; + --spacing-lg: 1.5rem; + --spacing-xl: 2rem; + --spacing-2xl: 3.5rem; +} + +html { + scroll-behavior: smooth; +} + body { - background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); - background-size: 400% 400%; - animation: gradient 15s ease infinite; - color: #212529; - font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; - font-size: 14px; margin: 0; - min-height: 100vh; + background: var(--bg); + color: var(--text); + font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + line-height: 1.6; + -webkit-font-smoothing: antialiased; } -.main-wrapper { - display: flex; - align-items: center; - justify-content: center; - min-height: 100vh; - width: 100%; - padding: 20px; - box-sizing: border-box; - position: relative; - z-index: 1; +body.modal-open { + padding-right: 0 !important; } -@keyframes gradient { - 0% { - background-position: 0% 50%; - } - 50% { - background-position: 100% 50%; - } - 100% { - background-position: 0% 50%; - } -} - -.chat-container { - width: 100%; - max-width: 600px; - background: rgba(255, 255, 255, 0.85); - border: 1px solid rgba(255, 255, 255, 0.3); - border-radius: 20px; - display: flex; - flex-direction: column; - height: 85vh; - box-shadow: 0 20px 40px rgba(0,0,0,0.2); - backdrop-filter: blur(15px); - -webkit-backdrop-filter: blur(15px); - overflow: hidden; -} - -.chat-header { - padding: 1.5rem; - border-bottom: 1px solid rgba(0, 0, 0, 0.05); - background: rgba(255, 255, 255, 0.5); - font-weight: 700; - font-size: 1.1rem; - display: flex; - justify-content: space-between; - align-items: center; -} - -.chat-messages { - flex: 1; - overflow-y: auto; - padding: 1.5rem; - display: flex; - flex-direction: column; - gap: 1.25rem; -} - -/* Custom Scrollbar */ -::-webkit-scrollbar { - width: 6px; -} - -::-webkit-scrollbar-track { - background: transparent; -} - -::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.3); - border-radius: 10px; -} - -::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.5); -} - -.message { - max-width: 85%; - padding: 0.85rem 1.1rem; - border-radius: 16px; - line-height: 1.5; - font-size: 0.95rem; - box-shadow: 0 4px 15px rgba(0,0,0,0.05); - animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); -} - -@keyframes fadeIn { - from { opacity: 0; transform: translateY(20px) scale(0.95); } - to { opacity: 1; transform: translateY(0) scale(1); } -} - -.message.visitor { - align-self: flex-end; - background: linear-gradient(135deg, #212529 0%, #343a40 100%); - color: #fff; - border-bottom-right-radius: 4px; -} - -.message.bot { - align-self: flex-start; - background: #ffffff; - color: #212529; - border-bottom-left-radius: 4px; -} - -.chat-input-area { - padding: 1.25rem; - background: rgba(255, 255, 255, 0.5); - border-top: 1px solid rgba(0, 0, 0, 0.05); -} - -.chat-input-area form { - display: flex; - gap: 0.75rem; -} - -.chat-input-area input { - flex: 1; - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 12px; - padding: 0.75rem 1rem; - outline: none; - background: rgba(255, 255, 255, 0.9); - transition: all 0.3s ease; -} - -.chat-input-area input:focus { - border-color: #23a6d5; - box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2); -} - -.chat-input-area button { - background: #212529; - color: #fff; - border: none; - padding: 0.75rem 1.5rem; - border-radius: 12px; - cursor: pointer; - font-weight: 600; - transition: all 0.3s ease; -} - -.chat-input-area button:hover { - background: #000; - transform: translateY(-2px); - box-shadow: 0 5px 15px rgba(0,0,0,0.2); -} - -/* Background Animations */ -.bg-animations { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 0; - overflow: hidden; - pointer-events: none; -} - -.blob { - position: absolute; - width: 500px; - height: 500px; - background: rgba(255, 255, 255, 0.2); - border-radius: 50%; - filter: blur(80px); - animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1); -} - -.blob-1 { - top: -10%; - left: -10%; - background: rgba(238, 119, 82, 0.4); -} - -.blob-2 { - bottom: -10%; - right: -10%; - background: rgba(35, 166, 213, 0.4); - animation-delay: -7s; - width: 600px; - height: 600px; -} - -.blob-3 { - top: 40%; - left: 30%; - background: rgba(231, 60, 126, 0.3); - animation-delay: -14s; - width: 450px; - height: 450px; -} - -@keyframes move { - 0% { transform: translate(0, 0) rotate(0deg) scale(1); } - 33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); } - 66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); } - 100% { transform: translate(0, 0) rotate(360deg) scale(1); } -} - -.header-link { - font-size: 14px; - color: #fff; - text-decoration: none; - background: rgba(0, 0, 0, 0.2); - padding: 0.5rem 1rem; - border-radius: 8px; - transition: all 0.3s ease; -} - -.header-link:hover { - background: rgba(0, 0, 0, 0.4); +a { + color: inherit; text-decoration: none; } -/* Admin Styles */ -.admin-container { - max-width: 900px; - margin: 3rem auto; - padding: 2.5rem; - background: rgba(255, 255, 255, 0.85); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border-radius: 24px; - box-shadow: 0 20px 50px rgba(0,0,0,0.15); - border: 1px solid rgba(255, 255, 255, 0.4); - position: relative; - z-index: 1; +a:hover { + color: inherit; } -.admin-container h1 { - margin-top: 0; - color: #212529; - font-weight: 800; -} - -.table { - width: 100%; - border-collapse: separate; - border-spacing: 0 8px; - margin-top: 1.5rem; -} - -.table th { - background: transparent; - border: none; - padding: 1rem; - color: #6c757d; - font-weight: 600; - text-transform: uppercase; - font-size: 0.75rem; - letter-spacing: 1px; -} - -.table td { - background: #fff; - padding: 1rem; - border: none; -} - -.table tr td:first-child { border-radius: 12px 0 0 12px; } -.table tr td:last-child { border-radius: 0 12px 12px 0; } - -.form-group { - margin-bottom: 1.25rem; -} - -.form-group label { +img { + max-width: 100%; display: block; - margin-bottom: 0.5rem; - font-weight: 600; +} + +.skip-link { + position: absolute; + left: 1rem; + top: -3rem; + z-index: 1100; + background: var(--surface-dark); + color: #fff; + padding: 0.625rem 0.9rem; + border-radius: var(--radius-sm); + transition: top 0.2s ease; +} + +.skip-link:focus { + top: 1rem; +} + +.page-main { + padding-top: 5.5rem; +} + +.page-section { + padding: 3.75rem 0; +} + +.section-head { + max-width: 760px; + margin-bottom: 1.5rem; +} + +.eyebrow, +.panel-label, +.section-kicker, +.footer-kicker { + display: inline-flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.14em; + font-size: 0.72rem; + font-weight: 700; + color: var(--primary); +} + +.section-kicker--muted { + color: var(--muted); +} + +.display-title, +.section-title, +.footer-title, +.page-title { + letter-spacing: -0.03em; + line-height: 1.06; +} + +.display-title { + font-size: clamp(2.2rem, 4vw, 3.6rem); + margin-bottom: 1rem; +} + +.page-title { + font-size: clamp(2rem, 3vw, 3rem); + margin-bottom: 0.9rem; +} + +.section-title, +.footer-title { + font-size: clamp(1.6rem, 2.2vw, 2.4rem); + margin-bottom: 0.8rem; +} + +.lead-copy, +.section-copy, +.footer-copy { + font-size: 1rem; + color: var(--muted); + max-width: 760px; +} + +.hero-shell, +.surface-card, +.table-shell, +.empty-panel, +.form-panel, +.contact-panel, +.dark-panel { + border: 1px solid var(--border); + border-radius: var(--radius-lg); + background: var(--surface); + box-shadow: var(--shadow-sm); +} + +.hero-shell { + padding: clamp(1.5rem, 3vw, 3rem); + box-shadow: var(--shadow-lg); +} + +.hero-panel { + padding: 1.4rem; + background: var(--surface-muted); +} + +.surface-card { + height: 100%; + padding: 1.35rem; +} + +.surface-card--dark, +.dark-panel { + background: var(--surface-dark); + border-color: rgba(255, 255, 255, 0.08); + color: rgba(255, 255, 255, 0.92); +} + +.surface-card--dark .section-kicker, +.surface-card--dark .meta-label, +.surface-card--dark .card-copy, +.dark-panel .section-copy, +.dark-panel .meta-inline, +.dark-panel .card-copy { + color: rgba(255, 255, 255, 0.74); +} + +.metric-strip { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.85rem; +} + +.metric-card { + padding: 1rem; + border: 1px solid var(--border); + border-radius: var(--radius-md); + background: var(--surface-muted); +} + +.metric-value { + display: block; + font-size: 1.1rem; + font-weight: 700; + letter-spacing: -0.03em; +} + +.metric-label { + display: block; + margin-top: 0.2rem; + color: var(--muted); font-size: 0.9rem; } -.form-control { - width: 100%; - padding: 0.75rem 1rem; - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 12px; - background: #fff; - transition: all 0.3s ease; - box-sizing: border-box; +.stack-list { + display: grid; + gap: 0.75rem; } -.form-control:focus { - outline: none; - border-color: #23a6d5; - box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1); -} - -.header-container { +.stack-item { display: flex; justify-content: space-between; - align-items: center; + gap: 1rem; + align-items: flex-start; + padding: 0.9rem 1rem; + border: 1px solid var(--border); + border-radius: var(--radius-md); + background: var(--surface); } -.header-links { - display: flex; +.stack-item span, +.card-title, +.list-title, +.queue-link, +.detail-title { + font-weight: 700; + letter-spacing: -0.02em; +} + +.stack-item small, +.card-copy, +.meta-inline, +.meta-label, +.note-muted, +.empty-copy, +.form-hint, +.helper-text, +.footer-note { + color: var(--muted); +} + +.divider { + height: 1px; + background: var(--border); + margin: 1rem 0; +} + +.signal-chip, +.status-badge, +.filter-chip, +.info-pill { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.3rem 0.72rem; + border-radius: 999px; + border: 1px solid var(--border); + background: var(--surface-muted); + color: var(--muted); + font-size: 0.78rem; + font-weight: 600; +} + +.signal-chip--primary, +.status-badge--new { + color: var(--primary); + background: var(--primary-soft); + border-color: rgba(0, 102, 204, 0.14); +} + +.signal-chip--accent { + color: #0d7f8c; + background: var(--accent-soft); + border-color: rgba(0, 172, 193, 0.18); +} + +.signal-chip--gold { + color: #8b7416; + background: var(--gold-soft); + border-color: rgba(191, 161, 0, 0.18); +} + +.btn { + border-radius: 999px; + font-weight: 600; + letter-spacing: -0.01em; + padding: 0.7rem 1.1rem; +} + +.btn-brand { + background: var(--primary); + border-color: var(--primary); + color: #fff; +} + +.btn-brand:hover, +.btn-brand:focus { + background: #0a57a8; + border-color: #0a57a8; + color: #fff; +} + +.btn-outline-brand { + background: transparent; + border-color: var(--border-strong); + color: var(--text); +} + +.btn-outline-brand:hover, +.btn-outline-brand:focus { + border-color: var(--primary); + color: var(--primary); + background: var(--surface); +} + +.btn-subtle { + background: var(--surface-muted); + border-color: var(--border); + color: var(--text); +} + +.btn-subtle:hover, +.btn-subtle:focus { + border-color: var(--border-strong); + background: var(--surface); +} + +.button-link { + display: inline-flex; + align-items: center; + gap: 0.4rem; + color: var(--primary); + font-weight: 600; +} + +.button-link::after { + content: '→'; + font-size: 0.95rem; +} + +.page-intro { + margin-bottom: 1.75rem; +} + +.feature-grid, +.capability-grid, +.preview-grid, +.partner-grid, +.stats-grid, +.timeline-grid, +.contact-grid { + display: grid; gap: 1rem; } -.admin-card { - background: rgba(255, 255, 255, 0.6); - padding: 2rem; - border-radius: 20px; - border: 1px solid rgba(255, 255, 255, 0.5); - margin-bottom: 2.5rem; - box-shadow: 0 10px 30px rgba(0,0,0,0.05); +.feature-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); } -.admin-card h3 { - margin-top: 0; - margin-bottom: 1.5rem; - font-weight: 700; +.capability-grid { + grid-template-columns: repeat(4, minmax(0, 1fr)); } -.btn-delete { - background: #dc3545; - color: white; - border: none; - padding: 0.25rem 0.5rem; - border-radius: 4px; - cursor: pointer; +.preview-grid, +.partner-grid, +.stats-grid, +.contact-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); } -.btn-add { - background: #212529; - color: white; - border: none; - padding: 0.5rem 1rem; - border-radius: 4px; - cursor: pointer; - margin-top: 1rem; +.timeline-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); } -.btn-save { - background: #0088cc; - color: white; - border: none; - padding: 0.8rem 1.5rem; - border-radius: 12px; - cursor: pointer; +.card-title, +.list-title, +.detail-title { + font-size: 1.05rem; + margin-bottom: 0.5rem; +} + +.card-copy, +.list-copy, +.detail-copy, +.queue-summary { + font-size: 0.94rem; +} + +.service-card { + display: flex; + flex-direction: column; + gap: 1rem; + min-height: 100%; +} + +.service-points, +.footer-links, +.value-list, +.detail-list, +.objective-list, +.tone-list { + list-style: none; + margin: 0; + padding: 0; +} + +.service-points li, +.value-list li, +.detail-list li, +.objective-list li, +.tone-list li { + position: relative; + padding-left: 1rem; + margin-bottom: 0.55rem; + color: var(--muted); +} + +.service-points li::before, +.value-list li::before, +.detail-list li::before, +.objective-list li::before, +.tone-list li::before { + content: ''; + position: absolute; + left: 0; + top: 0.62rem; + width: 0.38rem; + height: 0.38rem; + border-radius: 999px; + background: var(--primary); +} + +.meta-row, +.meta-stack, +.inline-actions { + display: flex; + flex-wrap: wrap; + gap: 0.65rem; +} + +.meta-stack { + flex-direction: column; + gap: 0.25rem; +} + +.section-panel { + padding: 1.4rem; +} + +.dark-block { + padding: clamp(1.4rem, 2vw, 2.4rem); + border-radius: var(--radius-lg); + background: var(--surface-dark); + color: #fff; + border: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: var(--shadow-lg); +} + +.dark-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1rem; +} + +.notice-banner, +.inline-alert { + padding: 1rem 1.1rem; + border-radius: var(--radius-md); + border: 1px solid var(--border); + background: var(--surface); +} + +.notice-banner--warning { + background: #fff8e7; + border-color: rgba(191, 161, 0, 0.28); +} + +.notice-banner--info { + background: var(--primary-soft); + border-color: rgba(0, 102, 204, 0.2); +} + +.form-panel { + padding: 1.5rem; +} + +.form-label { + font-size: 0.88rem; font-weight: 600; - width: 100%; - transition: all 0.3s ease; + color: var(--text); + margin-bottom: 0.45rem; } -.webhook-url { - font-size: 0.85em; - color: #555; - margin-top: 0.5rem; +.form-control, +.form-select { + border-color: var(--border); + border-radius: var(--radius-sm); + padding: 0.8rem 0.9rem; + font-size: 0.95rem; + box-shadow: none !important; } -.history-table-container { - overflow-x: auto; - background: rgba(255, 255, 255, 0.4); - padding: 1rem; - border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.3); +.form-control:focus, +.form-select:focus { + border-color: rgba(0, 102, 204, 0.48); } -.history-table { - width: 100%; +.form-text, +.invalid-feedback { + font-size: 0.82rem; } -.history-table-time { - width: 15%; - white-space: nowrap; - font-size: 0.85em; - color: #555; +textarea.form-control { + min-height: 160px; + resize: vertical; } -.history-table-user { - width: 35%; - background: rgba(255, 255, 255, 0.3); - border-radius: 8px; - padding: 8px; +.filter-bar { + display: flex; + flex-wrap: wrap; + gap: 0.7rem; + margin-bottom: 1.2rem; } -.history-table-ai { - width: 50%; - background: rgba(255, 255, 255, 0.5); - border-radius: 8px; - padding: 8px; +.filter-bar .btn.active { + background: var(--text); + color: #fff; + border-color: var(--text); } -.no-messages { +.queue-table { + margin: 0; +} + +.queue-table thead th { + border-bottom-color: var(--border); + color: var(--muted); + font-size: 0.82rem; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.queue-table tbody td { + vertical-align: top; + border-color: var(--border); + padding-top: 1rem; + padding-bottom: 1rem; +} + +.queue-row--active { + background: var(--primary-soft); +} + +.queue-link { + color: var(--primary); +} + +.detail-grid { + display: grid; + gap: 1rem; +} + +.detail-meta { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.75rem; +} + +.meta-block { + padding: 0.9rem; + border: 1px solid var(--border); + border-radius: var(--radius-md); + background: var(--surface-muted); +} + +.copy-inline { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.admin-layout { + display: grid; + grid-template-columns: 1.2fr 0.8fr; + gap: 1rem; +} + +.empty-panel { + padding: 1.6rem; text-align: center; - color: #777; -} \ No newline at end of file +} + +.site-navbar { + background: rgba(244, 246, 248, 0.92); + border-bottom: 1px solid rgba(221, 228, 236, 0.78); + backdrop-filter: blur(14px); +} + +.site-navbar.scrolled { + box-shadow: 0 12px 24px rgba(28, 39, 51, 0.06); +} + +.navbar-brand { + display: flex; + flex-direction: column; + gap: 0.05rem; +} + +.brand-mark__eyebrow { + text-transform: uppercase; + letter-spacing: 0.12em; + font-size: 0.66rem; + color: var(--muted); +} + +.brand-mark__title { + font-weight: 800; + letter-spacing: -0.03em; + color: var(--text); +} + +.nav-link { + color: var(--muted); + font-weight: 500; + padding: 0.5rem 0.7rem !important; + border-radius: 999px; +} + +.nav-link:hover, +.nav-link:focus, +.nav-link.active { + color: var(--text); + background: rgba(255, 255, 255, 0.92); +} + +.footer-links li { + margin-bottom: 0.55rem; +} + +.footer-links a { + color: var(--muted); +} + +.footer-links a:hover, +.footer-links a:focus { + color: var(--text); +} + +.footer-links--muted li { + color: var(--muted); +} + +.site-footer { + padding: 3rem 0 2rem; + border-top: 1px solid var(--border); + background: var(--surface); + margin-top: 2rem; +} + +.footer-title { + font-size: 1.5rem; +} + +.footer-label { + margin-bottom: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.12em; + font-size: 0.74rem; + font-weight: 700; + color: var(--muted); +} + +.footer-bar { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 0.75rem; + margin-top: 2rem; + padding-top: 1.25rem; + border-top: 1px solid var(--border); + color: var(--muted); + font-size: 0.88rem; +} + +.form-counter { + font-size: 0.8rem; + color: var(--muted); +} + +.anchor-offset { + scroll-margin-top: 6rem; +} + +@media (max-width: 991.98px) { + .feature-grid, + .capability-grid, + .preview-grid, + .partner-grid, + .timeline-grid, + .contact-grid, + .dark-grid, + .admin-layout { + grid-template-columns: 1fr; + } + + .metric-strip, + .detail-meta { + grid-template-columns: 1fr; + } + + .page-main { + padding-top: 4.9rem; + } +} + +@media (max-width: 767.98px) { + .page-section { + padding: 3rem 0; + } + + .hero-shell, + .surface-card, + .form-panel, + .empty-panel, + .contact-panel { + padding: 1.15rem; + } + + .btn { + width: 100%; + justify-content: center; + } + + .inline-actions .btn, + .inline-actions .button-link { + width: auto; + } + + .queue-table thead { + display: none; + } + + .queue-table tbody, + .queue-table tr, + .queue-table td { + display: block; + width: 100%; + } + + .queue-table tr { + border-bottom: 1px solid var(--border); + } + + .queue-table td { + border: 0; + padding: 0.25rem 0; + } +} + +*:focus-visible { + outline: 2px solid rgba(0, 102, 204, 0.42); + outline-offset: 2px; +} diff --git a/assets/js/main.js b/assets/js/main.js index d349598..7e0a5ef 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1,39 +1,68 @@ document.addEventListener('DOMContentLoaded', () => { - const chatForm = document.getElementById('chat-form'); - const chatInput = document.getElementById('chat-input'); - const chatMessages = document.getElementById('chat-messages'); - - const appendMessage = (text, sender) => { - const msgDiv = document.createElement('div'); - msgDiv.classList.add('message', sender); - msgDiv.textContent = text; - chatMessages.appendChild(msgDiv); - chatMessages.scrollTop = chatMessages.scrollHeight; + const navbar = document.querySelector('.site-navbar'); + const onScroll = () => { + if (navbar) { + navbar.classList.toggle('scrolled', window.scrollY > 12); + } }; - chatForm.addEventListener('submit', async (e) => { - e.preventDefault(); - const message = chatInput.value.trim(); - if (!message) return; + document.addEventListener('scroll', onScroll, { passive: true }); + onScroll(); - appendMessage(message, 'visitor'); - chatInput.value = ''; + if (window.bootstrap && typeof bootstrap.Toast === 'function') { + document.querySelectorAll('.toast').forEach((toastElement) => { + const toast = new bootstrap.Toast(toastElement, { delay: 5200 }); + toast.show(); + }); + } - try { - const response = await fetch('api/chat.php', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ message }) + const filterButtons = document.querySelectorAll('[data-service-filter]'); + const serviceItems = document.querySelectorAll('[data-service-track]'); + + if (filterButtons.length && serviceItems.length) { + filterButtons.forEach((button) => { + button.addEventListener('click', () => { + const filter = button.dataset.serviceFilter || 'all'; + filterButtons.forEach((candidate) => candidate.classList.toggle('active', candidate === button)); + serviceItems.forEach((item) => { + const track = item.dataset.serviceTrack || ''; + const visible = filter === 'all' || track === filter; + item.classList.toggle('d-none', !visible); + }); }); - const data = await response.json(); - - // Artificial delay for realism - setTimeout(() => { - appendMessage(data.reply, 'bot'); - }, 500); - } catch (error) { - console.error('Error:', error); - appendMessage("Sorry, something went wrong. Please try again.", 'bot'); - } + }); + } + + document.querySelectorAll('[data-copy-target]').forEach((button) => { + button.addEventListener('click', async () => { + const target = document.querySelector(button.dataset.copyTarget || ''); + if (!target) return; + + const originalText = button.textContent; + try { + await navigator.clipboard.writeText(target.textContent.trim()); + button.textContent = 'Copied'; + window.setTimeout(() => { + button.textContent = originalText; + }, 1600); + } catch (error) { + button.textContent = 'Unavailable'; + window.setTimeout(() => { + button.textContent = originalText; + }, 1600); + } + }); + }); + + document.querySelectorAll('[data-char-count]').forEach((textarea) => { + const counter = document.querySelector(textarea.dataset.charCount || ''); + if (!counter) return; + + const updateCount = () => { + counter.textContent = `${textarea.value.length} / ${textarea.maxLength}`; + }; + + textarea.addEventListener('input', updateCount); + updateCount(); }); }); diff --git a/contact.php b/contact.php new file mode 100644 index 0000000..6cc8bfd --- /dev/null +++ b/contact.php @@ -0,0 +1,53 @@ + 'Strategic discussion', 'summary' => 'Use the inquiry desk for partnership, package customization, or operational collaboration.'], + ['title' => 'Email-based follow-up', 'summary' => 'The intake form captures a work email so the next step can stay formal and documented.'], + ['title' => 'Location coordination', 'summary' => 'Share the preferred area or property context in the brief so site planning starts earlier.'], +]; + +require __DIR__ . '/includes/header.php'; +?> +
+
+
+
+
+ Contact guidance +

A clear route into strategic discussion.

+

Rather than scattering generic contact details, this MVP uses a structured intake path so every conversation starts with context, scale, and the right ecosystem lane.

+
+ +
+
+ +
+ +
+

+

+
+ +
+ +
+ Recommended next action +

Use the partnership page as the main intake channel.

+

It already captures the package or partner route, preferred timing, location context, and a short narrative brief—everything needed for a credible first review.

+ +
+
+
+ diff --git a/database/gbp_ecosystem.sql b/database/gbp_ecosystem.sql new file mode 100644 index 0000000..8f6725c --- /dev/null +++ b/database/gbp_ecosystem.sql @@ -0,0 +1,18 @@ +-- Genitri Ecco Wellness inquiry desk +CREATE TABLE IF NOT EXISTS ecosystem_inquiries ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + reference_code VARCHAR(32) NOT NULL UNIQUE, + inquiry_type VARCHAR(80) NOT NULL, + interest_area VARCHAR(120) NOT NULL, + contact_name VARCHAR(120) NOT NULL, + organization_name VARCHAR(160) NOT NULL, + email VARCHAR(160) NOT NULL, + phone VARCHAR(40) DEFAULT NULL, + target_date VARCHAR(40) DEFAULT NULL, + event_scale VARCHAR(80) DEFAULT NULL, + preferred_location VARCHAR(120) DEFAULT NULL, + notes TEXT NOT NULL, + status VARCHAR(30) NOT NULL DEFAULT 'New', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_status_created (status, created_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/healthz.php b/healthz.php new file mode 100644 index 0000000..4fb9295 --- /dev/null +++ b/healthz.php @@ -0,0 +1,14 @@ + 'ok', + 'timestamp' => gmdate(DATE_ATOM), + 'storage' => using_preview_storage() ? 'preview-session' : 'database', + 'site' => site_name(), +], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); diff --git a/includes/app.php b/includes/app.php new file mode 100644 index 0000000..c47be39 --- /dev/null +++ b/includes/app.php @@ -0,0 +1,709 @@ + 'home', 'label' => 'Home', 'href' => 'index.php'], + ['key' => 'about', 'label' => 'About Us', 'href' => 'about.php'], + ['key' => 'services', 'label' => 'Services', 'href' => 'services.php'], + ['key' => 'portfolio', 'label' => 'Portfolio', 'href' => 'portfolio.php'], + ['key' => 'partnership', 'label' => 'Partnership', 'href' => 'partnership.php'], + ['key' => 'publications', 'label' => 'Publications', 'href' => 'publications.php'], + ['key' => 'contact', 'label' => 'Contact Us', 'href' => 'contact.php'], + ]; +} + +function default_service_catalog(): array +{ + return [ + [ + 'slug' => 'eco-wellness', + 'track' => 'immersive', + 'category' => 'Eco Wellness Package', + 'headline' => 'Preventive wellbeing journeys grounded in place.', + 'summary' => 'Curated wellness experiences that combine guided recovery, quiet hospitality, and structured facilitation for premium groups.', + 'audience' => 'Clinics, communities, executive wellness groups', + 'duration' => 'Half day to 3 days', + 'outcome' => 'Participants leave with a calmer baseline, curated routines, and professionally coordinated aftercare recommendations.', + 'includes' => [ + 'Guided breathing, mobility, and reflection sessions', + 'Nature immersion flow with on-site facilitator', + 'Healthy hospitality setup and light nourishment', + 'Operational coordinator, safety briefing, and guest support', + ], + ], + [ + 'slug' => 'retreat', + 'track' => 'immersive', + 'category' => 'Retreat Package', + 'headline' => 'Immersive retreats with quiet luxury and operational discipline.', + 'summary' => 'Multi-day retreat programs for restorative groups, leadership circles, or curated community cohorts seeking a trusted delivery partner.', + 'audience' => 'Leadership teams, private communities, destination hosts', + 'duration' => '2 to 4 days', + 'outcome' => 'Guests receive a coherent retreat arc, documented experience moments, and a host-ready operating structure.', + 'includes' => [ + 'Retreat flow design from arrival to closing circle', + 'Hospitality curation and accommodation coordination', + 'Experience documentation and facilitator scheduling', + 'Transportation, guest movement, and contingency planning', + ], + ], + [ + 'slug' => 'corporate', + 'track' => 'corporate', + 'category' => 'Corporate Package', + 'headline' => 'Nature-based programs for teams that need focus, recovery, and alignment.', + 'summary' => 'Corporate offsites and culture journeys that balance executive standards, wellness objectives, and measurable group outcomes.', + 'audience' => 'Companies, HR teams, executive assistants, corporate communities', + 'duration' => '1 to 2 days', + 'outcome' => 'A more connected team, clean guest logistics, and a premium experience that avoids generic event energy.', + 'includes' => [ + 'Team reset sessions and guided collaboration moments', + 'Hospitality coordination with venue and catering partners', + 'Brand-safe itineraries, agendas, and support crew', + 'Post-program recap for internal reporting', + ], + ], + [ + 'slug' => 'hospitality', + 'track' => 'operational', + 'category' => 'Hospitality Package', + 'headline' => 'Hospitality collaborations that expand destination value.', + 'summary' => 'Program overlays for properties, destinations, or operators wanting to add curated wellness and experiential layers to their guest offering.', + 'audience' => 'Hotels, villas, destination operators, stay partners', + 'duration' => 'Modular activation', + 'outcome' => 'Partners gain a credible package layer, documented service standards, and a stronger experiential positioning.', + 'includes' => [ + 'Experience packaging and guest-ready positioning', + 'Venue flow, staffing, and hospitality service mapping', + 'Wellness or retreat modules fitted to the property', + 'Partner coordination with clear operating notes', + ], + ], + [ + 'slug' => 'event-mice', + 'track' => 'corporate', + 'category' => 'Event & MICE Package', + 'headline' => 'Operationally credible events with experiential depth.', + 'summary' => 'MICE and event support for organizations that need premium guest handling, destination texture, and dependable execution.', + 'audience' => 'Associations, brands, institutions, MICE organizers', + 'duration' => 'Single activation to multi-day agenda', + 'outcome' => 'An event environment that feels curated, calm, and professionally managed from transport to closing session.', + 'includes' => [ + 'Run-of-show design and ecosystem partner alignment', + 'Guest logistics, transport, and venue operations support', + 'Curated destination layers for delegates or VIP groups', + 'Documentation for post-event portfolio use', + ], + ], + [ + 'slug' => 'custom-experience', + 'track' => 'operational', + 'category' => 'Custom Experience Package', + 'headline' => 'Flexible program architecture for bespoke briefs.', + 'summary' => 'A consultative route for institutions or partners who need tailored combinations of wellness, hospitality, logistics, and activation.', + 'audience' => 'Strategic partners, institutions, multi-stakeholder projects', + 'duration' => 'Scoped during consultation', + 'outcome' => 'A custom proposal with the right operating modules, partner map, and delivery assumptions for the brief.', + 'includes' => [ + 'Discovery call and brief clarification', + 'Package architecture and ecosystem matchmaking', + 'Operational assumptions for location, scale, and timing', + 'Proposal-ready summary to support internal decision-making', + ], + ], + ]; +} + +function service_by_slug(string $slug): ?array +{ + foreach (service_catalog() as $service) { + if ($service['slug'] === $slug) { + return $service; + } + } + + return null; +} + +function default_portfolio_catalog(): array +{ + return [ + [ + 'category' => 'Retreat Documentation', + 'title' => 'Immersive reset for healthcare leaders', + 'summary' => 'Three-day private retreat with guided wellness, quiet hospitality, and destination movement planning.', + 'detail' => 'Built around trusted facilitation, transport sequencing, curated dining, and premium guest care.', + 'metric' => '48 delegates · 3 days · full service coordination', + ], + [ + 'category' => 'Hospitality Activities', + 'title' => 'Property-based eco wellness activation', + 'summary' => 'Hospitality collaboration that layered preventive wellness modules into a destination stay offer.', + 'detail' => 'Designed to expand partner value without disrupting existing property operations.', + 'metric' => '6 touchpoints · 2 host teams · reusable package model', + ], + [ + 'category' => 'Event Showcase', + 'title' => 'Institutional offsite with nature-led programming', + 'summary' => 'Corporate gathering that fused structured sessions, outdoor recovery windows, and logistics support.', + 'detail' => 'Focused on calm execution, executive-safe service, and documented experience moments.', + 'metric' => '120 participants · transport + venue + documentation', + ], + [ + 'category' => 'Operational Support', + 'title' => 'Partner coordination for a multi-vendor destination brief', + 'summary' => 'Cross-functional partner map covering venue, mobility, hospitality, and merchandise support.', + 'detail' => 'Organized into clear workstreams to reduce friction and improve delivery certainty.', + 'metric' => '9 partner lanes · single briefing track · review-ready notes', + ], + ]; +} + +function default_partnership_categories(): array +{ + return [ + ['title' => 'Medical Partner', 'summary' => 'Preventive health programs, professional credibility, and trusted wellness positioning.'], + ['title' => 'Food & Beverage Partner', 'summary' => 'Healthy dining curation, premium service alignment, and guest experience enhancement.'], + ['title' => 'Hospitality Partner', 'summary' => 'Hotels, villas, and destination stays that want elevated experiential packages.'], + ['title' => 'Transportation Partner', 'summary' => 'Ground movement, transfer planning, and reliable guest logistics.'], + ['title' => 'Vendor Partner', 'summary' => 'Operational vendors for events, staging, equipment, or curated supporting services.'], + ['title' => 'Venue Partner', 'summary' => 'Destination spaces that fit wellness, retreat, and premium gathering formats.'], + ['title' => 'Merchandise Partner', 'summary' => 'Thoughtful branded items, gifting, and supporting program materials.'], + ['title' => 'Talent Partner', 'summary' => 'Facilitators, wellness practitioners, speakers, and curated experience talent.'], + ['title' => 'Facilities Partner', 'summary' => 'Technical, utility, and infrastructure support for safe, smooth delivery.'], + ]; +} + +function default_publication_catalog(): array +{ + return [ + [ + 'slug' => 'governance-wellness', + 'category' => 'Whitepaper', + 'date' => '2026-05-12', + 'title' => 'Governance as a differentiator in eco wellness operations', + 'summary' => 'A concise look at why ethical operating systems increase trust in nature-based programs.', + 'body' => [ + 'Nature-based experiences become institution-ready when governance is visible, practical, and embedded into the delivery model.', + 'For GBP, governance is not an afterthought. It shapes partner qualification, guest flow, documentation, safety considerations, and post-program reporting.', + 'Publishing this kind of operational insight helps the website function as both a service platform and a credibility engine for future collaborations.', + ], + ], + [ + 'slug' => 'hospitality-ecosystem', + 'category' => 'Strategic Insight', + 'date' => '2026-04-28', + 'title' => 'Why hospitality ecosystems outperform isolated program offers', + 'summary' => 'Integrated destinations create stronger guest journeys when transport, venue, and wellness layers are coordinated early.', + 'body' => [ + 'Standalone experiences can look attractive in a brochure but collapse under operational pressure when the surrounding ecosystem is weak.', + 'An integrated hospitality ecosystem aligns venue readiness, movement planning, service staffing, food design, and escalation paths before guests arrive.', + 'That alignment is what allows a premium minimalist brand like Genitri Ecco Wellness to stay calm, polished, and credible at scale.', + ], + ], + [ + 'slug' => 'experience-customization', + 'category' => 'Ecosystem Update', + 'date' => '2026-03-30', + 'title' => 'A practical framework for customizing destination experiences', + 'summary' => 'How inquiry intake, partner matching, and brief translation can stay lightweight while still feeling premium.', + 'body' => [ + 'Customization is most effective when the intake process captures the essentials: audience, timing, desired outcome, and operating constraints.', + 'From there, GBP can match the right service modules, partner types, and delivery assumptions without overcomplicating the first conversation.', + 'This is why the first MVP slice focuses on a strategic inquiry desk that feels both editorial and operational.', + ], + ], + ]; +} + +function publication_by_slug(string $slug): ?array +{ + foreach (publication_catalog() as $publication) { + if ($publication['slug'] === $slug) { + return $publication; + } + } + + return null; +} + +function governance_values(): array +{ + return [ + ['title' => 'Excellence with Integrity', 'summary' => 'Premium execution grounded in transparent standards, safety, and reliable delivery.'], + ['title' => 'Shared Value Creation', 'summary' => 'Programs and partnerships designed to benefit guests, collaborators, and the wider ecosystem.'], + ['title' => 'Sustainable Equilibrium', 'summary' => 'Nature, hospitality, and operations balanced with long-term resilience in mind.'], + ]; +} + +function objective_list(): array +{ + return [ + 'Build institutional trust', + 'Showcase operational capability', + 'Open strategic partnership routes', + 'Generate qualified inquiry', + 'Provide curated package services', + 'Enable experience customization', + 'Display ecosystem portfolio', + 'Build publication authority', + 'Prepare governance infrastructure', + ]; +} + +function inquiry_type_options(): array +{ + return [ + 'Strategic Partnership', + 'Package Customization', + 'Operational Collaboration', + 'Publication / Media', + ]; +} + +function interest_groups(): array +{ + $services = []; + foreach (service_catalog() as $service) { + $services[] = $service['category']; + } + + $partners = []; + foreach (partnership_categories() as $partner) { + $partners[] = $partner['title']; + } + + return [ + 'Curated Programs' => $services, + 'Partnership Routes' => $partners, + ]; +} + +function scale_options(): array +{ + return [ + 'Private group (10–30)', + 'Team offsite (31–80)', + 'Corporate / MICE (81–200)', + 'Multi-site activation (200+)', + ]; +} + +function location_options(): array +{ + return [ + 'Tegal / Bregas corridor', + 'Central Java destination', + 'Partner property or venue', + 'To be discussed during scoping', + ]; +} + +function active_class(string $activePage, string $key): string +{ + return $activePage === $key ? 'active' : ''; +} + +function current_year(): string +{ + return gmdate('Y'); +} + +function csrf_token(): string +{ + if (empty($_SESSION['csrf_token'])) { + $_SESSION['csrf_token'] = bin2hex(random_bytes(16)); + } + + return $_SESSION['csrf_token']; +} + +function verify_csrf_token(?string $token): bool +{ + return is_string($token) && hash_equals($_SESSION['csrf_token'] ?? '', $token); +} + +function flash_set(string $type, string $message): void +{ + $_SESSION['flash'] = [ + 'type' => $type, + 'message' => $message, + ]; +} + +function flash_get(): ?array +{ + if (empty($_SESSION['flash']) || !is_array($_SESSION['flash'])) { + return null; + } + + $flash = $_SESSION['flash']; + unset($_SESSION['flash']); + + return $flash; +} + +function old_value(array $source, string $key, string $fallback = ''): string +{ + $value = $source[$key] ?? $fallback; + return trim((string) $value); +} + +function is_selected(?string $candidate, ?string $value): string +{ + return ((string) $candidate === (string) $value) ? 'selected' : ''; +} + +function app_db_connection(): ?PDO +{ + static $attempted = false; + static $pdo = null; + static $error = null; + + if ($attempted) { + return $pdo; + } + + $attempted = true; + + try { + $pdo = db(); + $error = null; + } catch (Throwable $exception) { + $pdo = null; + $error = $exception->getMessage(); + } + + $GLOBALS['APP_DB_ERROR'] = $error; + + return $pdo; +} + +function app_db_error(): ?string +{ + app_db_connection(); + $error = $GLOBALS['APP_DB_ERROR'] ?? null; + return is_string($error) && $error !== '' ? $error : null; +} + +function using_preview_storage(): bool +{ + return app_db_connection() === null; +} + +function inquiry_table_sql(): string +{ + return <<<'SQL' +CREATE TABLE IF NOT EXISTS ecosystem_inquiries ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + reference_code VARCHAR(32) NOT NULL UNIQUE, + inquiry_type VARCHAR(80) NOT NULL, + interest_area VARCHAR(120) NOT NULL, + contact_name VARCHAR(120) NOT NULL, + organization_name VARCHAR(160) NOT NULL, + email VARCHAR(160) NOT NULL, + phone VARCHAR(40) DEFAULT NULL, + target_date VARCHAR(40) DEFAULT NULL, + event_scale VARCHAR(80) DEFAULT NULL, + preferred_location VARCHAR(120) DEFAULT NULL, + notes TEXT NOT NULL, + status VARCHAR(30) NOT NULL DEFAULT 'New', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_status_created (status, created_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +SQL; +} + +function ensure_inquiry_table(): bool +{ + static $ensured = false; + + if ($ensured) { + return true; + } + + $pdo = app_db_connection(); + if (!$pdo) { + return false; + } + + try { + $pdo->exec(inquiry_table_sql()); + $ensured = true; + return true; + } catch (Throwable $exception) { + $GLOBALS['APP_DB_ERROR'] = $exception->getMessage(); + return false; + } +} + +function normalize_multiline(string $value): string +{ + $normalized = preg_replace("/\r\n?|\r/", "\n", $value); + $normalized = preg_replace("/\n{3,}/", "\n\n", (string) $normalized); + return trim((string) $normalized); +} + +function validate_inquiry(array $input): array +{ + $data = [ + 'inquiry_type' => trim((string) ($input['inquiry_type'] ?? '')), + 'interest_area' => trim((string) ($input['interest_area'] ?? '')), + 'contact_name' => trim((string) ($input['contact_name'] ?? '')), + 'organization_name' => trim((string) ($input['organization_name'] ?? '')), + 'email' => trim((string) ($input['email'] ?? '')), + 'phone' => trim((string) ($input['phone'] ?? '')), + 'target_date' => trim((string) ($input['target_date'] ?? '')), + 'event_scale' => trim((string) ($input['event_scale'] ?? '')), + 'preferred_location' => trim((string) ($input['preferred_location'] ?? '')), + 'notes' => normalize_multiline((string) ($input['notes'] ?? '')), + ]; + + $errors = []; + + if ($data['inquiry_type'] === '') { + $errors['inquiry_type'] = 'Choose the type of collaboration you want to discuss.'; + } + + if ($data['interest_area'] === '') { + $errors['interest_area'] = 'Select the package or partnership route that best matches your brief.'; + } + + if ($data['contact_name'] === '') { + $errors['contact_name'] = 'Add the primary contact name.'; + } + + if ($data['organization_name'] === '') { + $errors['organization_name'] = 'Add the company or organization name.'; + } + + if ($data['email'] === '' || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) { + $errors['email'] = 'Use a valid email address so the team can respond.'; + } + + if ($data['notes'] === '') { + $errors['notes'] = 'Describe the program scope, partner intent, or operational need.'; + } elseif (text_length($data['notes']) < 24) { + $errors['notes'] = 'Add a little more detail so the brief is useful for review.'; + } elseif (text_length($data['notes']) > 900) { + $errors['notes'] = 'Keep the brief under 900 characters for this first intake step.'; + } + + if ($data['phone'] !== '' && text_length($data['phone']) > 40) { + $errors['phone'] = 'Phone number is too long.'; + } + + if ($data['target_date'] !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $data['target_date'])) { + $errors['target_date'] = 'Use a valid preferred date.'; + } + + return [ + 'data' => $data, + 'errors' => $errors, + ]; +} + +function generate_reference_code(): string +{ + return 'GBP-' . gmdate('ymd') . '-' . strtoupper(bin2hex(random_bytes(2))); +} + +function save_inquiry(array $input): array +{ + $validated = validate_inquiry($input); + $data = $validated['data']; + $errors = $validated['errors']; + + if ($errors !== []) { + return [ + 'success' => false, + 'errors' => $errors, + 'data' => $data, + ]; + } + + $inquiry = $data; + $inquiry['reference_code'] = generate_reference_code(); + $inquiry['status'] = 'New'; + $inquiry['created_at'] = gmdate('Y-m-d H:i:s'); + + if (ensure_inquiry_table()) { + try { + $pdo = app_db_connection(); + if ($pdo instanceof PDO) { + $statement = $pdo->prepare( + 'INSERT INTO ecosystem_inquiries + (reference_code, inquiry_type, interest_area, contact_name, organization_name, email, phone, target_date, event_scale, preferred_location, notes, status) + VALUES + (:reference_code, :inquiry_type, :interest_area, :contact_name, :organization_name, :email, :phone, :target_date, :event_scale, :preferred_location, :notes, :status)' + ); + + $statement->bindValue(':reference_code', $inquiry['reference_code']); + $statement->bindValue(':inquiry_type', $inquiry['inquiry_type']); + $statement->bindValue(':interest_area', $inquiry['interest_area']); + $statement->bindValue(':contact_name', $inquiry['contact_name']); + $statement->bindValue(':organization_name', $inquiry['organization_name']); + $statement->bindValue(':email', $inquiry['email']); + $statement->bindValue(':phone', $inquiry['phone'] ?: null, $inquiry['phone'] === '' ? PDO::PARAM_NULL : PDO::PARAM_STR); + $statement->bindValue(':target_date', $inquiry['target_date'] ?: null, $inquiry['target_date'] === '' ? PDO::PARAM_NULL : PDO::PARAM_STR); + $statement->bindValue(':event_scale', $inquiry['event_scale'] ?: null, $inquiry['event_scale'] === '' ? PDO::PARAM_NULL : PDO::PARAM_STR); + $statement->bindValue(':preferred_location', $inquiry['preferred_location'] ?: null, $inquiry['preferred_location'] === '' ? PDO::PARAM_NULL : PDO::PARAM_STR); + $statement->bindValue(':notes', $inquiry['notes']); + $statement->bindValue(':status', $inquiry['status']); + $statement->execute(); + + $inquiry['id'] = (int) $pdo->lastInsertId(); + + return [ + 'success' => true, + 'inquiry' => $inquiry, + 'storage' => 'database', + ]; + } + } catch (Throwable $exception) { + $GLOBALS['APP_DB_ERROR'] = $exception->getMessage(); + } + } + + $_SESSION['preview_inquiries'] = $_SESSION['preview_inquiries'] ?? []; + $_SESSION['preview_inquiry_counter'] = (int) ($_SESSION['preview_inquiry_counter'] ?? 0) + 1; + $inquiry['id'] = (int) $_SESSION['preview_inquiry_counter']; + $_SESSION['preview_inquiries'][$inquiry['reference_code']] = $inquiry; + + return [ + 'success' => true, + 'inquiry' => $inquiry, + 'storage' => 'session', + 'warning' => 'Database connectivity is not available, so this submission is stored only for the current preview session.', + ]; +} + +function session_inquiries(): array +{ + $items = array_values($_SESSION['preview_inquiries'] ?? []); + usort($items, static fn (array $left, array $right): int => strcmp($right['created_at'], $left['created_at'])); + return $items; +} + +function list_inquiries(): array +{ + if (ensure_inquiry_table()) { + try { + $pdo = app_db_connection(); + if ($pdo instanceof PDO) { + $statement = $pdo->query('SELECT * FROM ecosystem_inquiries ORDER BY created_at DESC, id DESC LIMIT 50'); + return $statement->fetchAll() ?: []; + } + } catch (Throwable $exception) { + $GLOBALS['APP_DB_ERROR'] = $exception->getMessage(); + } + } + + return session_inquiries(); +} + +function find_inquiry(?int $id = null, ?string $reference = null): ?array +{ + if (ensure_inquiry_table()) { + try { + $pdo = app_db_connection(); + if ($pdo instanceof PDO) { + if ($reference !== null && $reference !== '') { + $statement = $pdo->prepare('SELECT * FROM ecosystem_inquiries WHERE reference_code = :reference LIMIT 1'); + $statement->bindValue(':reference', $reference); + $statement->execute(); + $found = $statement->fetch(); + return is_array($found) ? $found : null; + } + + if ($id !== null && $id > 0) { + $statement = $pdo->prepare('SELECT * FROM ecosystem_inquiries WHERE id = :id LIMIT 1'); + $statement->bindValue(':id', $id, PDO::PARAM_INT); + $statement->execute(); + $found = $statement->fetch(); + return is_array($found) ? $found : null; + } + } + } catch (Throwable $exception) { + $GLOBALS['APP_DB_ERROR'] = $exception->getMessage(); + } + } + + foreach (session_inquiries() as $item) { + if (($reference !== null && $reference !== '' && $item['reference_code'] === $reference) || ($id !== null && (int) $item['id'] === $id)) { + return $item; + } + } + + return null; +} + +function format_date_label(?string $date): string +{ + $value = trim((string) $date); + if ($value === '') { + return 'To be coordinated'; + } + + try { + return (new DateTimeImmutable($value))->format('j M Y'); + } catch (Throwable) { + return $value; + } +} diff --git a/includes/footer.php b/includes/footer.php new file mode 100644 index 0000000..c567ee4 --- /dev/null +++ b/includes/footer.php @@ -0,0 +1,69 @@ + + + + 'text-bg-success', + 'warning' => 'text-bg-warning', + 'danger' => 'text-bg-danger', + default => 'text-bg-primary', + }; +?> +
+
+
+
+ +
+
+
+ + + + + diff --git a/includes/header.php b/includes/header.php new file mode 100644 index 0000000..6ebcb78 --- /dev/null +++ b/includes/header.php @@ -0,0 +1,71 @@ + + + + + + + <?= h($fullTitle) ?> + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/index.php b/index.php index 7205f3d..7fcd5d2 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,256 @@ 'Eco wellness', 'summary' => 'Preventive programs with calm pacing, guided facilitation, and premium care.'], + ['title' => 'Retreat experiences', 'summary' => 'Immersive multi-day journeys built around clarity, reflection, and quiet hospitality.'], + ['title' => 'Hospitality collaboration', 'summary' => 'Package overlays for properties and destination operators seeking premium experience value.'], + ['title' => 'Transportation & logistics', 'summary' => 'Guest movement, timing, contingency, and arrival sequencing that reduce operational friction.'], + ['title' => 'Vendor & venue ecosystem', 'summary' => 'Curated supporting partners for staging, equipment, venues, and specialist services.'], + ['title' => 'Event & MICE', 'summary' => 'Institutional-grade events that still feel experiential, calm, and human-centered.'], + ['title' => 'Partnership collaboration', 'summary' => 'Strategic lanes for medical, hospitality, logistics, and ecosystem partners.'], + ['title' => 'Publications & governance', 'summary' => 'Editorial authority, reporting discipline, and trust-building operational narratives.'], +]; + +require __DIR__ . '/includes/header.php'; ?> - - - - - - New Style - - - - - - - - - - - - - - - - - - - - - -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

+
+
+
+
+
+ Institutional experience gateway +

Integrated Nature-Based Experience Ecosystem

+

Eco wellness, experiential tourism, hospitality collaboration, and strategic ecosystem partnership—structured in a premium, operationally credible format.

+ +
+
+ 6 curated programs + Wellness, retreat, corporate, hospitality, MICE, and custom experiences. +
+
+ 9 partner lanes + From medical and F&B collaboration to venue, vendor, and facilities support. +
+
+ 1 integrated desk + A thin-slice inquiry flow that turns interest into a review-ready brief. +
+
+
+
+
+

Operational lens

+
+
+ Curated services + Program architecture with premium restraint and clear delivery assumptions. +
+
+ Partner ecosystem + Healthcare, hospitality, logistics, vendor, venue, and talent collaboration lanes. +
+
+ Governance foundation + Editorial trust, qualification, and documented operations. +
+
+
+

First delivery focus

+ Open the strategic inquiry desk +
+
+
+
-
- - - + + +
+
+
+ Ecosystem overview +

An editorial, trust-first way to present a complex operating ecosystem.

+

The first release turns the masterplan into a professional front-of-house shell: clear structure, premium pacing, and an immediate path to a qualified inquiry.

+
+
+ +
+ Capability +

+

+
+ +
+
+
+ +
+
+
+
+ Package programs +

Curated programs that feel premium, specific, and consultation-ready.

+

Each package links directly into the inquiry workflow, so a visitor can browse, shortlist, and send a structured brief in one flow.

+
+ View all service packages +
+
+ + + +
+
+
+ +
+
+
+
+ Portfolio showcase +

A portfolio layer that demonstrates delivery capability—not just visual mood.

+

The content is structured like an institutional case reel: concise, premium, and tied to operational signals.

+
+ Explore portfolio +
+
+ +
+ +
+

+

+

+
+

+
+ +
+
+
+ +
+
+
+ Open partnership +

Strategic lanes for ecosystem growth.

+

The first MVP centers on partnership discovery because it creates immediate business value: a clear path from browsing to qualified conversation.

+
+
+ + + +
+
+
+ +
+
+
+
+ Publications +

Editorial authority that supports institutional trust.

+

A lightweight publication layer allows GBP to document insight, signal capability, and keep the ecosystem narrative alive.

+
+ Explore publications +
+
+ + + +
+
+
+ +
+
+
+
+ Governance & values +

Excellence, shared value, and sustainable equilibrium.

+

A dark premium block gives governance the weight it deserves, so the brand feels serious, stable, and ethically grounded.

+
+
+ +
+ Value +

+

+
+ +
+
+
+
+ +
+
+
+
+
+ Contact CTA +

Schedule a strategic discussion.

+

Use the inquiry desk to define the package, partner route, scale, and target timing. The submission is then visible in the preview desk for follow-up review.

+
+ +
+
+
+
+ diff --git a/partnership.php b/partnership.php new file mode 100644 index 0000000..d788d91 --- /dev/null +++ b/partnership.php @@ -0,0 +1,272 @@ + trim((string) ($_GET['route'] ?? '')), + 'interest_area' => trim((string) ($_GET['interest'] ?? '')), + 'contact_name' => '', + 'organization_name' => '', + 'email' => '', + 'phone' => '', + 'target_date' => '', + 'event_scale' => '', + 'preferred_location' => '', + 'notes' => '', +]; +$errors = []; +$confirmation = null; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $submittedData = [ + 'inquiry_type' => trim((string) ($_POST['inquiry_type'] ?? '')), + 'interest_area' => trim((string) ($_POST['interest_area'] ?? '')), + 'contact_name' => trim((string) ($_POST['contact_name'] ?? '')), + 'organization_name' => trim((string) ($_POST['organization_name'] ?? '')), + 'email' => trim((string) ($_POST['email'] ?? '')), + 'phone' => trim((string) ($_POST['phone'] ?? '')), + 'target_date' => trim((string) ($_POST['target_date'] ?? '')), + 'event_scale' => trim((string) ($_POST['event_scale'] ?? '')), + 'preferred_location' => trim((string) ($_POST['preferred_location'] ?? '')), + 'notes' => trim((string) ($_POST['notes'] ?? '')), + ]; + + $formData = $submittedData; + + if (!verify_csrf_token($_POST['csrf_token'] ?? null)) { + $errors['form'] = 'Your session expired. Please refresh and submit the inquiry again.'; + } else { + $result = save_inquiry($_POST); + if (!empty($result['success'])) { + if (!empty($result['warning'])) { + flash_set('warning', $result['warning']); + } else { + flash_set('success', 'Inquiry submitted. The preview desk now has a new review item.'); + } + header('Location: ' . page_url('partnership.php', ['reference' => $result['inquiry']['reference_code']])); + exit; + } + + $errors = $result['errors'] ?? ['form' => 'Something went wrong while saving the inquiry.']; + $formData = $result['data'] ?? $formData; + } +} + +$reference = trim((string) ($_GET['reference'] ?? '')); +if ($reference !== '') { + $confirmation = find_inquiry(null, $reference); + if (!$confirmation) { + $errors['lookup'] = 'That reference could not be located yet. Try submitting a fresh inquiry.'; + } +} + +$previewMode = using_preview_storage(); +$interestGroups = interest_groups(); + +require __DIR__ . '/includes/header.php'; +?> +
+
+
+
+
+ Strategic inquiry desk +

Capture a qualified partnership or experience brief in one clean flow.

+

The form collects just enough detail to feel operational: collaboration route, selected package or partner lane, timing, scale, and a narrative brief.

+
+
+
+ What happens next +
    +
  • Submit the structured brief.
  • +
  • Receive a reference code and confirmation summary.
  • +
  • Open the preview desk to review the saved inquiry.
  • +
+
+
+
+
+ + +
+ Preview mode: database connectivity is currently unavailable, so new inquiries are stored only for this browser session while the workflow is being validated. +
+ + + +
+ +
+ + + +
+
+
+ Confirmation +

Inquiry received and routed.

+

Use the reference below for follow-up and internal review.

+
+
+ + +
+
+
+
+ Inquiry type + +
+
+ Selected interest + +
+
+ Organization + +
+
+ Preferred date + +
+
+ +
+ + +
+
+
+
+
+ Brief form +

Create inquiry

+
+ Step 1 of 1 +
+
+ +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + 0 / 900 +
+ +
+
This first intake is intentionally lightweight. A more detailed scoping conversation can follow after review.
+
+
+
+ + Open preview desk +
+
+
+
+
+
+ Review-ready behavior +

Why this thin slice matters.

+
    +
  • Visitors can move from service discovery to inquiry without leaving the branded environment.
  • +
  • The confirmation layer adds confidence by showing a saved reference and structured summary.
  • +
  • The preview desk gives the team an immediate operational touchpoint for follow-up.
  • +
+
+ Channel guidance +
+
+

WhatsApp coordination

+

Use the phone field to nominate the best coordination channel for follow-up.

+
+
+

Email desk

+

The work email anchors the formal response lane for proposals and next steps.

+
+
+

Location planning

+

The location and target date fields make the first review more actionable.

+
+
+
+
+
+
+
+ diff --git a/portfolio.php b/portfolio.php new file mode 100644 index 0000000..bc24653 --- /dev/null +++ b/portfolio.php @@ -0,0 +1,59 @@ + 'Retreat operations', 'summary' => 'Guest flow, accommodation alignment, facilitator support, and calm sequencing.'], + ['title' => 'Hospitality ecosystem', 'summary' => 'Property partnership, dining curation, service standards, and experience layering.'], + ['title' => 'Logistics support', 'summary' => 'Transport coordination, contingency planning, and movement clarity across touchpoints.'], +]; + +require __DIR__ . '/includes/header.php'; +?> +
+
+
+ Portfolio +

Portfolio documentation that signals capability, not clutter.

+

This section turns operational proof into calm editorial cards—useful for clients, partners, and internal alignment.

+
+ +
+ +
+ +

+

+

+
+ +
+
+ +
+
+
+ +
+
+
+ Operational capability +

The supporting layers behind each experience.

+
+
+ +
+

+

+
+ +
+
+
+ diff --git a/publication.php b/publication.php new file mode 100644 index 0000000..259c941 --- /dev/null +++ b/publication.php @@ -0,0 +1,73 @@ + $publication ? $item['slug'] !== $publication['slug'] : true); +$relatedPublications = array_slice(array_values($relatedPublications), 0, 2); + +require __DIR__ . '/includes/header.php'; +?> +
+
+ +
+ 404 +

Publication unavailable

+

Return to the publication index to keep exploring the ecosystem editorial layer.

+ Back to publications +
+ +
+ +

+
+ + Publication detail +
+

+
+ +
+
+
+ +

+ +
+
+
+
+ Continue exploring +

Related reading

+
+ +
+
+ + +
+ Open +
+ +
+
+
+
+ +
+
+ diff --git a/publications.php b/publications.php new file mode 100644 index 0000000..e3e7a8e --- /dev/null +++ b/publications.php @@ -0,0 +1,37 @@ + +
+
+
+ Publication reels +

Editorial content that strengthens institutional authority.

+

This first release includes a lightweight journal layer with list and detail views, making the site feel like an ecosystem platform rather than a single brochure.

+
+
+ + + +
+
+
+ diff --git a/service.php b/service.php new file mode 100644 index 0000000..5915b8e --- /dev/null +++ b/service.php @@ -0,0 +1,109 @@ + $service ? $item['slug'] !== $service['slug'] : true); +$relatedServices = array_slice(array_values($relatedServices), 0, 3); + +require __DIR__ . '/includes/header.php'; +?> +
+
+ +
+ 404 +

Package detail unavailable

+

Return to the service catalog to continue browsing the current package programs.

+ Back to services +
+ +
+
+
+ Package detail +

+

+

+ +
+
+
+ Program snapshot +
+ Audience + +
+
+ Duration + +
+
+ Outcome +

+
+
+
+
+
+ +
+
+
+ Included modules +

A thin but complete package outline.

+
    + +
  • + +
+
+
+
+
+ Why it matters +

This detail page shows how a visitor can understand the package logic before moving into a structured conversation.

+

It also keeps the site from feeling like a single landing page by providing a usable browse → detail → inquiry journey.

+
+
+
+ +
+
+ Related packages +

More programs in the ecosystem.

+
+
+ + + +
+
+ +
+
+ diff --git a/services.php b/services.php new file mode 100644 index 0000000..79d4cc9 --- /dev/null +++ b/services.php @@ -0,0 +1,91 @@ + 'Scope the brief', 'summary' => 'Clarify audience, outcome, timing, location, and experience tone.'], + ['title' => 'Match the ecosystem', 'summary' => 'Combine the right hospitality, logistics, wellness, and partner modules.'], + ['title' => 'Prepare delivery', 'summary' => 'Translate the chosen package into a calm, review-ready operational plan.'], +]; + +require __DIR__ . '/includes/header.php'; +?> +
+
+
+
+
+ Curated packages +

A service catalog built for premium inquiry, not generic tourism browsing.

+

Each program card is concise, credible, and directly linked to the strategic inquiry desk so visitors can move from exploration to a brief without friction.

+
+
+
+ Filters +

Use the compact filters below to review immersive, corporate, or operational tracks.

+
+
+
+
+ +
+ + + + +
+ +
+ + + +
+
+
+ +
+
+
+ How delivery is structured +

A small operational model that makes the service catalog feel real.

+
+
+ +
+

+

+
+ +
+
+
+