1<!DOCTYPE html> 2<html lang="en"> 3 <head> 4 <meta charset="utf-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 <title>Apex â Support</title> 7 <meta name="description" content="Get in touch with the Apex team. We read every message." /> 8 <link rel="icon" type="image/png" href="/apexFavicon.png" /> 9 <link rel="apple-touch-icon" href="/apexFavicon.png" /> 10 <link rel="preconnect" href="https://fonts.googleapis.com" /> 11 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> 12 <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" /> 13 <style> 14 :root { 15 --cream: #faf7f2; 16 --copper: #c77d48; 17 --copper-dark: #b06835; 18 --brown: #2d2a26; 19 --brown-light: #5c5650; 20 --tan: #8b7355; 21 --white: #ffffff; 22 --card-border: rgba(199, 125, 72, 0.18); 23 } 24 * { box-sizing: border-box; margin: 0; padding: 0; } 25 body { 26 font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; 27 line-height: 1.65; 28 color: var(--brown); 29 background: var(--cream); 30 min-height: 100vh; 31 -webkit-font-smoothing: antialiased; 32 } 33 .wrap { max-width: 600px; margin: 0 auto; padding: 56px 24px 80px; } 34 header { margin-bottom: 28px; text-align: center; } 35 .icon { width: 64px; height: auto; margin: 0 auto 18px; display: block; } 36 h1 { font-size: 36px; margin: 0 0 10px; letter-spacing: -0.02em; font-weight: 800; color: var(--brown); } 37 .meta { color: var(--brown-light); font-size: 14px; } 38 .lead { font-size: 15px; color: var(--brown-light); line-height: 1.7; text-align: center; max-width: 460px; margin: 0 auto 32px; } 39 form { display: flex; flex-direction: column; gap: 16px; } 40 .field { display: flex; flex-direction: column; gap: 6px; } 41 label { 42 font-size: 11px; 43 font-weight: 700; 44 letter-spacing: 0.16em; 45 text-transform: uppercase; 46 color: var(--copper); 47 } 48 input, textarea { 49 font-family: inherit; 50 font-size: 15px; 51 color: var(--brown); 52 background: var(--white); 53 border: 1px solid var(--card-border); 54 border-radius: 12px; 55 padding: 14px 16px; 56 outline: none; 57 transition: border-color 0.2s, box-shadow 0.2s; 58 width: 100%; 59 resize: vertical; 60 } 61 input::placeholder, textarea::placeholder { color: var(--tan); opacity: 0.7; } 62 input:focus, textarea:focus { 63 border-color: var(--copper); 64 box-shadow: 0 0 0 3px rgba(199, 125, 72, 0.16); 65 } 66 textarea { min-height: 160px; line-height: 1.6; } 67 .hp { position: absolute; left: -9999px; opacity: 0; pointer-events: none; } 68 button[type="submit"] { 69 background: linear-gradient(135deg, var(--copper) 0%, var(--copper-dark) 100%); 70 color: var(--white); 71 border: none; 72 border-radius: 999px; 73 padding: 16px 36px; 74 font-size: 15px; 75 font-weight: 600; 76 cursor: pointer; 77 transition: transform 0.15s ease, box-shadow 0.15s ease; 78 margin-top: 8px; 79 box-shadow: 0 5px 16px rgba(199, 125, 72, 0.25); 80 } 81 button[type="submit"]:hover { 82 transform: translateY(-1px); 83 box-shadow: 0 8px 22px rgba(199, 125, 72, 0.32); 84 } 85 button[type="submit"]:disabled { opacity: 0.4; cursor: not-allowed; transform: none; box-shadow: none; } 86 .status { margin-top: 16px; padding: 14px 18px; border-radius: 12px; font-size: 14px; line-height: 1.5; display: none; } 87 .status.show { display: block; } 88 .status.error { background: rgba(176, 104, 53, 0.08); border: 1px solid rgba(176, 104, 53, 0.3); color: var(--copper-dark); } 89 .success-card { 90 text-align: center; 91 padding: 40px 28px; 92 background: var(--white); 93 border: 1px solid var(--card-border); 94 border-radius: 14px; 95 box-shadow: 0 2px 24px rgba(45, 42, 38, 0.06); 96 } 97 .success-card h2 { font-size: 24px; font-weight: 800; color: var(--brown); margin-bottom: 12px; } 98 .success-card p { color: var(--brown-light); } 99 .success-card .spark { font-size: 28px; margin-bottom: 16px; } 100 footer { margin-top: 48px; padding-top: 20px; border-top: 1px solid var(--card-border); font-size: 13px; color: var(--brown-light); text-align: center; } 101 footer a { margin: 0 10px; color: var(--brown-light); text-decoration: none; } 102 footer a:hover { color: var(--copper); } 103 </style> 104 </head> 105 <body> 106 <main class="wrap"> 107 <header> 108 <img class="icon" src="/apexLogo.png" alt="Apex" /> 109 <h1>Support</h1> 110 <div class="meta">We read every message.</div> 111 </header> 112 113 <p class="lead">Question, bug, feedback, or anything else? Send it our way and we'll get back to you. Usually within a day or two.</p> 114 115 <form id="support-form" novalidate> 116 <div class="field"> 117 <label for="name">Your Name</label> 118 <input id="name" name="name" type="text" required maxlength="200" placeholder="What should we call you?" /> 119 </div> 120 <div class="field"> 121 <label for="email">Your Email</label> 122 <input id="email" name="email" type="email" required maxlength="200" placeholder="So we can reply" /> 123 </div> 124 <div class="field"> 125 <label for="message">Message</label> 126 <textarea id="message" name="message" required maxlength="5000" placeholder="What's on your mind?"></textarea> 127 </div> 128 <input class="hp" type="text" name="hp" tabindex="-1" autocomplete="off" /> 129 <button id="submit-btn" type="submit">Send Message</button> 130 <div id="status" class="status"></div> 131 </form> 132 133 <div id="success" class="success-card" style="display:none"> 134 <div class="spark">✨</div> 135 <h2>Message sent</h2> 136 <p>Thanks for reaching out. We'll get back to you soon.</p> 137 </div> 138 139 <footer> 140 <a href="/">Home</a> · <a href="/privacy-policy.html">Privacy</a> · <a href="/terms-of-use.html">Terms</a> 141 </footer> 142 </main> 143 144
144<script> 145 const API_URL = "https://us-central1-petrhino-com.cloudfunctions.net/apex-submitSupport"; 146 147 const form = document.getElementById("support-form"); 148 const status = document.getElementById("status"); 149 const submitBtn = document.getElementById("submit-btn"); 150 const successCard = document.getElementById("success"); 151 152 function showStatus(kind, message) { 153 status.className = "status show " + kind; 154 status.textContent = message; 155 } 156 157 function hideStatus() { 158 status.className = "status"; 159 status.textContent = ""; 160 } 161 162 form.addEventListener("submit", async function(e) { 163 e.preventDefault(); 164 hideStatus(); 165 166 const name = document.getElementById("name").value.trim(); 167 const email = document.getElementById("email").value.trim(); 168 const message = document.getElementById("message").value.trim(); 169 const hp = form.querySelector('input[name="hp"]').value; 170 171 if (!name) { showStatus("error", "Please enter your name."); return; } 172 if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { showStatus("error", "Please enter a valid email."); return; } 173 if (!message) { showStatus("error", "Please enter a message."); return; } 174 175 submitBtn.disabled = true; 176 submitBtn.textContent = "Sending..."; 177 178 try { 179 const res = await fetch(API_URL, { 180 method: "POST", 181 headers: { "Content-Type": "application/json" }, 182 body: JSON.stringify({ name, email, message, hp }), 183 }); 184 if (!res.ok) { 185 const err = await res.json().catch(() => ({})); 186 if (res.status === 429) { 187 showStatus("error", "Too many submissions. Please try again later."); 188 } else { 189 showStatus("error", err.error || "Something went wrong. Please try again."); 190 } 191 submitBtn.disabled = false; 192 submitBtn.textContent = "Send Message"; 193 return; 194 } 195 form.style.display = "none"; 196 successCard.style.display = "block"; 197 window.scrollTo({ top: 0, behavior: "smooth" }); 198 } catch (err) { 199 console.error("Submit error:", err); 200 showStatus("error", "Network error. Please try again."); 201 submitBtn.disabled = false; 202 submitBtn.textContent = "Send Message"; 203 } 204 }); 205 </script>
205 206 </body> 207</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.