diff --git a/css/estimates.css b/css/estimates.css index 997df00..83cf59d 100644 --- a/css/estimates.css +++ b/css/estimates.css @@ -190,6 +190,43 @@ border-top: 1px solid rgba(255,255,255,0.08); } +/* Charity concessions footnote - sits at the end of each pricing section */ +.charity-note { + margin-top: 20px; + padding-top: 12px; + border-top: 1px dashed rgba(251,191,36,0.25); + text-align: center; +} +.charity-note-lead { + font-size: 0.74rem; + color: rgba(255,255,255,0.45); + margin-bottom: 8px; +} +.charity-note-lead i { color: var(--gold); margin-right: 4px; } +.charity-note-lead strong { color: rgba(255,255,255,0.7); font-weight: 700; } +.charity-table { + display: inline-flex; + gap: 28px; + font-size: 0.72rem; +} +.charity-col { + display: flex; + flex-direction: column; + gap: 5px; +} +.charity-item { + display: flex; + gap: 8px; + white-space: nowrap; +} +.charity-name { color: rgba(255,255,255,0.6); } +.charity-price { color: var(--gold); font-weight: 700; } +@media (max-width: 600px) { + .charity-table { flex-direction: column; gap: 4px; font-size: 0.68rem; } + .charity-col { gap: 4px; } + .charity-item { white-space: normal; justify-content: center; } +} + /* Wizard's Bench - wide card */ .bench-card { background: rgba(255,255,255,0.07); @@ -304,6 +341,12 @@ font-weight: 800; color: var(--gold); line-height: 1.15; + margin-bottom: 8px; +} +.ward-term { + font-size: 0.8rem; + line-height: 1.4; + color: rgba(255,255,255,0.55); margin-bottom: 20px; } .ward-price span { @@ -312,6 +355,60 @@ color: rgba(255,255,255,0.4); } +/* Extra hours note under the ward grid */ +.ward-extra { + margin-top: 32px; + background: rgba(255,255,255,0.05); + border: 1px solid rgba(255,255,255,0.12); + border-radius: var(--radius); + padding: 28px 32px; +} +.ward-extra-title { + font-family: var(--font-head); + font-size: 1.05rem; + font-weight: 800; + color: var(--white); + margin-bottom: 8px; +} +.ward-extra-title i { color: var(--gold); margin-right: 9px; } +.ward-extra-lead { + color: rgba(255,255,255,0.6); + font-size: 0.93rem; + line-height: 1.7; + margin-bottom: 20px; +} +.ward-extra-rates { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px; + list-style: none; + margin: 0; + padding: 0; +} +.ward-extra-rates li { + background: rgba(255,255,255,0.05); + border-radius: calc(var(--radius) / 2); + padding: 16px 18px; +} +.ward-extra-rate { + display: block; + font-family: var(--font-head); + font-size: 1.6rem; + font-weight: 800; + color: var(--gold); + line-height: 1.1; + margin-bottom: 4px; +} +.ward-extra-desc { + font-size: 0.85rem; + line-height: 1.45; + color: rgba(255,255,255,0.55); +} +@media (max-width: 900px) { + .ward-extra-rates { grid-template-columns: 1fr; } + .ward-extra { padding: 24px; } +} + /* Enquire button on each package */ .pricing-enquire { margin-top: 18px; @@ -320,7 +417,7 @@ font-size: 0.85rem; padding: 12px 18px; } -.bench-enquire { margin-top: 22px; } +.bench-enquire { display: inline-flex; margin-top: 20px; } .ward-enquire { margin-top: 20px; } /* "See full details" trigger */ @@ -333,7 +430,9 @@ font-size: 0.78rem; font-weight: 700; letter-spacing: 0.03em; - display: inline-flex; + /* block-level so it never shares a line with the enquire button */ + display: flex; + width: fit-content; align-items: center; gap: 7px; margin-top: 16px; @@ -347,9 +446,6 @@ .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; @@ -357,9 +453,31 @@ z-index: 1000; opacity: 0; visibility: hidden; - transition: opacity 0.25s ease, visibility 0.25s ease; + transition: opacity 0.2s ease, visibility 0.2s ease; +} +/* The darkening lives on its own layer so it can fade slowly + without dragging the modal's opacity down with it. */ +.pkg-modal-overlay::before { + content: ''; + position: absolute; + inset: 0; + background: rgba(10,6,30,0.78); + -webkit-backdrop-filter: blur(4px); + backdrop-filter: blur(4px); + opacity: 0; + /* closing fade */ + transition: opacity 0.25s ease; +} +.pkg-modal-overlay.open { + opacity: 1; + visibility: visible; + transition: opacity 0.2s ease, visibility 0s; +} +/* opening darken is deliberately slow */ +.pkg-modal-overlay.open::before { + opacity: 1; + transition: opacity 1.4s 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); diff --git a/css/style.css b/css/style.css index 359a50b..861b2d6 100644 --- a/css/style.css +++ b/css/style.css @@ -213,6 +213,7 @@ ul { list-style: none; } z-index: 102; } .logo-icon { font-size: 1.5rem; line-height: 1; } +.logo-icon-img { height: 1.5rem; width: auto; vertical-align: middle; } .logo-dot { color: var(--orange); } .nav-links { diff --git a/estimates.php b/estimates.php index 45dd4ac..1078d8e 100644 --- a/estimates.php +++ b/estimates.php @@ -173,6 +173,21 @@ + +
+

Doing good work for the community? Registered charities get 25% off every package below — just say so when you reach out.

+
+
+
The First SparkFrom £190
+
The Enchanted Workshop£565–£1,125
+
The Arcane Forge£1,125–£2,250
+
+
+
The Merchant's Portal£2,250–£3,750
+
The Grand Emporium£3,750–£5,625+
+
+
+
@@ -223,6 +238,18 @@ + +
+

Need a custom tool but watching the budget? Charities get 25% off a bespoke build too.

+
+
+
The Cantrip£565–£1,500
+
+
+
The Grand Working£2,250–£7,500+
+
+
+
@@ -308,6 +335,20 @@ + +
+

Good causes deserve to look good too — charities get 25% off all creative work.

+
+
+
The Sigil WorkshopFrom £115
+
The Moving PictureFrom £265
+
+
+
The Third DimensionFrom £225
+
Sonic AlchemyFrom £150
+
+
+
@@ -341,6 +382,15 @@ + +
+

Gremlin trouble on a charity budget? You get 25% off the bench rate, no quibbles.

+
+
+
The Wizard's Bench£30 / hour
+
+
+
@@ -350,19 +400,21 @@

Ongoing Care

Protection Charms

-

Monthly plans to keep your website healthy, secure and performing at its best.

+

Monthly plans to keep your website healthy, secure and performing at its best — and cheaper than calling on The Wizard’s Bench each time something needs doing. All plans run on a minimum 6 month term, then continue month to month.

Tier 1

Minor Ward

-
£10 / month
+
£25 / month
+

Minimum 6 month term, then rolling monthly.


Enquire about this @@ -373,13 +425,15 @@ + +
+

Need more than your plan covers?

+

Plan members pay less than the standard £40/hour bench rate on any extra work, and the longer the job, the lower the rate.

+
    +
  • £36 per hour
  • +
  • £33 per hour, over 4 hours
  • +
  • £30 per hour for a full day — £240 per day
  • +
+
+ +
+

Ongoing care shouldn't strain a charity budget — every plan is 25% off for you.

+
+
+
Minor Ward£18.75 / month
+
Greater Ward£37.50 / month
+
+
+
Merchant's Ward£56.25 / month
+
+
+
@@ -432,4 +510,4 @@ - \ No newline at end of file + diff --git a/index.php b/index.php index 71a827b..48aa583 100644 --- a/index.php +++ b/index.php @@ -1,11 +1,22 @@ @@ -27,7 +38,7 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';

Friendly website design, online apps, tech support and creative wizardry - with a touch of enchantment. Based in Oxfordshire.

- - wizard@fraxle.net + + Contact us Explore the magic @@ -147,7 +158,7 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
- Danny the tech-wizard +Danny the tech-wizard
@@ -292,7 +303,7 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
  • - wizard@fraxle.net + Contact us
  • @@ -311,10 +322,19 @@ $form_error = isset($_GET['error']) && $_GET['error'] === '1';
    + +

    Your message couldn't be delivered - the mail server didn't accept it. Please email me directly at Contact us.

    +

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

    +
    -
    + + +
    diff --git a/js/estimates.js b/js/estimates.js index 79a02df..8cc60ad 100644 --- a/js/estimates.js +++ b/js/estimates.js @@ -73,35 +73,38 @@ ] }, 'minor-ward': { - emoji: '🛡️', name: 'Minor Ward', price: '£10 / month', - lead: 'Essential upkeep to keep a simple WordPress site safe and current.', + emoji: '🛡️', name: 'Minor Ward', price: '£25 / month', + lead: 'Essential upkeep to keep a simple WordPress site safe and current, without paying bench rates every time something small needs doing.', specs: [ + { i: 'fa-file-signature', k: 'Minimum term', v: '6 months, then rolling' }, { 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-headset', k: 'Support included', v: '2 quick requests / month' }, { i: 'fa-circle-check', k: 'Best for', v: 'Brochure sites' } ] }, 'greater-ward': { - emoji: '🛡️', name: 'Greater Ward', price: '£25 / month', + emoji: '🛡️', name: 'Greater Ward', price: '£50 / month', lead: 'Everything in Minor Ward, plus proactive checks and priority support for active business sites.', specs: [ + { i: 'fa-file-signature', k: 'Minimum term', v: '6 months, then rolling' }, { i: 'fa-stethoscope', k: 'Health checks', v: 'Monthly' }, - { i: 'fa-bolt', k: 'Support', v: 'Priority email' }, + { i: 'fa-bolt', k: 'Support', v: 'Priority email, within 24 hours' }, { i: 'fa-gauge-high', k: 'Performance', v: 'Monitored' }, - { i: 'fa-clock', k: 'Support allowance', v: '~30 mins / month' }, + { i: 'fa-headset', k: 'Support included', v: '4 quick requests / month' }, { i: 'fa-circle-check', k: 'Best for', v: 'Active business sites' } ] }, 'merchants-ward': { - emoji: '🛡️', name: "Merchant's Ward", price: '£50 / month', + emoji: '🛡️', name: "Merchant's Ward", price: '£75 / month', lead: 'Everything in Greater Ward, plus ecommerce-grade monitoring and maintenance for online stores.', specs: [ + { i: 'fa-file-signature', k: 'Minimum term', v: '6 months, then rolling' }, { 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-bolt', k: 'Response', v: 'Priority, within 6 hours' }, + { i: 'fa-clock', k: 'Support included', v: '1 hour of requests / month' }, { i: 'fa-circle-check', k: 'Best for', v: 'Online stores' } ] }, @@ -304,7 +307,7 @@ 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 + opacity: t < 0.33 ? (0.7 + (t / 0.33) * 0.3).toFixed(2) : 1 }); } modalBox.animate(frames, { duration: duration, easing: 'linear' }); diff --git a/js/main.js b/js/main.js index 7e63036..753039d 100644 --- a/js/main.js +++ b/js/main.js @@ -2,6 +2,24 @@ FRAXLE.NET — main.js ================================================ */ +// --- De-obfuscate contact email (keeps it off scrapers, works for real visitors) --- +(function () { + var nodes = document.querySelectorAll('.js-email'); + if (!nodes.length) return; + nodes.forEach(function (node) { + var user = node.getAttribute('data-user'); + var domain = node.getAttribute('data-domain'); + if (!user || !domain) return; + var address = user + '@' + domain; + if (node.tagName === 'A') { + node.href = 'mailto:' + address; + } + if (node.children.length === 0) { + node.textContent = address; + } + }); +})(); + // --- Scroll position: always start at top unless coming from form --- if (history.scrollRestoration) { history.scrollRestoration = 'manual'; @@ -29,9 +47,9 @@ if (history.scrollRestoration) { '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", + 'minor-ward': "Hi Danny! I'm interested in the Minor Ward care plan (£25/month). A little about my website:\n\n", + 'greater-ward': "Hi Danny! I'm interested in the Greater Ward care plan (£50/month). A little about my website:\n\n", + 'merchants-ward': "Hi Danny! I'm interested in the Merchant's Ward care plan (£75/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", diff --git a/nc_assets/css/style.css b/nc_assets/css/style.css new file mode 100644 index 0000000..fa262eb --- /dev/null +++ b/nc_assets/css/style.css @@ -0,0 +1,447 @@ +@font-face { +font-family: 'Museo Sans'; +src: url('../fonts/museo-sans-300-webfont.eot'); +src: url('../fonts/museo-sans-300-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/museo-sans-300-webfont.woff') format('woff'), url('../fonts/museo-sans-300-webfont.ttf') format('truetype'), url('../fonts/museo-sans-300-webfont.svg#museo-sans-300-webfont') format('svg'); +font-weight: normal; +font-style: normal; +} +@font-face { +font-family: 'Museo Sans'; +src: url('../fonts/museo-sans-300-italic-webfont.eot'); +src: url('../fonts/museo-sans-300-italic-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/museo-sans-300-italic-webfont.woff') format('woff'), url('../fonts/museo-sans-300-italic-webfont.ttf') format('truetype'), url('../fonts/museo-sans-300-italic-webfont.svg#museo-sans-300-italic-webfont') format('svg'); +font-weight: normal; +font-style: italic; +} +@font-face { +font-family: 'Museo Sans'; +src: url('../fonts/museo-sans-700-webfont.eot'); +src: url('../fonts/museo-sans-700-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/museo-sans-700-webfont.woff') format('woff'), url('../fonts/museo-sans-700-webfont.ttf') format('truetype'), url('../fonts/museo-sans-700-webfont.svg#museo-sans-700-webfont') format('svg'); +font-weight: bold; +font-style: normal; +} +@font-face { +font-family: 'Museo Sans'; +src: url('../fonts/museo-sans-700-italic-webfont.eot'); +src: url('../fonts/museo-sans-700-italic-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/museo-sans-700-italic-webfont.woff') format('woff'), url('../fonts/museo-sans-700-italic-webfont.ttf') format('truetype'), url('../fonts/museo-sans-700-italic-webfont.svg#museo-sans-700-italic-webfont') format('svg'); +font-weight: bold; +font-style: italic; +} +/** + * ------------------------------------------------------ * + * Base + * ------------------------------------------------------ * +**/ + * { +-moz-box-sizing: border-box; +-webkit-box-sizing: border-box; +box-sizing: border-box; +} +body { +min-width: 300px; +height: 100%; +margin: 0 auto; +font-family: 'Museo Sans', Arial, Helvetica, sans-serif; +font-size: 100%; +line-height: 1.25; +color: #6d6e70; +overflow-x: hidden; +} +a { +text-decoration: none; +color: #de3723; +} +a:hover { +text-decoration: underline; +} +img { +border: 0; +} +/** + * ------------------------------------------------------ * + * Utilities + * ------------------------------------------------------ * +**/ +.group:before, .group:after { +content: ""; +display: table; +} +.group:after { +clear: both; +} +.group { +zoom: 1; +} +.center { +text-align: center; +} +.reset-mt { +margin-top: 0 !important; +} +.reset-mb { +margin-bottom: 0 !important; +} +.margin-bottom { +margin-bottom: 1em; +} +/** + * ------------------------------------------------------ * + * Styles + * ------------------------------------------------------ * +**/ +.headline { +margin: 0 auto 3em; +} +h1 { +font-size: 2em; +line-height: 1.125; +margin: 1.25em 0 0; +} +h2, h3, .h2 { +font-size: 1.5em; +margin: 1.25em 0 0; +font-weight: normal; +} +h4, .h4 { +font-size: 1em; +line-height: 1.5; +margin: 2em 0 0.5em; +} +p { +margin-top: 0; +line-height: 1.5; +} +/** + * ------------------------------------------------------ * + * Content + * ------------------------------------------------------ * +**/ +.wrapper { +max-width: 87.5em; +padding: 0 1.5em; +margin: 0 auto; +} +.header { +padding: 2em 0 1.5em; +border-bottom: 2px solid #8cc1c1; +} +.logo { +font-size: 0; +text-align: center; +} +.logo > * { +vertical-align: bottom; +} +.logo img { +height: 28px; +} +.logo > div { +display: inline-block; +margin-right: 1em; +font-size: 14px; +color: #999; +} +.grid-row { +width: 100%; +margin: 3em auto 3em; +} +.flex { +display: -webkit-box; +display: -moz-box; +display: -ms-flexbox; +display: -webkit-flex; +display: flex; +-webkit-flex-flow: row wrap; +-moz-flex-flow: row wrap; +-ms-flex-flow: row wrap; +flex-flow: row wrap; +} +.flex.middle { +-webkit-align-items: center; +-moz-align-items: center; +-ms-align-items: center; +align-items: center; +-webkit-justify-content: center; +-moz-justify-content: center; +-ms-justify-content: center; +justify-content: center; +} +.flex.first { +-webkit-flex-direction: column-reverse; +-moz-flex-direction: column-reverse; +-ms-flex-direction: column-reverse; +flex-direction: column-reverse; +} +.grid-row.four-fifths { +width: 100%; +} +.aside { +width: 300px; +-webkit-flex: 1 100%; +-moz-flex: 1 100%; +-ms-flex: 1 100%; +flex: 1 100%; +text-align: center; +} +.aside img { +width: 300px; +} +.main { +-webkit-flex: 1 100%; +-moz-flex: 1 100%; +-ms-flex: 1 100%; +flex: 1 100%; +padding-left: 0; +} +.main.fix-left { +padding-right: 0; +} +.main-all { +-webkit-flex: 1 100%; +-moz-flex: 1 100%; +-ms-flex: 1 100%; +flex: 1 100%; +} +.headline { +text-align: center; +} +.headline h1 { +margin: 0.25em 0 0; +font-size: 1.5em; +line-height: 1.125; +} +.headline h2 { +margin: 0.25em 0 0; +font-size: 1.25em; +line-height: 1.25; +} +ul.default { +margin: 0; +padding-left: 1em; +} +ul.default li { +color: #D6D6D6; +} +ul.default li p { +color: #6d6e70; +margin: 0; +padding-bottom: 0.25em; +} +ul.default li:last-child p { +padding-bottom: 0; +} +.live-chat { +margin: 2em 0 0; +padding: 1.5em; +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +background-color: #F2F2F2; +} +.live-chat .chat-content { +-webkit-flex: 1; +-moz-flex: 1; +-ms-flex: 1; +flex: 1; +padding-right: 0; +} +.live-chat .chat-content p { +position: relative; +padding-left: 0; +line-height: 1.5; +font-weight: bold; +} +.live-chat .chat-btn { +-webkit-flex: 1 100%; +-moz-flex: 1 100%; +-ms-flex: 1 100%; +flex: 1 100%; +} +.live-chat .chat-btn p { +margin-bottom: 0; +text-align: right; +} +.live-chat .btn { +width: 100%; +margin-top: 1em; +} +p.help-block { +position: relative; +padding-left: 2em; +line-height: 1.5; +} +.nc-icon { +position: absolute; +left: 0; +top: -5px; +width: 30px; +height: 30px; +} +.nc-icon.icon-info { +display: none; +background: url('../img/icons/icon-info-hd.png') no-repeat center center; +background-size: 100% auto; +} +.nc-icon.icon-checkmark { +background: url('../img/icons/checkmark-hd.png') no-repeat center center; +background-size: 100% auto; +} +.nc-icon.icon-start { +background: url('../img/pictograms/150/server-red.png') no-repeat center center; +background-size: 100% auto; +} +.nc-icon.icon-cpanel { +background: url('../img/pictograms/150/tools-red.png') no-repeat center center; +background-size: 100% auto; +} +.nc-icon.icon-mail { +background: url('../img/pictograms/150/email-red.png') no-repeat center center; +background-size: 100% auto; +} +.nc-icon.icon-support { +background: url('../img/pictograms/150/support-red.png') no-repeat center center; +background-size: 100% auto; +} +.nc-icon.icon-news { +background: url('../img/pictograms/150/news-red.png') no-repeat center center; +background-size: 100% auto; +} +.nc-icon.icon-blog { +background: url('../img/pictograms/150/browser-red.png') no-repeat center center; +background-size: 100% auto; +} +.btn { +font-size: 1.125em; +font-weight: bold; +text-align: center; +text-decoration: none; +color: #fff; +padding: 0.5em 1em; +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +display: inline-block; +} +.btn.green { +text-shadow: 0 -1px 1px #6d9796; +background: #89c1c0; /* Old browsers */ +background: -moz-linear-gradient(top, #89c1c0 0%, #6d9796 100%); /* FF3.6+ */ +background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #89c1c0), color-stop(100%, #6d9796)); /* Chrome, Safari4+ */ +background: -webkit-linear-gradient(top, #89c1c0 0%, #6d9796 100%); /* Chrome10+, Safari5.1+ */ +background: -o-linear-gradient(top, #89c1c0 0%, #6d9796 100%); /* Opera 11.10+ */ +background: -ms-linear-gradient(top, #89c1c0 0%, #6d9796 100%); /* IE10+ */ +background: linear-gradient(to bottom, #89c1c0 0%, #6d9796 100%); /* W3C */ +/*filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#89c1c0', endColorstr='#6d9796', GradientType=0);*/ /* IE6-9 */ +filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +border-color: #6c9494; +} +.btn.green:hover { +text-decoration: none; +text-shadow: 0 -1px 1px #4d7e7a; +background: #6b9494; /* Old browsers */ +background: -moz-linear-gradient(top, #6b9494 0%, #4d7e7a 100%); /* FF3.6+ */ +background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #6b9494), color-stop(100%, #4d7e7a)); /* Chrome, Safari4+ */ +background: -webkit-linear-gradient(top, #6b9494 0%, #4d7e7a 100%); /* Chrome10+, Safari5.1+ */ +background: -o-linear-gradient(top, #6b9494 0%, #4d7e7a 100%); /* Opera 11.10+ */ +background: -ms-linear-gradient(top, #6b9494 0%, #4d7e7a 100%); /* IE10+ */ +background: linear-gradient(to bottom, #6b9494 0%, #4d7e7a 100%); /* W3C */ +/*filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#6b9494', endColorstr='#4d7e7a', GradientType=0); /* IE6-9 */ +filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn.green:active { +background: #4d7e7a; /* Old browsers */ +background: -moz-linear-gradient(top, #4d7e7a 0%, #6b9494 100%); /* FF3.6+ */ +background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #4d7e7a), color-stop(100%, #6b9494)); /* Chrome, Safari4+ */ +background: -webkit-linear-gradient(top, #4d7e7a 0%, #6b9494 100%); /* Chrome10+, Safari5.1+ */ +background: -o-linear-gradient(top, #4d7e7a 0%, #6b9494 100%); /* Opera 11.10+ */ +background: -ms-linear-gradient(top, #4d7e7a 0%, #6b9494 100%); /* IE10+ */ +background: linear-gradient(to bottom, #4d7e7a 0%, #6b9494 100%); /* W3C */ +/*filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#4d7e7a', endColorstr='#6b9494', GradientType=0); /* IE6-9 */ +filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +@media all and (min-width: 480px) { +.headline h1 { +font-size: 2em; +line-height: 1.125; +} +.headline h2 { +font-size: 1.5em; +line-height: 1.25; +} +} +@media all and (min-width: 640px) { +.live-chat { +padding: 1.5em 1.5em 1.5em 1.125em; +} +.live-chat .chat-content { +-webkit-flex: 1; +-moz-flex: 1; +-ms-flex: 1; +flex: 1; +padding-right: 1.5em; +} +.live-chat .chat-content p { +padding-left: 2em; +} +.nc-icon.icon-info { +display: block; +} +.live-chat .chat-btn { +-webkit-flex: 0 240px; +-moz-flex: 0 240px; +-ms-flex: 0 240px; +flex: 0 240px; +} +.live-chat .btn { +margin-top: 0; +} +} +@media all and (min-width: 720px) { +.grid-row.four-fifths { +width: 80%; +} +} +@media all and (min-width: 800px) { +.wrapper { +padding: 0 3em; +} +.flex.first { +-webkit-flex-direction: row; +-moz-flex-direction: row; +-ms-flex-direction: row; +flex-direction: row; +} +.logo { +text-align: right; +} +.grid-row.four-fifths { +width: 100%; +} +.aside { +-webkit-flex: 0; +-moz-flex: 0; +-ms-flex: 0; +flex: 0; +} +.main { +-webkit-flex: 1; +-moz-flex: 1; +-ms-flex: 1; +flex: 1; +padding-left: 2em; +} +.main.fix-left { +padding-left: 0; +padding-right: 2em; +} +.headline { +text-align: left; +} +.headline h1 { +margin: 1.25em 0 0; +} +} +@media all and (min-width: 1180px) { +.grid-row.four-fifths { +width: 80%; +} +} diff --git a/nc_assets/fonts/museo-sans-300-italic-webfont.eot b/nc_assets/fonts/museo-sans-300-italic-webfont.eot new file mode 100644 index 0000000..33fb9a2 Binary files /dev/null and b/nc_assets/fonts/museo-sans-300-italic-webfont.eot differ diff --git a/nc_assets/fonts/museo-sans-300-italic-webfont.svg b/nc_assets/fonts/museo-sans-300-italic-webfont.svg new file mode 100644 index 0000000..01ca52c --- /dev/null +++ b/nc_assets/fonts/museo-sans-300-italic-webfont.svg @@ -0,0 +1,633 @@ + + +This is a Webfont from MyFonts. Full information about this font: +http://www.myfonts.com/fonts/exljbris/museo-sans/ +Copyright (c) 2008 by Jos Buivenga. All rights reserved.Museo Sans is a trademark of Jos Buivenga. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nc_assets/fonts/museo-sans-300-italic-webfont.ttf b/nc_assets/fonts/museo-sans-300-italic-webfont.ttf new file mode 100644 index 0000000..e1ae021 Binary files /dev/null and b/nc_assets/fonts/museo-sans-300-italic-webfont.ttf differ diff --git a/nc_assets/fonts/museo-sans-300-italic-webfont.woff b/nc_assets/fonts/museo-sans-300-italic-webfont.woff new file mode 100644 index 0000000..24ee07c Binary files /dev/null and b/nc_assets/fonts/museo-sans-300-italic-webfont.woff differ diff --git a/nc_assets/fonts/museo-sans-300-webfont.eot b/nc_assets/fonts/museo-sans-300-webfont.eot new file mode 100644 index 0000000..d71eac1 Binary files /dev/null and b/nc_assets/fonts/museo-sans-300-webfont.eot differ diff --git a/nc_assets/fonts/museo-sans-300-webfont.svg b/nc_assets/fonts/museo-sans-300-webfont.svg new file mode 100644 index 0000000..852e9d5 --- /dev/null +++ b/nc_assets/fonts/museo-sans-300-webfont.svg @@ -0,0 +1,593 @@ + + +This is a Webfont from MyFonts. Full information about this font: +http://www.myfonts.com/fonts/exljbris/museo-sans/ +Copyright (c) 2008 by Jos Buivenga. All rights reserved.Museo Sans is a trademark of Jos Buivenga. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nc_assets/fonts/museo-sans-300-webfont.ttf b/nc_assets/fonts/museo-sans-300-webfont.ttf new file mode 100644 index 0000000..297056a Binary files /dev/null and b/nc_assets/fonts/museo-sans-300-webfont.ttf differ diff --git a/nc_assets/fonts/museo-sans-300-webfont.woff b/nc_assets/fonts/museo-sans-300-webfont.woff new file mode 100644 index 0000000..871f3ce Binary files /dev/null and b/nc_assets/fonts/museo-sans-300-webfont.woff differ diff --git a/nc_assets/fonts/museo-sans-500-italic-webfont.eot b/nc_assets/fonts/museo-sans-500-italic-webfont.eot new file mode 100644 index 0000000..1954a28 Binary files /dev/null and b/nc_assets/fonts/museo-sans-500-italic-webfont.eot differ diff --git a/nc_assets/fonts/museo-sans-500-italic-webfont.svg b/nc_assets/fonts/museo-sans-500-italic-webfont.svg new file mode 100644 index 0000000..8524b6c --- /dev/null +++ b/nc_assets/fonts/museo-sans-500-italic-webfont.svg @@ -0,0 +1,636 @@ + + +This is a Webfont from MyFonts. Full information about this font: +http://www.myfonts.com/fonts/exljbris/museo-sans/ +Copyright (c) 2008 by Jos Buivenga. All rights reserved.Museo Sans is a trademark of Jos Buivenga. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nc_assets/fonts/museo-sans-500-italic-webfont.ttf b/nc_assets/fonts/museo-sans-500-italic-webfont.ttf new file mode 100644 index 0000000..4842945 Binary files /dev/null and b/nc_assets/fonts/museo-sans-500-italic-webfont.ttf differ diff --git a/nc_assets/fonts/museo-sans-500-italic-webfont.woff b/nc_assets/fonts/museo-sans-500-italic-webfont.woff new file mode 100644 index 0000000..e51b06a Binary files /dev/null and b/nc_assets/fonts/museo-sans-500-italic-webfont.woff differ diff --git a/nc_assets/fonts/museo-sans-500-webfont.eot b/nc_assets/fonts/museo-sans-500-webfont.eot new file mode 100644 index 0000000..a79b32c Binary files /dev/null and b/nc_assets/fonts/museo-sans-500-webfont.eot differ diff --git a/nc_assets/fonts/museo-sans-500-webfont.svg b/nc_assets/fonts/museo-sans-500-webfont.svg new file mode 100644 index 0000000..52369ed --- /dev/null +++ b/nc_assets/fonts/museo-sans-500-webfont.svg @@ -0,0 +1,596 @@ + + +This is a Webfont from MyFonts. Full information about this font: +http://www.myfonts.com/fonts/exljbris/museo-sans/ +Copyright (c) 2008 by Jos Buivenga. All rights reserved.Museo Sans is a trademark of Jos Buivenga. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nc_assets/fonts/museo-sans-500-webfont.ttf b/nc_assets/fonts/museo-sans-500-webfont.ttf new file mode 100644 index 0000000..f54c54e Binary files /dev/null and b/nc_assets/fonts/museo-sans-500-webfont.ttf differ diff --git a/nc_assets/fonts/museo-sans-500-webfont.woff b/nc_assets/fonts/museo-sans-500-webfont.woff new file mode 100644 index 0000000..a225179 Binary files /dev/null and b/nc_assets/fonts/museo-sans-500-webfont.woff differ diff --git a/nc_assets/fonts/museo-sans-700-italic-webfont.eot b/nc_assets/fonts/museo-sans-700-italic-webfont.eot new file mode 100644 index 0000000..15e3abf Binary files /dev/null and b/nc_assets/fonts/museo-sans-700-italic-webfont.eot differ diff --git a/nc_assets/fonts/museo-sans-700-italic-webfont.svg b/nc_assets/fonts/museo-sans-700-italic-webfont.svg new file mode 100644 index 0000000..0b053c7 --- /dev/null +++ b/nc_assets/fonts/museo-sans-700-italic-webfont.svg @@ -0,0 +1,640 @@ + + +This is a Webfont from MyFonts. Full information about this font: +http://www.myfonts.com/fonts/exljbris/museo-sans/ +Copyright (c) 2008 by Jos Buivenga. All rights reserved.Museo Sans is a trademark of Jos Buivenga. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nc_assets/fonts/museo-sans-700-italic-webfont.ttf b/nc_assets/fonts/museo-sans-700-italic-webfont.ttf new file mode 100644 index 0000000..b84fd57 Binary files /dev/null and b/nc_assets/fonts/museo-sans-700-italic-webfont.ttf differ diff --git a/nc_assets/fonts/museo-sans-700-italic-webfont.woff b/nc_assets/fonts/museo-sans-700-italic-webfont.woff new file mode 100644 index 0000000..b53e5a1 Binary files /dev/null and b/nc_assets/fonts/museo-sans-700-italic-webfont.woff differ diff --git a/nc_assets/fonts/museo-sans-700-webfont.eot b/nc_assets/fonts/museo-sans-700-webfont.eot new file mode 100644 index 0000000..97f5347 Binary files /dev/null and b/nc_assets/fonts/museo-sans-700-webfont.eot differ diff --git a/nc_assets/fonts/museo-sans-700-webfont.svg b/nc_assets/fonts/museo-sans-700-webfont.svg new file mode 100644 index 0000000..5144358 --- /dev/null +++ b/nc_assets/fonts/museo-sans-700-webfont.svg @@ -0,0 +1,602 @@ + + +This is a Webfont from MyFonts. Full information about this font: +http://www.myfonts.com/fonts/exljbris/museo-sans/ +Copyright (c) 2008 by Jos Buivenga. All rights reserved.Museo Sans is a trademark of Jos Buivenga. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nc_assets/fonts/museo-sans-700-webfont.ttf b/nc_assets/fonts/museo-sans-700-webfont.ttf new file mode 100644 index 0000000..d503e93 Binary files /dev/null and b/nc_assets/fonts/museo-sans-700-webfont.ttf differ diff --git a/nc_assets/fonts/museo-sans-700-webfont.woff b/nc_assets/fonts/museo-sans-700-webfont.woff new file mode 100644 index 0000000..e9222d6 Binary files /dev/null and b/nc_assets/fonts/museo-sans-700-webfont.woff differ diff --git a/nc_assets/img/featured/600/ready-to-go.png b/nc_assets/img/featured/600/ready-to-go.png new file mode 100644 index 0000000..9bfa363 Binary files /dev/null and b/nc_assets/img/featured/600/ready-to-go.png differ diff --git a/nc_assets/img/icons/checkmark-hd.png b/nc_assets/img/icons/checkmark-hd.png new file mode 100644 index 0000000..d057f89 Binary files /dev/null and b/nc_assets/img/icons/checkmark-hd.png differ diff --git a/nc_assets/img/icons/icon-info-hd.png b/nc_assets/img/icons/icon-info-hd.png new file mode 100644 index 0000000..9967dc0 Binary files /dev/null and b/nc_assets/img/icons/icon-info-hd.png differ diff --git a/nc_assets/img/logos/namecheap-hd.png b/nc_assets/img/logos/namecheap-hd.png new file mode 100644 index 0000000..4b5a942 Binary files /dev/null and b/nc_assets/img/logos/namecheap-hd.png differ diff --git a/nc_assets/img/nc-icon/favicon.ico b/nc_assets/img/nc-icon/favicon.ico new file mode 100644 index 0000000..22225e1 Binary files /dev/null and b/nc_assets/img/nc-icon/favicon.ico differ diff --git a/nc_assets/img/nc-icon/namecheap-icon-114x114.png b/nc_assets/img/nc-icon/namecheap-icon-114x114.png new file mode 100644 index 0000000..134f2ec Binary files /dev/null and b/nc_assets/img/nc-icon/namecheap-icon-114x114.png differ diff --git a/nc_assets/img/nc-icon/namecheap-icon-120x120.png b/nc_assets/img/nc-icon/namecheap-icon-120x120.png new file mode 100644 index 0000000..2981467 Binary files /dev/null and b/nc_assets/img/nc-icon/namecheap-icon-120x120.png differ diff --git a/nc_assets/img/nc-icon/namecheap-icon-144x144.png b/nc_assets/img/nc-icon/namecheap-icon-144x144.png new file mode 100644 index 0000000..9b157ba Binary files /dev/null and b/nc_assets/img/nc-icon/namecheap-icon-144x144.png differ diff --git a/nc_assets/img/nc-icon/namecheap-icon-152x152.png b/nc_assets/img/nc-icon/namecheap-icon-152x152.png new file mode 100644 index 0000000..8c82d1d Binary files /dev/null and b/nc_assets/img/nc-icon/namecheap-icon-152x152.png differ diff --git a/nc_assets/img/nc-icon/namecheap-icon-57x57.png b/nc_assets/img/nc-icon/namecheap-icon-57x57.png new file mode 100644 index 0000000..6b4822f Binary files /dev/null and b/nc_assets/img/nc-icon/namecheap-icon-57x57.png differ diff --git a/nc_assets/img/nc-icon/namecheap-icon-72x72.png b/nc_assets/img/nc-icon/namecheap-icon-72x72.png new file mode 100644 index 0000000..0b0bc80 Binary files /dev/null and b/nc_assets/img/nc-icon/namecheap-icon-72x72.png differ diff --git a/nc_assets/img/nc-icon/namecheap-icon-76x76.png b/nc_assets/img/nc-icon/namecheap-icon-76x76.png new file mode 100644 index 0000000..829a358 Binary files /dev/null and b/nc_assets/img/nc-icon/namecheap-icon-76x76.png differ diff --git a/nc_assets/img/pictograms/150/browser-red.png b/nc_assets/img/pictograms/150/browser-red.png new file mode 100644 index 0000000..eaca991 Binary files /dev/null and b/nc_assets/img/pictograms/150/browser-red.png differ diff --git a/nc_assets/img/pictograms/150/email-red.png b/nc_assets/img/pictograms/150/email-red.png new file mode 100644 index 0000000..e960e9c Binary files /dev/null and b/nc_assets/img/pictograms/150/email-red.png differ diff --git a/nc_assets/img/pictograms/150/news-red.png b/nc_assets/img/pictograms/150/news-red.png new file mode 100644 index 0000000..6083f97 Binary files /dev/null and b/nc_assets/img/pictograms/150/news-red.png differ diff --git a/nc_assets/img/pictograms/150/server-red.png b/nc_assets/img/pictograms/150/server-red.png new file mode 100644 index 0000000..727cdd3 Binary files /dev/null and b/nc_assets/img/pictograms/150/server-red.png differ diff --git a/nc_assets/img/pictograms/150/support-red.png b/nc_assets/img/pictograms/150/support-red.png new file mode 100644 index 0000000..9bce2c0 Binary files /dev/null and b/nc_assets/img/pictograms/150/support-red.png differ diff --git a/nc_assets/img/pictograms/150/tools-red.png b/nc_assets/img/pictograms/150/tools-red.png new file mode 100644 index 0000000..409cd15 Binary files /dev/null and b/nc_assets/img/pictograms/150/tools-red.png differ diff --git a/send.php b/send.php index 72c76dd..5da620e 100644 --- a/send.php +++ b/send.php @@ -3,13 +3,34 @@ session_start(); // Honeypot: if a bot filled in a hidden field, silently discard if (!empty($_POST['website'])) { - header('Location: index.php?sent=1#contact'); + header('Location: /?sent=1#contact'); exit; } -$name = trim($_POST['name'] ?? ''); -$email = trim($_POST['email'] ?? ''); -$message = trim($_POST['message'] ?? ''); +// Rate Limiting: Session and IP-based cooldown +$ip = $_SERVER['REMOTE_ADDR']; +$cooldown_time = 300; // 5 minutes + +// Session-based cooldown +if (isset($_SESSION['last_submission']) && (time() - $_SESSION['last_submission'] < $cooldown_time)) { + header('Location: /?error=too_fast#contact'); + exit; +} + +// IP-based cooldown using a temporary file +$ip_file = sys_get_temp_dir() . '/fraxle_rate_' . md5($ip); +if (file_exists($ip_file)) { + $last_hit = (int)file_get_contents($ip_file); + if ((time() - $last_hit) < $cooldown_time) { + header('Location: /?error=too_fast#contact'); + exit; + } +} +file_put_contents($ip_file, time()); + +$name = str_replace(["\r", "\n"], '', trim($_POST['name'] ?? '')); +$email = str_replace(["\r", "\n"], '', trim($_POST['email'] ?? '')); +$message = str_replace(["\r", "\n"], '', trim($_POST['message'] ?? '')); $captcha = intval($_POST['captcha'] ?? -9999); $expected = intval($_SESSION['captcha_answer'] ?? -1); @@ -17,22 +38,35 @@ $expected = intval($_SESSION['captcha_answer'] ?? -1); unset($_SESSION['captcha_answer']); // Validate -$valid = true; +$csrf_valid = isset($_POST['csrf_token'], $_SESSION['csrf_token']) + && hash_equals($_SESSION['csrf_token'], $_POST['csrf_token']); + +$fails = []; +if (!$csrf_valid) { + $fails[] = 'csrf-invalid'; +} if (empty($name) || strlen($name) > 200) { - $valid = false; + $fails[] = 'name'; } if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) { - $valid = false; + $fails[] = 'email'; } if (empty($message) || strlen($message) > 5000) { - $valid = false; + $fails[] = 'message'; } -if ($captcha !== $expected || $expected === -1) { - $valid = false; +if ($expected === -1) { + $fails[] = 'captcha-no-session'; +} elseif ($captcha !== $expected) { + $fails[] = 'captcha-mismatch'; } -if (!$valid) { - header('Location: index.php?error=1#contact'); +if ($fails) { + error_log('fraxle contact form rejected: ' . implode(',', $fails) + . ' | got=' . $captcha . ' expected=' . $expected + . ' | sid=' . session_id() + . ' | host=' . ($_SERVER['HTTP_HOST'] ?? '?') + . ' | ref=' . ($_SERVER['HTTP_REFERER'] ?? '?')); + header('Location: /?error=details#contact'); exit; } @@ -55,8 +89,11 @@ $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'); + $_SESSION['last_submission'] = time(); + header('Location: /?sent=1#contact'); } else { - header('Location: index.php?error=1#contact'); + error_log('fraxle contact form: mail() failed for ' . $safeEmail + . ' — ' . (error_get_last()['message'] ?? 'no PHP error reported')); + header('Location: /?error=mail#contact'); } exit; diff --git a/terms.php b/terms.php index 2b1d729..227fa55 100644 --- a/terms.php +++ b/terms.php @@ -63,7 +63,7 @@

    01 Who We Are

    -

    This website (fraxle.net) and the services described on it are operated by Fraxle.net ("we", "us", "our"), a small independent web & technology business based in Oxfordshire, United Kingdom. You can reach us any time at wizard@fraxle.net.

    +

    This website (fraxle.net) and the services described on it are operated by Fraxle.net ("we", "us", "our"), a small independent web & technology business based in Oxfordshire, United Kingdom. You can reach us any time at Contact us.

    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.

    @@ -95,7 +95,12 @@
  • Quotes are valid for 30 days from the date they are issued unless stated otherwise.
  • For larger projects we may ask for a deposit before work begins, with the balance due on completion.
  • Hourly support is charged at the rate agreed beforehand, with a minimum of one hour.
  • -
  • Monthly care plans ("Protection Charms") are billed monthly and can be cancelled with 30 days' notice.
  • +
  • Monthly care plans ("Protection Charms") run on a minimum initial term of 6 months, billed monthly in advance. The discounted plan rate is offered on the basis of that commitment.
  • +
  • After the initial 6 month term, plans continue month to month and can be cancelled at any time with 30 days' notice.
  • +
  • If a plan is cancelled during the initial 6 month term, the remaining months of that term are payable, or the support already provided may instead be charged at the standard hourly rate of £40 per hour - whichever is lower.
  • +
  • Care plan work is limited to the tasks and support allowance listed for that plan. Anything beyond it is quoted separately or charged at the hourly rate.
  • +
  • A "quick support request" means a single small task, such as a content tweak, a plugin issue or a settings change. Larger pieces of work are quoted separately.
  • +
  • Unused support requests or support time do not roll over to the following month.
  • Invoices are payable within 14 days unless otherwise agreed.
@@ -119,7 +124,7 @@

08 Privacy

-

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 wizard@fraxle.net.

+

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 Contact us.

@@ -133,7 +138,7 @@
- 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 wizard@fraxle.net. + 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 Contact us.