Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62fdd4cbbf | |||
| 2232d9f9d0 | |||
| 4e44418b50 | |||
| 3f6a472363 | |||
| 4f2a77dee8 | |||
| a84b596555 | |||
| 01b063ae35 | |||
| 9a02971b1c | |||
| 46b7e61695 | |||
| 291c46f33b | |||
| 253d4d29b1 | |||
| a88ef6a949 | |||
| be4955af0f | |||
| 5f28c96181 | |||
| 414e956497 | |||
| 249527083d | |||
| 5ebefd242e | |||
| 8f30c2f1ec | |||
| 6bdd9a3153 | |||
| e9eaa185c5 | |||
| 1620a36d95 | |||
| c2a3926cd1 | |||
| 80e12d8086 | |||
| 9028fb7e4f | |||
| 3f6ad6c717 | |||
| 56a093a17b | |||
| d5a8edceb0 | |||
| 813b1355bb | |||
| 35e540685e | |||
| c32a7ace4e | |||
| d63aaaa70c | |||
| 34ace42c15 | |||
| 11970caad3 | |||
| 940a09263d | |||
| 0b217cfd2f | |||
| aa04628170 | |||
| 8e6df74134 | |||
| e2fe707933 | |||
| fe87b80c76 | |||
| df20a2e7da |
@@ -42,9 +42,10 @@ netlify deploy --prod
|
||||
`index.html` is fully self-contained — every image used by the website is embedded as base64. The `/assets/` folder holds the master logo PNGs for label printing, social media, and business cards. The website does not require these files to render.
|
||||
|
||||
Current files in `/assets/`:
|
||||
- `volcan_logo_master.png` — primary lockup, 1024×1024 PNG on dark background
|
||||
- `volcan_logo_master.jpg` — primary lockup, 1024×1024 JPEG on black background
|
||||
|
||||
`/assets/archive/` holds older variants kept for reference:
|
||||
- `volcan_logo_master_v1.png` — earlier high-resolution PNG version of the master (2 MB, RGBA)
|
||||
- `volan lgo black.png` — alternate black-background variant
|
||||
- `volcan logo png.png` — same logo on a white background (alt option)
|
||||
- `index.html` — 1-byte stub from the original upload, kept for traceability
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 504 KiB |
|
After Width: | Height: | Size: 529 KiB |
|
After Width: | Height: | Size: 247 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 96 KiB |
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Order — Volcán Antigua Roasters</title>
|
||||
<!-- /order is the destination of the "SCAN TO ORDER" QR printed on labels. It forwards to the order form (orders.html). -->
|
||||
<link rel="canonical" href="https://www.volcanantiguaroasters.com/orders.html">
|
||||
<meta http-equiv="refresh" content="0; url=/orders.html">
|
||||
<meta name="robots" content="noindex">
|
||||
<style>
|
||||
html,body{height:100%;margin:0}
|
||||
body{background:#111111;color:#d9bd82;display:flex;align-items:center;justify-content:center;
|
||||
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;text-align:center;padding:24px}
|
||||
.wrap{max-width:420px}
|
||||
.mark{font-size:1.6rem;letter-spacing:.28em;font-weight:700;text-transform:uppercase}
|
||||
.sub{margin-top:6px;font-size:.7rem;letter-spacing:.3em;color:#a89b86;text-transform:uppercase}
|
||||
p{margin-top:28px;color:#f5f1e8;font-size:.95rem;line-height:1.5}
|
||||
a{color:#d9bd82;text-decoration:none;border-bottom:1px solid #d9bd82;padding-bottom:3px;
|
||||
font-size:.74rem;letter-spacing:.22em;text-transform:uppercase;display:inline-block;margin-top:22px}
|
||||
</style>
|
||||
<script>window.location.replace("/orders.html");</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="mark">Volcán Antigua</div>
|
||||
<div class="sub">Roasters</div>
|
||||
<p>Taking you to the order page…</p>
|
||||
<a href="/orders.html">Reserve your bag →</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,411 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<meta name="description" content="Reserve your bag of single-origin Antigua coffee. We'll email a secure invoice.">
|
||||
<meta name="robots" content="noindex">
|
||||
<title>Reserve Your Bag — Volcán Antigua Roasters</title>
|
||||
<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=Playfair+Display:ital,wght@0,400;0,500;0,700;1,400&family=Manrope:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg: #111111;
|
||||
--bg-2: #161616;
|
||||
--bg-3: #1a1a1a;
|
||||
--cream: #f5f1e8;
|
||||
--cream-dim: rgba(245,241,232,0.66);
|
||||
--line: rgba(245,241,232,0.12);
|
||||
--gold: #c8a96a;
|
||||
--gold-soft: #a88a4d;
|
||||
--gold-bright: #d9bd82;
|
||||
--display: 'Playfair Display', Georgia, serif;
|
||||
--sans: 'Manrope', system-ui, sans-serif;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body { background: var(--bg); color: var(--cream); font-family: var(--sans); -webkit-font-smoothing: antialiased; }
|
||||
body { padding: 24px 20px 64px; max-width: 520px; margin: 0 auto; min-height: 100vh; }
|
||||
|
||||
.lang-row { display: flex; justify-content: flex-end; margin-bottom: 4px; }
|
||||
.lang-toggle { background: none; border: 1px solid var(--line); color: var(--cream-dim); font-family: var(--sans); font-size: 0.62rem; letter-spacing: 0.28em; text-transform: uppercase; padding: 6px 12px; cursor: pointer; border-radius: 2px; }
|
||||
.lang-toggle .lang-active { color: var(--gold); }
|
||||
.lang-toggle [data-lang-en], .lang-toggle [data-lang-ko] { transition: color 0.2s; }
|
||||
|
||||
header.hero { text-align: center; padding: 12px 0 28px; border-bottom: 1px solid var(--line); }
|
||||
.logo { width: 180px; max-width: 60%; margin: 0 auto 18px; display: block; filter: drop-shadow(0 1px 8px rgba(200,169,106,0.16)); }
|
||||
.eyebrow { font-family: var(--sans); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.32em; text-transform: uppercase; color: var(--gold); margin-bottom: 10px; }
|
||||
.tagline { font-family: var(--display); font-size: 1.35rem; font-weight: 500; line-height: 1.35; }
|
||||
.tagline em { font-style: italic; color: var(--gold); font-weight: 400; }
|
||||
|
||||
.product { padding: 26px 0; border-bottom: 1px solid var(--line); }
|
||||
.product .name { font-family: var(--display); font-size: 1.35rem; font-weight: 700; margin-bottom: 6px; }
|
||||
.product .meta { font-size: 0.78rem; color: var(--cream-dim); letter-spacing: 0.04em; line-height: 1.7; }
|
||||
.product .price-note { margin-top: 14px; font-size: 0.7rem; color: var(--gold-soft); letter-spacing: 0.1em; line-height: 1.5; }
|
||||
|
||||
form { padding-top: 24px; }
|
||||
.field { margin-bottom: 20px; }
|
||||
.label { display: block; font-size: 0.64rem; font-weight: 500; letter-spacing: 0.28em; text-transform: uppercase; color: var(--gold); margin-bottom: 10px; }
|
||||
input[type=text], input[type=email], input[type=tel], input[type=number], select, textarea {
|
||||
width: 100%;
|
||||
background: var(--bg-2);
|
||||
border: 1px solid var(--line);
|
||||
color: var(--cream);
|
||||
font-family: var(--sans);
|
||||
font-size: 0.95rem;
|
||||
padding: 12px 14px;
|
||||
border-radius: 2px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
select {
|
||||
background-image: linear-gradient(45deg, transparent 50%, var(--gold) 50%), linear-gradient(135deg, var(--gold) 50%, transparent 50%);
|
||||
background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
|
||||
background-size: 5px 5px, 5px 5px;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 36px;
|
||||
}
|
||||
textarea { resize: vertical; min-height: 84px; line-height: 1.5; }
|
||||
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--gold); }
|
||||
input::placeholder, textarea::placeholder { color: rgba(245,241,232,0.32); }
|
||||
|
||||
.row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||
|
||||
.fulfillment { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
||||
.fulfillment label { cursor: pointer; position: relative; }
|
||||
.fulfillment label input { position: absolute; opacity: 0; pointer-events: none; }
|
||||
.fulfillment .opt {
|
||||
display: block; text-align: center; padding: 14px 10px;
|
||||
background: var(--bg-2); border: 1px solid var(--line); border-radius: 2px;
|
||||
font-size: 0.76rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--cream-dim);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.fulfillment label input:checked + .opt { border-color: var(--gold); color: var(--gold); background: rgba(200,169,106,0.06); }
|
||||
|
||||
.conditional { display: none; }
|
||||
.conditional.active { display: block; }
|
||||
|
||||
button.submit {
|
||||
width: 100%; padding: 16px;
|
||||
background: var(--gold); color: #0a0a0a;
|
||||
border: none; font-family: var(--sans);
|
||||
font-size: 0.76rem; font-weight: 600; letter-spacing: 0.28em; text-transform: uppercase;
|
||||
border-radius: 2px; cursor: pointer; transition: background 0.2s;
|
||||
margin-top: 8px;
|
||||
}
|
||||
button.submit:hover { background: var(--gold-bright); }
|
||||
button.submit:disabled { opacity: 0.55; cursor: not-allowed; }
|
||||
|
||||
.form-msg { margin-top: 14px; font-size: 0.8rem; color: var(--cream-dim); text-align: center; min-height: 20px; line-height: 1.5; }
|
||||
.form-msg.error { color: #e89a9a; }
|
||||
|
||||
.success { display: none; text-align: center; padding: 80px 16px 60px; }
|
||||
.success.active { display: block; }
|
||||
.success .tick {
|
||||
width: 56px; height: 56px; margin: 0 auto 24px;
|
||||
border-radius: 50%; border: 1px solid var(--gold);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: var(--gold); font-size: 1.4rem;
|
||||
}
|
||||
.success h2 { font-family: var(--display); font-size: 1.6rem; font-weight: 700; margin-bottom: 14px; }
|
||||
.success p { color: var(--cream-dim); font-size: 0.92rem; line-height: 1.65; max-width: 360px; margin: 0 auto; }
|
||||
|
||||
footer.foot { margin-top: 56px; padding-top: 22px; border-top: 1px solid var(--line); text-align: center; font-size: 0.64rem; letter-spacing: 0.14em; color: var(--cream-dim); }
|
||||
footer.foot a { color: var(--gold); text-decoration: none; }
|
||||
|
||||
html[lang="ko"] body { font-family: 'Manrope', 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', system-ui, sans-serif; }
|
||||
html[lang="ko"] .tagline, html[lang="ko"] .product .name, html[lang="ko"] .success h2 {
|
||||
font-family: 'Playfair Display', 'Noto Serif KR', 'Apple SD Gothic Neo', Georgia, serif;
|
||||
}
|
||||
html[lang="ko"] .eyebrow, html[lang="ko"] .label { letter-spacing: 0.16em; }
|
||||
html[lang="ko"] .lang-toggle { letter-spacing: 0.2em; }
|
||||
html[lang="ko"] em { font-style: normal; color: var(--gold); font-weight: 500; }
|
||||
|
||||
@media (max-width: 380px) {
|
||||
body { padding: 20px 16px 56px; }
|
||||
.tagline { font-size: 1.2rem; }
|
||||
.product .name { font-size: 1.2rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="lang-row lang-skip">
|
||||
<button class="lang-toggle" type="button" aria-label="Toggle language">
|
||||
<span data-lang-en class="lang-active">EN</span> · <span data-lang-ko>KO</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="orderRoot">
|
||||
<header class="hero">
|
||||
<a href="/" aria-label="Volcán Antigua Roasters home">
|
||||
<img src="assets/logo-main.webp" alt="Volcán Antigua Roasters" class="logo">
|
||||
</a>
|
||||
<div class="eyebrow">Reserve Your Bag</div>
|
||||
<p class="tagline">Single-origin Antigua, <em>roasted to order</em>.</p>
|
||||
</header>
|
||||
|
||||
<section class="product">
|
||||
<div class="name">Antigua Highland · Medium Roast</div>
|
||||
<div class="meta">Single Origin · Antigua, Guatemala · SHB · 5,000 ft · 12 oz (340g)</div>
|
||||
<div class="price-note">Pricing and shipping confirmed on the emailed invoice.</div>
|
||||
</section>
|
||||
|
||||
<form id="orderForm">
|
||||
<div class="field">
|
||||
<label class="label" for="grind">Grind</label>
|
||||
<select id="grind" name="grind" required>
|
||||
<option value="whole-bean">Whole Bean</option>
|
||||
<option value="drip">Drip</option>
|
||||
<option value="pour-over">Pour-over</option>
|
||||
<option value="espresso">Espresso</option>
|
||||
<option value="french-press">French Press</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="qty">Quantity</label>
|
||||
<input type="number" id="qty" name="qty" min="1" max="12" value="1" required inputmode="numeric">
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<span class="label">Fulfillment</span>
|
||||
<div class="fulfillment">
|
||||
<label>
|
||||
<input type="radio" name="fulfillment" value="ship" checked>
|
||||
<span class="opt">Ship (US)</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="fulfillment" value="local">
|
||||
<span class="opt">Local Pickup / Delivery</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="name">Name</label>
|
||||
<input type="text" id="name" name="name" autocomplete="name" required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="email">Email</label>
|
||||
<input type="email" id="email" name="email" autocomplete="email" required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="phone">Phone</label>
|
||||
<input type="tel" id="phone" name="phone" autocomplete="tel" required>
|
||||
</div>
|
||||
|
||||
<div id="shipFields" class="conditional active">
|
||||
<div class="field">
|
||||
<label class="label" for="addr1">Shipping address</label>
|
||||
<input type="text" id="addr1" name="addr1" autocomplete="address-line1" placeholder="Street address">
|
||||
</div>
|
||||
<div class="field">
|
||||
<input type="text" id="addr2" name="addr2" autocomplete="address-line2" placeholder="Apt / unit (optional)">
|
||||
</div>
|
||||
<div class="field row-2">
|
||||
<input type="text" id="city" name="city" autocomplete="address-level2" placeholder="City">
|
||||
<input type="text" id="state" name="state" autocomplete="address-level1" placeholder="State">
|
||||
</div>
|
||||
<div class="field">
|
||||
<input type="text" id="zip" name="zip" autocomplete="postal-code" placeholder="ZIP code">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="localFields" class="conditional">
|
||||
<div class="field">
|
||||
<label class="label" for="area">Area note</label>
|
||||
<input type="text" id="area" name="area" placeholder="Neighborhood / preferred pickup or delivery">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="notes">Notes (optional)</label>
|
||||
<textarea id="notes" name="notes" placeholder="Anything we should know?"></textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="submit">Reserve My Bag</button>
|
||||
<div class="form-msg" id="formMsg" role="status" aria-live="polite"></div>
|
||||
</form>
|
||||
|
||||
<footer class="foot">
|
||||
Volcán Antigua Roasters · <a href="/">volcanantiguaroasters.com</a>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div id="successRoot" class="success" aria-live="polite">
|
||||
<div class="tick">✓</div>
|
||||
<h2>Order received.</h2>
|
||||
<p>We'll email your secure invoice shortly. Thank you for reserving direct from the roaster.</p>
|
||||
<footer class="foot" style="margin-top:48px;">
|
||||
Volcán Antigua Roasters · <a href="/">volcanantiguaroasters.com</a>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// CONFIG — paste your Formspree form endpoint below.
|
||||
// 1. Sign up at https://formspree.io and create a new form.
|
||||
// 2. Copy the endpoint, e.g. https://formspree.io/f/xpwzabcd
|
||||
// 3. Replace the empty string below with that URL.
|
||||
// Until this is set, the form submits to a friendly "not configured" message.
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
const FORM_ENDPOINT = 'https://formspree.io/f/xojbbdzv';
|
||||
|
||||
(function() {
|
||||
const form = document.getElementById('orderForm');
|
||||
const msg = document.getElementById('formMsg');
|
||||
const root = document.getElementById('orderRoot');
|
||||
const success = document.getElementById('successRoot');
|
||||
const shipFields = document.getElementById('shipFields');
|
||||
const localFields = document.getElementById('localFields');
|
||||
const submitBtn = form.querySelector('button.submit');
|
||||
const submitOriginalText = submitBtn.textContent;
|
||||
|
||||
const shipRequired = ['addr1','city','state','zip'];
|
||||
|
||||
function updateRequired() {
|
||||
const local = form.querySelector('input[name=fulfillment]:checked').value === 'local';
|
||||
shipFields.classList.toggle('active', !local);
|
||||
localFields.classList.toggle('active', local);
|
||||
shipRequired.forEach(id => { document.getElementById(id).required = !local; });
|
||||
document.getElementById('area').required = local;
|
||||
}
|
||||
form.querySelectorAll('input[name=fulfillment]').forEach(r => r.addEventListener('change', updateRequired));
|
||||
updateRequired();
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
msg.classList.remove('error');
|
||||
msg.textContent = '';
|
||||
|
||||
if (!FORM_ENDPOINT) {
|
||||
msg.classList.add('error');
|
||||
msg.textContent = 'Order endpoint not configured yet. Email orders@volcanantiguaroasters.com directly.';
|
||||
return;
|
||||
}
|
||||
|
||||
const data = new FormData(form);
|
||||
data.append('_subject', 'New order — ' + (data.get('name') || 'unknown') + ' · ' + (data.get('fulfillment') || ''));
|
||||
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = 'Sending…';
|
||||
|
||||
fetch(FORM_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: { 'Accept': 'application/json' },
|
||||
body: data
|
||||
}).then(function(r) {
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
return r.json().catch(() => ({}));
|
||||
}).then(function() {
|
||||
root.style.display = 'none';
|
||||
success.classList.add('active');
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}).catch(function(err) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = submitOriginalText;
|
||||
msg.classList.add('error');
|
||||
msg.textContent = 'Something went wrong. Please try again, or email orders@volcanantiguaroasters.com.';
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
// ── Korean toggle (same pattern as the rest of the site) ──
|
||||
(function() {
|
||||
const TRANSLATIONS_KO = {
|
||||
"Reserve Your Bag": "예약 주문",
|
||||
"Single-origin Antigua,": "안티구아 싱글 오리진,",
|
||||
"roasted to order": "주문 후 로스팅",
|
||||
"Antigua Highland · Medium Roast": "안티구아 하이랜드 · 미디엄 로스트",
|
||||
"Single Origin · Antigua, Guatemala · SHB · 5,000 ft · 12 oz (340g)": "싱글 오리진 · 과테말라 안티구아 · SHB · 해발 5,000ft · 340g (12oz)",
|
||||
"Pricing and shipping confirmed on the emailed invoice.": "가격과 배송비는 이메일 인보이스에서 확정됩니다.",
|
||||
"Grind": "분쇄도",
|
||||
"Whole Bean": "원두 그대로",
|
||||
"Drip": "드립",
|
||||
"Pour-over": "푸어오버",
|
||||
"Espresso": "에스프레소",
|
||||
"French Press": "프렌치 프레스",
|
||||
"Quantity": "수량",
|
||||
"Fulfillment": "수령 방식",
|
||||
"Ship (US)": "배송 (미국 내)",
|
||||
"Local Pickup / Delivery": "근거리 픽업 / 배달",
|
||||
"Name": "이름",
|
||||
"Email": "이메일",
|
||||
"Phone": "전화번호",
|
||||
"Shipping address": "배송 주소",
|
||||
"Street address": "도로명 주소",
|
||||
"Apt / unit (optional)": "동/호수 (선택)",
|
||||
"City": "시",
|
||||
"State": "주",
|
||||
"ZIP code": "우편번호",
|
||||
"Area note": "지역 메모",
|
||||
"Neighborhood / preferred pickup or delivery": "동네 / 픽업 또는 배달 선호 안내",
|
||||
"Notes (optional)": "메모 (선택)",
|
||||
"Anything we should know?": "전달하실 내용이 있다면",
|
||||
"Reserve My Bag": "예약하기",
|
||||
"Sending…": "전송 중…",
|
||||
"Order received.": "주문이 접수되었습니다.",
|
||||
"We'll email your secure invoice shortly. Thank you for reserving direct from the roaster.": "곧 안전한 인보이스를 이메일로 보내드립니다. 로스터에서 직접 예약해 주셔서 감사합니다.",
|
||||
"Order endpoint not configured yet. Email orders@volcanantiguaroasters.com directly.": "주문 엔드포인트가 아직 설정되지 않았습니다. orders@volcanantiguaroasters.com 으로 이메일 부탁드립니다.",
|
||||
"Something went wrong. Please try again, or email orders@volcanantiguaroasters.com.": "문제가 발생했습니다. 다시 시도하시거나 orders@volcanantiguaroasters.com 으로 이메일 부탁드립니다."
|
||||
};
|
||||
|
||||
let currentLang = 'en';
|
||||
const originalTexts = new WeakMap();
|
||||
let koFontsLoaded = false;
|
||||
function loadKoFonts() {
|
||||
if (koFontsLoaded) return; koFontsLoaded = true;
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;600;700&family=Noto+Serif+KR:wght@400;500;700&display=swap';
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
function walk(node, found) {
|
||||
if (!node) return;
|
||||
if (node.nodeType === 3) {
|
||||
if (node.textContent.trim()) found.push(node);
|
||||
return;
|
||||
}
|
||||
if (node.nodeType !== 1) return;
|
||||
const tag = node.tagName;
|
||||
if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'NOSCRIPT') return;
|
||||
if (node.classList && (node.classList.contains('lang-toggle') || node.classList.contains('lang-skip'))) return;
|
||||
for (let i = 0; i < node.childNodes.length; i++) walk(node.childNodes[i], found);
|
||||
}
|
||||
function setLang(lang) {
|
||||
if (lang === 'ko') loadKoFonts();
|
||||
const nodes = []; walk(document.body, nodes);
|
||||
nodes.forEach(n => {
|
||||
if (!originalTexts.has(n)) originalTexts.set(n, n.textContent);
|
||||
const orig = originalTexts.get(n);
|
||||
const t = orig.trim();
|
||||
if (lang === 'ko' && TRANSLATIONS_KO[t]) {
|
||||
const lead = orig.match(/^\s*/)[0];
|
||||
const trail = orig.match(/\s*$/)[0];
|
||||
n.textContent = lead + TRANSLATIONS_KO[t] + trail;
|
||||
} else if (lang === 'en') {
|
||||
n.textContent = orig;
|
||||
}
|
||||
});
|
||||
document.querySelectorAll('input[placeholder], textarea[placeholder]').forEach(inp => {
|
||||
if (!inp.dataset.origPlaceholder) inp.dataset.origPlaceholder = inp.placeholder;
|
||||
if (lang === 'ko' && TRANSLATIONS_KO[inp.dataset.origPlaceholder]) inp.placeholder = TRANSLATIONS_KO[inp.dataset.origPlaceholder];
|
||||
else inp.placeholder = inp.dataset.origPlaceholder;
|
||||
});
|
||||
document.querySelectorAll('.lang-toggle').forEach(btn => {
|
||||
const en = btn.querySelector('[data-lang-en]'), ko = btn.querySelector('[data-lang-ko]');
|
||||
if (en && ko) { en.classList.toggle('lang-active', lang === 'en'); ko.classList.toggle('lang-active', lang === 'ko'); }
|
||||
});
|
||||
document.documentElement.setAttribute('lang', lang === 'ko' ? 'ko' : 'en');
|
||||
currentLang = lang;
|
||||
}
|
||||
document.querySelectorAll('.lang-toggle').forEach(btn => {
|
||||
btn.addEventListener('click', () => setLang(currentLang === 'en' ? 'ko' : 'en'));
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||