commit 9ac8763f66339578165bb4327e89d565e849e092 Author: Fraxle Date: Sat May 30 00:02:13 2026 +0100 Initial project structure diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..a4c4748 --- /dev/null +++ b/css/style.css @@ -0,0 +1,933 @@ +/* ================================================ + FRAXLE.NET — Main Stylesheet + Bold & Playful Wizard Theme + ================================================ */ + +/* --- Custom Properties --- */ +:root { + --purple-deep: #1E1B4B; + --purple-mid: #4C1D95; + --purple-bright: #7C3AED; + --purple-light: #F5F3FF; + --orange: #F97316; + --orange-light: #FFEDD5; + --gold: #FBBF24; + --teal: #0D9488; + --teal-light: #CCFBF1; + --white: #FFFFFF; + --gray-50: #FAFAFA; + --gray-100: #F3F4F6; + --gray-200: #E5E7EB; + --gray-400: #9CA3AF; + --gray-600: #6B7280; + --text: #1F2937; + --font-head: 'Space Grotesk', sans-serif; + --font-body: 'Inter', sans-serif; + --radius: 16px; + --radius-sm: 8px; + --shadow: 0 2px 10px rgba(0,0,0,0.05); + --shadow-lg: 0 6px 20px rgba(0,0,0,0.08); + --transition: 0.3s ease; +} + +/* --- Reset --- */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } +html { scroll-behavior: smooth; } +body { + font-family: var(--font-body); + color: var(--text); + background: var(--white); + line-height: 1.6; + overflow-x: hidden; +} +img { max-width: 100%; display: block; } +a { text-decoration: none; color: inherit; } +ul { list-style: none; } + +/* --- Layout --- */ +.container { + max-width: 1180px; + margin: 0 auto; + padding: 0 24px; +} +.section-pad { padding: 100px 0; } + +/* --- Section Headers --- */ +.section-header { + text-align: center; + margin-bottom: 64px; +} +.section-eyebrow { + display: block; + font-family: var(--font-head); + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.2em; + text-transform: uppercase; + color: var(--gold); + margin-bottom: 12px; +} +.section-title { + font-family: var(--font-head); + font-size: clamp(1.9rem, 4vw, 2.8rem); + font-weight: 800; + color: var(--white); + line-height: 1.15; + margin-bottom: 16px; +} +.section-lead { + color: rgba(255,255,255,0.65); + font-size: 1.05rem; + max-width: 560px; + margin: 0 auto; +} + +/* --- Buttons --- */ +.btn { + display: inline-flex; + align-items: center; + gap: 9px; + padding: 14px 28px; + border-radius: 50px; + font-family: var(--font-head); + font-size: 0.93rem; + font-weight: 600; + cursor: pointer; + border: none; + transition: transform var(--transition), box-shadow var(--transition), background var(--transition); + text-decoration: none; + white-space: nowrap; + position: relative; + overflow: hidden; +} +/* Wand & sparkle trail */ +@keyframes sparklePopFade { + 0% { opacity: 0; transform: scale(0); } + 20% { opacity: 1; transform: scale(1.1); } + 65% { opacity: 0.75; transform: scale(0.9); } + 100% { opacity: 0; transform: scale(0); } +} +.btn-sparkle { + position: absolute; + pointer-events: none; + z-index: 10; + line-height: 1; + user-select: none; + transform-origin: center; + animation: sparklePopFade var(--dur, 0.65s) ease-out forwards; +} +.btn-primary { + background: linear-gradient(135deg, var(--orange) 0%, #EF4444 100%); + color: var(--white); + box-shadow: 0 2px 10px rgba(249,115,22,0.18); +} +.btn-primary:hover { + box-shadow: 0 0 24px rgba(249,115,22,0.5), 0 2px 10px rgba(249,115,22,0.25); +} +.btn-ghost { + background: transparent; + color: rgba(255,255,255,0.9); + border: 2px solid rgba(255,255,255,0.35); +} +.btn-ghost:hover { + background: rgba(255,255,255,0.12); + border-color: rgba(255,255,255,0.7); +} +.btn-accent { + background: linear-gradient(135deg, var(--gold) 0%, var(--orange) 100%); + color: var(--white); + box-shadow: 0 2px 10px rgba(251,191,36,0.18); + padding: 16px 36px; + font-size: 1rem; + justify-content: center; +} +.btn-accent:hover { + box-shadow: 0 0 24px rgba(251,191,36,0.5), 0 2px 10px rgba(251,191,36,0.25); +} + +/* --- Reveal Animation --- */ +.reveal { + opacity: 0; + transform: translateY(28px); + transition: opacity 0.65s ease, transform 0.65s ease; +} +.reveal.visible { + opacity: 1; + transform: translateY(0); +} + +/* ================================================ + NAVIGATION + ================================================ */ +.site-header { + 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; +} +.site-header.scrolled { + background: rgba(22, 18, 58, 0.96); + backdrop-filter: blur(14px); + -webkit-backdrop-filter: blur(14px); + padding: 14px 0; + box-shadow: 0 1px 12px rgba(0,0,0,0.15); +} +.nav-container { + max-width: 1180px; + margin: 0 auto; + padding: 0 24px; + display: flex; + align-items: center; + justify-content: space-between; +} +.nav-logo { + display: flex; + align-items: center; + gap: 10px; + font-family: var(--font-head); + font-size: 1.35rem; + font-weight: 800; + color: var(--white); + z-index: 102; +} +.logo-icon { font-size: 1.5rem; line-height: 1; } +.logo-dot { color: var(--orange); } + +.nav-links { + display: flex; + align-items: center; + gap: 6px; +} +.nav-links a { + font-family: var(--font-head); + font-size: 0.88rem; + font-weight: 600; + color: rgba(255,255,255,0.82); + padding: 8px 16px; + border-radius: 8px; + background: rgba(255,255,255,0.07); + border: 1px solid rgba(255,255,255,0.1); + transition: background var(--transition), color var(--transition), border-color var(--transition); +} +.nav-links a:hover { + color: var(--white); + background: rgba(255,255,255,0.15); + border-color: rgba(255,255,255,0.22); +} +.nav-cta { + background: linear-gradient(135deg, var(--orange), #EF4444) !important; + color: var(--white) !important; + padding: 10px 22px !important; + border-radius: 50px; + font-weight: 700 !important; + box-shadow: 0 1px 8px rgba(249,115,22,0.18); + transition: transform var(--transition), box-shadow var(--transition) !important; +} +.nav-cta:hover { + box-shadow: 0 0 24px rgba(249,115,22,0.5), 0 2px 10px rgba(249,115,22,0.25) !important; +} + +.nav-hamburger { + display: none; + flex-direction: column; + gap: 5px; + background: none; + border: none; + cursor: pointer; + padding: 6px; + z-index: 102; +} +.nav-hamburger span { + display: block; + width: 24px; + height: 2px; + background: var(--white); + border-radius: 2px; + transition: transform 0.3s, opacity 0.3s; +} +.nav-hamburger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); } +.nav-hamburger.open span:nth-child(2) { opacity: 0; } +.nav-hamburger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); } + +/* ================================================ + HERO + ================================================ */ +.hero { + background: linear-gradient(145deg, var(--purple-deep) 0%, var(--purple-mid) 55%, #5B21B6 100%); + position: relative; + display: flex; + align-items: flex-start; + overflow: hidden; + padding-top: 80px; +} + +/* Stars (generated by JS) */ +.hero-stars { + position: absolute; + inset: 0; + pointer-events: none; + z-index: 1; +} + +/* Glowing orbs */ +.hero-orbs { position: absolute; inset: 0; z-index: 0; pointer-events: none; } +.orb { + position: absolute; + border-radius: 50%; + filter: blur(80px); + opacity: 0.25; +} +.orb-1 { + width: 500px; height: 500px; + background: var(--purple-bright); + top: -100px; right: -100px; + animation: orbDrift 18s ease-in-out infinite alternate; +} +.orb-2 { + width: 320px; height: 320px; + background: var(--orange); + bottom: 80px; left: -80px; + animation: orbDrift 14s ease-in-out 4s infinite alternate-reverse; +} +.orb-3 { + width: 260px; height: 260px; + background: #06B6D4; + top: 40%; right: 20%; + animation: orbDrift 20s ease-in-out 2s infinite alternate; +} +@keyframes orbDrift { + from { transform: translate(0, 0) scale(1); } + to { transform: translate(40px, 30px) scale(1.1); } +} + +.hero-content { + position: relative; + z-index: 2; + width: 100%; + max-width: 1180px; + margin: 0 auto; + padding: 80px 24px 160px; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 64px; + align-items: center; +} + +.hero-eyebrow { + font-family: var(--font-head); + font-size: 0.8rem; + font-weight: 700; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--gold); + margin-bottom: 20px; +} +.hero-title { + font-family: var(--font-head); + font-size: clamp(2.8rem, 6vw, 5rem); + font-weight: 800; + color: var(--white); + line-height: 1.08; + margin-bottom: 24px; +} +.hero-title-accent { + background: linear-gradient(135deg, var(--orange), var(--gold)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} +.hero-subtitle { + font-size: 1.1rem; + color: rgba(255,255,255,0.75); + line-height: 1.75; + margin-bottom: 40px; + max-width: 500px; +} +.hero-actions { + display: flex; + gap: 16px; + flex-wrap: wrap; + margin-bottom: 28px; +} +.hero-caption { + font-size: 0.78rem; + color: rgba(255,255,255,0.38); + font-style: italic; + text-align: center; + margin-top: -25px; +} + +/* Mascot */ +.hero-mascots-mobile { display: none; } +.hero-mascots { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + align-items: center; +} +.mascot-ring { + position: relative; + display: flex; + align-items: flex-end; + justify-content: center; +} +/* Soft glow pool beneath the characters */ +.mascot-ring::after { + content: ''; + position: absolute; + bottom: -20px; + left: 50%; + transform: translateX(-50%); + width: 70%; + height: 40px; + background: radial-gradient(ellipse, rgba(124,58,237,0.45) 0%, transparent 70%); + filter: blur(12px); + pointer-events: none; +} +@keyframes floatMascot { + 0%, 100% { transform: translateY(0px); } + 50% { transform: translateY(-18px); } +} +.mascot-img { + max-width: 580px; + width: 100%; + height: auto; + display: block; + filter: drop-shadow(0 8px 24px rgba(0,0,0,0.35)); +} + +/* Wave */ +.hero-wave { + position: absolute; + bottom: -1px; left: 0; right: 0; + z-index: 3; + line-height: 0; +} +.hero-wave svg { display: block; width: 100%; height: 100px; } + +/* ================================================ + SERVICES + ================================================ */ +.services { background: #251D70; } + +.services-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 28px; +} +.service-card { + background: rgba(255,255,255,0.07); + border: 1px solid rgba(255,255,255,0.12); + border-radius: var(--radius); + padding: 40px 32px; + transition: box-shadow var(--transition), border-color var(--transition), background var(--transition); + position: relative; + overflow: hidden; +} +.service-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; +} +.service-card:hover { + background: rgba(255,255,255,0.11); + border-color: rgba(255,255,255,0.2); + box-shadow: 0 8px 32px rgba(0,0,0,0.25); +} +.service-card:hover::after { transform: scaleX(1); } + +.service-icon { + width: 60px; + height: 60px; + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.4rem; + margin-bottom: 24px; +} +.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-card h3 { + font-family: var(--font-head); + font-size: 1.1rem; + font-weight: 700; + line-height: 1.3; + margin-bottom: 14px; + color: var(--white); +} +.service-card p { + color: rgba(255,255,255,0.68); + line-height: 1.72; + font-size: 0.94rem; +} + +/* ================================================ + ABOUT + ================================================ */ +.about { background: #3B2DA8; } + +.about-grid { + display: grid; + grid-template-columns: 1fr 1.1fr; + gap: 80px; + align-items: center; +} +.about-image { position: relative; } +.about-img-placeholder { + width: 100%; + aspect-ratio: 1 / 1; + max-width: 420px; + 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); + box-shadow: var(--shadow-lg); +} +.about-badge { + position: absolute; + bottom: -16px; + right: -16px; + background: var(--white); + color: var(--purple-bright); + font-family: var(--font-head); + font-size: 0.85rem; + font-weight: 700; + padding: 12px 20px; + border-radius: 50px; + box-shadow: var(--shadow); + display: flex; + align-items: center; + gap: 8px; +} +.about-badge i { color: var(--orange); } + +.about-text .section-eyebrow { margin-bottom: 8px; } +.about-text .section-title { margin-bottom: 20px; } +.about-text p { + color: rgba(255,255,255,0.75); + line-height: 1.8; + margin-bottom: 16px; + font-size: 0.97rem; +} +.about-text p strong { color: var(--white); } +.about-bullets { + display: flex; + flex-direction: column; + gap: 12px; + margin: 24px 0 32px; +} +.about-bullet { + display: flex; + align-items: center; + gap: 12px; + font-size: 0.93rem; + color: rgba(255,255,255,0.88); + font-weight: 500; +} +.about-bullet i { color: var(--gold); font-size: 1rem; } + +/* ================================================ + PROJECTS + ================================================ */ +.projects { background: #251D70; } + +.projects-list { + display: flex; + flex-direction: column; + gap: 0; + border-top: 1px solid rgba(255,255,255,0.12); +} + +.project-row { + display: grid; + grid-template-columns: 280px 1fr auto; + align-items: center; + gap: 36px; + padding: 24px 0; + border-bottom: 1px solid rgba(255,255,255,0.12); + cursor: pointer; + transition: background var(--transition), padding var(--transition); + border-radius: 0; + position: relative; +} +.project-row::before { + content: ''; + position: absolute; + inset: 0 -24px; + background: rgba(255,255,255,0.06); + border-radius: var(--radius-sm); + opacity: 0; + transition: opacity var(--transition); + z-index: 0; +} +.project-row:hover::before { opacity: 1; } +.project-row > * { position: relative; z-index: 1; } + +/* Thumbnail */ +.project-row-img { + width: 100%; + height: 168px; + border-radius: 12px; + overflow: hidden; + flex-shrink: 0; +} +.project-img-placeholder { + width: 100%; + height: 100%; + background: rgba(255,255,255,0.08); + display: flex; + align-items: center; + justify-content: center; + font-size: 3rem; + color: var(--orange); + transition: transform 0.4s ease; +} +.project-row:hover .project-img-placeholder { transform: scale(1.08); } + +/* Info */ +.project-row-info { + display: flex; + flex-direction: column; + gap: 6px; + justify-content: center; + padding: 8px 0; +} +.project-row-meta { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 6px; +} +.project-number { + font-family: var(--font-head); + font-size: 0.72rem; + font-weight: 800; + color: var(--orange); + letter-spacing: 0.1em; +} +.project-type { + font-size: 0.75rem; + font-weight: 600; + color: rgba(255,255,255,0.6); + text-transform: uppercase; + letter-spacing: 0.1em; + background: rgba(255,255,255,0.1); + padding: 3px 10px; + border-radius: 50px; +} +.project-row-info h3 { + font-family: var(--font-head); + font-size: 1.5rem; + font-weight: 800; + color: var(--white); + line-height: 1.2; + transition: color var(--transition); +} +.project-row:hover .project-row-info h3 { color: var(--gold); } +.project-work { + font-size: 0.88rem; + font-weight: 500; + color: rgba(255,255,255,0.6); + display: flex; + align-items: center; + gap: 8px; + margin-top: 4px; +} +.project-work i { color: #2DD4BF; } + +/* Arrow */ +.project-row-arrow { + width: 40px; + height: 40px; + border-radius: 50%; + border: 1.5px solid rgba(255,255,255,0.2); + display: flex; + align-items: center; + justify-content: center; + color: rgba(255,255,255,0.4); + flex-shrink: 0; + transition: background var(--transition), border-color var(--transition), + color var(--transition), transform var(--transition); +} +.project-row:hover .project-row-arrow { + background: var(--orange); + border-color: var(--orange); + color: var(--white); + transform: translateX(4px); +} + +/* ================================================ + CONTACT + ================================================ */ +.contact { + background: linear-gradient(145deg, var(--purple-deep) 0%, #2D1860 100%); + color: var(--white); + position: relative; + overflow: hidden; +} +.contact::before { + content: ''; + position: absolute; + width: 600px; height: 600px; + background: radial-gradient(circle, rgba(124,58,237,0.2) 0%, transparent 70%); + top: -200px; right: -100px; + pointer-events: none; +} + +.contact-wrapper { + display: grid; + grid-template-columns: 1fr 1.5fr; + gap: 80px; + align-items: start; + position: relative; + z-index: 1; +} +.contact-intro .section-title { margin-bottom: 18px; } +.contact-intro > p { + color: rgba(255,255,255,0.68); + line-height: 1.8; + margin-bottom: 36px; + font-size: 0.97rem; +} +.contact-details { display: flex; flex-direction: column; gap: 16px; } +.contact-details li { + display: flex; + align-items: center; + gap: 14px; + font-size: 0.93rem; + color: rgba(255,255,255,0.72); +} +.contact-details i { color: var(--gold); font-size: 1rem; width: 18px; text-align: center; } +.contact-details a { color: rgba(255,255,255,0.85); transition: color var(--transition); } +.contact-details a:hover { color: var(--gold); } + +/* Form wrapper */ +.contact-form-wrap { + background: rgba(255,255,255,0.06); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(255,255,255,0.1); + border-radius: var(--radius); + padding: 44px 40px; +} +.contact-form { display: flex; flex-direction: column; gap: 22px; } +.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } +.form-group { display: flex; flex-direction: column; gap: 8px; } +.form-group label { + font-family: var(--font-head); + font-size: 0.75rem; + font-weight: 700; + color: rgba(255,255,255,0.6); + letter-spacing: 0.12em; + text-transform: uppercase; +} +.form-group input, +.form-group textarea { + background: rgba(255,255,255,0.07); + border: 1px solid rgba(255,255,255,0.14); + border-radius: var(--radius-sm); + padding: 14px 16px; + color: var(--white); + font-family: var(--font-body); + font-size: 0.94rem; + transition: border-color var(--transition), background var(--transition); + resize: vertical; +} +.form-group input::placeholder, +.form-group textarea::placeholder { color: rgba(255,255,255,0.3); } +.form-group input:focus, +.form-group textarea:focus { + outline: none; + border-color: var(--gold); + background: rgba(255,255,255,0.11); +} + +/* Captcha */ +.form-captcha { display: flex; flex-direction: column; gap: 10px; } +.form-captcha label { + font-size: 0.9rem; + color: rgba(255,255,255,0.68); + font-family: var(--font-body); + font-weight: normal; + letter-spacing: 0; + text-transform: none; +} +.form-captcha strong { color: var(--gold); } +.form-captcha input { + background: rgba(255,255,255,0.07); + border: 1px solid rgba(255,255,255,0.14); + border-radius: var(--radius-sm); + padding: 14px 16px; + color: var(--white); + font-size: 0.94rem; + max-width: 130px; + transition: border-color var(--transition), background var(--transition); +} +.form-captcha input:focus { + outline: none; + border-color: var(--gold); + background: rgba(255,255,255,0.11); +} + +/* Form feedback */ +.form-success { + text-align: center; + padding: 48px 24px; +} +.form-success-icon { + width: 64px; height: 64px; + background: rgba(74,222,128,0.15); + border: 2px solid #4ADE80; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 20px; + font-size: 1.6rem; + color: #4ADE80; +} +.form-success h3 { + font-family: var(--font-head); + font-size: 1.5rem; + color: var(--white); + margin-bottom: 10px; +} +.form-success p { color: rgba(255,255,255,0.65); } + +.form-error-msg { + background: rgba(248,113,113,0.12); + border: 1px solid rgba(248,113,113,0.3); + border-radius: var(--radius-sm); + padding: 16px 20px; + display: flex; + align-items: center; + gap: 12px; + color: #FCA5A5; + font-size: 0.9rem; + margin-bottom: 8px; +} + +/* ================================================ + FOOTER + ================================================ */ +.site-footer { + background: #13112E; + padding: 44px 0; + text-align: center; + color: rgba(255,255,255,0.4); + border-top: 1px solid rgba(255,255,255,0.05); +} +.footer-logo { + font-family: var(--font-head); + font-size: 1.5rem; + font-weight: 800; + color: var(--white); + margin-bottom: 6px; +} +.footer-logo span { color: var(--orange); } +.footer-tagline { font-size: 0.88rem; margin-bottom: 20px; } +.footer-copy { font-size: 0.78rem; } + +/* ================================================ + RESPONSIVE + ================================================ */ + +/* Tablet */ +@media (max-width: 1024px) { + .hero-content { + grid-template-columns: 1fr; + padding-bottom: 140px; + text-align: center; + } + .hero-subtitle { max-width: 100%; } + .hero-actions { justify-content: center; } + .hero-mascots { display: none; } + .hero-mascots-mobile { + display: block; + margin: 24px auto 8px; + max-width: 300px; + } + .hero-mascots-mobile .mascot-img { max-width: 100%; } + .about-grid { + grid-template-columns: 1fr; + gap: 40px; + } + .about-img-placeholder { max-width: 100%; aspect-ratio: 16/9; } + .about-badge { right: 16px; } + .contact-wrapper { + grid-template-columns: 1fr; + gap: 48px; + } +} + +/* Medium tablets */ +@media (max-width: 900px) { + .services-grid { grid-template-columns: 1fr 1fr; } +} + +/* Mobile */ +@media (max-width: 680px) { + .section-pad { padding: 72px 0; } + + /* Nav mobile */ + .nav-hamburger { display: flex; } + .nav-links { + display: none; + position: fixed; + inset: 0; + background: rgba(22, 18, 58, 0.98); + backdrop-filter: blur(12px); + flex-direction: column; + align-items: center; + justify-content: center; + gap: 36px; + z-index: 101; + } + .nav-links.open { display: flex; } + .nav-links a { + font-size: 1.1rem; + color: var(--white); + padding: 14px 40px; + border-radius: 10px; + background: rgba(255,255,255,0.08); + border-color: rgba(255,255,255,0.14); + width: 220px; + text-align: center; + justify-content: center; + } + .nav-cta { + background: linear-gradient(135deg, var(--orange), #EF4444) !important; + padding: 14px 32px !important; + } + + .services-grid { grid-template-columns: 1fr; } + .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; } + .project-row-info h3 { font-size: 1.05rem; } + .form-row { grid-template-columns: 1fr; } + .contact-form-wrap { padding: 28px 22px; } + + .hero-title { font-size: 2.6rem; } + .hero-subtitle { font-size: 1rem; } + .hero-actions { flex-direction: column; } + .hero-actions .btn { justify-content: center; } +} + +@media (max-width: 400px) { + .btn { padding: 13px 22px; font-size: 0.88rem; } + .section-title { font-size: 1.75rem; } +} diff --git a/img/wizcat3.webp b/img/wizcat3.webp new file mode 100644 index 0000000..183e39c Binary files /dev/null and b/img/wizcat3.webp differ diff --git a/img/wizcat4.webp b/img/wizcat4.webp new file mode 100644 index 0000000..ac811d8 Binary files /dev/null and b/img/wizcat4.webp differ diff --git a/img/wizfixer.webp b/img/wizfixer.webp new file mode 100644 index 0000000..e9e7b3a Binary files /dev/null and b/img/wizfixer.webp differ diff --git a/index.php b/index.php new file mode 100644 index 0000000..8a4af2a --- /dev/null +++ b/index.php @@ -0,0 +1,316 @@ + + + + + + + Fraxle.net – Web & Tech for Small Businesses + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+

✦ Magical Digital Services ✦

+

+ Web & Tech
+ for Small Business +

+
+ Danny the tech-wizard and Astro the cosmic cat +

Danny the tech-wizard & Astro the cosmic cat

+
+

Friendly website design, technology support and gadget wizardry — with a touch of enchantment. Based in Oxfordshire.

+ +
+
+
+ Danny the tech-wizard and Astro the cosmic cat +
+

Danny the tech-wizard & Astro the cosmic cat

+
+
+
+ + + +
+
+ + +
+
+
+

What I do

+

Spells I Cast

+

From conjuring websites to taming rogue gadgets — here's how I can help your business.

+
+
+
+
+ +
+

Web Design with a Touch of Alchemy

+

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.

+
+
+
+ +
+

Tech Support & Digital Sorcery

+

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.

+
+
+
+ +
+

Empowering Ethical, Independent Business

+

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.

+
+
+
+
+ + +
+
+
+
+
+ +
+
+ Oxfordshire, UK +
+
+
+

About Fraxle.net

+

Conjuring Digital Magic for Small Businesses

+

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.

+

Based primarily in Oxfordshire, I can bring these skills directly to your local organisation — and work remotely with anyone, anywhere.

+
+
+ + Friendly, jargon-free support +
+
+ + Affordable packages for small budgets +
+
+ + Ethical, independent & community-focused +
+
+ Start a Conversation +
+
+
+
+ + +
+
+
+

Portfolio

+

Recent Enchantments

+

A few recent projects from the spell book.

+
+
+
+
+
+
+
+
+ 01 + Organic Farmer +
+

James Norman

+

Office Network Update

+
+
+
+
+
+
+
+
+
+ 02 + Organic Farming Event +
+

The Organic Growers Gathering

+

Video Recording & Editing

+
+
+
+
+
+
+
+
+
+ 03 + Weather Application +
+

Sunscope.net

+

Advanced Weather App

+
+
+
+
+
+
+
+
+
+ 04 + Camping & Outdoors +
+

Camplight

+

Website Design & Build

+
+
+
+
+
+
+
+
+
+ 05 + Hemp & CBD Products +
+

Hempen Organic

+

eCommerce Website

+
+
+
+
+
+
+ + +
+
+
+
+

Say Hello

+

Get in Touch

+

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.

+ +
+
+ +
+
+

Incantation sent!

+

Thanks for getting in touch. I'll reply as soon as I can.

+
+ + +
+ +

Something went wrong — please check your details (including the maths answer) and try again.

+
+ +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+ +
+ +
+
+
+
+ + + + + + + diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..05a6838 --- /dev/null +++ b/js/main.js @@ -0,0 +1,228 @@ +/* ================================================ + FRAXLE.NET — main.js + ================================================ */ + +// --- Scroll position: always start at top unless coming from form --- +if (history.scrollRestoration) { + history.scrollRestoration = 'manual'; +} +(function () { + var params = new URLSearchParams(window.location.search); + if (!params.has('sent') && !params.has('error')) { + window.scrollTo(0, 0); + if (window.location.hash) { + history.replaceState(null, '', window.location.pathname); + } + } +})(); + +// --- Hero starfield --- +(function () { + const container = document.getElementById('hero-stars'); + if (!container) return; + + const style = document.createElement('style'); + style.textContent = ` + @keyframes twinkle { + 0%, 100% { opacity: 0.2; transform: scale(0.8); } + 50% { opacity: 1; transform: scale(1.3); } + } + @keyframes shootingStar { + 0% { transform: translateX(0) translateY(0); opacity: 1; } + 100% { transform: translateX(200px) translateY(80px); opacity: 0; } + } + `; + document.head.appendChild(style); + + // Static stars + for (let i = 0; i < 160; i++) { + const s = document.createElement('div'); + const size = Math.random() * 2.5 + 0.5; + s.style.cssText = [ + 'position:absolute', + `width:${size}px`, + `height:${size}px`, + `background:rgba(255,255,255,${(Math.random() * 0.5 + 0.3).toFixed(2)})`, + 'border-radius:50%', + `top:${(Math.random() * 100).toFixed(2)}%`, + `left:${(Math.random() * 100).toFixed(2)}%`, + `animation:twinkle ${(Math.random() * 3 + 2).toFixed(1)}s ease-in-out ${(Math.random() * 5).toFixed(1)}s infinite`, + ].join(';'); + container.appendChild(s); + } + + // Gold sparkles + for (let i = 0; i < 28; i++) { + const s = document.createElement('div'); + s.style.cssText = [ + 'position:absolute', + 'width:3px', 'height:3px', + 'background:rgba(251,191,36,0.9)', + 'border-radius:50%', + `top:${(Math.random() * 100).toFixed(2)}%`, + `left:${(Math.random() * 100).toFixed(2)}%`, + `animation:twinkle ${(Math.random() * 2 + 1.5).toFixed(1)}s ease-in-out ${(Math.random() * 4).toFixed(1)}s infinite`, + ].join(';'); + container.appendChild(s); + } +})(); + +// --- Nav scroll effect --- +const header = document.getElementById('site-header'); +window.addEventListener('scroll', function () { + header.classList.toggle('scrolled', window.scrollY > 60); +}, { passive: true }); + +// --- Hamburger menu --- +const hamburger = document.getElementById('nav-hamburger'); +const navLinks = document.getElementById('nav-links'); + +if (hamburger && navLinks) { + hamburger.addEventListener('click', function () { + const isOpen = navLinks.classList.toggle('open'); + hamburger.classList.toggle('open', isOpen); + hamburger.setAttribute('aria-expanded', isOpen); + document.body.style.overflow = isOpen ? 'hidden' : ''; + }); + + // Close on link click + navLinks.querySelectorAll('a').forEach(function (link) { + link.addEventListener('click', function () { + navLinks.classList.remove('open'); + hamburger.classList.remove('open'); + hamburger.setAttribute('aria-expanded', 'false'); + document.body.style.overflow = ''; + }); + }); + + // Close on outside click + document.addEventListener('click', function (e) { + if (!hamburger.contains(e.target) && !navLinks.contains(e.target)) { + navLinks.classList.remove('open'); + hamburger.classList.remove('open'); + hamburger.setAttribute('aria-expanded', 'false'); + document.body.style.overflow = ''; + } + }); +} + +// --- Scroll reveal --- +const reveals = document.querySelectorAll('.reveal'); +if ('IntersectionObserver' in window) { + const observer = new IntersectionObserver(function (entries) { + entries.forEach(function (entry) { + if (entry.isIntersecting) { + entry.target.classList.add('visible'); + observer.unobserve(entry.target); + } + }); + }, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' }); + + reveals.forEach(function (el) { observer.observe(el); }); +} else { + // Fallback: show all + reveals.forEach(function (el) { el.classList.add('visible'); }); +} + +// --- Magic wand button effect --- +var WAND_CHARS = ['✦', '✧', '✶', '✦', '✧']; +var WAND_COLORS = [ + 'rgba(255,255,255,1)', + 'rgba(251,191,36,1)', + 'rgba(253,224,120,1)', + 'rgba(255,255,255,0.88)', +]; + +function spawnSparkleCluster(btn, x, btnH) { + var n = 16 + 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 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)]; + var ch = WAND_CHARS [Math.floor(Math.random() * WAND_CHARS.length)]; + s.textContent = ch; + s.style.cssText = [ + 'left:' + (x + ox).toFixed(1) + 'px', + 'top:' + fy.toFixed(1) + 'px', + 'font-size:' + sz + 'rem', + 'color:' + col, + 'text-shadow:0 0 5px ' + col + ',0 0 11px rgba(255,255,255,0.35)', + '--dur:' + dur + 's', + ].join(';'); + btn.appendChild(s); + (function (el, d) { + setTimeout(function () { if (el.parentNode) el.remove(); }, (parseFloat(d) + 0.1) * 1000); + }(s, dur)); + } +} + +function triggerWandEffect(btn) { + if (btn.dataset.wandActive) return; + btn.dataset.wandActive = '1'; + + var btnW = btn.offsetWidth; + var btnH = btn.offsetHeight; + var duration = 520; + var start = null; + var lastCluster = -1; + + function ease(t) { return t < 0.5 ? 2*t*t : -1 + (4 - 2*t) * t; } + + function frame(ts) { + if (!start) start = ts; + var elapsed = ts - start; + var p = Math.min(elapsed / duration, 1); + var x = p * btnW; + + var ci = Math.floor(elapsed / 36); + if (ci > lastCluster) { + lastCluster = ci; + spawnSparkleCluster(btn, x, btnH); + } + + if (p < 1) { + requestAnimationFrame(frame); + } else { + setTimeout(function () { + delete btn.dataset.wandActive; + }, 60); + } + } + + requestAnimationFrame(frame); +} + +document.querySelectorAll('.btn').forEach(function (btn) { + btn.addEventListener('mouseenter', function () { + triggerWandEffect(btn); + }); +}); + +// --- Stagger cards/rows within grids and lists --- +document.querySelectorAll('.services-grid, .projects-list').forEach(function (grid) { + grid.querySelectorAll('.reveal').forEach(function (card, i) { + card.style.transitionDelay = (i * 0.1) + 's'; + }); +}); + +// --- Smooth active nav highlight on scroll --- +const sections = document.querySelectorAll('section[id]'); +const navAnchors = document.querySelectorAll('.nav-links a[href^="#"]'); + +window.addEventListener('scroll', function () { + let current = ''; + sections.forEach(function (sec) { + if (window.scrollY >= sec.offsetTop - 120) { + current = sec.getAttribute('id'); + } + }); + navAnchors.forEach(function (a) { + a.style.color = a.getAttribute('href') === '#' + current + ? 'var(--white)' + : ''; + }); +}, { passive: true }); diff --git a/send.php b/send.php new file mode 100644 index 0000000..ef6423d --- /dev/null +++ b/send.php @@ -0,0 +1,54 @@ + 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'); + exit; +} + +// Sanitize for email body +$safeName = strip_tags($name); +$safeEmail = strip_tags($email); +$safeMessage = strip_tags($message); + +$to = 'wizard@fraxle.net'; +$subject = "Fraxle.net enquiry from $safeName"; +$body = "You have a new enquiry from your website.\n\n" + . "Name: $safeName\n" + . "Email: $safeEmail\n" + . "Message:\n$safeMessage\n\n" + . "---\nSent via fraxle.net contact form"; + +$headers = "From: noreply@fraxle.net\r\n"; +$headers .= "Reply-To: $safeEmail\r\n"; +$headers .= "X-Mailer: PHP/" . phpversion() . "\r\n"; +$headers .= "Content-Type: text/plain; charset=UTF-8\r\n"; + +if (mail($to, $subject, $body, $headers)) { + header('Location: index.php?sent=1#contact'); +} else { + header('Location: index.php?error=1#contact'); +} +exit;