Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae22501a6d | ||
|
|
a7fd6d145d | ||
|
|
9c5d1ab2cd | ||
|
|
afc32567eb | ||
|
|
1a3a6d61f9 | ||
|
|
5a9fc51de5 | ||
|
|
9303dc7f4d | ||
|
|
206daaef5d | ||
|
|
0535232446 | ||
|
|
da12910feb |
@@ -0,0 +1,27 @@
|
||||
# Normalise line endings across the repo.
|
||||
# Text files are stored as LF so editors and tools stay consistent.
|
||||
* text=auto eol=lf
|
||||
|
||||
# Explicit text formats - always LF
|
||||
*.php text eol=lf
|
||||
*.html text eol=lf
|
||||
*.css text eol=lf
|
||||
*.js text eol=lf
|
||||
*.json text eol=lf
|
||||
*.md text eol=lf
|
||||
*.txt text eol=lf
|
||||
*.svg text eol=lf
|
||||
*.xml text eol=lf
|
||||
|
||||
# Binary formats - leave bytes untouched
|
||||
*.webp binary
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
*.pdf binary
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
*.ttf binary
|
||||
*.eot binary
|
||||
@@ -31,3 +31,8 @@ public_html/
|
||||
# Gitea/Local Dev Artifacts
|
||||
.gitea/
|
||||
.tmp/
|
||||
email-signature.html
|
||||
|
||||
# Cowork session artifacts
|
||||
backup/
|
||||
.claude/
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
Options -Indexes
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Redirect /page.php → /page (canonical clean URL)
|
||||
RewriteCond %{THE_REQUEST} /([^.]+)\.php [NC]
|
||||
RewriteRule ^ /%1 [R=301,L,NE]
|
||||
|
||||
# Internally serve /page as /page.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME}\.php -f
|
||||
RewriteRule ^([^.]+)$ $1.php [L,NC]
|
||||
@@ -0,0 +1,519 @@
|
||||
/* ---- Pricing page specific styles ---- */
|
||||
|
||||
/* Mini hero */
|
||||
.pricing-hero {
|
||||
background: linear-gradient(145deg, var(--purple-deep) 0%, var(--purple-mid) 55%, #5B21B6 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 140px 0 100px;
|
||||
text-align: center;
|
||||
}
|
||||
.pricing-hero-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.pricing-hero-content .hero-eyebrow { margin-bottom: 16px; }
|
||||
.pricing-hero-title {
|
||||
font-family: var(--font-head);
|
||||
font-size: clamp(2.4rem, 5vw, 3.8rem);
|
||||
font-weight: 800;
|
||||
color: var(--white);
|
||||
line-height: 1.1;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.pricing-hero-title span {
|
||||
background: linear-gradient(135deg, var(--orange), var(--gold));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
.pricing-hero-lead {
|
||||
color: rgba(255,255,255,0.7);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.75;
|
||||
}
|
||||
.pricing-hero-wave {
|
||||
position: absolute;
|
||||
bottom: -1px; left: 0; right: 0;
|
||||
z-index: 3;
|
||||
line-height: 0;
|
||||
}
|
||||
.pricing-hero-wave svg { display: block; width: 100%; height: 80px; }
|
||||
|
||||
/* Section backgrounds */
|
||||
.section-bg-deep { background: #251D70; }
|
||||
.section-bg-mid { background: #3B2DA8; }
|
||||
|
||||
/* Nav active state */
|
||||
.nav-links a.nav-active {
|
||||
background: rgba(251,191,36,0.15) !important;
|
||||
border-color: rgba(251,191,36,0.3) !important;
|
||||
color: var(--gold) !important;
|
||||
}
|
||||
|
||||
/* Pricing card grid */
|
||||
.pricing-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 28px;
|
||||
}
|
||||
.pricing-grid--2col {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
max-width: 800px;
|
||||
margin: 28px auto 0;
|
||||
}
|
||||
|
||||
/* Base pricing card */
|
||||
.pricing-card {
|
||||
background: rgba(255,255,255,0.07);
|
||||
border: 1px solid rgba(255,255,255,0.12);
|
||||
border-radius: var(--radius);
|
||||
padding: 36px 32px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: box-shadow var(--transition), border-color var(--transition), background var(--transition);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.pricing-card::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, var(--gold), var(--orange));
|
||||
transform: scaleX(0);
|
||||
transition: transform var(--transition);
|
||||
transform-origin: left;
|
||||
}
|
||||
.pricing-card:hover {
|
||||
background: rgba(255,255,255,0.11);
|
||||
border-color: rgba(255,255,255,0.22);
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.25);
|
||||
}
|
||||
.pricing-card:hover::after { transform: scaleX(1); }
|
||||
|
||||
/* Popular highlight */
|
||||
.pricing-card--popular {
|
||||
border-color: var(--gold);
|
||||
background: rgba(251,191,36,0.07);
|
||||
}
|
||||
.pricing-card--popular::before {
|
||||
content: '\2736 Most Popular';
|
||||
position: absolute;
|
||||
top: 16px; right: 16px;
|
||||
background: linear-gradient(135deg, var(--gold), var(--orange));
|
||||
color: var(--white);
|
||||
font-family: var(--font-head);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
padding: 5px 12px;
|
||||
border-radius: 50px;
|
||||
}
|
||||
.pricing-card--popular:hover {
|
||||
border-color: var(--gold);
|
||||
box-shadow: 0 8px 40px rgba(251,191,36,0.18);
|
||||
}
|
||||
|
||||
/* Card elements */
|
||||
.pricing-emoji {
|
||||
font-size: 2.2rem;
|
||||
margin-bottom: 14px;
|
||||
line-height: 1;
|
||||
}
|
||||
.pricing-name {
|
||||
font-family: var(--font-head);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 800;
|
||||
color: var(--white);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.pricing-price {
|
||||
font-family: var(--font-head);
|
||||
font-size: 1.55rem;
|
||||
font-weight: 800;
|
||||
color: var(--gold);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.pricing-price span {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
color: rgba(255,255,255,0.45);
|
||||
}
|
||||
.pricing-divider {
|
||||
border: none;
|
||||
border-top: 1px solid rgba(255,255,255,0.12);
|
||||
margin: 16px 0;
|
||||
}
|
||||
.pricing-includes-label {
|
||||
font-family: var(--font-head);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.15em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(255,255,255,0.4);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.pricing-includes {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 9px;
|
||||
margin-bottom: 20px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.pricing-includes li {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
font-size: 0.88rem;
|
||||
color: rgba(255,255,255,0.75);
|
||||
line-height: 1.4;
|
||||
}
|
||||
.pricing-includes li::before {
|
||||
content: '\2736';
|
||||
color: var(--gold);
|
||||
font-size: 0.6rem;
|
||||
flex-shrink: 0;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.pricing-tagline {
|
||||
font-size: 0.83rem;
|
||||
color: rgba(255,255,255,0.4);
|
||||
font-style: italic;
|
||||
line-height: 1.55;
|
||||
margin-top: auto;
|
||||
padding-top: 14px;
|
||||
border-top: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
|
||||
/* Wizard's Bench - wide card */
|
||||
.bench-card {
|
||||
background: rgba(255,255,255,0.07);
|
||||
border: 1px solid rgba(255,255,255,0.12);
|
||||
border-radius: var(--radius);
|
||||
padding: 48px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 48px;
|
||||
align-items: start;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: box-shadow var(--transition), background var(--transition);
|
||||
}
|
||||
.bench-card::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, var(--teal), var(--purple-bright));
|
||||
}
|
||||
.bench-card:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.25);
|
||||
}
|
||||
.bench-emoji { font-size: 3rem; margin-bottom: 16px; line-height: 1; }
|
||||
.bench-name {
|
||||
font-family: var(--font-head);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 800;
|
||||
color: var(--white);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.bench-rate {
|
||||
font-family: var(--font-head);
|
||||
font-size: 2.8rem;
|
||||
font-weight: 800;
|
||||
color: var(--gold);
|
||||
line-height: 1;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.bench-rate span {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: rgba(255,255,255,0.45);
|
||||
}
|
||||
.bench-desc {
|
||||
color: rgba(255,255,255,0.6);
|
||||
font-size: 0.93rem;
|
||||
line-height: 1.75;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
/* Protection Charms / ward cards */
|
||||
.ward-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 28px;
|
||||
}
|
||||
.ward-card {
|
||||
background: rgba(255,255,255,0.07);
|
||||
border: 1px solid rgba(255,255,255,0.12);
|
||||
border-radius: var(--radius);
|
||||
padding: 36px 32px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: box-shadow var(--transition), border-color var(--transition), background var(--transition);
|
||||
}
|
||||
.ward-card::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, var(--gold), var(--orange));
|
||||
transform: scaleX(0);
|
||||
transition: transform var(--transition);
|
||||
transform-origin: left;
|
||||
}
|
||||
.ward-card:hover {
|
||||
background: rgba(255,255,255,0.11);
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.25);
|
||||
}
|
||||
.ward-card:hover::after { transform: scaleX(1); }
|
||||
.ward-card--mid {
|
||||
border-color: rgba(124,58,237,0.55);
|
||||
background: rgba(124,58,237,0.09);
|
||||
}
|
||||
.ward-card--top {
|
||||
border-color: var(--gold);
|
||||
background: rgba(251,191,36,0.06);
|
||||
}
|
||||
.ward-card--top:hover { border-color: var(--gold); }
|
||||
.ward-tier {
|
||||
font-family: var(--font-head);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--gold);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.ward-name {
|
||||
font-family: var(--font-head);
|
||||
font-size: 1.3rem;
|
||||
font-weight: 800;
|
||||
color: var(--white);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.ward-price {
|
||||
font-family: var(--font-head);
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
color: var(--gold);
|
||||
line-height: 1.15;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.ward-price span {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
color: rgba(255,255,255,0.4);
|
||||
}
|
||||
|
||||
/* Enquire button on each package */
|
||||
.pricing-enquire {
|
||||
margin-top: 18px;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
font-size: 0.85rem;
|
||||
padding: 12px 18px;
|
||||
}
|
||||
.bench-enquire { margin-top: 22px; }
|
||||
.ward-enquire { margin-top: 20px; }
|
||||
|
||||
/* "See full details" trigger */
|
||||
.pkg-details-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--gold);
|
||||
font-family: var(--font-head);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.03em;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
margin-top: 16px;
|
||||
padding: 6px 0;
|
||||
transition: gap var(--transition), color var(--transition);
|
||||
}
|
||||
.pkg-details-btn:hover { gap: 11px; color: var(--white); }
|
||||
.pricing-card, .ward-card, .bench-card { cursor: pointer; }
|
||||
|
||||
/* Package detail modal */
|
||||
.pkg-modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(10,6,30,0.78);
|
||||
-webkit-backdrop-filter: blur(4px);
|
||||
backdrop-filter: blur(4px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.25s ease, visibility 0.25s ease;
|
||||
}
|
||||
.pkg-modal-overlay.open { opacity: 1; visibility: visible; }
|
||||
.pkg-modal {
|
||||
background: linear-gradient(160deg, #2D1F6E 0%, #1E1550 100%);
|
||||
border: 1px solid rgba(251,191,36,0.25);
|
||||
border-radius: var(--radius);
|
||||
max-width: 560px;
|
||||
width: 100%;
|
||||
max-height: 88vh;
|
||||
overflow-y: auto;
|
||||
padding: 40px;
|
||||
position: relative;
|
||||
box-shadow: 0 24px 70px rgba(0,0,0,0.5);
|
||||
transform-origin: 50% 50%;
|
||||
will-change: transform;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.pkg-modal { transform: none !important; transition: none !important; }
|
||||
}
|
||||
.pkg-sparkle {
|
||||
position: fixed;
|
||||
z-index: 1100;
|
||||
pointer-events: none;
|
||||
line-height: 1;
|
||||
transform: translate(-50%, -50%);
|
||||
text-shadow: 0 0 6px rgba(255,255,255,0.4);
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
.pkg-modal-close {
|
||||
position: absolute;
|
||||
top: 18px; right: 18px;
|
||||
width: 38px; height: 38px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,0.18);
|
||||
background: rgba(255,255,255,0.06);
|
||||
color: rgba(255,255,255,0.8);
|
||||
font-size: 1.3rem;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background 0.2s, color 0.2s;
|
||||
}
|
||||
.pkg-modal-close:hover { background: rgba(255,255,255,0.14); color: var(--white); }
|
||||
.pkg-modal-emoji { font-size: 2.4rem; line-height: 1; margin-bottom: 10px; }
|
||||
.pkg-modal-name {
|
||||
font-family: var(--font-head);
|
||||
font-size: 1.6rem;
|
||||
font-weight: 800;
|
||||
color: var(--white);
|
||||
margin-bottom: 4px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
.pkg-modal-price {
|
||||
font-family: var(--font-head);
|
||||
font-size: 1.3rem;
|
||||
font-weight: 800;
|
||||
color: var(--gold);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.pkg-modal-price span { font-size: 0.85rem; font-weight: 500; color: rgba(255,255,255,0.45); }
|
||||
.pkg-modal-lead {
|
||||
color: rgba(255,255,255,0.62);
|
||||
font-size: 0.93rem;
|
||||
line-height: 1.65;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.pkg-modal-divider {
|
||||
border: none;
|
||||
border-top: 1px solid rgba(255,255,255,0.12);
|
||||
margin: 22px 0;
|
||||
}
|
||||
.pkg-range {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
.pkg-range-col {
|
||||
background: rgba(255,255,255,0.05);
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 14px;
|
||||
padding: 16px 18px;
|
||||
}
|
||||
.pkg-range-col h4 {
|
||||
font-family: var(--font-head);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--gold);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.pkg-range-col .amt {
|
||||
font-family: var(--font-head);
|
||||
font-size: 1.15rem;
|
||||
font-weight: 800;
|
||||
color: var(--white);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.pkg-range-col p { font-size: 0.84rem; color: rgba(255,255,255,0.72); line-height: 1.5; }
|
||||
.pkg-specs { display: flex; flex-direction: column; }
|
||||
.pkg-spec {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
font-size: 0.9rem;
|
||||
padding: 11px 0;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
.pkg-spec:last-child { border-bottom: none; }
|
||||
.pkg-spec .k { color: rgba(255,255,255,0.5); display: flex; align-items: center; gap: 9px; }
|
||||
.pkg-spec .k i { color: var(--gold); width: 16px; text-align: center; }
|
||||
.pkg-spec .v { color: var(--white); font-weight: 600; text-align: right; }
|
||||
.pkg-modal-enquire { margin-top: 26px; }
|
||||
html.modal-open { overflow: hidden; }
|
||||
@media (max-width: 520px) {
|
||||
.pkg-range { grid-template-columns: 1fr; }
|
||||
.pkg-modal { padding: 30px 22px; }
|
||||
}
|
||||
|
||||
/* CTA strip */
|
||||
.pricing-cta {
|
||||
background: linear-gradient(145deg, var(--purple-deep) 0%, #2D1860 100%);
|
||||
padding: 90px 0;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.pricing-cta::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 500px; height: 500px;
|
||||
background: radial-gradient(circle, rgba(124,58,237,0.2) 0%, transparent 70%);
|
||||
top: -200px; left: 50%;
|
||||
transform: translateX(-50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.pricing-cta-inner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.pricing-cta .section-lead { margin-bottom: 36px; }
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 900px) {
|
||||
.pricing-grid { grid-template-columns: 1fr 1fr; }
|
||||
.pricing-grid--2col { grid-template-columns: 1fr 1fr; max-width: 100%; margin-top: 28px; }
|
||||
.bench-card { grid-template-columns: 1fr; gap: 28px; padding: 32px; }
|
||||
.ward-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.pricing-grid { grid-template-columns: 1fr; }
|
||||
.pricing-grid--2col { grid-template-columns: 1fr; }
|
||||
.bench-rate { font-size: 2rem; }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.mascot-teleport { opacity: 1; }
|
||||
@@ -32,7 +32,11 @@
|
||||
|
||||
/* --- Reset --- */
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html { scroll-behavior: smooth; }
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
background: #251D70; /* matches dark sections so the reserved gutter blends */
|
||||
scrollbar-gutter: stable; /* reserve scrollbar space so locking scroll doesn't shift the page */
|
||||
}
|
||||
body {
|
||||
font-family: var(--font-body);
|
||||
color: var(--text);
|
||||
@@ -40,6 +44,23 @@ body {
|
||||
line-height: 1.6;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* --- Custom scrollbars (themed) --- */
|
||||
html { scrollbar-width: thin; scrollbar-color: rgba(251,191,36,0.55) rgba(255,255,255,0.06); }
|
||||
.pkg-modal, .nav-links { scrollbar-width: thin; scrollbar-color: rgba(251,191,36,0.55) rgba(255,255,255,0.06); }
|
||||
::-webkit-scrollbar { width: 12px; height: 12px; }
|
||||
::-webkit-scrollbar-track { background: rgba(255,255,255,0.05); }
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(180deg, var(--gold), var(--orange));
|
||||
background-clip: padding-box;
|
||||
border: 3px solid transparent; /* slims the thumb and leaves breathing room in the track */
|
||||
border-radius: 10px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(180deg, var(--orange), var(--gold));
|
||||
background-clip: padding-box;
|
||||
}
|
||||
::-webkit-scrollbar-corner { background: transparent; }
|
||||
img { max-width: 100%; display: block; }
|
||||
a { text-decoration: none; color: inherit; }
|
||||
ul { list-style: none; }
|
||||
@@ -163,14 +184,14 @@ ul { list-style: none; }
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0;
|
||||
z-index: 100;
|
||||
padding: 22px 0;
|
||||
transition: background 0.35s, padding 0.35s, box-shadow 0.35s;
|
||||
padding: 16px 0;
|
||||
transition: background 0.35s, box-shadow 0.35s;
|
||||
}
|
||||
.site-header.scrolled {
|
||||
background: rgba(22, 18, 58, 0.96);
|
||||
backdrop-filter: blur(14px);
|
||||
-webkit-backdrop-filter: blur(14px);
|
||||
padding: 14px 0;
|
||||
padding: 16px 0;
|
||||
box-shadow: 0 1px 12px rgba(0,0,0,0.15);
|
||||
}
|
||||
.nav-container {
|
||||
@@ -215,6 +236,11 @@ ul { list-style: none; }
|
||||
background: rgba(255,255,255,0.15);
|
||||
border-color: rgba(255,255,255,0.22);
|
||||
}
|
||||
.nav-links a.nav-active {
|
||||
background: rgba(251,191,36,0.15) !important;
|
||||
border-color: rgba(251,191,36,0.3) !important;
|
||||
color: var(--gold) !important;
|
||||
}
|
||||
.nav-cta {
|
||||
background: linear-gradient(135deg, var(--orange), #EF4444) !important;
|
||||
color: var(--white) !important;
|
||||
@@ -397,6 +423,8 @@ ul { list-style: none; }
|
||||
display: block;
|
||||
filter: drop-shadow(0 8px 24px rgba(0,0,0,0.35));
|
||||
}
|
||||
/* Hidden until the JS "transporter" effect materialises it (see main.js) */
|
||||
.mascot-teleport { opacity: 0; }
|
||||
|
||||
/* Wave */
|
||||
.hero-wave {
|
||||
@@ -456,6 +484,7 @@ ul { list-style: none; }
|
||||
.service-icon--orange { background: rgba(249,115,22,0.2); color: var(--orange); }
|
||||
.service-icon--purple { background: rgba(167,139,250,0.2); color: #A78BFA; }
|
||||
.service-icon--teal { background: rgba(13,148,136,0.2); color: #2DD4BF; }
|
||||
.service-icon--gold { background: rgba(251,191,36,0.2); color: var(--gold); }
|
||||
|
||||
.service-card h3 {
|
||||
font-family: var(--font-head);
|
||||
@@ -471,6 +500,102 @@ ul { list-style: none; }
|
||||
font-size: 0.94rem;
|
||||
}
|
||||
|
||||
/* Keep the icon and text above the plant row */
|
||||
.service-wide-icon, .service-wide-text { position: relative; z-index: 2; }
|
||||
|
||||
/* Full width feature band - sits below the 3 card grid */
|
||||
.service-wide {
|
||||
margin-top: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
background: linear-gradient(135deg, rgba(13,148,136,0.14), rgba(124,58,237,0.14));
|
||||
border: 1px solid rgba(255,255,255,0.12);
|
||||
border-radius: var(--radius);
|
||||
padding: 40px 44px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: box-shadow var(--transition), border-color var(--transition), background var(--transition);
|
||||
}
|
||||
.service-wide::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, var(--teal), var(--purple-bright));
|
||||
}
|
||||
.service-wide:hover {
|
||||
border-color: rgba(255,255,255,0.2);
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.25);
|
||||
}
|
||||
.service-wide-icon {
|
||||
flex-shrink: 0;
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.8rem;
|
||||
background: rgba(13,148,136,0.2);
|
||||
color: #2DD4BF;
|
||||
}
|
||||
.service-wide-text h3 {
|
||||
font-family: var(--font-head);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
margin-bottom: 10px;
|
||||
color: var(--white);
|
||||
}
|
||||
.service-wide-text p {
|
||||
color: rgba(255,255,255,0.7);
|
||||
line-height: 1.72;
|
||||
font-size: 0.97rem;
|
||||
max-width: 70ch;
|
||||
}
|
||||
|
||||
/* Row of plants that grow up from the bottom edge on hover - themed to the sustainable growth message */
|
||||
.service-wide-plants {
|
||||
position: absolute;
|
||||
left: 0; right: 0; bottom: 3px;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
padding: 0 28px;
|
||||
gap: 6px;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
.service-wide-plants i {
|
||||
color: var(--teal);
|
||||
font-size: 1.1rem;
|
||||
transform: translateY(120%);
|
||||
opacity: 0;
|
||||
transition: transform 0.55s cubic-bezier(0.34, 1.4, 0.5, 1), opacity 0.35s ease;
|
||||
}
|
||||
.service-wide:hover .service-wide-plants i {
|
||||
transform: translateY(0);
|
||||
opacity: 0.9;
|
||||
}
|
||||
.service-wide-plants i:nth-child(1) { transition-delay: 0.00s; font-size: 1.00rem; }
|
||||
.service-wide-plants i:nth-child(2) { transition-delay: 0.05s; font-size: 1.30rem; }
|
||||
.service-wide-plants i:nth-child(3) { transition-delay: 0.10s; font-size: 0.95rem; }
|
||||
.service-wide-plants i:nth-child(4) { transition-delay: 0.15s; font-size: 1.20rem; }
|
||||
.service-wide-plants i:nth-child(5) { transition-delay: 0.20s; font-size: 1.05rem; }
|
||||
.service-wide-plants i:nth-child(6) { transition-delay: 0.25s; font-size: 1.35rem; }
|
||||
.service-wide-plants i:nth-child(7) { transition-delay: 0.30s; font-size: 1.00rem; }
|
||||
.service-wide-plants i:nth-child(8) { transition-delay: 0.35s; font-size: 1.20rem; }
|
||||
.service-wide-plants i:nth-child(9) { transition-delay: 0.40s; font-size: 0.95rem; }
|
||||
.service-wide-plants i:nth-child(10) { transition-delay: 0.45s; font-size: 1.25rem; }
|
||||
.service-wide-plants i:nth-child(3n) { color: #34D399; }
|
||||
.service-wide-plants i:nth-child(3n+1) { color: #5EEAD4; }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.service-wide-plants i { transition: opacity 0.25s ease; transform: none; }
|
||||
.pricing-teaser-astro img { animation: none; }
|
||||
}
|
||||
|
||||
/* ================================================
|
||||
ABOUT
|
||||
================================================ */
|
||||
@@ -485,17 +610,16 @@ ul { list-style: none; }
|
||||
.about-image { position: relative; }
|
||||
.about-img-placeholder {
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
max-width: 420px;
|
||||
max-width: 480px;
|
||||
border-radius: 24px;
|
||||
background: linear-gradient(145deg, var(--purple-mid), var(--purple-bright));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 7rem;
|
||||
color: rgba(255,255,255,0.25);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
.about-img-placeholder img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
.about-badge {
|
||||
position: absolute;
|
||||
bottom: -16px;
|
||||
@@ -596,6 +720,14 @@ ul { list-style: none; }
|
||||
transition: transform 0.4s ease;
|
||||
}
|
||||
.project-row:hover .project-img-placeholder { transform: scale(1.08); }
|
||||
.project-row-img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
transition: transform 0.4s ease;
|
||||
}
|
||||
.project-row:hover .project-row-img img { transform: scale(1.08); }
|
||||
|
||||
/* Info */
|
||||
.project-row-info {
|
||||
@@ -648,6 +780,10 @@ ul { list-style: none; }
|
||||
}
|
||||
.project-work i { color: #2DD4BF; }
|
||||
|
||||
/* No-link row */
|
||||
.project-row--no-link { cursor: default; }
|
||||
.project-row--no-link::before { display: none; }
|
||||
|
||||
/* Arrow */
|
||||
.project-row-arrow {
|
||||
width: 40px;
|
||||
@@ -669,6 +805,59 @@ ul { list-style: none; }
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
/* ================================================
|
||||
PRICING TEASER
|
||||
================================================ */
|
||||
.pricing-teaser {
|
||||
background: #3B2DA8;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.pricing-teaser::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 420px;
|
||||
height: 420px;
|
||||
right: 12%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: radial-gradient(circle, rgba(251,191,36,0.16) 0%, transparent 68%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.pricing-teaser-inner {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(180px, 300px);
|
||||
gap: 48px;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.pricing-teaser-copy {
|
||||
text-align: left;
|
||||
max-width: 650px;
|
||||
}
|
||||
.pricing-teaser-copy .section-lead {
|
||||
margin: 0;
|
||||
max-width: 620px;
|
||||
}
|
||||
.pricing-teaser-btn { margin-top: 32px; }
|
||||
.pricing-teaser-astro {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
.pricing-teaser-astro img {
|
||||
width: min(100%, 280px);
|
||||
height: auto;
|
||||
filter: drop-shadow(0 18px 30px rgba(0,0,0,0.3));
|
||||
transform: rotate(8deg);
|
||||
animation: astroFloat 5.8s ease-in-out infinite;
|
||||
}
|
||||
@keyframes astroFloat {
|
||||
0%, 100% { transform: translateY(0) rotate(8deg); }
|
||||
50% { transform: translateY(-16px) rotate(11deg); }
|
||||
}
|
||||
|
||||
/* ================================================
|
||||
CONTACT
|
||||
================================================ */
|
||||
@@ -839,6 +1028,13 @@ ul { list-style: none; }
|
||||
}
|
||||
.footer-logo span { color: var(--orange); }
|
||||
.footer-tagline { font-size: 0.88rem; margin-bottom: 20px; }
|
||||
.footer-links { font-size: 0.82rem; margin-bottom: 14px; }
|
||||
.footer-links a {
|
||||
color: rgba(255,255,255,0.6);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition);
|
||||
}
|
||||
.footer-links a:hover { color: var(--orange); }
|
||||
.footer-copy { font-size: 0.78rem; }
|
||||
|
||||
/* ================================================
|
||||
@@ -865,12 +1061,24 @@ ul { list-style: none; }
|
||||
grid-template-columns: 1fr;
|
||||
gap: 40px;
|
||||
}
|
||||
.about-img-placeholder { max-width: 100%; aspect-ratio: 16/9; }
|
||||
.about-img-placeholder { max-width: 100%; }
|
||||
.about-badge { right: 16px; }
|
||||
.contact-wrapper {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 48px;
|
||||
}
|
||||
.pricing-teaser-inner {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 28px;
|
||||
text-align: center;
|
||||
}
|
||||
.pricing-teaser-copy {
|
||||
text-align: center;
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.pricing-teaser-copy .section-lead { margin: 0 auto; }
|
||||
.pricing-teaser-astro img { width: 210px; }
|
||||
}
|
||||
|
||||
/* Medium tablets */
|
||||
@@ -914,6 +1122,16 @@ ul { list-style: none; }
|
||||
}
|
||||
|
||||
.services-grid { grid-template-columns: 1fr; }
|
||||
.service-wide { flex-direction: column; text-align: center; gap: 20px; padding: 32px 24px; }
|
||||
.service-wide-text p { max-width: none; }
|
||||
.pricing-teaser::before {
|
||||
right: 50%;
|
||||
transform: translate(50%, -50%);
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
.pricing-teaser-inner { gap: 22px; }
|
||||
.pricing-teaser-astro img { width: 150px; }
|
||||
.project-row { grid-template-columns: 110px 1fr auto; gap: 18px; padding: 20px 0; }
|
||||
.project-row-img { height: 80px; }
|
||||
.project-img-placeholder { font-size: 1.8rem; }
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/* ---- Terms page specific styles ---- */
|
||||
.terms-hero {
|
||||
background: linear-gradient(145deg, var(--purple-deep) 0%, var(--purple-mid) 55%, #5B21B6 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 140px 0 100px;
|
||||
text-align: center;
|
||||
}
|
||||
.terms-hero-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.terms-hero-content .hero-eyebrow { margin-bottom: 16px; }
|
||||
.terms-hero-title {
|
||||
font-family: var(--font-head);
|
||||
font-size: clamp(2.4rem, 5vw, 3.8rem);
|
||||
font-weight: 800;
|
||||
color: var(--white);
|
||||
line-height: 1.1;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.terms-hero-title span {
|
||||
background: linear-gradient(135deg, var(--orange), var(--gold));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
.terms-hero-lead {
|
||||
color: rgba(255,255,255,0.7);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.75;
|
||||
}
|
||||
.terms-hero-wave {
|
||||
position: absolute;
|
||||
bottom: -1px; left: 0; right: 0;
|
||||
z-index: 3;
|
||||
line-height: 0;
|
||||
}
|
||||
.terms-hero-wave svg { display: block; width: 100%; height: 80px; }
|
||||
|
||||
.terms-body {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.terms-updated {
|
||||
display: inline-block;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
color: var(--purple-bright);
|
||||
background: var(--purple-light);
|
||||
padding: 6px 14px;
|
||||
border-radius: 999px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.terms-section { margin-bottom: 40px; }
|
||||
.terms-section h2 {
|
||||
font-family: var(--font-head);
|
||||
font-size: 1.45rem;
|
||||
font-weight: 700;
|
||||
color: var(--purple-deep);
|
||||
margin-bottom: 14px;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
}
|
||||
.terms-section h2 .terms-num {
|
||||
font-size: 1rem;
|
||||
color: var(--orange);
|
||||
font-weight: 800;
|
||||
}
|
||||
.terms-section p { margin-bottom: 14px; line-height: 1.8; color: var(--text); }
|
||||
.terms-section ul {
|
||||
list-style: none;
|
||||
margin: 0 0 14px;
|
||||
padding: 0;
|
||||
}
|
||||
.terms-section ul li {
|
||||
position: relative;
|
||||
padding-left: 28px;
|
||||
margin-bottom: 10px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
.terms-section ul li::before {
|
||||
content: "\2728";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 1px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.terms-section a { color: var(--purple-bright); text-decoration: underline; }
|
||||
.terms-section a:hover { color: var(--orange); }
|
||||
.terms-note {
|
||||
background: var(--gray-50);
|
||||
border: 1px solid var(--gray-200);
|
||||
border-left: 4px solid var(--orange);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 18px 22px;
|
||||
margin-top: 50px;
|
||||
font-size: 0.92rem;
|
||||
color: var(--gray-600);
|
||||
line-height: 1.7;
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
<?php
|
||||
// Estimates and pricing page
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Spells & Pricing - Fraxle.net</title>
|
||||
<meta name="description" content="Transparent pricing for web design, web apps, graphics, video, 3D, audio, tech support and ongoing maintenance from Fraxle.net - friendly digital services for small businesses in Oxfordshire and beyond.">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700;800&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/estimates.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- ===================== NAV ===================== -->
|
||||
<header class="site-header" id="site-header">
|
||||
<nav class="nav-container">
|
||||
<a href="/" class="nav-logo">
|
||||
<span class="logo-icon"><img style="width:32px;" src="img/tinyicon.png"></span>
|
||||
<span class="logo-text">Fraxle<span class="logo-dot">.net</span></span>
|
||||
</a>
|
||||
<ul class="nav-links" id="nav-links">
|
||||
<li><a href="index.php#services">Services</a></li>
|
||||
<li><a href="index.php#about">About</a></li>
|
||||
<li><a href="index.php#projects">Projects</a></li>
|
||||
<li><a href="estimates" class="nav-active">Estimates</a></li>
|
||||
<li><a href="index.php#contact" class="btn nav-cta">Get in Touch</a></li>
|
||||
</ul>
|
||||
<button class="nav-hamburger" id="nav-hamburger" aria-label="Toggle menu" aria-expanded="false">
|
||||
<span></span><span></span><span></span>
|
||||
</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- ===================== HERO ===================== -->
|
||||
<section class="pricing-hero">
|
||||
<div class="hero-orbs">
|
||||
<div class="orb orb-1"></div>
|
||||
<div class="orb orb-2"></div>
|
||||
<div class="orb orb-3"></div>
|
||||
</div>
|
||||
<div class="pricing-hero-content">
|
||||
<p class="hero-eyebrow">✦ Transparent Pricing ✦</p>
|
||||
<h1 class="pricing-hero-title">Spells & <span>Pricing</span></h1>
|
||||
<p class="pricing-hero-lead">Clear, honest estimates for websites, tech support and ongoing care. No hidden fees - just magic at a fair price.</p>
|
||||
</div>
|
||||
<div class="pricing-hero-wave">
|
||||
<svg viewBox="0 0 1440 80" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
|
||||
<path d="M0,40 C300,80 900,0 1440,50 L1440,80 L0,80 Z" fill="#251D70"/>
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== WEBSITE PACKAGES ===================== -->
|
||||
<section class="section-pad section-bg-deep">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<p class="section-eyebrow">Website Design</p>
|
||||
<h2 class="section-title">Choose Your Spell</h2>
|
||||
<p class="section-lead">From a simple first presence to a full ecommerce empire - there is a package for every ambition.</p>
|
||||
</div>
|
||||
|
||||
<div class="pricing-grid">
|
||||
<!-- The First Spark -->
|
||||
<div class="pricing-card reveal">
|
||||
<div class="pricing-emoji">🌱</div>
|
||||
<div class="pricing-name">The First Spark</div>
|
||||
<div class="pricing-price">From £250</div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Includes</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>Up to 5 pages</li>
|
||||
<li>Mobile-friendly design</li>
|
||||
<li>Contact form</li>
|
||||
<li>Basic SEO setup</li>
|
||||
<li>WordPress or Squarespace</li>
|
||||
</ul>
|
||||
<p class="pricing-tagline">Perfect for businesses taking their first steps into the digital realm.</p>
|
||||
<a href="index.php?package=first-spark#contact" class="btn btn-accent pricing-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- The Enchanted Workshop -->
|
||||
<div class="pricing-card reveal">
|
||||
<div class="pricing-emoji">✨</div>
|
||||
<div class="pricing-name">The Enchanted Workshop</div>
|
||||
<div class="pricing-price">£750 <span>- £1,500</span></div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Includes</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>Custom design</li>
|
||||
<li>Blog or news section</li>
|
||||
<li>Photo galleries</li>
|
||||
<li>Social media integration</li>
|
||||
<li>Analytics setup</li>
|
||||
<li>Search engine optimisation</li>
|
||||
</ul>
|
||||
<p class="pricing-tagline">For businesses ready to spread their wings and get properly noticed online.</p>
|
||||
<a href="index.php?package=enchanted-workshop#contact" class="btn btn-accent pricing-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- The Arcane Forge - Most Popular -->
|
||||
<div class="pricing-card pricing-card--popular reveal">
|
||||
<div class="pricing-emoji">🔮</div>
|
||||
<div class="pricing-name">The Arcane Forge</div>
|
||||
<div class="pricing-price">£1,500 <span>- £3,000</span></div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Includes</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>Advanced page layouts</li>
|
||||
<li>Custom functionality</li>
|
||||
<li>Enhanced forms</li>
|
||||
<li>Third-party integrations</li>
|
||||
<li>Performance optimisation</li>
|
||||
<li>Training and support</li>
|
||||
</ul>
|
||||
<p class="pricing-tagline">When a standard website simply won't contain your ambitions.</p>
|
||||
<a href="index.php?package=arcane-forge#contact" class="btn btn-accent pricing-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pricing-grid pricing-grid--2col">
|
||||
<!-- The Merchant's Portal -->
|
||||
<div class="pricing-card reveal">
|
||||
<div class="pricing-emoji">🛒</div>
|
||||
<div class="pricing-name">The Merchant's Portal</div>
|
||||
<div class="pricing-price">£3,000 <span>- £5,000</span></div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Includes</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>Shopify or WooCommerce</li>
|
||||
<li>Product catalogue setup</li>
|
||||
<li>Payment gateways</li>
|
||||
<li>Shipping configuration</li>
|
||||
<li>Customer account features</li>
|
||||
<li>Store management training</li>
|
||||
</ul>
|
||||
<p class="pricing-tagline">Everything you need to throw open the doors of your digital marketplace.</p>
|
||||
<a href="index.php?package=merchants-portal#contact" class="btn btn-accent pricing-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- The Grand Emporium -->
|
||||
<div class="pricing-card reveal">
|
||||
<div class="pricing-emoji">⚡</div>
|
||||
<div class="pricing-name">The Grand Emporium</div>
|
||||
<div class="pricing-price">£5,000 <span>- £7,500+</span></div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Includes</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>Multi-category stores</li>
|
||||
<li>Subscription products</li>
|
||||
<li>Advanced customer accounts</li>
|
||||
<li>Automation and workflows</li>
|
||||
<li>Marketing integrations</li>
|
||||
<li>Bespoke functionality</li>
|
||||
<li>Launch support</li>
|
||||
</ul>
|
||||
<p class="pricing-tagline">For businesses seeking a digital flagship rather than a simple online shop.</p>
|
||||
<a href="index.php?package=grand-emporium#contact" class="btn btn-accent pricing-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== BESPOKE WEB APPS ===================== -->
|
||||
<section class="section-pad section-bg-mid">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<p class="section-eyebrow">Custom Builds</p>
|
||||
<h2 class="section-title">The Spellforge</h2>
|
||||
<p class="section-lead">When a website alone won't cut it - bespoke web apps and tools, conjured to fit exactly how you work.</p>
|
||||
</div>
|
||||
<div class="pricing-grid pricing-grid--2col">
|
||||
<div class="pricing-card reveal">
|
||||
<div class="pricing-emoji">⚙️</div>
|
||||
<div class="pricing-name">The Cantrip</div>
|
||||
<div class="pricing-price">£750 <span>- £2,000</span></div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Includes</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>Single-purpose tool or mini-app</li>
|
||||
<li>Forms and data capture</li>
|
||||
<li>Database-backed</li>
|
||||
<li>Simple admin view</li>
|
||||
<li>Hosting and setup</li>
|
||||
</ul>
|
||||
<p class="pricing-tagline">A small but mighty tool, built to fit your workflow.</p>
|
||||
<a href="index.php?package=the-cantrip#contact" class="btn btn-accent pricing-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
<div class="pricing-card reveal">
|
||||
<div class="pricing-emoji">🏗️</div>
|
||||
<div class="pricing-name">The Grand Working</div>
|
||||
<div class="pricing-price">£3,000 <span>- £10,000+</span></div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Includes</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>Bespoke functionality</li>
|
||||
<li>User accounts and roles</li>
|
||||
<li>Custom database design</li>
|
||||
<li>Integrations and APIs</li>
|
||||
<li>Testing and launch</li>
|
||||
<li>Ongoing support</li>
|
||||
</ul>
|
||||
<p class="pricing-tagline">When off-the-shelf simply won't do - a system built around you.</p>
|
||||
<a href="index.php?package=grand-working#contact" class="btn btn-accent pricing-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== MYSTIC DIGITAL ARTS ===================== -->
|
||||
<section class="section-pad section-bg-deep">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<p class="section-eyebrow">Creative Services</p>
|
||||
<h2 class="section-title">The Mystic Digital Arts</h2>
|
||||
<p class="section-lead">Beyond the web - graphics, video, 3D and audio, crafted to make your brand unforgettable.</p>
|
||||
</div>
|
||||
<div class="pricing-grid pricing-grid--2col">
|
||||
<div class="pricing-card reveal">
|
||||
<div class="pricing-emoji">🎨</div>
|
||||
<div class="pricing-name">The Sigil Workshop</div>
|
||||
<div class="pricing-price">From £150</div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Includes</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>Logo design</li>
|
||||
<li>Brand colour and font palette</li>
|
||||
<li>Social media templates</li>
|
||||
<li>Print-ready business cards</li>
|
||||
<li>Source and web-ready files</li>
|
||||
</ul>
|
||||
<p class="pricing-tagline">A brand identity that turns heads and casts a lasting impression.</p>
|
||||
<a href="index.php?package=sigil-workshop#contact" class="btn btn-accent pricing-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
<div class="pricing-card reveal">
|
||||
<div class="pricing-emoji">🎬</div>
|
||||
<div class="pricing-name">The Moving Picture</div>
|
||||
<div class="pricing-price">From £350</div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Includes</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>On-location filming</li>
|
||||
<li>Editing and colour grading</li>
|
||||
<li>Titles and captions</li>
|
||||
<li>Licensed music and sound</li>
|
||||
<li>Social media cut-downs</li>
|
||||
</ul>
|
||||
<p class="pricing-tagline">Bring your story to life - lights, camera, enchantment.</p>
|
||||
<a href="index.php?package=moving-picture#contact" class="btn btn-accent pricing-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
<div class="pricing-card reveal">
|
||||
<div class="pricing-emoji">🧊</div>
|
||||
<div class="pricing-name">The Third Dimension</div>
|
||||
<div class="pricing-price">From £300</div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Includes</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>3D modelling</li>
|
||||
<li>Product visualisation</li>
|
||||
<li>Logo and title animation</li>
|
||||
<li>Render-ready assets</li>
|
||||
<li>Short animated clips</li>
|
||||
</ul>
|
||||
<p class="pricing-tagline">Objects and ideas, conjured into three dimensions.</p>
|
||||
<a href="index.php?package=third-dimension#contact" class="btn btn-accent pricing-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
<div class="pricing-card reveal">
|
||||
<div class="pricing-emoji">🎙️</div>
|
||||
<div class="pricing-name">Sonic Alchemy</div>
|
||||
<div class="pricing-price">From £200</div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Includes</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>Audio editing and cleanup</li>
|
||||
<li>Podcast production</li>
|
||||
<li>Voiceover mixing</li>
|
||||
<li>Music and jingles</li>
|
||||
<li>Mastering for release</li>
|
||||
</ul>
|
||||
<p class="pricing-tagline">Sound that casts a spell on the ear.</p>
|
||||
<a href="index.php?package=sonic-alchemy#contact" class="btn btn-accent pricing-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== WIZARD'S BENCH ===================== -->
|
||||
<section class="section-pad section-bg-mid">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<p class="section-eyebrow">Ad-hoc Support</p>
|
||||
<h2 class="section-title">The Wizard's Bench</h2>
|
||||
<p class="section-lead">For those unexpected gremlins that find their way into websites, email systems and office technology.</p>
|
||||
</div>
|
||||
<div class="bench-card reveal">
|
||||
<div class="bench-left">
|
||||
<div class="bench-emoji">🛠️</div>
|
||||
<div class="bench-name">The Wizard's Bench</div>
|
||||
<div class="bench-rate">£40 <span>/ hour</span></div>
|
||||
<p class="bench-desc">Technical support billed by the hour. Minimum one hour applies. Remote support available - no call-out charge for remote sessions.</p>
|
||||
<a href="index.php?package=wizards-bench#contact" class="btn btn-accent bench-enquire">
|
||||
<i class="fas fa-envelope"></i> Book some support
|
||||
</a>
|
||||
</div>
|
||||
<div class="bench-right">
|
||||
<p class="pricing-includes-label">Services include</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>WordPress troubleshooting</li>
|
||||
<li>Shopify assistance</li>
|
||||
<li>Email issues</li>
|
||||
<li>Domain and DNS management</li>
|
||||
<li>Website repairs</li>
|
||||
<li>General digital support</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== PROTECTION CHARMS ===================== -->
|
||||
<section class="section-pad section-bg-deep">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<p class="section-eyebrow">Ongoing Care</p>
|
||||
<h2 class="section-title">Protection Charms</h2>
|
||||
<p class="section-lead">Monthly plans to keep your website healthy, secure and performing at its best.</p>
|
||||
</div>
|
||||
<div class="ward-grid">
|
||||
<!-- Minor Ward -->
|
||||
<div class="ward-card reveal">
|
||||
<p class="ward-tier">Tier 1</p>
|
||||
<p class="ward-name">Minor Ward</p>
|
||||
<div class="ward-price">£10 <span>/ month</span></div>
|
||||
<hr class="pricing-divider">
|
||||
<ul class="pricing-includes">
|
||||
<li>WordPress updates</li>
|
||||
<li>Security monitoring</li>
|
||||
<li>Monthly backups</li>
|
||||
</ul>
|
||||
<a href="index.php?package=minor-ward#contact" class="btn btn-accent ward-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Greater Ward -->
|
||||
<div class="ward-card ward-card--mid reveal">
|
||||
<p class="ward-tier">Tier 2</p>
|
||||
<p class="ward-name">Greater Ward</p>
|
||||
<div class="ward-price">£25 <span>/ month</span></div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Everything in Minor Ward, plus</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>Monthly health checks</li>
|
||||
<li>Priority email support</li>
|
||||
<li>Performance monitoring</li>
|
||||
</ul>
|
||||
<a href="index.php?package=greater-ward#contact" class="btn btn-accent ward-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Merchant's Ward -->
|
||||
<div class="ward-card ward-card--top reveal">
|
||||
<p class="ward-tier">Tier 3</p>
|
||||
<p class="ward-name">Merchant's Ward</p>
|
||||
<div class="ward-price">£50 <span>/ month</span></div>
|
||||
<hr class="pricing-divider">
|
||||
<p class="pricing-includes-label">Everything in Greater Ward, plus</p>
|
||||
<ul class="pricing-includes">
|
||||
<li>Ecommerce monitoring</li>
|
||||
<li>Store maintenance</li>
|
||||
<li>Priority response times</li>
|
||||
<li>Monthly support allowance</li>
|
||||
</ul>
|
||||
<a href="index.php?package=merchants-ward#contact" class="btn btn-accent ward-enquire">
|
||||
<i class="fas fa-envelope"></i> Enquire about this
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== CTA ===================== -->
|
||||
<section class="pricing-cta">
|
||||
<div class="pricing-cta-inner">
|
||||
<p class="section-eyebrow">Ready to Begin?</p>
|
||||
<h2 class="section-title">Not sure which spell you need?</h2>
|
||||
<p class="section-lead">Drop me a message and we can figure out the right package together - no pressure, no jargon.</p>
|
||||
<a href="index.php#contact" class="btn btn-accent">
|
||||
<i class="fas fa-envelope"></i> Get in Touch
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== FOOTER ===================== -->
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="footer-logo">Fraxle<span>.net</span></p>
|
||||
<p class="footer-tagline">Friendly web & tech for small businesses</p>
|
||||
<p class="footer-links"><a href="terms">Terms & Conditions</a></p>
|
||||
<p class="footer-copy">© <?= date('Y') ?> Fraxle.net All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
<script src="js/estimates.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 193 KiB |
@@ -12,13 +12,14 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Fraxle.net – Web & Tech for Small Businesses</title>
|
||||
<meta name="description" content="Friendly website design, technology support and gadget wizardry for small businesses in Oxfordshire and beyond.">
|
||||
<title>Fraxle.net - Web & Tech for Small Businesses</title>
|
||||
<meta name="description" content="Friendly website design, online apps, tech and network support, plus graphics, video, 3D and audio for small businesses in Oxfordshire and beyond.">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700;800&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<noscript><link rel="stylesheet" href="css/noscript.css"></noscript>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -26,13 +27,14 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<header class="site-header" id="site-header">
|
||||
<nav class="nav-container">
|
||||
<a href="#hero" class="nav-logo">
|
||||
<span class="logo-icon">🧙</span>
|
||||
<span class="logo-icon"><img style="width:32px;" src="img/tinyicon.png"></span>
|
||||
<span class="logo-text">Fraxle<span class="logo-dot">.net</span></span>
|
||||
</a>
|
||||
<ul class="nav-links" id="nav-links">
|
||||
<li><a href="#services">Services</a></li>
|
||||
<li><a href="#about">About</a></li>
|
||||
<li><a href="#projects">Projects</a></li>
|
||||
<li><a href="estimates">Estimates</a></li>
|
||||
<li><a href="#contact" class="btn nav-cta">Get in Touch</a></li>
|
||||
</ul>
|
||||
<button class="nav-hamburger" id="nav-hamburger" aria-label="Toggle menu" aria-expanded="false">
|
||||
@@ -57,10 +59,10 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<span class="hero-title-accent">for Small Business</span>
|
||||
</h1>
|
||||
<div class="hero-mascots-mobile">
|
||||
<img src="img/wizcat4.webp" alt="Danny the tech-wizard and Astro the cosmic cat" class="mascot-img">
|
||||
<img src="img/wizcat4.webp" alt="Danny the tech-wizard and Astro the cosmic cat" class="mascot-img mascot-teleport">
|
||||
<p class="hero-caption">Danny the tech-wizard & Astro the cosmic cat</p>
|
||||
</div>
|
||||
<p class="hero-subtitle">Friendly website design, technology support and gadget wizardry — with a touch of enchantment. Based in Oxfordshire.</p>
|
||||
<p class="hero-subtitle">Friendly website design, online apps, tech support and creative wizardry - with a touch of enchantment. Based in Oxfordshire.</p>
|
||||
<div class="hero-actions">
|
||||
<a href="mailto:wizard@fraxle.net" class="btn btn-primary">
|
||||
<i class="fas fa-envelope"></i> wizard@fraxle.net
|
||||
@@ -72,7 +74,7 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
</div>
|
||||
<div class="hero-mascots">
|
||||
<div class="mascot-ring">
|
||||
<img src="img/wizcat4.webp" alt="Danny the tech-wizard and Astro the cosmic cat" class="mascot-img">
|
||||
<img src="img/wizcat4.webp" alt="Danny the tech-wizard and Astro the cosmic cat" class="mascot-img mascot-teleport">
|
||||
</div>
|
||||
<p class="hero-caption">Danny the tech-wizard & Astro the cosmic cat</p>
|
||||
</div>
|
||||
@@ -90,7 +92,7 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<div class="section-header">
|
||||
<p class="section-eyebrow">What I do</p>
|
||||
<h2 class="section-title">Spells I Cast</h2>
|
||||
<p class="section-lead">From conjuring websites to taming rogue gadgets — here's how I can help your business.</p>
|
||||
<p class="section-lead">From conjuring websites to taming rogue gadgets - here's how I can help your business.</p>
|
||||
</div>
|
||||
<div class="services-grid">
|
||||
<div class="service-card reveal">
|
||||
@@ -98,21 +100,42 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<i class="fas fa-wand-magic-sparkles"></i>
|
||||
</div>
|
||||
<h3>Web Design with a Touch of Alchemy</h3>
|
||||
<p>I conjure websites that don't just look good — they work like magic. Whether you need a simple landing page or an online store, I'll craft a site that's fast, secure, and aligned with your brand.</p>
|
||||
<p>I conjure websites that don't just look pretty - they load fast, climb the search rankings and turn curious visitors into customers. From a simple landing page to a full online store - WordPress, Shopify or Squarespace - every site is built to be quick, secure and unmistakably yours. Need something truly bespoke? I build custom online apps too.</p>
|
||||
</div>
|
||||
<div class="service-card reveal">
|
||||
<div class="service-icon service-icon--purple">
|
||||
<i class="fas fa-mobile-screen-button"></i>
|
||||
</div>
|
||||
<h3>Tech Support & Digital Sorcery</h3>
|
||||
<p>Need help taming your magical devices? Whether it's setting up your smartphone, syncing your tablet, connecting smart gadgets, or rescuing a misbehaving laptop — I'll help you make the most of your tech.</p>
|
||||
<p>Gadgets gone rogue? Whether it's setting up a new smartphone, syncing a stubborn tablet, sorting out office computers and networks, connecting smart-home gadgets or reviving a misbehaving laptop, I'll get your tech behaving - and show you how to keep it that way.</p>
|
||||
</div>
|
||||
<div class="service-card reveal">
|
||||
<div class="service-icon service-icon--teal">
|
||||
<div class="service-icon service-icon--gold">
|
||||
<i class="fas fa-palette"></i>
|
||||
</div>
|
||||
<h3>Master of the Mystic Digital Arts</h3>
|
||||
<p>Beyond the web, I craft the creative magic too - logos and graphics, video filming and editing, 3D modelling and animation, and audio production. Whatever your project conjures up, I can help bring it to life.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="service-wide reveal">
|
||||
<div class="service-wide-icon">
|
||||
<i class="fas fa-seedling"></i>
|
||||
</div>
|
||||
<h3>Empowering Ethical, Independent Business</h3>
|
||||
<p>I work especially with small businesses, not-for-profits, charities and farmers who want to grow sustainably in the digital age. Affordable, human-centred, and as easy to use as possible.</p>
|
||||
<div class="service-wide-text">
|
||||
<h3>Magic for Ethical, Independent Business</h3>
|
||||
<p>I work especially with small businesses, not-for-profits, charities and farmers who want to grow sustainably in the digital age. Affordable, human-centred and as easy to use as a well-cast spell.</p>
|
||||
</div>
|
||||
<div class="service-wide-plants" aria-hidden="true">
|
||||
<i class="fas fa-seedling"></i>
|
||||
<i class="fas fa-leaf"></i>
|
||||
<i class="fas fa-seedling"></i>
|
||||
<i class="fas fa-spa"></i>
|
||||
<i class="fas fa-seedling"></i>
|
||||
<i class="fas fa-leaf"></i>
|
||||
<i class="fas fa-seedling"></i>
|
||||
<i class="fas fa-spa"></i>
|
||||
<i class="fas fa-seedling"></i>
|
||||
<i class="fas fa-leaf"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,7 +147,8 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<div class="about-grid">
|
||||
<div class="about-image reveal">
|
||||
<div class="about-img-placeholder">
|
||||
<i class="fas fa-hat-wizard"></i>
|
||||
<img src="img/wizfixer.webp" alt="Danny the tech-wizard" class="mascot-img">
|
||||
<!-- <i class="fas fa-hat-wizard"></i> -->
|
||||
</div>
|
||||
<div class="about-badge">
|
||||
<i class="fas fa-location-dot"></i> Oxfordshire, UK
|
||||
@@ -134,7 +158,7 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<p class="section-eyebrow">About Fraxle.net</p>
|
||||
<h2 class="section-title">Conjuring Digital Magic for Small Businesses</h2>
|
||||
<p>Welcome to the realm where technology meets wizardry! Whether you're planting the seeds of a new venture or looking to level up your digital presence, I blend design, functionality, and futuristic tools to help your business flourish.</p>
|
||||
<p>Based primarily in <strong>Oxfordshire</strong>, I can bring these skills directly to your local organisation — and work remotely with anyone, anywhere.</p>
|
||||
<p>Based primarily in <strong>Oxfordshire</strong>, I can bring these skills directly to your local organisation - and work remotely with anyone, anywhere.</p>
|
||||
<div class="about-bullets">
|
||||
<div class="about-bullet">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
@@ -164,9 +188,10 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<p class="section-lead">A few recent projects from the spell book.</p>
|
||||
</div>
|
||||
<div class="projects-list">
|
||||
<article class="project-row reveal">
|
||||
<!-- No link — James Norman has no website -->
|
||||
<article class="project-row project-row--no-link reveal">
|
||||
<div class="project-row-img">
|
||||
<div class="project-img-placeholder"><i class="fas fa-leaf"></i></div>
|
||||
<img src="img/james.jpg" alt="James Norman" loading="lazy">
|
||||
</div>
|
||||
<div class="project-row-info">
|
||||
<div class="project-row-meta">
|
||||
@@ -176,11 +201,10 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<h3>James Norman</h3>
|
||||
<p class="project-work"><i class="fas fa-network-wired"></i> Office Network Update</p>
|
||||
</div>
|
||||
<div class="project-row-arrow"><i class="fas fa-arrow-right"></i></div>
|
||||
</article>
|
||||
<article class="project-row reveal">
|
||||
<a href="https://organicgrowersgathering.org/" target="_blank" rel="noopener" class="project-row reveal">
|
||||
<div class="project-row-img">
|
||||
<div class="project-img-placeholder"><i class="fas fa-tractor"></i></div>
|
||||
<img src="img/ogg.jpg" alt="The Organic Growers Gathering" loading="lazy">
|
||||
</div>
|
||||
<div class="project-row-info">
|
||||
<div class="project-row-meta">
|
||||
@@ -191,10 +215,10 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<p class="project-work"><i class="fas fa-video"></i> Video Recording & Editing</p>
|
||||
</div>
|
||||
<div class="project-row-arrow"><i class="fas fa-arrow-right"></i></div>
|
||||
</article>
|
||||
<article class="project-row reveal">
|
||||
</a>
|
||||
<a href="https://sunscope.net" target="_blank" rel="noopener" class="project-row reveal">
|
||||
<div class="project-row-img">
|
||||
<div class="project-img-placeholder"><i class="fas fa-cloud-bolt"></i></div>
|
||||
<img src="img/sunscope.jpg" alt="Sunscope.net" loading="lazy">
|
||||
</div>
|
||||
<div class="project-row-info">
|
||||
<div class="project-row-meta">
|
||||
@@ -205,10 +229,10 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<p class="project-work"><i class="fas fa-cloud-sun"></i> Advanced Weather App</p>
|
||||
</div>
|
||||
<div class="project-row-arrow"><i class="fas fa-arrow-right"></i></div>
|
||||
</article>
|
||||
<article class="project-row reveal">
|
||||
</a>
|
||||
<a href="https://camplight.co.uk/" target="_blank" rel="noopener" class="project-row reveal">
|
||||
<div class="project-row-img">
|
||||
<div class="project-img-placeholder"><i class="fas fa-campground"></i></div>
|
||||
<img src="img/camplight.jpg" alt="Camplight" loading="lazy">
|
||||
</div>
|
||||
<div class="project-row-info">
|
||||
<div class="project-row-meta">
|
||||
@@ -219,10 +243,10 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<p class="project-work"><i class="fas fa-globe"></i> Website Design & Build</p>
|
||||
</div>
|
||||
<div class="project-row-arrow"><i class="fas fa-arrow-right"></i></div>
|
||||
</article>
|
||||
<article class="project-row reveal">
|
||||
</a>
|
||||
<a href="https://www.hempen.co.uk/" target="_blank" rel="noopener" class="project-row reveal">
|
||||
<div class="project-row-img">
|
||||
<div class="project-img-placeholder"><i class="fas fa-store"></i></div>
|
||||
<img src="img/hempen.jpg" alt="Hempen Organic" loading="lazy">
|
||||
</div>
|
||||
<div class="project-row-info">
|
||||
<div class="project-row-meta">
|
||||
@@ -233,7 +257,26 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<p class="project-work"><i class="fas fa-cart-shopping"></i> eCommerce Website</p>
|
||||
</div>
|
||||
<div class="project-row-arrow"><i class="fas fa-arrow-right"></i></div>
|
||||
</article>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== PRICING TEASER ===================== -->
|
||||
<section id="pricing-teaser" class="pricing-teaser section-pad">
|
||||
<div class="container">
|
||||
<div class="pricing-teaser-inner reveal">
|
||||
<div class="pricing-teaser-copy">
|
||||
<p class="section-eyebrow">Transparent Pricing</p>
|
||||
<h2 class="section-title">Spells & Pricing</h2>
|
||||
<p class="section-lead">From a simple first website to a full ecommerce empire - plus ongoing care plans and ad-hoc tech support. See clear, honest estimates with no hidden fees, and enquire about any package in a click.</p>
|
||||
<a href="estimates" class="btn btn-primary pricing-teaser-btn">
|
||||
<i class="fas fa-scroll"></i> View Estimates & Packages
|
||||
</a>
|
||||
</div>
|
||||
<div class="pricing-teaser-astro" aria-hidden="true">
|
||||
<img src="img/astrofly2.webp" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -245,7 +288,7 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<div class="contact-intro reveal">
|
||||
<p class="section-eyebrow section-eyebrow--light">Say Hello</p>
|
||||
<h2 class="section-title section-title--light">Get in Touch</h2>
|
||||
<p>Have a project in mind? Need some tech support? Drop me a message and I'll get back to you as soon as the stars align.</p>
|
||||
<p>Have a project in mind? Need some tech support? Drop me a message and I'll get back to you - usually within one working day, as soon as the stars align.</p>
|
||||
<ul class="contact-details">
|
||||
<li>
|
||||
<i class="fas fa-envelope"></i>
|
||||
@@ -268,7 +311,7 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<?php if ($form_error): ?>
|
||||
<div class="form-error-msg">
|
||||
<i class="fas fa-triangle-exclamation"></i>
|
||||
<p>Something went wrong — please check your details (including the maths answer) and try again.</p>
|
||||
<p>Something went wrong - please check your details (including the maths answer) and try again.</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form class="contact-form" action="send.php" method="POST" novalidate>
|
||||
@@ -307,7 +350,8 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
|
||||
<div class="container">
|
||||
<p class="footer-logo">Fraxle<span>.net</span></p>
|
||||
<p class="footer-tagline">Friendly web & tech for small businesses</p>
|
||||
<p class="footer-copy">© <?= date('Y') ?> Fraxle.net. All rights reserved.</p>
|
||||
<p class="footer-links"><a href="terms">Terms & Conditions</a></p>
|
||||
<p class="footer-copy">© <?= date('Y') ?> Fraxle.net All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
@@ -0,0 +1,380 @@
|
||||
/* ===== Package detail modals ===== */
|
||||
(function () {
|
||||
var DETAILS = {
|
||||
'first-spark': {
|
||||
emoji: '🌱', name: 'The First Spark', price: 'From £250',
|
||||
lead: 'A clean, professional first website to get your business online quickly and affordably.',
|
||||
low: { amt: '£250', desc: 'A tidy 1–3 page, template-based site with your branding, a contact form and one round of revisions.' },
|
||||
high: { amt: '~£450', desc: 'Up to 5 pages with light customisation, basic SEO setup and a second round of revisions.' },
|
||||
specs: [
|
||||
{ i: 'fa-file-lines', k: 'Pages', v: 'Up to 5' },
|
||||
{ i: 'fa-rotate', k: 'Revisions', v: '2 rounds' },
|
||||
{ i: 'fa-clock', k: 'Typical turnaround', v: '1–2 weeks' },
|
||||
{ i: 'fa-wand-magic-sparkles', k: 'Platform', v: 'WordPress or Squarespace' }
|
||||
]
|
||||
},
|
||||
'enchanted-workshop': {
|
||||
emoji: '✨', name: 'The Enchanted Workshop', price: '£750 – £1,500',
|
||||
lead: 'A custom-designed site for businesses ready to stand out and grow their audience.',
|
||||
low: { amt: '£750', desc: '5–7 pages with a custom design, a blog or news section and social media links.' },
|
||||
high: { amt: '£1,500', desc: 'Up to 10 pages with photo galleries, analytics and full search-engine optimisation.' },
|
||||
specs: [
|
||||
{ i: 'fa-file-lines', k: 'Pages', v: '5 – 10' },
|
||||
{ i: 'fa-rotate', k: 'Revisions', v: '3 rounds' },
|
||||
{ i: 'fa-clock', k: 'Typical turnaround', v: '2–4 weeks' },
|
||||
{ i: 'fa-wand-magic-sparkles', k: 'Platform', v: 'WordPress or Squarespace' }
|
||||
]
|
||||
},
|
||||
'arcane-forge': {
|
||||
emoji: '🔮', name: 'The Arcane Forge', price: '£1,500 – £3,000',
|
||||
lead: 'Advanced layouts and bespoke functionality for businesses with bigger ambitions.',
|
||||
low: { amt: '£1,500', desc: '8–10 pages with advanced layouts, enhanced forms and a tailored design.' },
|
||||
high: { amt: '£3,000', desc: '12+ pages with custom functionality, third-party integrations, performance tuning and training.' },
|
||||
specs: [
|
||||
{ i: 'fa-file-lines', k: 'Pages', v: '10+' },
|
||||
{ i: 'fa-rotate', k: 'Revisions', v: 'Unlimited in scope' },
|
||||
{ i: 'fa-clock', k: 'Typical turnaround', v: '3–5 weeks' },
|
||||
{ i: 'fa-plug', k: 'Integrations', v: 'Included' }
|
||||
]
|
||||
},
|
||||
'merchants-portal': {
|
||||
emoji: '🛒', name: "The Merchant's Portal", price: '£3,000 – £5,000',
|
||||
lead: 'Everything you need to open a fully working online shop.',
|
||||
low: { amt: '£3,000', desc: 'Shopify or WooCommerce setup with up to ~50 products, payment gateways and shipping.' },
|
||||
high: { amt: '£5,000', desc: 'Up to ~200 products with a custom store design, customer accounts and store-management training.' },
|
||||
specs: [
|
||||
{ i: 'fa-box', k: 'Products', v: 'Up to ~200' },
|
||||
{ i: 'fa-credit-card', k: 'Payments', v: 'Gateways configured' },
|
||||
{ i: 'fa-clock', k: 'Typical turnaround', v: '4–6 weeks' },
|
||||
{ i: 'fa-store', k: 'Platform', v: 'Shopify / WooCommerce' }
|
||||
]
|
||||
},
|
||||
'grand-emporium': {
|
||||
emoji: '⚡', name: 'The Grand Emporium', price: '£5,000 – £7,500+',
|
||||
lead: 'A digital flagship for established and high-volume retailers.',
|
||||
low: { amt: '£5,000', desc: 'Multi-category store with subscription products and advanced customer accounts.' },
|
||||
high: { amt: '£7,500+', desc: 'Automation and workflows, marketing integrations, bespoke functionality and full launch support.' },
|
||||
specs: [
|
||||
{ i: 'fa-box', k: 'Products', v: 'Unlimited' },
|
||||
{ i: 'fa-robot', k: 'Automation', v: 'Workflows & marketing' },
|
||||
{ i: 'fa-clock', k: 'Typical turnaround', v: '6–10 weeks' },
|
||||
{ i: 'fa-rocket', k: 'Launch support', v: 'Included' }
|
||||
]
|
||||
},
|
||||
'wizards-bench': {
|
||||
emoji: '🛠️', name: "The Wizard's Bench", price: '£40 <span>/ hour</span>',
|
||||
lead: 'Pay-as-you-go technical support for websites, email and office tech. Minimum one hour; remote sessions carry no call-out charge.',
|
||||
specs: [
|
||||
{ i: 'fa-bolt', k: 'What an hour covers', v: 'Fixes, updates, email & DNS tweaks' },
|
||||
{ i: 'fa-hourglass-half', k: 'Minimum', v: '1 hour' },
|
||||
{ i: 'fa-house-laptop', k: 'Remote', v: 'No call-out charge' },
|
||||
{ i: 'fa-location-dot', k: 'On-site', v: 'Oxfordshire area' },
|
||||
{ i: 'fa-file-invoice', k: 'Larger jobs', v: 'Quoted upfront' }
|
||||
]
|
||||
},
|
||||
'minor-ward': {
|
||||
emoji: '🛡️', name: 'Minor Ward', price: '£10 <span>/ month</span>',
|
||||
lead: 'Essential upkeep to keep a simple WordPress site safe and current.',
|
||||
specs: [
|
||||
{ i: 'fa-arrows-rotate', k: 'Updates', v: 'WordPress core & plugins' },
|
||||
{ i: 'fa-shield-halved', k: 'Security', v: 'Monitoring' },
|
||||
{ i: 'fa-database', k: 'Backups', v: 'Monthly' },
|
||||
{ i: 'fa-headset', k: 'Support time', v: 'Billed at bench rate' },
|
||||
{ i: 'fa-circle-check', k: 'Best for', v: 'Brochure sites' }
|
||||
]
|
||||
},
|
||||
'greater-ward': {
|
||||
emoji: '🛡️', name: 'Greater Ward', price: '£25 <span>/ month</span>',
|
||||
lead: 'Everything in Minor Ward, plus proactive checks and priority support for active business sites.',
|
||||
specs: [
|
||||
{ i: 'fa-stethoscope', k: 'Health checks', v: 'Monthly' },
|
||||
{ i: 'fa-bolt', k: 'Support', v: 'Priority email' },
|
||||
{ i: 'fa-gauge-high', k: 'Performance', v: 'Monitored' },
|
||||
{ i: 'fa-clock', k: 'Support allowance', v: '~30 mins / month' },
|
||||
{ i: 'fa-circle-check', k: 'Best for', v: 'Active business sites' }
|
||||
]
|
||||
},
|
||||
'merchants-ward': {
|
||||
emoji: '🛡️', name: "Merchant's Ward", price: '£50 <span>/ month</span>',
|
||||
lead: 'Everything in Greater Ward, plus ecommerce-grade monitoring and maintenance for online stores.',
|
||||
specs: [
|
||||
{ i: 'fa-cart-shopping', k: 'Ecommerce', v: 'Store monitoring' },
|
||||
{ i: 'fa-screwdriver-wrench', k: 'Maintenance', v: 'Store upkeep' },
|
||||
{ i: 'fa-bolt', k: 'Response', v: 'Priority' },
|
||||
{ i: 'fa-clock', k: 'Support allowance', v: '~1 hour / month' },
|
||||
{ i: 'fa-circle-check', k: 'Best for', v: 'Online stores' }
|
||||
]
|
||||
},
|
||||
'the-cantrip': {
|
||||
emoji: '⚙️', name: 'The Cantrip', price: '£750 – £2,000',
|
||||
lead: 'A focused web tool or mini-app, built to handle one job brilliantly.',
|
||||
low: { amt: '£750', desc: 'A single-purpose tool with a form, a database and a simple admin view.' },
|
||||
high: { amt: '£2,000', desc: 'A richer tool with several features, user logins and a tidy dashboard.' },
|
||||
specs: [
|
||||
{ i: 'fa-screwdriver-wrench', k: 'Type', v: 'Web app / tool' },
|
||||
{ i: 'fa-user', k: 'Accounts', v: 'Optional logins' },
|
||||
{ i: 'fa-clock', k: 'Typical turnaround', v: '2–5 weeks' },
|
||||
{ i: 'fa-server', k: 'Hosting', v: 'Set up for you' }
|
||||
]
|
||||
},
|
||||
'grand-working': {
|
||||
emoji: '🏗️', name: 'The Grand Working', price: '£3,000 – £10,000+',
|
||||
lead: 'A full custom web application, designed and built around your business.',
|
||||
low: { amt: '£3,000', desc: 'A focused custom app with accounts, a database and your core features.' },
|
||||
high: { amt: '£10,000+', desc: 'A larger platform with integrations, user roles and ongoing support.' },
|
||||
specs: [
|
||||
{ i: 'fa-gears', k: 'Type', v: 'Full custom app' },
|
||||
{ i: 'fa-users-gear', k: 'Accounts', v: 'Roles & permissions' },
|
||||
{ i: 'fa-plug', k: 'Integrations', v: 'APIs included' },
|
||||
{ i: 'fa-clock', k: 'Typical turnaround', v: '6–12 weeks' },
|
||||
{ i: 'fa-headset', k: 'Support', v: 'Available after launch' }
|
||||
]
|
||||
},
|
||||
'sigil-workshop': {
|
||||
emoji: '🎨', name: 'The Sigil Workshop', price: 'From £150',
|
||||
lead: 'Logos, brand kits and graphics that make your business instantly recognisable.',
|
||||
low: { amt: '£150', desc: 'A standalone logo with two concepts and the final files.' },
|
||||
high: { amt: '~£900', desc: 'A full brand kit: logo suite, colour and font system, templates and stationery.' },
|
||||
specs: [
|
||||
{ i: 'fa-lightbulb', k: 'Concepts', v: '2 – 3' },
|
||||
{ i: 'fa-rotate', k: 'Revisions', v: '3 rounds' },
|
||||
{ i: 'fa-clock', k: 'Typical turnaround', v: '1–2 weeks' },
|
||||
{ i: 'fa-file-export', k: 'Formats', v: 'Vector, web & print' }
|
||||
]
|
||||
},
|
||||
'moving-picture': {
|
||||
emoji: '🎬', name: 'The Moving Picture', price: 'From £350',
|
||||
lead: 'Filming and editing for promos, events and social media.',
|
||||
low: { amt: '£350', desc: 'A short edit from footage you provide, with titles and music.' },
|
||||
high: { amt: '£1,500+', desc: 'A full filming day on location, plus edit, colour grade and social cut-downs.' },
|
||||
specs: [
|
||||
{ i: 'fa-video', k: 'Filming', v: 'Half or full day' },
|
||||
{ i: 'fa-rotate', k: 'Revisions', v: '2 rounds' },
|
||||
{ i: 'fa-clock', k: 'Typical turnaround', v: '1–3 weeks' },
|
||||
{ i: 'fa-share-nodes', k: 'Delivery', v: 'Web & social formats' }
|
||||
]
|
||||
},
|
||||
'third-dimension': {
|
||||
emoji: '🧊', name: 'The Third Dimension', price: 'From £300',
|
||||
lead: '3D models, product renders and animation for the web and video.',
|
||||
low: { amt: '£300', desc: 'A single 3D model or a polished product render.' },
|
||||
high: { amt: '£2,000+', desc: 'A set of models with animated sequences and high-resolution renders.' },
|
||||
specs: [
|
||||
{ i: 'fa-cube', k: 'Output', v: 'Models, renders, animation' },
|
||||
{ i: 'fa-rotate', k: 'Revisions', v: '2 – 3 rounds' },
|
||||
{ i: 'fa-clock', k: 'Typical turnaround', v: '1–4 weeks' },
|
||||
{ i: 'fa-file-export', k: 'Formats', v: 'Web, video & print' }
|
||||
]
|
||||
},
|
||||
'sonic-alchemy': {
|
||||
emoji: '🎙️', name: 'Sonic Alchemy', price: 'From £200',
|
||||
lead: 'Editing, mixing and mastering for podcasts, video and adverts.',
|
||||
low: { amt: '£200', desc: 'Editing and cleanup of a single recording or podcast episode.' },
|
||||
high: { amt: '£800+', desc: 'Full podcast or advert production with music, mixing and mastering.' },
|
||||
specs: [
|
||||
{ i: 'fa-sliders', k: 'Services', v: 'Edit, mix, master' },
|
||||
{ i: 'fa-rotate', k: 'Revisions', v: '2 rounds' },
|
||||
{ i: 'fa-clock', k: 'Typical turnaround', v: '3–7 days' },
|
||||
{ i: 'fa-music', k: 'Delivery', v: 'Broadcast-ready files' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// Build the modal shell once
|
||||
var overlay = document.createElement('div');
|
||||
overlay.className = 'pkg-modal-overlay';
|
||||
overlay.innerHTML =
|
||||
'<div class="pkg-modal" role="dialog" aria-modal="true" aria-labelledby="pkgModalName">' +
|
||||
'<button class="pkg-modal-close" type="button" aria-label="Close">×</button>' +
|
||||
'<div class="pkg-modal-emoji"></div>' +
|
||||
'<div class="pkg-modal-name" id="pkgModalName"></div>' +
|
||||
'<div class="pkg-modal-price"></div>' +
|
||||
'<p class="pkg-modal-lead"></p>' +
|
||||
'<div class="pkg-modal-body"></div>' +
|
||||
'<a class="btn btn-accent pricing-enquire pkg-modal-enquire"><i class="fas fa-envelope"></i> Enquire about this</a>' +
|
||||
'</div>';
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
var modalBox = overlay.querySelector('.pkg-modal');
|
||||
var elEmoji = overlay.querySelector('.pkg-modal-emoji');
|
||||
var elName = overlay.querySelector('.pkg-modal-name');
|
||||
var elPrice = overlay.querySelector('.pkg-modal-price');
|
||||
var elLead = overlay.querySelector('.pkg-modal-lead');
|
||||
var elBody = overlay.querySelector('.pkg-modal-body');
|
||||
var elEnquire = overlay.querySelector('.pkg-modal-enquire');
|
||||
var elClose = overlay.querySelector('.pkg-modal-close');
|
||||
var lastFocus = null;
|
||||
|
||||
var REDUCED = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
var SPARKLE_CHARS = ['✦', '✧', '✶', '✩', '✨'];
|
||||
var SPARKLE_COLORS = ['rgba(255,255,255,1)', 'rgba(251,191,36,1)', 'rgba(253,224,120,1)', 'rgba(249,115,22,0.95)'];
|
||||
|
||||
function dropSparkle(x, y) {
|
||||
var sp = document.createElement('span');
|
||||
sp.className = 'pkg-sparkle';
|
||||
var size = (0.5 + Math.random() * 0.9).toFixed(2);
|
||||
var col = SPARKLE_COLORS[Math.floor(Math.random() * SPARKLE_COLORS.length)];
|
||||
sp.textContent = SPARKLE_CHARS[Math.floor(Math.random() * SPARKLE_CHARS.length)];
|
||||
sp.style.left = x + 'px';
|
||||
sp.style.top = y + 'px';
|
||||
sp.style.fontSize = size + 'rem';
|
||||
sp.style.color = col;
|
||||
document.body.appendChild(sp);
|
||||
var da = Math.random() * Math.PI * 2;
|
||||
var dd = 8 + Math.random() * 22;
|
||||
var ddx = (Math.cos(da) * dd).toFixed(0);
|
||||
var ddy = (Math.sin(da) * dd).toFixed(0);
|
||||
sp.animate([
|
||||
{ opacity: 1, transform: 'translate(-50%, -50%) scale(' + size + ') rotate(0deg)' },
|
||||
{ opacity: 0, transform: 'translate(calc(-50% + ' + ddx + 'px), calc(-50% + ' + ddy + 'px)) scale(' + (size * 0.25).toFixed(2) + ') rotate(120deg)' }
|
||||
], { duration: 460 + Math.random() * 300, easing: 'ease-out' }).onfinish = function () { sp.remove(); };
|
||||
}
|
||||
|
||||
function edgeSparkles(box, duration) {
|
||||
var start = null;
|
||||
function frame(ts) {
|
||||
if (!start) start = ts;
|
||||
var p = Math.min((ts - start) / duration, 1);
|
||||
var r = box.getBoundingClientRect();
|
||||
for (var i = 0; i < 2; i++) {
|
||||
var side = Math.random(), x, y;
|
||||
if (side < 0.25) { x = r.left + Math.random() * r.width; y = r.top; }
|
||||
else if (side < 0.5) { x = r.left + Math.random() * r.width; y = r.bottom; }
|
||||
else if (side < 0.75) { x = r.left; y = r.top + Math.random() * r.height; }
|
||||
else { x = r.right; y = r.top + Math.random() * r.height; }
|
||||
dropSparkle(x, y);
|
||||
}
|
||||
if (p < 1) requestAnimationFrame(frame);
|
||||
}
|
||||
requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
function openModal(slug, ox, oy) {
|
||||
var d = DETAILS[slug];
|
||||
if (!d) return;
|
||||
|
||||
elEmoji.textContent = d.emoji;
|
||||
elName.textContent = d.name;
|
||||
elPrice.innerHTML = d.price;
|
||||
elLead.textContent = d.lead;
|
||||
|
||||
var html = '';
|
||||
if (d.low && d.high) {
|
||||
html += '<div class="pkg-range">' +
|
||||
'<div class="pkg-range-col"><h4>Lower end</h4><div class="amt">' + d.low.amt + '</div><p>' + d.low.desc + '</p></div>' +
|
||||
'<div class="pkg-range-col"><h4>Upper end</h4><div class="amt">' + d.high.amt + '</div><p>' + d.high.desc + '</p></div>' +
|
||||
'</div>';
|
||||
}
|
||||
html += '<hr class="pkg-modal-divider"><div class="pkg-specs">';
|
||||
d.specs.forEach(function (s) {
|
||||
html += '<div class="pkg-spec"><span class="k"><i class="fas ' + s.i + '"></i> ' + s.k + '</span><span class="v">' + s.v + '</span></div>';
|
||||
});
|
||||
html += '</div>';
|
||||
elBody.innerHTML = html;
|
||||
|
||||
elEnquire.setAttribute('href', 'index.php?package=' + slug + '#contact');
|
||||
|
||||
lastFocus = document.activeElement;
|
||||
overlay.classList.add('open');
|
||||
document.documentElement.classList.add('modal-open');
|
||||
modalBox.scrollTop = 0;
|
||||
|
||||
if (!REDUCED) {
|
||||
var rect = modalBox.getBoundingClientRect();
|
||||
var cx = rect.left + rect.width / 2;
|
||||
var cy = rect.top + rect.height / 2;
|
||||
if (typeof ox !== 'number') { ox = cx; oy = cy; }
|
||||
var tx = ox - cx, ty = oy - cy;
|
||||
var dist = Math.hypot(tx, ty);
|
||||
var startAng = Math.atan2(ty, tx);
|
||||
var maxR = Math.max(55, Math.min(dist * 0.5, 120));
|
||||
var turns = 1.25;
|
||||
var duration = 1000;
|
||||
|
||||
var STEPS = 44, frames = [];
|
||||
for (var i = 0; i <= STEPS; i++) {
|
||||
var t = i / STEPS;
|
||||
var g = Math.pow(t, 1.4);
|
||||
var baseX = tx * (1 - g);
|
||||
var baseY = ty * (1 - g);
|
||||
var amp = maxR * Math.sin(Math.PI * Math.pow(t, 0.7));
|
||||
var ang = startAng + turns * 2 * Math.PI * t;
|
||||
var px = baseX + Math.cos(ang) * amp;
|
||||
var py = baseY + Math.sin(ang) * amp;
|
||||
var sc = 0.05 + 0.95 * g;
|
||||
frames.push({
|
||||
transform: 'translate(' + px.toFixed(1) + 'px,' + py.toFixed(1) + 'px) scale(' + sc.toFixed(3) + ')',
|
||||
opacity: t < 0.08 ? (t / 0.08).toFixed(2) : 1
|
||||
});
|
||||
}
|
||||
modalBox.animate(frames, { duration: duration, easing: 'linear' });
|
||||
|
||||
edgeSparkles(modalBox, duration);
|
||||
}
|
||||
|
||||
elClose.focus();
|
||||
}
|
||||
|
||||
function perimeterBurst(r, n) {
|
||||
var per = 2 * (r.width + r.height);
|
||||
for (var i = 0; i < n; i++) {
|
||||
var d = (i / n) * per + (Math.random() - 0.5) * (per / n);
|
||||
var x, y;
|
||||
if (d < r.width) { x = r.left + d; y = r.top; }
|
||||
else if (d < r.width + r.height) { x = r.right; y = r.top + (d - r.width); }
|
||||
else if (d < 2 * r.width + r.height){ x = r.right - (d - r.width - r.height); y = r.bottom; }
|
||||
else { x = r.left; y = r.bottom - (d - 2 * r.width - r.height); }
|
||||
dropSparkle(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
if (!REDUCED && overlay.classList.contains('open')) {
|
||||
perimeterBurst(modalBox.getBoundingClientRect(), 48);
|
||||
}
|
||||
overlay.classList.remove('open');
|
||||
document.documentElement.classList.remove('modal-open');
|
||||
if (lastFocus && lastFocus.focus) lastFocus.focus();
|
||||
}
|
||||
|
||||
elClose.addEventListener('click', closeModal);
|
||||
overlay.addEventListener('click', function (e) {
|
||||
if (e.target === overlay) closeModal();
|
||||
});
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape' && overlay.classList.contains('open')) closeModal();
|
||||
});
|
||||
|
||||
function clickPoint(e, el) {
|
||||
if (e && e.clientX) return [e.clientX, e.clientY];
|
||||
var r = el.getBoundingClientRect();
|
||||
return [r.left + r.width / 2, r.top + r.height / 2];
|
||||
}
|
||||
|
||||
document.querySelectorAll('a.pricing-enquire, a.bench-enquire, a.ward-enquire').forEach(function (link) {
|
||||
var query = ((link.getAttribute('href') || '').split('?')[1] || '').split('#')[0];
|
||||
var slug = new URLSearchParams(query).get('package');
|
||||
if (!slug || !DETAILS[slug]) return;
|
||||
|
||||
var btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'pkg-details-btn';
|
||||
btn.innerHTML = '<i class="fas fa-circle-info"></i> See full details';
|
||||
btn.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var pt = clickPoint(e, btn);
|
||||
openModal(slug, pt[0], pt[1]);
|
||||
});
|
||||
link.parentNode.insertBefore(btn, link);
|
||||
|
||||
var card = link.closest('.pricing-card, .bench-card, .ward-card');
|
||||
if (card) {
|
||||
card.addEventListener('click', function (e) {
|
||||
if (e.target.closest('a, button')) return;
|
||||
var pt = clickPoint(e, card);
|
||||
openModal(slug, pt[0], pt[1]);
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -8,7 +8,7 @@ if (history.scrollRestoration) {
|
||||
}
|
||||
(function () {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
if (!params.has('sent') && !params.has('error')) {
|
||||
if (!params.has('sent') && !params.has('error') && !params.has('package')) {
|
||||
window.scrollTo(0, 0);
|
||||
if (window.location.hash) {
|
||||
history.replaceState(null, '', window.location.pathname);
|
||||
@@ -16,6 +16,47 @@ if (history.scrollRestoration) {
|
||||
}
|
||||
})();
|
||||
|
||||
// --- Pre-fill contact form when arriving from a pricing package ---
|
||||
(function () {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var pkg = params.get('package');
|
||||
if (!pkg) return;
|
||||
|
||||
var MESSAGES = {
|
||||
'first-spark': "Hi Danny! I'm interested in The First Spark website package (from £250). A little about my project:\n\n",
|
||||
'enchanted-workshop': "Hi Danny! I'm interested in The Enchanted Workshop website package (£750–£1,500). A little about my project:\n\n",
|
||||
'arcane-forge': "Hi Danny! I'm interested in The Arcane Forge website package (£1,500–£3,000). A little about my project:\n\n",
|
||||
'merchants-portal': "Hi Danny! I'm interested in The Merchant's Portal ecommerce package (£3,000–£5,000). A little about my project:\n\n",
|
||||
'grand-emporium': "Hi Danny! I'm interested in The Grand Emporium ecommerce package (£5,000–£7,500+). A little about my project:\n\n",
|
||||
'wizards-bench': "Hi Danny! I'd like some ad-hoc tech support from The Wizard's Bench (£40/hour). Here's what I need help with:\n\n",
|
||||
'minor-ward': "Hi Danny! I'm interested in the Minor Ward care plan (£10/month). A little about my website:\n\n",
|
||||
'greater-ward': "Hi Danny! I'm interested in the Greater Ward care plan (£25/month). A little about my website:\n\n",
|
||||
'merchants-ward': "Hi Danny! I'm interested in the Merchant's Ward care plan (£50/month). A little about my store:\n\n",
|
||||
'the-cantrip': "Hi Danny! I'm interested in The Cantrip web app (£750–£2,000). A little about what I need:\n\n",
|
||||
'grand-working': "Hi Danny! I'm interested in The Grand Working custom app (£3,000–£10,000+). A little about my project:\n\n",
|
||||
'sigil-workshop': "Hi Danny! I'm interested in The Sigil Workshop graphics & branding (from £150). A little about what I need:\n\n",
|
||||
'moving-picture': "Hi Danny! I'm interested in The Moving Picture video production (from £350). A little about my project:\n\n",
|
||||
'third-dimension': "Hi Danny! I'm interested in The Third Dimension 3D & animation (from £300). A little about my project:\n\n",
|
||||
'sonic-alchemy': "Hi Danny! I'm interested in Sonic Alchemy audio production (from £200). A little about my project:\n\n"
|
||||
};
|
||||
|
||||
var field = document.getElementById('message');
|
||||
if (field && MESSAGES[pkg]) {
|
||||
field.value = MESSAGES[pkg];
|
||||
}
|
||||
|
||||
var contact = document.getElementById('contact');
|
||||
if (contact) {
|
||||
requestAnimationFrame(function () { contact.scrollIntoView(); });
|
||||
}
|
||||
|
||||
if (field && MESSAGES[pkg]) {
|
||||
field.focus();
|
||||
var len = field.value.length;
|
||||
try { field.setSelectionRange(len, len); } catch (e) {}
|
||||
}
|
||||
})();
|
||||
|
||||
// --- Hero starfield ---
|
||||
(function () {
|
||||
const container = document.getElementById('hero-stars');
|
||||
@@ -56,7 +97,8 @@ if (history.scrollRestoration) {
|
||||
const s = document.createElement('div');
|
||||
s.style.cssText = [
|
||||
'position:absolute',
|
||||
'width:3px', 'height:3px',
|
||||
|
||||
'width:2px', 'height:3px',
|
||||
'background:rgba(251,191,36,0.9)',
|
||||
'border-radius:50%',
|
||||
`top:${(Math.random() * 100).toFixed(2)}%`,
|
||||
@@ -134,12 +176,12 @@ var WAND_COLORS = [
|
||||
];
|
||||
|
||||
function spawnSparkleCluster(btn, x, btnH) {
|
||||
var n = 16 + Math.floor(Math.random() * 13); // 16–28 per cluster
|
||||
var n = 14 + Math.floor(Math.random() * 13); // 16–28 per cluster
|
||||
for (var i = 0; i < n; i++) {
|
||||
var s = document.createElement('span');
|
||||
s.className = 'btn-sparkle';
|
||||
var ox = (Math.random() - 0.5) * 40; // ±20px horizontal spread
|
||||
var fy = Math.random() * btnH - 8; // shifted up a few px
|
||||
var fy = Math.random() * btnH - 7; // shifted up a few px
|
||||
var sz = (0.28 + Math.random() * 0.55).toFixed(2);
|
||||
var dur = (0.38 + Math.random() * 0.45).toFixed(3);
|
||||
var col = WAND_COLORS[Math.floor(Math.random() * WAND_COLORS.length)];
|
||||
@@ -166,7 +208,12 @@ function triggerWandEffect(btn) {
|
||||
|
||||
var btnW = btn.offsetWidth;
|
||||
var btnH = btn.offsetHeight;
|
||||
var duration = 520;
|
||||
// Scale swipe speed and sparkle density to the button width so narrow and wide buttons
|
||||
// feel consistent: constant px/ms sweep speed, and constant spacing between sparkle bursts.
|
||||
var SPEED = 0.73; // px per ms the swipe travels
|
||||
var SPACING = 15; // px between sparkle bursts
|
||||
var duration = Math.max(160, Math.min(btnW / SPEED, 720)); // clamp for tiny / huge buttons
|
||||
var CLUSTERS = Math.max(3, Math.round(btnW / SPACING)); // fewer bursts on narrow buttons
|
||||
var start = null;
|
||||
var lastCluster = -1;
|
||||
|
||||
@@ -178,7 +225,7 @@ function triggerWandEffect(btn) {
|
||||
var p = Math.min(elapsed / duration, 1);
|
||||
var x = p * btnW;
|
||||
|
||||
var ci = Math.floor(elapsed / 36);
|
||||
var ci = Math.floor(p * CLUSTERS);
|
||||
if (ci > lastCluster) {
|
||||
lastCluster = ci;
|
||||
spawnSparkleCluster(btn, x, btnH);
|
||||
@@ -221,8 +268,187 @@ window.addEventListener('scroll', function () {
|
||||
}
|
||||
});
|
||||
navAnchors.forEach(function (a) {
|
||||
a.style.color = a.getAttribute('href') === '#' + current
|
||||
? 'var(--white)'
|
||||
: '';
|
||||
a.classList.toggle('nav-active', a.getAttribute('href') === '#' + current);
|
||||
});
|
||||
}, { passive: true });
|
||||
|
||||
// --- Mascot "transporter" teleport-in ---
|
||||
(function () {
|
||||
var imgs = document.querySelectorAll('.mascot-teleport');
|
||||
if (!imgs.length) return;
|
||||
|
||||
var REDUCED = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
if (REDUCED) {
|
||||
imgs.forEach(function (im) { im.classList.remove('mascot-teleport'); im.style.opacity = '1'; });
|
||||
return;
|
||||
}
|
||||
|
||||
var T_CHARS = ['✦', '✧', '✶', '✩', '✨', '⋆'];
|
||||
var T_COLORS = ['rgba(255,255,255,1)', 'rgba(251,191,36,1)', 'rgba(253,224,120,1)', 'rgba(45,212,191,1)']; // white, gold, teal
|
||||
|
||||
function teleSpark(x, y, life) {
|
||||
var s = document.createElement('span');
|
||||
var size = (0.45 + Math.random() * 0.8).toFixed(2);
|
||||
var col = T_COLORS[(Math.random() * T_COLORS.length) | 0];
|
||||
s.textContent = T_CHARS[(Math.random() * T_CHARS.length) | 0];
|
||||
s.style.cssText = 'position:absolute;left:' + x + 'px;top:' + y + 'px;z-index:90;pointer-events:none;line-height:1;'
|
||||
+ 'font-size:' + size + 'rem;color:' + col + ';text-shadow:0 0 4px ' + col + ',0 0 2px rgba(255,255,255,0.95);'
|
||||
+ 'will-change:transform,opacity;';
|
||||
document.body.appendChild(s);
|
||||
s.animate([
|
||||
{ transform: 'translate(-50%,-50%) scale(0)' },
|
||||
{ transform: 'translate(-50%,-50%) scale(' + size + ')', offset: 0.08 }, // pop in almost instantly
|
||||
{ transform: 'translate(-50%,-50%) scale(' + size + ')', offset: 0.6 },
|
||||
{ transform: 'translate(-50%,-50%) scale(0)' }
|
||||
], { duration: life, easing: 'ease-out' }).onfinish = function () { s.remove(); };
|
||||
}
|
||||
|
||||
// Convert a viewport rect into document-space, so absolutely-positioned sparkles
|
||||
// stay anchored to the page (and scroll with it) instead of following the viewport.
|
||||
function pageRect(rc) {
|
||||
var l = rc.left + window.scrollX, t = rc.top + window.scrollY;
|
||||
return { left: l, top: t, width: rc.width, height: rc.height, right: l + rc.width, bottom: t + rc.height };
|
||||
}
|
||||
|
||||
function teleport(img, startDelay) {
|
||||
var r = pageRect(img.getBoundingClientRect());
|
||||
var START_DELAY = (typeof startDelay === 'number') ? startDelay : 500; // pause before anything happens
|
||||
var CHARGE = 1000; // sparkles gather along the bottom before the figure grows
|
||||
var GROW = 2200; // the bottom-up materialise
|
||||
var baseShadow = 'drop-shadow(0 8px 24px rgba(0,0,0,0.35))';
|
||||
function ease(t) { return t * t; } // ease-in: gentle at the bottom, no slow-down toward the top
|
||||
|
||||
img.dataset.teleBusy = '1';
|
||||
// Keep it invisible (clipped) but opaque from the outset, so nothing flashes during the delay
|
||||
img.style.opacity = '1';
|
||||
img.style.webkitClipPath = img.style.clipPath = 'inset(100% 0 0 0)';
|
||||
|
||||
setTimeout(function () {
|
||||
var start = null;
|
||||
function frame(ts) {
|
||||
if (!start) start = ts;
|
||||
var el = ts - start;
|
||||
|
||||
// --- Charge phase: just sparkles building along the bottom edge ---
|
||||
if (el < CHARGE) {
|
||||
var cp = el / CHARGE; // 0..1 through the charge
|
||||
var cn = Math.round(1 + 5 * cp); // build up density toward the grow
|
||||
for (var c = 0; c < cn; c++) {
|
||||
var cx = r.left + Math.random() * r.width;
|
||||
var cy = r.bottom - Math.random() * r.height * 0.06; // hug the base
|
||||
teleSpark(cx, cy, 480 + Math.random() * 360);
|
||||
}
|
||||
requestAnimationFrame(frame);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Grow phase: bottom-up reveal + rising beam ---
|
||||
var raw = Math.min((el - CHARGE) / GROW, 1);
|
||||
var p = ease(raw);
|
||||
|
||||
var clip = ((1 - p) * 100).toFixed(2);
|
||||
img.style.webkitClipPath = img.style.clipPath = 'inset(' + clip + '% 0 0 0)';
|
||||
|
||||
// Bright near-white energy wash that holds, then settles to the normal drop-shadow
|
||||
var hx = 1 - p;
|
||||
var hold = hx * hx * hx * (hx * (hx * 6 - 15) + 10); // smootherstep(1-p): holds, then fades smoothly
|
||||
var bright = (1 + 1.8 * hold).toFixed(2); // up to ~2.8 — near white-hot
|
||||
var wash = (1 - 0.55 * hold).toFixed(2); // desaturate early for a whiter look
|
||||
var glow = (0.85 * hold).toFixed(2);
|
||||
img.style.filter = baseShadow
|
||||
+ ' drop-shadow(0 0 16px rgba(255,255,255,' + glow + '))'
|
||||
+ ' brightness(' + bright + ') saturate(' + wash + ')';
|
||||
|
||||
// Sparkle beam sitting on the rising materialisation edge
|
||||
var lineY = r.bottom - p * r.height;
|
||||
if (raw < 0.98) {
|
||||
var n = Math.round(1 + 8 * (1 - p)); // dense at the start (beam low), sparse as it rises
|
||||
for (var i = 0; i < n; i++) {
|
||||
var x = r.left + Math.random() * r.width;
|
||||
var y = (Math.random() < 0.7)
|
||||
? lineY + (Math.random() - 0.5) * r.height * 0.12 // tight on the beam line
|
||||
: lineY - Math.random() * Math.max(0, lineY - r.top); // shimmer in the un-formed area above
|
||||
y = Math.max(r.top, Math.min(r.bottom, y));
|
||||
var vf = (y - r.top) / r.height; // 0 at top, 1 at bottom
|
||||
teleSpark(x, y, (480 + Math.random() * 360) * (0.4 + 0.6 * vf)); // fade quicker higher up
|
||||
}
|
||||
}
|
||||
|
||||
if (raw < 1) {
|
||||
requestAnimationFrame(frame);
|
||||
} else {
|
||||
img.style.webkitClipPath = img.style.clipPath = '';
|
||||
img.style.filter = ''; // CSS drop-shadow returns
|
||||
img.classList.remove('mascot-teleport');
|
||||
img.style.opacity = '1';
|
||||
delete img.dataset.teleBusy;
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(frame);
|
||||
}, START_DELAY);
|
||||
}
|
||||
|
||||
// Reverse "beam out": dissolve top-down into a puff of sparkles, then stay hidden
|
||||
function dematerialize(img, done) {
|
||||
var r = pageRect(img.getBoundingClientRect());
|
||||
var DUR = 800;
|
||||
var baseShadow = 'drop-shadow(0 8px 24px rgba(0,0,0,0.35))';
|
||||
var start = null;
|
||||
function frame(ts) {
|
||||
if (!start) start = ts;
|
||||
var p = Math.min((ts - start) / DUR, 1);
|
||||
|
||||
// Dissolve away from the top downwards
|
||||
img.style.webkitClipPath = img.style.clipPath = 'inset(' + (p * 100).toFixed(2) + '% 0 0 0)';
|
||||
|
||||
// Energy flash that builds as it beams out
|
||||
var bright = (1 + 1.2 * p).toFixed(2);
|
||||
var glow = (0.7 * p).toFixed(2);
|
||||
img.style.filter = baseShadow + ' drop-shadow(0 0 14px rgba(255,255,255,' + glow + ')) brightness(' + bright + ')';
|
||||
|
||||
// Sparkles riding the dissolving edge
|
||||
var lineY = r.top + p * r.height;
|
||||
if (p < 0.98) {
|
||||
for (var i = 0; i < 6; i++) {
|
||||
var x = r.left + Math.random() * r.width;
|
||||
var y = lineY + (Math.random() - 0.5) * r.height * 0.1;
|
||||
y = Math.max(r.top, Math.min(r.bottom, y));
|
||||
teleSpark(x, y, 480 + Math.random() * 360);
|
||||
}
|
||||
}
|
||||
|
||||
if (p < 1) {
|
||||
requestAnimationFrame(frame);
|
||||
} else {
|
||||
// A final puff of sparkles where it stood
|
||||
for (var j = 0; j < 16; j++) {
|
||||
teleSpark(r.left + Math.random() * r.width, r.top + Math.random() * r.height, 420 + Math.random() * 360);
|
||||
}
|
||||
img.style.opacity = '0'; // stay hidden during the wait
|
||||
img.style.webkitClipPath = img.style.clipPath = '';
|
||||
img.style.filter = '';
|
||||
if (done) done();
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
var io = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (e) {
|
||||
if (e.isIntersecting) { io.unobserve(e.target); teleport(e.target); }
|
||||
});
|
||||
}, { threshold: 0.25 });
|
||||
|
||||
imgs.forEach(function (im) {
|
||||
io.observe(im);
|
||||
// Click to beam out, then re-materialise after ~3s
|
||||
im.style.cursor = 'pointer';
|
||||
im.addEventListener('click', function () {
|
||||
if (im.dataset.teleBusy) return; // ignore while an effect is already running
|
||||
im.dataset.teleBusy = '1';
|
||||
dematerialize(im, function () {
|
||||
setTimeout(function () { teleport(im, 0); }, 3000);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -18,10 +18,18 @@ unset($_SESSION['captcha_answer']);
|
||||
|
||||
// Validate
|
||||
$valid = true;
|
||||
if (empty($name) || strlen($name) > 200) $valid = false;
|
||||
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) $valid = false;
|
||||
if (empty($message) || strlen($message) > 5000) $valid = false;
|
||||
if ($captcha !== $expected || $expected === -1) $valid = false;
|
||||
if (empty($name) || strlen($name) > 200) {
|
||||
$valid = false;
|
||||
}
|
||||
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$valid = false;
|
||||
}
|
||||
if (empty($message) || strlen($message) > 5000) {
|
||||
$valid = false;
|
||||
}
|
||||
if ($captcha !== $expected || $expected === -1) {
|
||||
$valid = false;
|
||||
}
|
||||
|
||||
if (!$valid) {
|
||||
header('Location: index.php?error=1#contact');
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
// Terms and Conditions page
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Terms & Conditions - Fraxle.net</title>
|
||||
<meta name="description" content="The terms and conditions for using the Fraxle.net website and the web design, tech support and maintenance services provided by Fraxle.net.">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700;800&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/terms.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- ===================== NAV ===================== -->
|
||||
<header class="site-header" id="site-header">
|
||||
<nav class="nav-container">
|
||||
<a href="/" class="nav-logo">
|
||||
<span class="logo-icon"><img style="width:32px;" src="img/tinyicon.png"></span>
|
||||
<span class="logo-text">Fraxle<span class="logo-dot">.net</span></span>
|
||||
</a>
|
||||
<ul class="nav-links" id="nav-links">
|
||||
<li><a href="index.php#services">Services</a></li>
|
||||
<li><a href="index.php#about">About</a></li>
|
||||
<li><a href="index.php#projects">Projects</a></li>
|
||||
<li><a href="estimates">Estimates</a></li>
|
||||
<li><a href="index.php#contact" class="btn nav-cta">Get in Touch</a></li>
|
||||
</ul>
|
||||
<button class="nav-hamburger" id="nav-hamburger" aria-label="Toggle menu" aria-expanded="false">
|
||||
<span></span><span></span><span></span>
|
||||
</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- ===================== HERO ===================== -->
|
||||
<section class="terms-hero">
|
||||
<div class="hero-orbs">
|
||||
<div class="orb orb-1"></div>
|
||||
<div class="orb orb-2"></div>
|
||||
<div class="orb orb-3"></div>
|
||||
</div>
|
||||
<div class="terms-hero-content">
|
||||
<p class="hero-eyebrow">✦ The Fine Print ✦</p>
|
||||
<h1 class="terms-hero-title">Terms & <span>Conditions</span></h1>
|
||||
<p class="terms-hero-lead">The everyday rules of the realm - covering how this website works and the services Fraxle.net provides. Plain English, no jargon spells.</p>
|
||||
</div>
|
||||
<div class="terms-hero-wave">
|
||||
<svg viewBox="0 0 1440 80" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
|
||||
<path d="M0,40 C300,80 900,0 1440,50 L1440,80 L0,80 Z" fill="var(--white)"/>
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== TERMS CONTENT ===================== -->
|
||||
<section class="section-pad">
|
||||
<div class="terms-body">
|
||||
<span class="terms-updated"><i class="fas fa-calendar-day"></i> Last updated: <?= date('j F Y') ?></span>
|
||||
|
||||
<div class="terms-section">
|
||||
<h2><span class="terms-num">01</span> Who We Are</h2>
|
||||
<p>This website (fraxle.net) and the services described on it are operated by <strong>Fraxle.net</strong> ("we", "us", "our"), a small independent web & technology business based in Oxfordshire, United Kingdom. You can reach us any time at <a href="mailto:wizard@fraxle.net">wizard@fraxle.net</a>.</p>
|
||||
<p>By using this website or engaging our services, you agree to these Terms & Conditions. If you don't agree with them, please don't use the site or services.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-section">
|
||||
<h2><span class="terms-num">02</span> Using This Website</h2>
|
||||
<p>You're welcome to browse and use this website for lawful purposes. In return, you agree not to:</p>
|
||||
<ul>
|
||||
<li>Use the site in any way that breaks UK law or infringes anyone else's rights.</li>
|
||||
<li>Attempt to gain unauthorised access to the site, its server, or any connected systems.</li>
|
||||
<li>Introduce viruses, malware, or any other harmful or disruptive code.</li>
|
||||
<li>Misuse the contact form to send spam, abusive, or fraudulent messages.</li>
|
||||
</ul>
|
||||
<p>We may update, change, or withdraw any part of the website at any time without notice.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-section">
|
||||
<h2><span class="terms-num">03</span> Our Services</h2>
|
||||
<p>Fraxle.net provides web design and build, technology support, and ongoing maintenance services. Details of typical packages and pricing are shown on our <a href="estimates">Estimates</a> page.</p>
|
||||
<ul>
|
||||
<li>All prices and estimates shown are guides only and may vary depending on the specifics of your project.</li>
|
||||
<li>A formal quote will be provided and agreed before any chargeable work begins.</li>
|
||||
<li>The exact scope, deliverables, and timescales for each project will be confirmed in writing (for example by email) before we start.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="terms-section">
|
||||
<h2><span class="terms-num">04</span> Quotes, Payment & Cancellation</h2>
|
||||
<ul>
|
||||
<li>Quotes are valid for 30 days from the date they are issued unless stated otherwise.</li>
|
||||
<li>For larger projects we may ask for a deposit before work begins, with the balance due on completion.</li>
|
||||
<li>Hourly support is charged at the rate agreed beforehand, with a minimum of one hour.</li>
|
||||
<li>Monthly care plans ("Protection Charms") are billed monthly and can be cancelled with 30 days' notice.</li>
|
||||
<li>Invoices are payable within 14 days unless otherwise agreed.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="terms-section">
|
||||
<h2><span class="terms-num">05</span> Intellectual Property</h2>
|
||||
<p>The content, branding, mascots, and design of this website belong to Fraxle.net and may not be copied or reused without permission.</p>
|
||||
<p>Once a project is completed and paid for in full, you own the final deliverables we create specifically for you. Third-party assets (such as stock images, fonts, plugins, or software) remain subject to their own licences.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-section">
|
||||
<h2><span class="terms-num">06</span> Third-Party Services</h2>
|
||||
<p>Some projects rely on third-party platforms and services - for example web hosting, domain registrars, WordPress, Shopify, or payment providers. These are governed by their own terms, and we aren't responsible for changes, outages, or issues caused by those third parties.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-section">
|
||||
<h2><span class="terms-num">07</span> Liability</h2>
|
||||
<p>We take great care in the work we do, but we can't guarantee that the website or any service will always be uninterrupted or completely error-free.</p>
|
||||
<p>To the extent permitted by law, Fraxle.net is not liable for any indirect or consequential loss arising from use of this website or our services. Nothing in these terms limits our liability for anything that cannot lawfully be limited.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-section">
|
||||
<h2><span class="terms-num">08</span> Privacy</h2>
|
||||
<p>Any personal details you share with us (for example through the contact form) are used only to respond to your enquiry and provide the services you've requested. We don't sell your data, and we keep it only as long as needed. If you'd like your details removed, just email us at <a href="mailto:wizard@fraxle.net">wizard@fraxle.net</a>.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-section">
|
||||
<h2><span class="terms-num">09</span> Changes to These Terms</h2>
|
||||
<p>We may update these Terms & Conditions from time to time. The latest version will always be available on this page, with the "last updated" date shown above.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-section">
|
||||
<h2><span class="terms-num">10</span> Governing Law</h2>
|
||||
<p>These terms are governed by the laws of England and Wales, and any disputes will be subject to the exclusive jurisdiction of the courts of England and Wales.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-note">
|
||||
<i class="fas fa-circle-info"></i> These terms are provided as a friendly, plain-English summary and don't constitute legal advice. For anything business-critical, it's always worth checking with a qualified professional. Got a question? Drop us a line at <a href="mailto:wizard@fraxle.net">wizard@fraxle.net</a>.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== FOOTER ===================== -->
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="footer-logo">Fraxle<span>.net</span></p>
|
||||
<p class="footer-tagline">Friendly web & tech for small businesses</p>
|
||||
<p class="footer-links"><a href="terms">Terms & Conditions</a></p>
|
||||
<p class="footer-copy">© <?= date('Y') ?> Fraxle.net All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||