From d5ac0af598f240ab360897624e15541692efe420 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 1 May 2026 21:45:54 +0000 Subject: [PATCH] beta --- assets/css/custom.css | 977 ++++++++++++++++++++++++++--------------- assets/js/main.js | 118 +++-- brief.php | 125 ++++++ case-study.php | 139 ++++++ contact.php | 196 +++++++++ includes/inquiries.php | 87 ++++ includes/site.php | 431 ++++++++++++++++++ index.php | 351 +++++++++------ insight.php | 91 ++++ insights.php | 71 +++ partials/footer.php | 43 ++ partials/header.php | 65 +++ work.php | 80 ++++ 13 files changed, 2258 insertions(+), 516 deletions(-) create mode 100644 brief.php create mode 100644 case-study.php create mode 100644 contact.php create mode 100644 includes/inquiries.php create mode 100644 includes/site.php create mode 100644 insight.php create mode 100644 insights.php create mode 100644 partials/footer.php create mode 100644 partials/header.php create mode 100644 work.php diff --git a/assets/css/custom.css b/assets/css/custom.css index 789132e..fdf1039 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,403 +1,694 @@ -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; +:root { + --page-bg: #f4f6f8; + --surface: #ffffff; + --surface-muted: #f8fafc; + --ink: #0f172a; + --ink-soft: #475569; + --line: #d9e1ea; + --line-strong: #b8c4d4; + --accent: #1d4ed8; + --success: #0f766e; + --shadow: 0 16px 38px rgba(15, 23, 42, 0.06); + --radius-sm: 8px; + --radius-md: 12px; + --radius-lg: 16px; + --container-max: 1180px; } -.main-wrapper { - display: flex; +html { + scroll-behavior: smooth; +} + +body { + background: var(--page-bg); + color: var(--ink); + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + font-size: 15px; + line-height: 1.65; + min-height: 100vh; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; +} + +.container { + max-width: var(--container-max); +} + +a { + color: inherit; + text-decoration: none; +} + +a:hover { + color: inherit; +} + +img { + max-width: 100%; + height: auto; +} + +.section-shell { + padding: 4.5rem 0; +} + +.page-hero { + padding-top: 3.5rem; + padding-bottom: 3.5rem; +} + +.site-header { + background: rgba(244, 246, 248, 0.92); + backdrop-filter: blur(14px); + border-bottom: 1px solid rgba(217, 225, 234, 0.85); + transition: box-shadow 0.2s ease, background-color 0.2s ease; +} + +.site-header.is-scrolled { + box-shadow: 0 12px 30px rgba(15, 23, 42, 0.08); + background: rgba(244, 246, 248, 0.97); +} + +.navbar { + padding: 1rem 0; +} + +.navbar-toggler { + border-color: var(--line); + border-radius: var(--radius-sm); + padding: 0.5rem 0.65rem; +} + +.navbar-toggler:focus, +.btn:focus, +.form-control:focus, +.form-select:focus, +.filter-chip:focus, +a:focus-visible, +button:focus-visible { + outline: none; + box-shadow: 0 0 0 0.25rem rgba(29, 78, 216, 0.14); +} + +.navbar-brand { + color: var(--ink); +} + +.brand-mark { + width: 2.75rem; + height: 2.75rem; + display: inline-flex; align-items: center; justify-content: center; - min-height: 100vh; - width: 100%; - padding: 20px; - box-sizing: border-box; - position: relative; - z-index: 1; -} - -@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); + border-radius: 10px; + background: var(--ink); + color: #ffffff; + font-size: 0.8rem; font-weight: 700; - font-size: 1.1rem; - display: flex; - justify-content: space-between; - align-items: center; + letter-spacing: 0.14em; } -.chat-messages { - flex: 1; - overflow-y: auto; - padding: 1.5rem; - display: flex; - flex-direction: column; - gap: 1.25rem; +.brand-mark--footer { + width: 2.5rem; + height: 2.5rem; + background: #ffffff; + color: var(--ink); } -/* Custom Scrollbar */ -::-webkit-scrollbar { - width: 6px; +.brand-name { + font-size: 0.95rem; + font-weight: 700; + letter-spacing: 0.02em; } -::-webkit-scrollbar-track { +.brand-subtitle, +.footer-muted, +.footer-small, +.mini-label, +.detail-label, +.panel-label, +.eyebrow, +.card-topline span, +.hero-proof span, +.service-badge { + color: var(--ink-soft); + font-size: 0.82rem; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.nav-link { + font-size: 0.92rem; + font-weight: 500; + color: var(--ink-soft); + padding: 0.55rem 0.9rem !important; + border-radius: var(--radius-sm); +} + +.nav-link:hover, +.nav-link.active { + color: var(--ink); + background: rgba(15, 23, 42, 0.05); +} + +.btn { + border-radius: 10px; + font-weight: 600; + padding: 0.82rem 1.15rem; + border-width: 1px; +} + +.btn-sm { + padding: 0.55rem 0.9rem; +} + +.btn-lg { + padding: 0.95rem 1.3rem; + font-size: 0.98rem; +} + +.btn-primary { + background: var(--ink); + border-color: var(--ink); +} + +.btn-primary:hover, +.btn-primary:focus { + background: #09101d; + border-color: #09101d; +} + +.btn-outline-dark { + border-color: var(--line-strong); + color: var(--ink); background: transparent; } -::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.3); - border-radius: 10px; +.btn-outline-dark:hover, +.btn-outline-dark:focus { + background: var(--surface); + border-color: var(--ink); + color: var(--ink); } -::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.5); +.hero-section { + padding-top: 5rem; + padding-bottom: 4rem; } -.message { - max-width: 85%; - padding: 0.85rem 1.1rem; - border-radius: 16px; - line-height: 1.5; +.hero-title, +.section-title { + font-size: clamp(2.3rem, 4.4vw, 4rem); + line-height: 1.05; + letter-spacing: -0.04em; + font-weight: 700; + margin: 0.85rem 0 1rem; + max-width: 13ch; +} + +.section-title { + font-size: clamp(2rem, 3.6vw, 3.1rem); + max-width: 15ch; +} + +.hero-copy, +.section-copy, +.footer-copy, +.surface-panel p, +.service-card p, +.case-study-card p, +.insight-card p, +.empty-state p, +.message-panel p { + color: var(--ink-soft); + font-size: 1rem; + max-width: 62ch; +} + +.hero-proof span, +.service-badge { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.45rem 0.75rem; + border: 1px solid var(--line); + background: var(--surface); + border-radius: 999px; + letter-spacing: 0.02em; + text-transform: none; + font-size: 0.85rem; +} + +.hero-panel, +.surface-panel, +.metric-card, +.service-card, +.case-study-card, +.insight-card, +.process-card, +.cta-panel, +.empty-state { + background: var(--surface); + border: 1px solid var(--line); + border-radius: var(--radius-lg); + box-shadow: var(--shadow); +} + +.surface-panel, +.service-card, +.case-study-card, +.insight-card, +.process-card, +.cta-panel, +.empty-state, +.metric-card { + padding: 1.5rem; +} + +.panel-heading { + font-size: 1.1rem; + font-weight: 700; + margin-top: 0.65rem; + margin-bottom: 1.2rem; + max-width: 18ch; +} + +.panel-stack { + display: grid; + gap: 0.85rem; +} + +.panel-item { + border: 1px solid var(--line); + border-radius: var(--radius-md); + padding: 0.95rem 1rem; + background: var(--surface-muted); +} + +.panel-item span, +.detail-label, +.panel-label { + display: block; + margin-bottom: 0.35rem; +} + +.panel-item strong, +.metric-card strong, +.reference-code, +.detail-grid strong { + font-size: 1rem; + font-weight: 700; + color: var(--ink); +} + +.hero-note { + margin-top: 1.1rem; + color: var(--ink-soft); 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); - 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; -} - -.admin-container h1 { - margin-top: 0; - color: #212529; - font-weight: 800; -} - -.table { - width: 100%; - border-collapse: separate; - border-spacing: 0 8px; +.stats-row { 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; +.metric-card { + height: 100%; + background: rgba(255, 255, 255, 0.92); } -.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 { +.metric-card strong { display: block; - margin-bottom: 0.5rem; - font-weight: 600; - font-size: 0.9rem; + font-size: 1.25rem; + margin-bottom: 0.2rem; + letter-spacing: -0.03em; } -.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; +.metric-card span { + display: block; + color: var(--ink-soft); + font-size: 0.92rem; } -.form-control:focus { - outline: none; - border-color: #23a6d5; - box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1); +.section-heading-row { + margin-bottom: 1rem; } -.header-container { - display: flex; - justify-content: space-between; - align-items: center; -} - -.header-links { +.service-card, +.case-study-card, +.insight-card { display: flex; + flex-direction: column; 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); -} - -.admin-card h3 { - margin-top: 0; - margin-bottom: 1.5rem; +.service-card h3, +.case-study-card h3, +.insight-card h3, +.content-row h3, +.process-card h3, +.detail-list-item h2, +.article-section h2 { + font-size: 1.12rem; + line-height: 1.35; font-weight: 700; + margin: 0; + letter-spacing: -0.02em; } -.btn-delete { - background: #dc3545; - color: white; - border: none; - padding: 0.25rem 0.5rem; - border-radius: 4px; - cursor: pointer; +.service-points, +.detail-list, +.detail-grid, +.process-grid { + display: grid; + gap: 0.95rem; } -.btn-add { - background: #212529; - color: white; - border: none; - padding: 0.5rem 1rem; - border-radius: 4px; - cursor: pointer; - margin-top: 1rem; +.service-points li { + position: relative; + padding-left: 1.2rem; + color: var(--ink-soft); } -.btn-save { - background: #0088cc; - color: white; - border: none; - padding: 0.8rem 1.5rem; - border-radius: 12px; - cursor: pointer; +.service-points li::before { + content: ''; + position: absolute; + left: 0; + top: 0.62rem; + width: 0.4rem; + height: 0.4rem; + border-radius: 999px; + background: var(--ink); +} + +.compact-panel .content-row, +.detail-list-item, +.article-section { + padding-bottom: 1rem; + border-bottom: 1px solid var(--line); + margin-bottom: 1rem; +} + +.compact-panel .content-row:last-child, +.detail-list-item:last-child, +.article-section:last-child { + margin-bottom: 0; + padding-bottom: 0; + border-bottom: 0; +} + +.process-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.process-card { + min-height: 100%; +} + +.process-index { + width: 2rem; + height: 2rem; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 999px; + background: var(--ink); + color: #ffffff; + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.08em; + margin-bottom: 1rem; +} + +.card-topline { + display: flex; + justify-content: space-between; + gap: 0.75rem; + align-items: center; +} + +.result-pills { + display: flex; + flex-wrap: wrap; + gap: 0.6rem; +} + +.result-pills span { + display: inline-flex; + align-items: center; + padding: 0.48rem 0.72rem; + border-radius: 999px; + background: var(--surface-muted); + border: 1px solid var(--line); + color: var(--ink-soft); + font-size: 0.88rem; +} + +.result-pills--large span { + font-size: 0.92rem; +} + +.text-link, +.crumb-link { font-weight: 600; - width: 100%; - transition: all 0.3s ease; + color: var(--ink); } -.webhook-url { - font-size: 0.85em; - color: #555; - margin-top: 0.5rem; +.text-link:hover, +.crumb-link:hover { + color: var(--accent); } -.history-table-container { - overflow-x: auto; - background: rgba(255, 255, 255, 0.4); +.filter-toolbar { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin-bottom: 1rem; +} + +.filter-chip { + border: 1px solid var(--line); + border-radius: 999px; + background: var(--surface); + color: var(--ink-soft); + font-size: 0.88rem; + padding: 0.6rem 0.9rem; + font-weight: 600; + transition: all 0.2s ease; +} + +.filter-chip:hover, +.filter-chip.is-active { + border-color: var(--ink); + color: var(--ink); + background: var(--surface-muted); +} + +[data-filter-item].is-hidden { + display: none !important; +} + +.form-panel { + padding: 1.75rem; +} + +.form-label { + font-size: 0.88rem; + font-weight: 600; + color: var(--ink); + margin-bottom: 0.45rem; +} + +.form-control, +.form-select { + min-height: 3rem; + border-radius: 10px; + border-color: var(--line-strong); + background: var(--surface); + color: var(--ink); + padding: 0.75rem 0.9rem; +} + +textarea.form-control { + min-height: 10rem; + resize: vertical; +} + +.form-control::placeholder { + color: #8fa0b5; +} + +.reference-code { + font-size: 1.5rem; + letter-spacing: 0.08em; +} + +.detail-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.detail-grid > div, +.message-panel { + border: 1px solid var(--line); + border-radius: var(--radius-md); padding: 1rem; + background: var(--surface-muted); +} + +.message-panel p { + margin: 0; +} + +.quote-panel { + border-left: 3px solid var(--ink); + padding-left: 1rem; +} + +.quote-panel p { + font-size: 1.05rem; + color: var(--ink); + margin-bottom: 0.45rem; +} + +.quote-panel span { + color: var(--ink-soft); + font-size: 0.9rem; +} + +.cta-panel { + display: flex; + gap: 1.5rem; + align-items: center; + justify-content: space-between; +} + +.site-footer { + background: var(--ink); + color: #ffffff; + border-color: rgba(255, 255, 255, 0.08) !important; +} + +.site-footer .footer-copy, +.site-footer .footer-muted, +.site-footer .footer-small, +.site-footer .footer-links a { + color: rgba(255, 255, 255, 0.72); +} + +.footer-links { + display: flex; + flex-wrap: wrap; + gap: 1rem; + justify-content: flex-start; +} + +.footer-links a:hover { + color: #ffffff; +} + +.site-toast { + border: 1px solid rgba(15, 23, 42, 0.12); border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.3); + box-shadow: 0 20px 40px rgba(15, 23, 42, 0.18); } -.history-table { - width: 100%; +.toast-header { + background: #ffffff; + color: var(--ink); } -.history-table-time { - width: 15%; - white-space: nowrap; - font-size: 0.85em; - color: #555; +.toast-body { + background: var(--surface); + color: var(--ink-soft); } -.history-table-user { - width: 35%; - background: rgba(255, 255, 255, 0.3); - border-radius: 8px; - padding: 8px; +.toast-dot { + width: 0.6rem; + height: 0.6rem; + border-radius: 999px; + background: var(--success); + display: inline-block; + margin-right: 0.5rem; } -.history-table-ai { - width: 50%; - background: rgba(255, 255, 255, 0.5); - border-radius: 8px; - padding: 8px; -} - -.no-messages { +.empty-state { text-align: center; - color: #777; -} \ No newline at end of file + padding: 2.5rem; +} + +.empty-state--page { + max-width: 720px; + margin: 0 auto; +} + +.empty-state h1, +.empty-state h2 { + font-size: 1.8rem; + line-height: 1.2; + letter-spacing: -0.03em; + margin-bottom: 0.75rem; +} + +.article-panel { + padding: 1.75rem; +} + +.article-section p { + margin-bottom: 0; +} + +.alert { + border-radius: 12px; + border-width: 1px; +} + +@media (max-width: 991.98px) { + .section-shell { + padding: 3.5rem 0; + } + + .hero-section { + padding-top: 4rem; + } + + .process-grid, + .detail-grid { + grid-template-columns: 1fr; + } + + .cta-panel { + flex-direction: column; + align-items: flex-start; + } + + .footer-links { + justify-content: flex-start; + } +} + +@media (max-width: 767.98px) { + .section-title, + .hero-title { + max-width: none; + } + + .surface-panel, + .service-card, + .case-study-card, + .insight-card, + .process-card, + .cta-panel, + .empty-state, + .metric-card { + padding: 1.25rem; + } + + .hero-proof { + gap: 0.55rem !important; + } + + .hero-proof span, + .service-badge { + width: 100%; + justify-content: flex-start; + } +} diff --git a/assets/js/main.js b/assets/js/main.js index d349598..df50818 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1,39 +1,99 @@ document.addEventListener('DOMContentLoaded', () => { - const chatForm = document.getElementById('chat-form'); - const chatInput = document.getElementById('chat-input'); - const chatMessages = document.getElementById('chat-messages'); + const header = document.querySelector('.site-header'); + const toastElement = document.getElementById('siteToast'); + const toastBody = toastElement ? toastElement.querySelector('.toast-body') : null; + const pageToastMessage = document.body.dataset.toastMessage || ''; - 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 showToast = (message) => { + if (!toastElement || !toastBody || !window.bootstrap) { + return; + } + toastBody.textContent = message; + window.bootstrap.Toast.getOrCreateInstance(toastElement, { delay: 3000 }).show(); }; - chatForm.addEventListener('submit', async (e) => { - e.preventDefault(); - const message = chatInput.value.trim(); - if (!message) return; + if (header) { + const toggleHeaderState = () => { + header.classList.toggle('is-scrolled', window.scrollY > 10); + }; + toggleHeaderState(); + window.addEventListener('scroll', toggleHeaderState, { passive: true }); + } - appendMessage(message, 'visitor'); - chatInput.value = ''; + if (pageToastMessage) { + showToast(pageToastMessage); + } - try { - const response = await fetch('api/chat.php', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ message }) + document.querySelectorAll('[data-copy-target]').forEach((button) => { + button.addEventListener('click', async () => { + const target = document.querySelector(button.dataset.copyTarget || ''); + if (!target) { + return; + } + + const value = target.textContent.trim(); + if (!value) { + return; + } + + try { + await navigator.clipboard.writeText(value); + showToast('Reference copied to clipboard.'); + } catch (error) { + showToast('Copy is unavailable in this browser.'); + } + }); + }); + + document.querySelectorAll('[data-filter-group]').forEach((group) => { + const targetSelector = group.dataset.filterTarget; + const emptySelector = group.dataset.emptyState; + const resetButton = document.querySelector('[data-reset-filter]'); + const target = targetSelector ? document.querySelector(targetSelector) : null; + const emptyState = emptySelector ? document.querySelector(emptySelector) : null; + const buttons = Array.from(group.querySelectorAll('[data-filter]')); + const items = target ? Array.from(target.querySelectorAll('[data-filter-item]')) : []; + + if (!target || buttons.length === 0 || items.length === 0) { + return; + } + + const applyFilter = (filter) => { + let visibleCount = 0; + items.forEach((item) => { + const matches = filter === 'all' || item.dataset.filterItem === filter; + item.classList.toggle('is-hidden', !matches); + if (matches) { + visibleCount += 1; + } }); - 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'); + + buttons.forEach((button) => { + button.classList.toggle('is-active', button.dataset.filter === filter); + }); + + if (emptyState) { + emptyState.classList.toggle('d-none', visibleCount > 0); + } + }; + + buttons.forEach((button) => { + button.addEventListener('click', () => applyFilter(button.dataset.filter || 'all')); + }); + + if (resetButton) { + resetButton.addEventListener('click', () => applyFilter('all')); } }); + + const navbarCollapse = document.getElementById('primaryNav'); + if (navbarCollapse && window.bootstrap) { + document.querySelectorAll('#primaryNav .nav-link').forEach((link) => { + link.addEventListener('click', () => { + if (window.innerWidth < 992) { + window.bootstrap.Collapse.getOrCreateInstance(navbarCollapse).hide(); + } + }); + }); + } }); diff --git a/brief.php b/brief.php new file mode 100644 index 0000000..4a6d941 --- /dev/null +++ b/brief.php @@ -0,0 +1,125 @@ +getMessage()); + $loadError = 'We could not load that brief right now.'; + } +} + +require __DIR__ . '/partials/header.php'; +?> +
+
+
+ Project brief +

Your request is in the queue.

+

Use the reference below if you want to discuss this brief internally or when we wire notifications next.

+
+
+ +
+
+ + + +
+

We could not find that brief.

+

Start a new project brief and we will capture the details from there.

+ Start a project brief +
+ +
+
+
+
+
+
Reference
+
+
+ +
+
+
+ Name + +
+
+ Company + +
+
+ Email + +
+
+ Status + +
+
+ Project type + +
+
+ Budget + +
+
+ Target launch + +
+
+ Submitted + +
+
+
+ Project brief +

+
+
+
+
+
+
Next steps
+
+

Internal review

+

We review the brief against scope, urgency, and likely team shape.

+
+
+

Response path

+

We recommend a discovery sprint, delivery pod, or a smaller technical diligence engagement.

+
+
+

What you can do now

+

Share the reference with stakeholders or continue exploring our work and insights.

+
+
+
+
Explore while you wait
+ +
+
+
+ +
+
+
+ diff --git a/case-study.php b/case-study.php new file mode 100644 index 0000000..cdf4bb1 --- /dev/null +++ b/case-study.php @@ -0,0 +1,139 @@ + +
+
+
+
+

Case study not found

+

The selected case study is unavailable. You can return to the work overview and choose another representative engagement.

+ Back to work +
+
+
+
+ +
+
+
+ ← Back to case studies +
+
+ +

+

+
+
+
+
+

Client profile

+

·

+
+
+

Timeline

+

+
+
+
+
+
+ +
+
+ + +
+
+ +
+
+
+ +
+
+
+
+
+
Challenge
+
+ +
+

needed a lower-risk path forward

+

+
+ +
+
+
+
+
+
Approach
+
+ +
+

What we changed

+

+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
Capabilities used
+
+ + + +
+
+

+ +
+
+
+
+
+
Technology focus
+
    + +
  • + +
+ Discuss a similar project +
+
+
+
+
+
+ diff --git a/contact.php b/contact.php new file mode 100644 index 0000000..8a83ecf --- /dev/null +++ b/contact.php @@ -0,0 +1,196 @@ + '', + 'company_name' => '', + 'email' => '', + 'project_type' => project_type_options()[0], + 'budget_range' => budget_options()[0], + 'launch_window' => launch_options()[0], + 'message' => '', +]; +$errors = []; +$submissionError = ''; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $form['full_name'] = trim((string) ($_POST['full_name'] ?? '')); + $form['company_name'] = trim((string) ($_POST['company_name'] ?? '')); + $form['email'] = trim((string) ($_POST['email'] ?? '')); + $form['project_type'] = trim((string) ($_POST['project_type'] ?? '')); + $form['budget_range'] = trim((string) ($_POST['budget_range'] ?? '')); + $form['launch_window'] = trim((string) ($_POST['launch_window'] ?? '')); + $form['message'] = trim((string) ($_POST['message'] ?? '')); + + if ($form['full_name'] === '' || $textLength($form['full_name']) < 2) { + $errors['full_name'] = 'Please enter your name.'; + } + if ($form['company_name'] === '' || $textLength($form['company_name']) < 2) { + $errors['company_name'] = 'Please enter your company name.'; + } + if (!filter_var($form['email'], FILTER_VALIDATE_EMAIL)) { + $errors['email'] = 'Please enter a valid work email.'; + } + if (!in_array($form['project_type'], project_type_options(), true)) { + $errors['project_type'] = 'Please choose a valid project type.'; + } + if (!in_array($form['budget_range'], budget_options(), true)) { + $errors['budget_range'] = 'Please choose a valid budget range.'; + } + if (!in_array($form['launch_window'], launch_options(), true)) { + $errors['launch_window'] = 'Please choose a valid launch window.'; + } + if ($form['message'] === '' || $textLength($form['message']) < 30) { + $errors['message'] = 'Please share at least a short project summary (30+ characters).'; + } + + if ($errors === []) { + try { + $reference = create_project_inquiry($form); + header('Location: /brief.php?ref=' . urlencode($reference) . '&created=1'); + exit; + } catch (Throwable $exception) { + error_log('Project inquiry save failed: ' . $exception->getMessage()); + $submissionError = 'We could not save your brief right now. Please try again in a moment.'; + } + } +} + +$fieldValue = static function (string $key) use ($form): string { + return htmlspecialchars($form[$key] ?? ''); +}; + +require __DIR__ . '/partials/header.php'; +?> +
+
+
+
+
+ Contact +

Tell us what you are building and we will shape the next step.

+

This first form is intentionally concise. It gives us enough context to propose the right engagement model, timeline, and next conversation.

+
+
+
+
What happens next
+
+

1. Brief review

+

We assess scope, urgency, and likely delivery shape within one business day.

+
+
+

2. Working session

+

If there is a fit, we use the first call to refine goals, constraints, and success metrics.

+
+
+

3. Clear recommendation

+

You receive a pragmatic recommendation: discovery sprint, delivery pod, or a smaller advisory step.

+
+
+
+
+
+
+ +
+
+
+
+
+ + + + + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + Stored securely in your project database for follow-up and future admin tooling. +
+
+
+
+
+
+
+
Good fit
+
    +
  • New customer-facing products with real launch pressure
  • +
  • Internal platforms that need cleaner workflows and reporting
  • +
  • Modernization programs where downtime or regressions are expensive
  • +
  • AI and automation work linked to measurable operational outcomes
  • +
+
+
+
Prefer to explore first?
+

Review our representative work and editorial thinking before sending a brief.

+ +
+
+
+
+
+
+ diff --git a/includes/inquiries.php b/includes/inquiries.php new file mode 100644 index 0000000..8bacf2c --- /dev/null +++ b/includes/inquiries.php @@ -0,0 +1,87 @@ +exec( + 'CREATE TABLE IF NOT EXISTS project_inquiries ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + reference_code VARCHAR(32) NOT NULL UNIQUE, + full_name VARCHAR(120) NOT NULL, + company_name VARCHAR(150) NOT NULL, + email VARCHAR(190) NOT NULL, + project_type VARCHAR(80) NOT NULL, + budget_range VARCHAR(80) NOT NULL, + launch_window VARCHAR(80) NOT NULL, + message TEXT NOT NULL, + status VARCHAR(30) NOT NULL DEFAULT "new", + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' + ); +} + +function generate_inquiry_reference(): string +{ + return 'NSL-' . gmdate('ymd') . '-' . strtoupper(bin2hex(random_bytes(2))); +} + +function create_project_inquiry(array $payload): string +{ + $pdo = db(); + ensure_project_inquiries_table($pdo); + + $reference = generate_inquiry_reference(); + $statement = $pdo->prepare( + 'INSERT INTO project_inquiries ( + reference_code, + full_name, + company_name, + email, + project_type, + budget_range, + launch_window, + message + ) VALUES ( + :reference_code, + :full_name, + :company_name, + :email, + :project_type, + :budget_range, + :launch_window, + :message + )' + ); + + $statement->bindValue(':reference_code', $reference, PDO::PARAM_STR); + $statement->bindValue(':full_name', $payload['full_name'], PDO::PARAM_STR); + $statement->bindValue(':company_name', $payload['company_name'], PDO::PARAM_STR); + $statement->bindValue(':email', $payload['email'], PDO::PARAM_STR); + $statement->bindValue(':project_type', $payload['project_type'], PDO::PARAM_STR); + $statement->bindValue(':budget_range', $payload['budget_range'], PDO::PARAM_STR); + $statement->bindValue(':launch_window', $payload['launch_window'], PDO::PARAM_STR); + $statement->bindValue(':message', $payload['message'], PDO::PARAM_STR); + $statement->execute(); + + return $reference; +} + +function find_project_inquiry(string $reference): ?array +{ + $pdo = db(); + ensure_project_inquiries_table($pdo); + + $statement = $pdo->prepare( + 'SELECT reference_code, full_name, company_name, email, project_type, budget_range, launch_window, message, status, created_at + FROM project_inquiries + WHERE reference_code = :reference_code + LIMIT 1' + ); + $statement->bindValue(':reference_code', $reference, PDO::PARAM_STR); + $statement->execute(); + + $result = $statement->fetch(); + return is_array($result) ? $result : null; +} diff --git a/includes/site.php b/includes/site.php new file mode 100644 index 0000000..0bfaa6f --- /dev/null +++ b/includes/site.php @@ -0,0 +1,431 @@ += 2) { + break; + } + } + + return $letters !== '' ? $letters : 'NL'; +} + +function site_description_default(): string +{ + $description = $_SERVER['PROJECT_DESCRIPTION'] ?? getenv('PROJECT_DESCRIPTION') ?: ''; + if (is_string($description) && trim($description) !== '') { + return trim($description); + } + + return 'Premium software engineering, modernization, and AI delivery for founders, product teams, and enterprise buyers.'; +} + +function page_title(string $pageTitle = ''): string +{ + return trim($pageTitle) !== '' ? trim($pageTitle) . ' | ' . site_name() : site_name(); +} + +function asset_url(string $path): string +{ + $relativePath = ltrim($path, '/'); + $absolutePath = dirname(__DIR__) . '/' . $relativePath; + $version = is_file($absolutePath) ? (string) filemtime($absolutePath) : gmdate('U'); + + return '/' . $relativePath . '?v=' . rawurlencode($version); +} + +function slugify_label(string $value): string +{ + $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', trim($value)) ?? ''); + return trim($slug, '-'); +} + +function nav_items(): array +{ + return [ + ['key' => 'home', 'label' => 'Home', 'href' => '/'], + ['key' => 'services', 'label' => 'Services', 'href' => '/#services'], + ['key' => 'work', 'label' => 'Work', 'href' => '/work.php'], + ['key' => 'insights', 'label' => 'Insights', 'href' => '/insights.php'], + ['key' => 'contact', 'label' => 'Contact', 'href' => '/contact.php'], + ]; +} + +function home_stats(): array +{ + return [ + ['value' => '2 weeks', 'label' => 'Typical discovery sprint'], + ['value' => 'Senior-only', 'label' => 'Delivery team composition'], + ['value' => 'Weekly', 'label' => 'Executive decision cadence'], + ['value' => '0 fluff', 'label' => 'Practical communication style'], + ]; +} + +function service_catalog(): array +{ + return [ + [ + 'badge' => 'Strategy', + 'title' => 'Product strategy & discovery', + 'summary' => 'Clarify scope, technical risk, and launch priorities before you commit engineering capacity.', + 'points' => ['Discovery workshops', 'Technical due diligence', 'Execution roadmap'], + ], + [ + 'badge' => 'Build', + 'title' => 'Custom product development', + 'summary' => 'Design and engineering for web platforms, internal tools, and customer-facing software that must perform under pressure.', + 'points' => ['UX & UI systems', 'Front-end & back-end delivery', 'QA and release management'], + ], + [ + 'badge' => 'Modernize', + 'title' => 'Platform modernization', + 'summary' => 'Reduce release friction, untangle legacy dependencies, and upgrade critical paths without pausing the business.', + 'points' => ['Architecture refactoring', 'Incremental migrations', 'Performance and security hardening'], + ], + [ + 'badge' => 'Scale', + 'title' => 'AI & workflow automation', + 'summary' => 'Apply automation and AI where it improves margin, speed, or decision quality — not as a gimmick.', + 'points' => ['Operational copilots', 'Workflow orchestration', 'Internal knowledge systems'], + ], + ]; +} + +function company_principles(): array +{ + return [ + [ + 'title' => 'Senior operators, not a bloated bench', + 'body' => 'Lean teams of senior product, design, and engineering leaders who can make trade-offs quickly.', + ], + [ + 'title' => 'Commercially grounded decisions', + 'body' => 'Every scope, tooling, and roadmap choice is tied back to launch speed, operating cost, and customer value.', + ], + [ + 'title' => 'Structured communication', + 'body' => 'You get concise weekly updates, clear next actions, and visibility into blockers before they become problems.', + ], + ]; +} + +function process_steps(): array +{ + return [ + [ + 'title' => 'Align the commercial problem', + 'body' => 'We start with goals, constraints, and the decisions leadership actually needs to make.', + ], + [ + 'title' => 'Design the operating model', + 'body' => 'Delivery plans cover scope, architecture, staffing, milestones, and risk management from day one.', + ], + [ + 'title' => 'Ship with cadence', + 'body' => 'Weekly demos, tight QA loops, and direct access to the people doing the work.', + ], + ]; +} + +function engagement_models(): array +{ + return [ + [ + 'title' => 'Dedicated delivery pod', + 'body' => 'Best for teams that need continuous product and engineering execution over multiple releases.', + ], + [ + 'title' => 'Strike team', + 'body' => 'A focused squad to unblock a launch, migration, or high-stakes product initiative.', + ], + [ + 'title' => 'Advisory sprint', + 'body' => 'Discovery, technical diligence, and roadmap planning for teams defining what to build next.', + ], + ]; +} + +function case_studies(): array +{ + return [ + [ + 'slug' => 'ledgerflow-payments-platform', + 'title' => 'Payments platform modernization without service interruption', + 'client' => 'LedgerFlow', + 'sector' => 'Fintech', + 'engagement' => 'Representative modernization program', + 'timeline' => '16-week phased rollout', + 'summary' => 'A staged modernization of payout infrastructure that reduced release risk while the business continued to scale.', + 'challenge' => [ + 'A legacy payout engine was slowing new feature delivery and introducing operational risk across finance workflows.', + 'Leadership needed a modernization plan that would not interrupt transaction volume or require a full rewrite.', + ], + 'solution' => [ + 'Reframed the platform into bounded services and rebuilt the highest-risk release path first.', + 'Introduced shared observability, release runbooks, and a decision forum across product, engineering, and operations.', + ], + 'results' => [ + ['value' => '10 days', 'label' => 'Release cycle, down from 6 weeks'], + ['value' => '38%', 'label' => 'Faster payout completion'], + ['value' => '0', 'label' => 'Customer-facing downtime during rollout'], + ], + 'capabilities' => ['Architecture modernization', 'Payments workflow design', 'Back-office tooling', 'QA automation'], + 'technology' => ['PHP', 'Vue', 'MySQL', 'Queue-based job orchestration'], + 'quote' => 'They brought clarity to a messy modernization program and made every decision feel lower risk.', + 'quote_by' => 'Representative VP Product', + ], + [ + 'slug' => 'atlasops-service-operations-suite', + 'title' => 'Unified operations software for a multi-region field service team', + 'client' => 'AtlasOps', + 'sector' => 'Enterprise', + 'engagement' => 'Representative platform build', + 'timeline' => '20-week delivery programme', + 'summary' => 'A custom operating layer for dispatch, customer visibility, and internal analytics across multiple service regions.', + 'challenge' => [ + 'Operations teams were switching between spreadsheets, email, and legacy systems to manage critical field work.', + 'Executives wanted one system of record without forcing a long procurement cycle for off-the-shelf software.', + ], + 'solution' => [ + 'Designed a modular operations suite with role-based dashboards, dispatch tooling, and client-facing status views.', + 'Mapped the rollout by region so training, process change, and adoption were handled incrementally.', + ], + 'results' => [ + ['value' => '41%', 'label' => 'Faster dispatch coordination'], + ['value' => '29%', 'label' => 'Reduction in manual status updates'], + ['value' => '3 regions', 'label' => 'Rolled out in the first release wave'], + ], + 'capabilities' => ['Product design', 'Operations tooling', 'Reporting dashboards', 'Change management'], + 'technology' => ['PHP', 'Bootstrap', 'MySQL', 'REST integrations'], + 'quote' => 'The new platform gave our operators one place to work and our clients a far more credible experience.', + 'quote_by' => 'Representative COO', + ], + [ + 'slug' => 'carebridge-patient-access-portal', + 'title' => 'Patient access portal for a regulated care experience', + 'client' => 'CareBridge', + 'sector' => 'Health Tech', + 'engagement' => 'Representative regulated product launch', + 'timeline' => '14-week MVP launch', + 'summary' => 'A patient portal and intake experience built for clarity, compliance, and a lower support burden.', + 'challenge' => [ + 'Patient onboarding was fragmented across forms, call centers, and third-party systems, creating friction and errors.', + 'The product needed to feel calm and trustworthy while supporting auditability and internal coordination.', + ], + 'solution' => [ + 'Designed an accessibility-first portal with guided intake, milestone tracking, and internal routing workflows.', + 'Embedded compliance checkpoints into the delivery process so the product team could move quickly with confidence.', + ], + 'results' => [ + ['value' => '33%', 'label' => 'Fewer incomplete intakes'], + ['value' => '21%', 'label' => 'Reduction in support tickets'], + ['value' => '4.8/5', 'label' => 'Pilot user satisfaction'], + ], + 'capabilities' => ['UX architecture', 'Accessible interfaces', 'Workflow automation', 'Compliance-aware delivery'], + 'technology' => ['PHP', 'Bootstrap', 'MySQL', 'API integrations'], + 'quote' => 'The experience felt clear, trustworthy, and operationally realistic from the first pilot cohort.', + 'quote_by' => 'Representative Product Lead', + ], + ]; +} + +function featured_case_studies(int $limit = 3): array +{ + return array_slice(case_studies(), 0, $limit); +} + +function case_study_by_slug(string $slug): ?array +{ + foreach (case_studies() as $study) { + if ($study['slug'] === $slug) { + return $study; + } + } + + return null; +} + +function insights(): array +{ + return [ + [ + 'slug' => 'de-risking-software-roadmaps', + 'title' => 'How senior teams de-risk software roadmaps before engineering begins', + 'category' => 'Delivery strategy', + 'published' => '2026-04-18', + 'read_time' => '5 min read', + 'excerpt' => 'High-performing delivery teams reduce uncertainty long before the first sprint starts. Here is the operating model we recommend.', + 'takeaways' => [ + 'Clarify decision rights before the kickoff.', + 'Define the minimum credible release, not the dream backlog.', + 'Treat risk management as a weekly operating rhythm.', + ], + 'sections' => [ + [ + 'heading' => 'Start with the decisions that matter', + 'body' => 'Most software projects do not fail because teams cannot code. They fail because leadership, product, and delivery move with different assumptions. Senior teams align on commercial priorities, delivery constraints, and what success means in measurable terms before engineering capacity is committed.', + ], + [ + 'heading' => 'Name what you will not build', + 'body' => 'A credible roadmap has edges. Defining what stays out of scope protects launch velocity, reduces hidden dependencies, and forces cleaner product thinking. The discipline to leave nice-to-have work behind is often what protects momentum.', + ], + [ + 'heading' => 'Review risk every week', + 'body' => 'Risk should not live in a static planning document. It should be reviewed as part of an operating cadence: what changed, what assumptions are now weaker, and what decisions leadership needs to make to keep delivery moving.', + ], + ], + ], + [ + 'slug' => 'when-custom-software-beats-saas', + 'title' => 'When custom software is the strategic move — and when it is not', + 'category' => 'Product decisions', + 'published' => '2026-03-29', + 'read_time' => '4 min read', + 'excerpt' => 'Custom builds create leverage when they protect the operating model, customer experience, or economics of the business.', + 'takeaways' => [ + 'Buy standard tools for standard processes.', + 'Build when software is tightly linked to margin, speed, or defensibility.', + 'The right answer may be a hybrid of both.', + ], + 'sections' => [ + [ + 'heading' => 'Start from the business model', + 'body' => 'If the workflow is part of how you win, generic tooling can become expensive friction. The right custom product is not about novelty; it is about protecting the parts of the business that create real advantage.', + ], + [ + 'heading' => 'Cost of ownership matters more than sticker price', + 'body' => 'Software decisions are often framed as build versus buy. In practice, the better question is which path creates the lowest total cost of ownership once workarounds, support burden, training, and change management are included.', + ], + [ + 'heading' => 'Prototype strategically', + 'body' => 'Teams do not need to build everything at once. A well-defined MVP can validate the operating model, prove adoption, and create the confidence needed for a broader platform investment.', + ], + ], + ], + [ + 'slug' => 'calmer-enterprise-launches', + 'title' => 'The communication pattern behind calmer enterprise software launches', + 'category' => 'Operations', + 'published' => '2026-02-11', + 'read_time' => '6 min read', + 'excerpt' => 'Tense launches are usually communication problems in disguise. A tighter operating rhythm changes the quality of execution.', + 'takeaways' => [ + 'Use one source of truth for blockers and release decisions.', + 'Keep executive updates concise and action-oriented.', + 'Design the rollout alongside the product, not after it.', + ], + 'sections' => [ + [ + 'heading' => 'Executives need clarity, not volume', + 'body' => 'The most helpful project update is often the shortest one: what moved, what is at risk, and what decision is required. When updates become narrative-heavy, the signal leadership needs often gets buried.', + ], + [ + 'heading' => 'Release planning is an operating discipline', + 'body' => 'Strong launch programs think through enablement, support, ownership, and fallbacks early. Shipping code is only one part of the release. The surrounding operating plan determines whether the launch feels controlled.', + ], + [ + 'heading' => 'Create room for calm escalation', + 'body' => 'Healthy software teams know how issues escalate, who decides, and how quickly that decision gets made. This removes ambiguity when a launch gets noisy and protects both speed and trust.', + ], + ], + ], + ]; +} + +function featured_insights(int $limit = 3): array +{ + return array_slice(insights(), 0, $limit); +} + +function insight_by_slug(string $slug): ?array +{ + foreach (insights() as $article) { + if ($article['slug'] === $slug) { + return $article; + } + } + + return null; +} + +function project_type_options(): array +{ + return [ + 'New product build', + 'Platform modernization', + 'AI workflow automation', + 'Design system / front-end refresh', + 'Technical discovery', + ]; +} + +function budget_options(): array +{ + return [ + '$15k–$30k', + '$30k–$60k', + '$60k–$120k', + '$120k+', + ]; +} + +function launch_options(): array +{ + return [ + 'ASAP (0–4 weeks)', + '1–2 months', + 'Quarterly plan (2–4 months)', + 'Exploring / timing not fixed', + ]; +} + +function format_display_date(string $date): string +{ + try { + return (new DateTimeImmutable($date))->format('F j, Y'); + } catch (Throwable $exception) { + return $date; + } +} + +function format_display_datetime(string $date): string +{ + try { + return (new DateTimeImmutable($date, new DateTimeZone('UTC')))->format('F j, Y \a\t H:i \U\T\C'); + } catch (Throwable $exception) { + return $date; + } +} + +function current_year(): string +{ + return gmdate('Y'); +} diff --git a/index.php b/index.php index 7205f3d..26f9188 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,213 @@ - - - - - - New Style - - - - - - - - - - - - - - - - - - - - - -
-
-

Analyzing your requirements and generating your website…

-
- Loading… +
+
+
+
+
+ Software engineering partner for high-stakes digital products +

Build serious software with a team that thinks like owners.

+

We help founders, product teams, and enterprise leaders design, build, and modernize software that needs to move with confidence. Strategy, delivery, and communication stay crisp from day one.

+ +
+ Senior-only delivery pods + Launch-focused roadmaps + Calm stakeholder communication +
+
+
+
+
Delivery snapshot
+
How we structure the first 30 days
+
+
+ Week 1 + Commercial and technical discovery +
+
+ Week 2 + Decision-ready roadmap and solution direction +
+
+ Week 3 + Design, architecture, and delivery sprint setup +
+
+ Week 4 + Production-ready build cadence with reporting +
+
+
Designed for teams that need a premium execution partner — not more noise.
+
+
+
+
+ +
+
+ + +
+
+
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

-
-
- Page updated: (UTC) -
- - + + +
+
+
+
+ Services +

A compact service mix built for momentum, quality, and commercial clarity.

+
+
+

Whether you need a new product, a cleaner architecture, or a more resilient operating system, we focus on the smallest move that creates durable leverage.

+
+
+
+ +
+
+ +

+

+
    + +
  • + +
+
+
+ +
+
+
+ +
+
+
+
+ How we work +

High-trust delivery for teams that care about both craft and decision speed.

+

We combine strategic clarity with hands-on execution so leadership gets confidence without needing to chase updates.

+
+
Why teams bring us in
+ +
+

+

+
+ +
+
+
+
+ $step): ?> +
+ 0 +

+

+
+ +
+
+
+
+
+ +
+
+
+
+ Representative work +

Selected engagements that show how we approach product, platform, and operations problems.

+
+ +
+
+ +
+ +
+ +
+
+
+ +
+
+
+
+ Insights +

Editorial thinking for teams planning their next software move.

+
+ +
+
+ +
+ +
+ +
+
+
+ +
+
+
+
+ Start the conversation +

Need a team that can turn ambiguity into a clear execution plan?

+

Send a short brief and we will shape the next steps around your business context, not a generic process.

+
+ +
+
+
+
+ diff --git a/insight.php b/insight.php new file mode 100644 index 0000000..d691f05 --- /dev/null +++ b/insight.php @@ -0,0 +1,91 @@ + +
+
+
+
+

Insight not found

+

The article you requested is unavailable. You can return to the insights overview and pick another piece.

+ Back to insights +
+
+
+
+ +
+
+
+ ← Back to insights +
+
+ +

+

+
+
+
+
+

Published

+

·

+
+
+
+
+
+
+ +
+
+
+
+
+
Key takeaways
+
    + +
  • + +
+
+
+
+ +
+
+
+
+
+ diff --git a/insights.php b/insights.php new file mode 100644 index 0000000..a31456e --- /dev/null +++ b/insights.php @@ -0,0 +1,71 @@ + +
+
+
+
+
+ Insights +

Short, decision-ready thinking for software leaders.

+

A library of practical guidance on product strategy, delivery discipline, and how to move faster without creating unnecessary risk.

+
+
+
+
Editorial focus
+
+

Roadmaps

+

What to build, what to defer, and how to protect launch velocity.

+
+
+

Delivery operations

+

Communication patterns, risk review, and release planning for complex programs.

+
+
+

Build vs buy

+

Where custom software creates strategic leverage and where it does not.

+
+
+
+
+
+
+ +
+
+
+ +
+ +
+ +
+
+
+
+ diff --git a/partials/footer.php b/partials/footer.php new file mode 100644 index 0000000..069a22d --- /dev/null +++ b/partials/footer.php @@ -0,0 +1,43 @@ + + + +
+
+
+ + + +
+
+
+
+ + + + + diff --git a/partials/header.php b/partials/header.php new file mode 100644 index 0000000..7bdc992 --- /dev/null +++ b/partials/header.php @@ -0,0 +1,65 @@ + + + + + + + <?= htmlspecialchars($resolvedTitle) ?> + + + + + + + + + + + + + + + + + data-toast-message=""> + diff --git a/work.php b/work.php new file mode 100644 index 0000000..174754c --- /dev/null +++ b/work.php @@ -0,0 +1,80 @@ + 'All sectors']; +foreach ($caseStudies as $study) { + $filters[slugify_label($study['sector'])] = $study['sector']; +} + +require __DIR__ . '/partials/header.php'; +?> +
+
+
+
+
+ Work +

Representative case studies for teams buying senior software delivery.

+

A look at the product, platform, and operations problems we solve when software has to feel credible, fast, and commercially sound.

+
+
+
+
Engagement models
+ +
+

+

+
+ +
+
+
+
+
+ +
+
+
+ $label): ?> + + +
+
+ +
+ +
+ +
+
+

No case studies match that filter.

+

Try a different sector or view the full list again.

+ +
+
+
+
+