added first three mockups

This commit is contained in:
2026-07-20 05:38:53 -05:00
parent 4dce423c4f
commit f839df7863
864 changed files with 235286 additions and 1 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,471 @@
/* Shared UX gallery + pricing hub (platform proposals) */
:root {
--ink: #12141a;
--muted: #5c6570;
--surface: #f3f5f7;
--card: #fff;
--border: #d9dee5;
--accent: #1a4f6e;
--accent-2: #c45c26;
--ok: #1a7f4b;
--warn: #b45309;
--banner: #111827;
--radius: 4px;
--font: "Segoe UI", system-ui, sans-serif;
--display: Georgia, "Times New Roman", serif;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: var(--font);
color: var(--ink);
background: var(--surface);
line-height: 1.5;
}
a { color: var(--accent); }
.proposal-banner {
background: var(--banner);
color: #e5e7eb;
font-size: 13px;
padding: 8px 16px;
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
justify-content: space-between;
position: sticky;
top: 0;
z-index: 100;
}
.proposal-banner a { color: #93c5fd; text-decoration: none; }
.proposal-banner a:hover { text-decoration: underline; }
.proposal-banner .tag {
display: inline-block;
background: var(--accent);
color: #fff;
padding: 2px 8px;
border-radius: 2px;
font-size: 11px;
letter-spacing: 0.04em;
text-transform: uppercase;
font-weight: 600;
}
.hub-hero {
background: linear-gradient(135deg, #0b1f33 0%, #1a4f6e 55%, #2a6f8f 100%);
color: #fff;
padding: 56px 24px 48px;
}
.hub-hero.paint {
background: linear-gradient(135deg, #1a2332 0%, #3d4a5c 50%, #c4a574 120%);
}
.hub-hero h1 {
font-family: var(--display);
font-weight: 700;
letter-spacing: -0.02em;
margin: 0 0 12px;
font-size: clamp(1.6rem, 3vw, 2.2rem);
}
.hub-hero p { max-width: 560px; margin: 0; opacity: 0.92; }
.hub-section {
max-width: 1100px;
margin: 0 auto;
padding: 32px 24px 8px;
}
.hub-section h2 {
font-family: var(--display);
font-size: 1.25rem;
margin: 0 0 6px;
}
.hub-section .sec-lead {
margin: 0 0 16px;
color: var(--muted);
font-size: 14px;
}
.hub-grid {
max-width: 1100px;
margin: 0 auto 40px;
padding: 0 24px;
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.hub-card {
background: var(--card);
border: 1px solid var(--border);
padding: 20px;
text-decoration: none;
color: inherit;
transition: border-color 0.15s, transform 0.15s;
display: block;
}
.hub-card:hover {
border-color: var(--accent);
transform: translateY(-2px);
color: inherit;
text-decoration: none;
}
.hub-card.planned {
opacity: 0.85;
border-style: dashed;
}
.hub-card .eyebrow {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--accent);
font-weight: 600;
margin-bottom: 6px;
}
.hub-card.paint .eyebrow { color: var(--accent-2); }
.hub-card h3 {
margin: 0 0 8px;
font-size: 18px;
font-family: var(--display);
}
.hub-card p { margin: 0; font-size: 14px; color: var(--muted); }
.hub-card .status {
display: inline-block;
margin-top: 10px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--ok);
}
.hub-card.planned .status { color: var(--warn); }
/* Pricing */
.pricing-layout {
max-width: 960px;
margin: 0 auto;
padding: 32px 24px 64px;
display: grid;
gap: 24px;
grid-template-columns: 1fr minmax(240px, 300px);
align-items: start;
}
@media (max-width: 800px) {
.pricing-layout { grid-template-columns: 1fr; }
}
.price-panel, .price-summary {
background: var(--card);
border: 1px solid var(--border);
padding: 20px;
}
.price-panel h2, .price-summary h2 {
font-family: var(--display);
font-size: 1.15rem;
margin: 0 0 16px;
}
.feat-row {
display: grid;
grid-template-columns: auto 1fr auto auto;
gap: 12px;
align-items: start;
padding: 14px 0;
border-bottom: 1px solid var(--border);
}
.feat-row:last-child { border-bottom: none; }
.feat-row.locked { background: #f8fafc; margin: 0 -12px; padding-left: 12px; padding-right: 12px; }
.feat-row label { font-weight: 600; cursor: pointer; }
.feat-row .desc { font-size: 13px; color: var(--muted); font-weight: 400; margin-top: 2px; }
.feat-row .amt { font-size: 13px; text-align: right; white-space: nowrap; color: var(--muted); }
.feat-row .amt strong { display: block; color: var(--ink); font-size: 14px; }
.feat-row input:disabled + .feat-text label { cursor: default; }
.dep-note { font-size: 12px; color: var(--warn); margin-top: 4px; }
.price-summary { position: sticky; top: 56px; }
.price-summary .line {
display: flex;
justify-content: space-between;
gap: 12px;
padding: 8px 0;
font-size: 14px;
}
.price-summary .total {
border-top: 2px solid var(--ink);
margin-top: 12px;
padding-top: 14px;
font-size: 18px;
font-weight: 700;
font-family: var(--display);
}
.price-summary .disclaimer {
margin-top: 16px;
font-size: 12px;
color: var(--muted);
}
.stub-page {
min-height: calc(100vh - 40px);
display: flex;
align-items: center;
justify-content: center;
padding: 40px 24px;
}
.stub-card {
background: var(--card);
border: 1px solid var(--border);
max-width: 480px;
padding: 32px;
text-align: center;
}
.stub-card h1 { font-family: var(--display); font-size: 1.5rem; margin: 0 0 8px; }
.stub-card p { color: var(--muted); margin: 0 0 20px; }
.stub-card a.btn {
display: inline-block;
background: var(--accent);
color: #fff;
text-decoration: none;
padding: 10px 18px;
font-weight: 600;
font-size: 14px;
}
/* —— Portal shell —— */
body.portal { margin: 0; background: var(--surface); font-family: var(--font); color: var(--ink); }
.portal-shell { display: flex; min-height: calc(100vh - 36px); }
.portal-sidebar {
width: 220px; flex-shrink: 0; background: #0b1f33; color: #cbd5e1;
display: flex; flex-direction: column;
}
.portal-brand {
padding: 16px 18px; border-bottom: 1px solid rgba(255,255,255,.08);
font-family: var(--display); font-weight: 700; color: #fff; font-size: 14px;
text-decoration: none; display: block;
}
.portal-brand span { display: block; color: #7dd3da; font-size: 11px; font-weight: 600; letter-spacing: .04em; margin-top: 2px; font-family: var(--font); }
.portal-nav { padding: 12px 0; flex: 1; overflow-y: auto; }
.portal-nav .nav-section {
font-size: 10px; text-transform: uppercase; letter-spacing: .1em; color: #64748b;
padding: 14px 18px 6px;
}
.portal-nav a {
display: block; padding: 9px 18px; color: #94a3b8; text-decoration: none; font-size: 13px;
border-left: 3px solid transparent;
}
.portal-nav a:hover { color: #fff; background: rgba(255,255,255,.04); }
.portal-nav a.active { color: #fff; background: rgba(26,79,110,.35); border-left-color: #7dd3da; }
.portal-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.portal-topbar {
background: #fff; border-bottom: 1px solid var(--border);
padding: 14px 24px; display: flex; align-items: center; justify-content: space-between; gap: 16px;
}
.portal-topbar h1 { margin: 0; font-size: 20px; font-family: var(--display); font-weight: 600; }
.portal-user { font-size: 13px; color: var(--muted); }
.portal-user strong { color: var(--ink); }
.portal-user a { color: var(--accent); }
.portal-content { padding: 24px; flex: 1; }
.proposal-note {
border-left: 3px solid var(--accent); background: #e8f0f5; padding: 12px 16px;
margin: 0 0 20px; font-size: 14px; color: #0d3a45;
}
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 16px; margin-bottom: 24px; }
.stat-card { background: #fff; border: 1px solid var(--border); padding: 18px; }
.stat-card .label { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.stat-card .value { font-size: 28px; font-weight: 700; font-family: var(--display); margin-top: 4px; }
.stat-card .delta { font-size: 12px; margin-top: 4px; color: var(--ok); }
.panel { background: #fff; border: 1px solid var(--border); margin-bottom: 20px; }
.panel-h {
padding: 14px 18px; border-bottom: 1px solid var(--border);
display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.panel-h h2 { margin: 0; font-size: 15px; font-family: var(--display); font-weight: 600; }
.panel-b { padding: 18px; }
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 900px) {
.portal-shell { flex-direction: column; }
.portal-sidebar { width: 100%; }
.portal-nav { display: flex; flex-wrap: wrap; padding: 8px; }
.portal-nav .nav-section { display: none; }
.portal-nav a { border-left: none; border-bottom: 2px solid transparent; padding: 8px 12px; }
.portal-nav a.active { border-bottom-color: #7dd3da; background: transparent; }
.split { grid-template-columns: 1fr; }
}
.btn {
display: inline-block; padding: 9px 18px; font-size: 13px; font-weight: 600;
border: none; cursor: pointer; text-decoration: none; font-family: var(--font);
}
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: #143d56; color: #fff; text-decoration: none; }
.btn-ghost { background: transparent; border: 1px solid var(--border); color: var(--ink); }
.btn-sm { padding: 6px 12px; font-size: 12px; }
.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th {
text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
color: var(--muted); padding: 10px 12px; border-bottom: 1px solid var(--border); font-weight: 600;
}
.table td { padding: 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.table tr:hover td { background: #f8fafc; }
.table a { color: var(--accent); text-decoration: none; font-weight: 500; }
.badge {
display: inline-block; padding: 2px 8px; font-size: 11px; font-weight: 600;
border-radius: 2px; text-transform: uppercase; letter-spacing: .04em;
}
.badge-new { background: #d0eef0; color: #00626c; }
.badge-contacted { background: #fef3c7; color: #92400e; }
.badge-won { background: #d1fae5; color: #065f46; }
.badge-lost { background: #fee2e2; color: #991b1b; }
.badge-sent { background: #d0eef0; color: #00626c; }
.badge-scheduled { background: #ede9fe; color: #5b21b6; }
.badge-paid { background: #d1fae5; color: #065f46; }
.badge-open { background: #fef3c7; color: #92400e; }
.badge-draft { background: #f3f4f6; color: #4b5563; }
.form-grid { display: grid; gap: 16px; }
.form-grid.cols-2 { grid-template-columns: 1fr 1fr; }
@media (max-width: 700px) { .form-grid.cols-2 { grid-template-columns: 1fr; } }
.field label { display: block; font-size: 12px; font-weight: 600; margin-bottom: 6px; color: #374151; }
.field input, .field select, .field textarea {
width: 100%; padding: 10px 12px; border: 1px solid var(--border); font: inherit; background: #fff; box-sizing: border-box;
}
.field textarea { min-height: 100px; resize: vertical; }
.field .hint { font-size: 12px; color: var(--muted); margin-top: 4px; }
.login-wrap {
min-height: calc(100vh - 36px); display: flex; align-items: center; justify-content: center;
background: linear-gradient(160deg, #0b1f33, #1a4f6e 60%, #e8f0f5 60%); padding: 24px;
}
.login-card { background: #fff; width: 100%; max-width: 400px; padding: 36px 32px; border: 1px solid var(--border); }
.login-card h1 { font-family: var(--display); font-size: 22px; margin: 0 0 6px; }
.login-card .sub { color: var(--muted); font-size: 14px; margin: 0 0 24px; }
.chart-ph {
height: 220px; background: linear-gradient(180deg, #e8f0f5 0%, #fff 100%);
border: 1px dashed #a8d8dc; display: flex; align-items: flex-end; gap: 8px; padding: 24px 16px 16px;
}
.chart-ph .bar { flex: 1; background: var(--accent); opacity: .75; border-radius: 2px 2px 0 0; min-height: 20px; }
.channel-tabs { display: flex; border-bottom: 1px solid var(--border); margin-bottom: 18px; }
.channel-tabs a {
padding: 10px 16px; font-size: 13px; font-weight: 600; color: var(--muted);
text-decoration: none; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.channel-tabs a.active { color: var(--accent); border-bottom-color: var(--accent); }
.connect-grid { display: grid; gap: 12px; }
.connect-card {
display: flex; gap: 14px; align-items: flex-start; text-align: left;
padding: 14px; border: 1px solid var(--border); background: #fff; width: 100%;
cursor: pointer; font: inherit; color: inherit;
}
.connect-card:hover { border-color: var(--accent); }
.platform-icon {
width: 40px; height: 40px; border-radius: 8px; display: flex; align-items: center;
justify-content: center; color: #fff; font-weight: 700; flex-shrink: 0; font-size: 14px;
}
.platform-icon.meta { background: #1877f2; }
.platform-icon.ig { background: linear-gradient(45deg, #f58529, #dd2a7b, #8134af); font-size: 12px; }
.platform-icon.li { background: #0a66c2; }
.social-phone {
width: 100%; max-width: 360px; background: #fff; border: 1px solid #d1d5db;
box-shadow: 0 8px 30px rgba(15,23,42,.12); overflow: hidden; font-size: 14px;
}
.soc-header { display: flex; align-items: center; gap: 10px; padding: 12px 14px; }
.soc-avatar {
width: 40px; height: 40px; background: var(--accent); color: #fff; display: flex;
align-items: center; justify-content: center; font-weight: 700; font-size: 13px; flex-shrink: 0; border-radius: 50%;
}
.soc-name { font-weight: 700; font-size: 13px; }
.soc-meta { font-size: 11px; color: #6b7280; }
.soc-caption { padding: 0 14px 12px; white-space: pre-wrap; line-height: 1.4; font-size: 13px; }
.soc-image { height: 200px; background: linear-gradient(135deg, #e5e7eb, #cbd5e1); }
.soc-actions { padding: 10px 14px; border-top: 1px solid #e5e7eb; font-size: 12px; color: #4b5563; font-weight: 600; }
.ai-chat { display: flex; flex-direction: column; height: min(640px, 75vh); background: #fff; border: 1px solid var(--border); }
.ai-chat-h { padding: 12px 14px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }
.ai-chat-h h2 { margin: 0; font-size: 15px; font-family: var(--display); }
.ai-pill { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; background: #e8f0f5; color: var(--accent); padding: 3px 8px; }
.ai-messages { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; background: linear-gradient(180deg, #f8fafc, #fff 40%); }
.ai-msg { max-width: 92%; font-size: 13px; line-height: 1.45; }
.ai-msg.bot { align-self: flex-start; }
.ai-msg.user { align-self: flex-end; }
.ai-msg .bubble { padding: 10px 12px; border: 1px solid var(--border); background: #fff; }
.ai-msg.user .bubble { background: var(--accent); color: #fff; border-color: var(--accent); }
.ai-msg .who { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-bottom: 4px; }
.ai-draft { margin-top: 8px; padding: 10px; background: #f8fafc; border: 1px dashed #a8d8dc; white-space: pre-wrap; font-size: 12px; }
.ai-chat-input { border-top: 1px solid var(--border); padding: 10px 12px; display: flex; gap: 8px; align-items: flex-end; }
.ai-chat-input textarea { flex: 1; min-height: 44px; max-height: 100px; resize: vertical; border: 1px solid var(--border); padding: 8px 10px; font: inherit; font-size: 13px; }
.postcard {
width: 100%; max-width: 420px; aspect-ratio: 6/4; position: relative; overflow: hidden;
background: #111; box-shadow: 0 12px 40px rgba(15,23,42,.25); color: #fff;
}
.postcard-media { position: absolute; inset: 0; background: linear-gradient(135deg, #1a4f6e, #0b1f33 60%, #c45c26); }
.postcard-copy { position: absolute; left: 20px; right: 20px; bottom: 18px; z-index: 2; }
.postcard-headline { font-family: var(--display); font-weight: 700; font-size: 26px; border-left: 4px solid #7dd3da; padding-left: 10px; line-height: 1.1; }
.postcard-sub { margin-top: 8px; font-size: 14px; opacity: .95; }
.size-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.size-option {
border: 1px solid var(--border); background: #fff; padding: 10px 12px; text-align: left;
cursor: pointer; font: inherit; color: inherit;
}
.size-option.active { border-color: var(--accent); background: #e8f0f5; box-shadow: inset 3px 0 0 var(--accent); }
.designer-layout { display: grid; grid-template-columns: minmax(240px, 320px) 1fr; gap: 20px; align-items: start; }
@media (max-width: 960px) { .designer-layout { grid-template-columns: 1fr; } }
.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; font-size: 12px; }
.calendar-grid .dow { text-align: center; color: var(--muted); font-weight: 600; padding: 6px; }
.calendar-grid .day {
aspect-ratio: 1; border: 1px solid var(--border); background: #fff; padding: 6px; position: relative;
}
.calendar-grid .day.has-post { background: #e8f0f5; }
.calendar-grid .day .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); margin-top: 4px; }
.steps { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 20px; }
.step {
display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--muted);
padding: 8px 12px; background: #fff; border: 1px solid var(--border);
}
.step span {
width: 22px; height: 22px; border-radius: 50%; background: #e5e7eb; color: #374151;
display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700;
}
.step.active { border-color: var(--accent); color: var(--ink); }
.step.active span { background: var(--accent); color: #fff; }
.dropzone { border: 2px dashed var(--border); padding: 32px; text-align: center; background: #f8fafc; }
.public-simple { max-width: 720px; margin: 40px auto; padding: 0 24px 64px; }
.public-simple .card { background: #fff; border: 1px solid var(--border); padding: 32px; }
.public-nav {
background: #fff; border-bottom: 1px solid var(--border);
padding: 14px 24px; display: flex; align-items: center; justify-content: space-between; gap: 16px;
}
.public-nav .brand { font-family: var(--display); font-weight: 700; color: var(--ink); text-decoration: none; font-size: 18px; }
.public-nav .links a { margin-left: 16px; font-size: 14px; color: var(--muted); text-decoration: none; }
.public-nav .links a:hover { color: var(--accent); }
.blog-list { display: grid; gap: 16px; }
.blog-item { background: #fff; border: 1px solid var(--border); padding: 20px; text-decoration: none; color: inherit; display: block; }
.blog-item:hover { border-color: var(--accent); color: inherit; text-decoration: none; }
.blog-item h3 { margin: 0 0 6px; font-family: var(--display); }
.blog-item .meta { font-size: 12px; color: var(--muted); margin-bottom: 8px; }
.blog-item p { margin: 0; font-size: 14px; color: var(--muted); }
.pay-box { max-width: 480px; margin: 48px auto; background: #fff; border: 1px solid var(--border); padding: 32px; }
.pay-box h1 { font-family: var(--display); margin: 0 0 8px; }
.pay-amount { font-size: 36px; font-weight: 700; font-family: var(--display); margin: 16px 0; }
.error-hero { text-align: center; padding: 80px 24px; }
.error-hero h1 { font-family: var(--display); font-size: 4rem; margin: 0; color: var(--accent); }
.muted { color: var(--muted); font-size: 12px; }
.theme-paint {
--accent: #c45c26;
--accent-2: #1a2332;
}
.theme-paint .portal-sidebar { background: #1a2332; }
.theme-paint .portal-nav a.active { background: rgba(196,92,38,.25); border-left-color: #c45c26; }
.theme-paint .login-wrap { background: linear-gradient(160deg, #1a2332, #3d4a5c 60%, #f3efe8 60%); }
.theme-paint .proposal-note { border-left-color: #c45c26; background: #faf6f1; color: #5c3d2e; }
.theme-paint .btn-primary { background: #c45c26; }
.theme-paint .btn-primary:hover { background: #a34a1e; }
.theme-paint .portal-nav a.active { border-left-color: #e8b88a; }
/* Vendor credit */
.aiml-site-credit {
text-align: center;
font-size: 12px;
color: var(--muted);
padding: 16px 24px 28px;
}
.aiml-site-credit a {
color: var(--accent);
text-decoration: none;
font-weight: 600;
}
.aiml-site-credit a:hover { text-decoration: underline; }
body.portal .aiml-site-credit {
border-top: 1px solid var(--border);
background: #fff;
margin-top: auto;
}
.brand-logo-link { display:inline-flex; align-items:center; }
.brand-logo--hub { height: 40px; width: auto; max-width: 180px; object-fit: contain; }
Binary file not shown.

After

Width:  |  Height:  |  Size: 847 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+146
View File
@@ -0,0 +1,146 @@
// ============================================
// PRELOADER
// ============================================
window.addEventListener('load', () => {
setTimeout(() => {
document.getElementById('preloader').classList.add('hidden');
document.body.style.overflow = 'visible';
}, 2200);
});
document.body.style.overflow = 'hidden';
// ============================================
// AOS INIT
// ============================================
AOS.init({
duration: 700,
once: true,
easing: 'ease-out-cubic',
offset: 60,
});
// ============================================
// STICKY NAVBAR
// ============================================
const nav = document.getElementById('mainNav');
window.addEventListener('scroll', () => {
nav.classList.toggle('scrolled', window.scrollY > 50);
});
// ============================================
// SMOOTH SCROLL FOR NAV LINKS
// ============================================
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
const target = document.querySelector(this.getAttribute('href'));
if (target) {
e.preventDefault();
const offset = nav.offsetHeight;
const top = target.getBoundingClientRect().top + window.scrollY - offset;
window.scrollTo({ top, behavior: 'smooth' });
// Close mobile menu if open
const bsCollapse = bootstrap.Collapse.getInstance(document.getElementById('navMenu'));
if (bsCollapse) bsCollapse.hide();
}
});
});
// ============================================
// ANIMATED COUNTERS
// ============================================
const counters = document.querySelectorAll('[data-target]');
const counterObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const el = entry.target;
const target = parseInt(el.dataset.target);
const suffix = el.querySelector('span') ? el.querySelector('span').textContent : '';
const duration = 1800;
const step = Math.ceil(target / (duration / 16));
let current = 0;
const update = () => {
current = Math.min(current + step, target);
el.innerHTML = current.toLocaleString() + `<span>${suffix}</span>`;
if (current < target) requestAnimationFrame(update);
};
update();
counterObserver.unobserve(el);
}
});
}, { threshold: 0.5 });
counters.forEach(c => counterObserver.observe(c));
// ============================================
// TESTIMONIAL SLIDER
// ============================================
(() => {
const track = document.getElementById('testiTrack');
const slides = track.children;
const totalSlides = slides.length;
const dots = document.querySelectorAll('.slider-dot');
let current = 0;
const go = (idx) => {
current = (idx + totalSlides) % totalSlides;
track.style.transform = `translateX(-${current * 100}%)`;
dots.forEach((d, i) => d.classList.toggle('active', i === current));
};
document.getElementById('testiPrev').addEventListener('click', () => go(current - 1));
document.getElementById('testiNext').addEventListener('click', () => go(current + 1));
dots.forEach(d => d.addEventListener('click', () => go(parseInt(d.dataset.index))));
// Auto-advance
let autoTimer = setInterval(() => go(current + 1), 5000);
track.parentElement.addEventListener('mouseenter', () => clearInterval(autoTimer));
track.parentElement.addEventListener('mouseleave', () => { autoTimer = setInterval(() => go(current + 1), 5000); });
// Touch support
let startX = 0;
track.addEventListener('touchstart', e => { startX = e.touches[0].clientX; }, { passive: true });
track.addEventListener('touchend', e => {
const diff = startX - e.changedTouches[0].clientX;
if (Math.abs(diff) > 50) go(diff > 0 ? current + 1 : current - 1);
});
})();
// ============================================
// SCROLL TO TOP BUTTON
// ============================================
const scrollTopBtn = document.getElementById('scrollTop');
window.addEventListener('scroll', () => {
scrollTopBtn.classList.toggle('visible', window.scrollY > 400);
});
scrollTopBtn.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
// ============================================
// CONTACT FORM SUBMISSION (demo)
// ============================================
document.getElementById('contactForm').addEventListener('submit', function (e) {
e.preventDefault();
const name = document.getElementById('name').value.trim();
const email = document.getElementById('email').value.trim();
const message = document.getElementById('message').value.trim();
if (!name || !email || !message) {
alert('Please fill in all required fields (Name, Email, Message).');
return;
}
this.style.display = 'none';
document.getElementById('formSuccess').style.display = 'block';
});
// ============================================
// NEWSLETTER (demo)
// ============================================
document.querySelector('.newsletter-btn').addEventListener('click', function () {
const input = this.previousElementSibling;
if (input.value && input.value.includes('@')) {
this.innerHTML = '<i class="fas fa-check"></i>';
this.style.background = '#22c55e';
input.value = '';
input.placeholder = 'Subscribed!';
}
});