chore: baseline of the live help4bis.com docroot as of 2026-10-23 (cockpit #168)

Excludes analytics/data, vendor, support, .env, ACME tokens.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 06:45:34 +10:00
co-authored by Claude Sonnet 5.5
parent 84c1a01338
commit 9e6a41fc5d
86 changed files with 20852 additions and 0 deletions
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 KiB

+59
View File
@@ -0,0 +1,59 @@
# Help4BIS Website Images - Download Manifest
**Date**: 2026-02-28
**Source**: https://help4bis.com
**Destination**: /var/www/html/dev-help4bis.com/assets/images/
**Total Size**: 2.2M
**Total Images**: 23 files
## Image Categories
### Logo & Branding (4 files)
- `logo-favicon.jpg` - 9.5K - Favicon version of ContactMan logo
- `logo-cropped.jpg` - 15K - Full cropped help4bis logo
- `logo-300x56.jpg` - 13K - Thumbnail version of logo
- `design-by-help4bis.jpg` - 3.6K - "Design by Help4BIS" badge
### Personnel (1 file)
- `danielle-photo.jpg` - 23K - Photo of Danielle (Running Duck Studio)
### Illustrations & Icons (3 files)
- `puzzle-man.png` - 69K - 3D man with puzzle illustration (full)
- `puzzle-man-188x300.png` - 37K - Puzzle man thumbnail
- `expert-technical-support.jpg` - 28K - Technical support icon/illustration
### Features & Benefits (4 files)
- `piggy-bank.jpg` - 29K - Cost savings/pricing illustration
- `seamless-scalability.jpg` - 32K - Scalability feature illustration
- `get-started.png` - 193K - Call-to-action visual
- `ancop-privacy-data-sovereignty.jpg` - 75K - Privacy/data sovereignty logo
### Portfolio / Client Case Studies (8 files)
- `rds-managed-wordpress.jpg` - 296K - Running Duck Studio managed WordPress project
- `mbac-managed-wordpress.jpg` - 132K - MBAC managed WordPress project
- `mineral-water-direct.jpg` - 184K - Mineral Water Direct portfolio
- `tradeshack-envisioning.jpg` - 133K - TradeShack Envisioning project
- `above-and-beyond.jpg` - 108K - Above & Beyond portfolio
- `bohemian-decor.jpg` - 137K - Bohemian Decor portfolio
- `madilly-border-collies.jpg` - 127K - Madilly Border Collies portfolio
- `shear-water.jpg` - 127K - ShearWater portfolio
- `tawny-trails.jpg` - 108K - Tawny Trails portfolio
- `startup-savvy-sarah.jpg` - 103K - Startup Savvy Sarah portfolio
### Screenshots & Documentation (1 file)
- `screenshot-2023-08-26.jpg` - 169K - Website feature screenshot
## Notes
1. **help3bis.com**: This domain was unreachable and appears to be a typo
2. **All images sourced from**: https://help4bis.com/wp-content/uploads/
3. **Image naming**: Converted from original WordPress uploads to descriptive lowercase with hyphens
4. **Permissions**: All files owned by help4bis:help4bis (644 permissions)
5. **Quality**: Original full-resolution images downloaded where available; thumbnails also included for key images
## Usage in Website Rebuild
- Portfolio images showcase Help4BIS's client work across various industries
- Logo variations available for different layout contexts
- Personnel photo provides business legitimacy and personal touch
- Feature illustrations support sales/marketing messaging
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

+226
View File
@@ -0,0 +1,226 @@
'use strict';
/* ══════════════════════════════════════
NAVIGATION — DROPDOWN (click-based)
══════════════════════════════════════ */
function initDropdowns() {
document.querySelectorAll('.nav__dropdown').forEach(item => {
const trigger = item.querySelector('.nav__trigger');
const menu = item.querySelector('.nav__dropdown-menu');
if (!trigger || !menu) return;
trigger.addEventListener('click', (e) => {
e.stopPropagation();
const isOpen = menu.classList.toggle('open');
trigger.setAttribute('aria-expanded', isOpen);
// Close siblings
document.querySelectorAll('.nav__dropdown-menu.open').forEach(m => {
if (m !== menu) {
m.classList.remove('open');
m.closest('.nav__dropdown').querySelector('.nav__trigger')?.setAttribute('aria-expanded','false');
}
});
});
});
// Close on outside click
document.addEventListener('click', () => {
document.querySelectorAll('.nav__dropdown-menu.open').forEach(m => {
m.classList.remove('open');
m.closest('.nav__dropdown').querySelector('.nav__trigger')?.setAttribute('aria-expanded','false');
});
});
// Close on Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
document.querySelectorAll('.nav__dropdown-menu.open').forEach(m => {
m.classList.remove('open');
m.closest('.nav__dropdown').querySelector('.nav__trigger')?.setAttribute('aria-expanded','false');
});
}
});
}
/* ══════════════════════════════════════
NAVIGATION — MOBILE BURGER
══════════════════════════════════════ */
function initMobileNav() {
const burger = document.getElementById('nav-burger');
const navLinks = document.getElementById('nav-links');
if (!burger || !navLinks) return;
burger.addEventListener('click', (e) => {
e.stopPropagation();
const isOpen = navLinks.classList.toggle('nav--open');
burger.setAttribute('aria-expanded', isOpen);
document.body.style.overflow = isOpen ? 'hidden' : '';
burger.innerHTML = isOpen ? iconX() : iconMenu();
});
// Close on link click
navLinks.querySelectorAll('a').forEach(link => {
link.addEventListener('click', () => {
navLinks.classList.remove('nav--open');
burger.setAttribute('aria-expanded','false');
document.body.style.overflow = '';
burger.innerHTML = iconMenu();
});
});
// Close on outside click
document.addEventListener('click', (e) => {
if (!burger.contains(e.target) && !navLinks.contains(e.target)) {
navLinks.classList.remove('nav--open');
burger.setAttribute('aria-expanded','false');
document.body.style.overflow = '';
burger.innerHTML = iconMenu();
}
});
}
function iconMenu() {
return `<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>`;
}
function iconX() {
return `<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`;
}
/* ══════════════════════════════════════
ACTIVE NAV LINK
══════════════════════════════════════ */
function initActiveNav() {
const path = window.location.pathname;
const file = path.split('/').pop() || 'index.html';
document.querySelectorAll('.nav__links a').forEach(a => {
const href = a.getAttribute('href');
if (href && (href === file || (file === '' && href === 'index.html'))) {
a.classList.add('active');
}
});
}
/* ══════════════════════════════════════
STICKY NAV SHADOW
══════════════════════════════════════ */
function initStickyNav() {
const nav = document.querySelector('.nav');
if (!nav) return;
const update = () => nav.classList.toggle('scrolled', window.scrollY > 30);
window.addEventListener('scroll', update, { passive: true });
update();
}
/* ══════════════════════════════════════
SCROLL REVEAL
══════════════════════════════════════ */
function initReveal() {
const io = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
e.target.classList.add('visible');
io.unobserve(e.target);
}
});
}, { threshold: 0.06, rootMargin: '0px 0px -24px 0px' });
// Individual reveal elements
document.querySelectorAll('.reveal').forEach(el => io.observe(el));
// Stagger groups (observe the container, trigger class on it)
document.querySelectorAll('.stagger').forEach(el => io.observe(el));
}
/* ══════════════════════════════════════
SMOOTH ANCHOR SCROLL
══════════════════════════════════════ */
function initAnchorScroll() {
document.querySelectorAll('a[href^="#"]').forEach(a => {
a.addEventListener('click', (e) => {
const id = a.getAttribute('href');
const target = document.querySelector(id);
if (target) {
e.preventDefault();
const top = target.getBoundingClientRect().top + window.scrollY - 76;
window.scrollTo({ top, behavior: 'smooth' });
}
});
});
}
/* ══════════════════════════════════════
FORM HANDLER
══════════════════════════════════════ */
function initForms() {
document.querySelectorAll('.intake-form').forEach(form => {
form.addEventListener('submit', async (e) => {
e.preventDefault();
const btn = form.querySelector('[type="submit"]');
const originalText = btn.textContent;
btn.textContent = 'Sending…';
btn.disabled = true;
// Collect all named fields
const data = {};
new FormData(form).forEach((val, key) => { data[key] = val; });
try {
const res = await fetch('/contact.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const json = await res.json();
if (json.ok) {
form.innerHTML = `
<div style="text-align:center;padding:2.5rem 1rem;">
<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" viewBox="0 0 24 24" fill="none" stroke="#16A34A" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin:0 auto 1.25rem;display:block"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
<h3 style="font-family:'DM Sans',sans-serif;color:#1A1A2E;margin-bottom:.5rem;font-size:22px;">Sent.</h3>
<p style="color:#6B7280;font-size:15px;max-width:400px;margin:0 auto;">We'll be back within one business day with a clear recommendation and price.</p>
</div>`;
} else {
throw new Error(json.error || 'Send failed');
}
} catch (err) {
// Show inline error — don't wipe the form
let errEl = form.querySelector('.form-error');
if (!errEl) {
errEl = document.createElement('p');
errEl.className = 'form-error';
errEl.style.cssText = 'color:#E8352A;font-size:14px;margin-top:0.75rem;';
form.querySelector('.intake-form__actions').appendChild(errEl);
}
errEl.textContent = err.message || 'Something went wrong. Please email project@help4bis.com directly.';
btn.textContent = originalText;
btn.disabled = false;
}
});
});
}
/* ══════════════════════════════════════
BACK TO TOP
══════════════════════════════════════ */
function initBackToTop() {
const btn = document.getElementById('back-to-top');
if (!btn) return;
window.addEventListener('scroll', () => {
btn.classList.toggle('visible', window.scrollY > 500);
}, { passive: true });
btn.addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' }));
}
/* ══════════════════════════════════════
INIT
══════════════════════════════════════ */
document.addEventListener('DOMContentLoaded', () => {
initDropdowns();
initMobileNav();
initActiveNav();
initStickyNav();
initReveal();
initAnchorScroll();
initForms();
initBackToTop();
});