diff options
| author | Elizabeth Hunt <me@liz.coffee> | 2026-01-01 18:32:49 -0800 |
|---|---|---|
| committer | Elizabeth Hunt <me@liz.coffee> | 2026-02-07 14:24:53 -0800 |
| commit | d086e9e753da10269b856aea76f47ba32a7c44e0 (patch) | |
| tree | 64e40df1e4d25d91c1169cd1e21c4ee34a6a1b3d /src | |
| parent | 0248a3899ed910f005dccaeefc1d9dcb893e8154 (diff) | |
| download | mistymountainstherapy-d086e9e753da10269b856aea76f47ba32a7c44e0.tar.gz mistymountainstherapy-d086e9e753da10269b856aea76f47ba32a7c44e0.zip | |
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.css | 461 | ||||
| -rw-r--r-- | src/app.html | 25 | ||||
| -rw-r--r-- | src/components/DirectionCard.svelte | 71 | ||||
| -rw-r--r-- | src/components/Footer.svelte | 154 | ||||
| -rw-r--r-- | src/components/NavBar.svelte | 306 | ||||
| -rw-r--r-- | src/lib/data/images.ts | 4 | ||||
| -rw-r--r-- | src/lib/data/people.ts | 2 | ||||
| -rw-r--r-- | src/routes/+layout.svelte | 40 | ||||
| -rw-r--r-- | src/routes/+page.svelte | 265 | ||||
| -rw-r--r-- | src/routes/__layout.svelte | 13 | ||||
| -rw-r--r-- | src/routes/approach/+page.svelte | 188 | ||||
| -rw-r--r-- | src/routes/approach/index.svelte | 45 | ||||
| -rw-r--r-- | src/routes/contact/+page.svelte | 325 | ||||
| -rw-r--r-- | src/routes/contact/index.svelte | 158 | ||||
| -rw-r--r-- | src/routes/contact/submit/+server.js (renamed from src/routes/contact/submit.js) | 31 | ||||
| -rw-r--r-- | src/routes/index.svelte | 84 | ||||
| -rw-r--r-- | src/routes/services/+page.svelte (renamed from src/routes/services/index.svelte) | 4 | ||||
| -rw-r--r-- | src/routes/team/+page.svelte (renamed from src/routes/team/index.svelte) | 8 |
18 files changed, 1735 insertions, 449 deletions
diff --git a/src/app.css b/src/app.css index cd98343..b86ae71 100644 --- a/src/app.css +++ b/src/app.css @@ -1,7 +1,468 @@ +@import '@fontsource/cormorant/400.css'; +@import '@fontsource/cormorant/500.css'; +@import '@fontsource/cormorant/600.css'; +@import 'bootstrap-icons/font/bootstrap-icons.css'; + +:root { + --font-serif: 'Cormorant', ui-serif, Georgia, 'Times New Roman', serif; + --font-sans: + ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, + 'Apple Color Emoji', 'Segoe UI Emoji'; + + --color-bg: #e8e8e8; + --color-surface: #f7f7f3; + --color-ink: #1d1d1b; + --color-muted: #555555; + --color-border: #cfcfc8; + + --color-accent: #2b6cb0; + --color-accent-strong: #2c5282; + --color-accent-soft: #63b3ed; + + --color-warn: #b7791f; + --color-warn-soft: #f6ad55; + + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06), 0 10px 30px rgba(0, 0, 0, 0.05); + --radius-sm: 12px; + --radius-pill: 999px; + + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-5: 1.5rem; + --space-6: 2rem; + --space-7: 3rem; +} + +* { + box-sizing: border-box; +} + +html { + color-scheme: light; + -webkit-text-size-adjust: 100%; +} + +body { + margin: 0; + overflow-x: hidden; + min-height: 100vh; + display: flex; + flex-direction: column; + background: var(--color-bg); + color: var(--color-ink); + font-family: var(--font-sans); + line-height: 1.5; + text-rendering: optimizeLegibility; +} + +main { + flex: 1; +} + +h1, +h2, +h3 { + font-family: var(--font-serif); + line-height: 1.1; + letter-spacing: 0.01em; + margin: 0 0 var(--space-4); +} + +h1 { + font-size: clamp(2.2rem, 4.2vw, 3.1rem); +} + +h2 { + font-size: clamp(1.6rem, 3vw, 2.25rem); +} + +h3 { + font-size: 1.4rem; +} + +img { + max-width: 100%; + height: auto; +} + +.img-fluid { + max-width: 100%; + height: auto; + display: block; +} + a { + color: inherit; + text-decoration-thickness: 1px; + text-underline-offset: 0.18em; + transition: text-decoration-thickness 100ms ease; +} + +a:hover { + text-decoration-thickness: 2px; +} + +img { + transition: transform 200ms ease; +} + +a:focus-visible, +button:focus-visible, +input:focus-visible, +textarea:focus-visible, +select:focus-visible { + outline: 3px solid var(--color-accent); + outline-offset: 3px; +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + +.container { + width: min(1120px, calc(100% - 2rem)); + margin-inline: auto; +} + +.page { + padding-block: var(--space-5) var(--space-7); +} + +.skip-link { + position: absolute; + left: var(--space-3); + top: var(--space-3); + padding: var(--space-2) var(--space-3); + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + transform: translateY(-200%); + transition: transform 120ms ease; + z-index: 1000; +} + +.skip-link:focus { + transform: translateY(0); +} + +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.button, +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.65rem 1rem; + border-radius: var(--radius-pill); + border: 1px solid var(--color-border); + background: transparent; + color: var(--color-ink); + font: inherit; + font-weight: 650; text-decoration: none; + box-shadow: none; + cursor: pointer; + transition: + background 150ms ease, + border-color 150ms ease, + transform 100ms ease; +} + +.button:hover, +.btn:hover { + transform: translateY(-1px); +} + +.button:hover, +.btn:hover { + border-color: var(--color-accent); +} + +.button--primary, +.btn-primary { + background: var(--color-accent); + border-color: var(--color-accent); + color: white; +} + +.button--primary:hover, +.btn-primary:hover { + background: var(--color-accent-strong); + border-color: var(--color-accent-strong); +} + +.btn-success { + background: var(--color-accent-soft); + border-color: var(--color-accent-soft); + color: #0b1a12; +} + +.btn-success:hover { + filter: brightness(0.95); +} + +.notice { + margin: var(--space-4) auto; + padding: var(--space-3) var(--space-4); + border-radius: var(--radius-sm); + border: 1px solid var(--color-border); + background: var(--color-surface); + max-width: 70ch; +} + +.notice--info { + border-color: color-mix(in srgb, var(--color-accent) 35%, transparent); +} + +.notice--success { + border-color: color-mix(in srgb, var(--color-accent) 55%, transparent); + background: color-mix(in srgb, var(--color-accent-soft) 22%, transparent); +} + +.notice--warn { + border-color: color-mix(in srgb, var(--color-warn) 55%, transparent); + background: color-mix(in srgb, var(--color-warn-soft) 18%, transparent); +} + +.notice--error { + border-color: rgba(197, 48, 48, 0.65); + background: rgba(245, 101, 101, 0.15); +} + +.contact-card__icon { + font-size: 2.25rem; + line-height: 1; + margin-bottom: var(--space-2); +} + +.form-control { + width: 100%; + padding: 0.65rem 0.8rem; + border-radius: var(--radius-sm); + border: 1px solid var(--color-border); + background: #ffffff; + font: inherit; +} + +.form-control:focus { + border-color: var(--color-accent); +} + +.text-muted { + color: var(--color-muted); +} + +.bg-light { + background: var(--color-surface); +} + +.bg-dark { + background: #101413; +} + +.text-white { + color: white; +} + +.border { + border: 1px solid var(--color-border); +} + +.border-bottom { + border-bottom: 1px solid var(--color-border); +} + +.rounded { + border-radius: var(--radius-sm); +} + +.shadow { + box-shadow: var(--shadow-sm); +} + +.card { + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + box-shadow: var(--shadow-sm); + padding: var(--space-4); +} + +.card--warn { + border-color: color-mix(in srgb, var(--color-warn) 60%, var(--color-border)); + background: color-mix(in srgb, var(--color-warn-soft) 10%, var(--color-surface)); +} + +@media (min-width: 768px) { + .card { + padding: var(--space-5); + } +} + +/* Minimal legacy layout helpers (for iterative migration) */ +.row { + display: grid; + grid-template-columns: repeat(12, 1fr); + gap: var(--space-4); +} + +.row > * { + grid-column: 1 / -1; + min-width: 0; +} + +@media (min-width: 768px) { + .col-md-4 { + grid-column: span 4; + } + .col-md-5 { + grid-column: span 5; + } + .col-md-6 { + grid-column: span 6; + } + .col-md-7 { + grid-column: span 7; + } + .col-md-8 { + grid-column: span 8; + } +} + +@media (min-width: 576px) { + .col-sm-10 { + grid-column: 2 / span 10; + } +} + +@media (min-width: 992px) { + .col-lg-3 { + grid-column: span 3; + } +} + +.d-flex { + display: flex; +} + +.flex-column { + flex-direction: column; +} + +.flex-row { + flex-direction: row; +} + +.align-items-center { + align-items: center; +} + +.justify-content-center { + justify-content: center; +} + +.justify-content-end { + justify-content: flex-end; +} + +.ms-auto { + margin-left: auto; +} + +.mt-auto { + margin-top: auto; +} + +.min-vh-100 { + min-height: 100vh; +} + +.my-2 { + margin-top: var(--space-4); + margin-bottom: var(--space-4); +} + +.mt-2 { + margin-top: var(--space-4); +} + +.mb-2 { + margin-bottom: var(--space-4); +} + +.p-1 { + padding: var(--space-2); +} + +.p-3 { + padding: var(--space-4); +} + +.p-4 { + padding: var(--space-6); +} + +.py-2 { + padding-top: var(--space-4); + padding-bottom: var(--space-4); +} + +.px-4 { + padding-left: var(--space-6); + padding-right: var(--space-6); +} + +.m-2 { + margin: var(--space-4); +} + +.text-center { + text-align: center; } .border-darkish { border-bottom: 1px dashed #666666; } + +blockquote { + margin: 0 0 var(--space-5); + padding-left: var(--space-5); + border-left: 4px solid var(--color-accent); + font-family: var(--font-serif); + font-size: clamp(1.25rem, 2.5vw, 1.5rem); + font-style: italic; + line-height: 1.4; + color: var(--color-ink); +} + +blockquote footer { + margin-top: var(--space-3); + font-size: 1rem; + font-style: normal; + color: var(--color-muted); + text-align: right; +} + +blockquote footer::before { + content: '\2014\00A0'; +} diff --git a/src/app.html b/src/app.html index 78a884c..7249e98 100644 --- a/src/app.html +++ b/src/app.html @@ -3,30 +3,13 @@ <head> <meta charset="utf-8" /> <meta name="description" content="" /> - <link rel="icon" href="%svelte.assets%/favicon.png" /> + <link rel="icon" href="%sveltekit.assets%/favicon.png" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Misty Mountains Therapy</title> - <link - href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" - rel="stylesheet" - integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" - crossorigin="anonymous" - /> - <script - src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" - integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" - crossorigin="anonymous" - ></script> - - <link - rel="stylesheet" - href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css" - /> - - %svelte.head% + %sveltekit.head% </head> - <body class="d-flex flex-column min-vh-100" style="background-color: #e8e8e8"> - %svelte.body% + <body> + <div style="display: contents">%sveltekit.body%</div> </body> </html> diff --git a/src/components/DirectionCard.svelte b/src/components/DirectionCard.svelte index 0097d33..909feac 100644 --- a/src/components/DirectionCard.svelte +++ b/src/components/DirectionCard.svelte @@ -1,26 +1,51 @@ <script> - export let imageSpec; - export let direction; + export let imageSpec; + export let direction; </script> -<main> - {#if direction == 'right'} - <div class="row d-flex align-items-center my-2"> - <div class="col-md-4 text-center order-sm-1"> - <img class="img-fluid rounded shadow" src={imageSpec.image} alt={imageSpec.alt} /> - </div> - <div class="col-md-8 border p-4 bg-light rounded shadow order-sm-2"> - <slot /> - </div> - </div> - {:else} - <div class="row d-flex align-items-center my-2"> - <div class="col-md-8 border p-4 bg-light rounded shadow order-md-first order-last"> - <slot /> - </div> - <div class="col-md-4 text-center"> - <img class="img-fluid rounded shadow" src={imageSpec.image} alt={imageSpec.alt} /> - </div> - </div> - {/if} -</main> +<section class="direction-card" class:reverse={direction === 'right'}> + <div class="direction-card__row row my-2"> + <div class="direction-card__media col-md-4"> + <img + class="img-fluid rounded shadow" + src={imageSpec.image} + alt={imageSpec.alt} + loading="lazy" + decoding="async" + /> + </div> + <div class="direction-card__content col-md-8 card"> + <slot /> + </div> + </div> +</section> + +<style> + .direction-card__row { + align-items: center; + } + + .direction-card__media { + max-width: 420px; + margin-inline: auto; + } + + @media (min-width: 768px) { + .direction-card__media { + max-width: none; + } + + .reverse .direction-card__media { + order: 2; + } + + .reverse .direction-card__content { + order: 1; + } + } + + .direction-card :global(img) { + max-height: 520px; + object-fit: cover; + } +</style> diff --git a/src/components/Footer.svelte b/src/components/Footer.svelte index 00d3e07..c9375e5 100644 --- a/src/components/Footer.svelte +++ b/src/components/Footer.svelte @@ -1,46 +1,110 @@ -<footer class="mt-auto bg-dark text-white"> - <div class="container mt-2 mb-2"> - <div class="row"> - <div class="col-md-6"> - <h3>Contact</h3> - <div class="d-flex flex-row align-items-center border-darkish p-1"> - <i class="bi bi-telephone"></i> - <div class="px-4"> - <a href="tel:12084994517"> - <span>(208) 499 - 4517</span> - </a> - </div> - </div> - <div class="d-flex flex-row align-items-center border-darkish p-1"> - <i class="bi bi-mailbox"></i> - <a href="mailto:contact@mistymountainstherapy.com"> - <div class="px-4"> - <span>contact@mistymountainstherapy.com</span> - </div> - </a> - </div> - <div class="d-flex flex-row align-items-center p-1"> - <i class="bi bi-pin-map"></i> - <a href="https://maps.app.goo.gl/s1AFqfKvUKgXDCrq5"> - <div class="px-4"> - <span>534 Trejo Street, Suite 200F</span> - <br> - <span class="ml-2">Rexburg, ID, 83440</span> - </div> - </a> - </div> - </div> - <div class="col-md-6"> - <h3>About</h3> - <div class="border-darkish p-1"> - <a href="https://docs.google.com/document/d/10S_-yfiaDZnuD-0tvXTEIIyZiErVwrTj0y_0JiRT5_U/edit?usp=sharing" target="_blank">Privacy Policy</a> - </div> - <div class="p-1"> - <span class="text-muted">Copyright © 2024 Misty Mountains Therapy</span> - <br> - <span class="text-muted">High quality therapy services for the greater Rexburg area.</span> - </div> - </div> - </div> - </div> +<footer class="site-footer"> + <div class="container site-footer__inner"> + <section class="site-footer__col" aria-label="Contact"> + <h2 class="site-footer__heading">Contact</h2> + + <address class="site-footer__list"> + <div class="site-footer__item border-darkish"> + <i class="bi bi-telephone" aria-hidden="true"></i> + <a class="site-footer__link" href="tel:12084994517">(208) 499 - 4517</a> + </div> + + <div class="site-footer__item border-darkish"> + <i class="bi bi-mailbox" aria-hidden="true"></i> + <a class="site-footer__link" href="mailto:contact@mistymountainstherapy.com" + >contact@mistymountainstherapy.com</a + > + </div> + + <div class="site-footer__item"> + <i class="bi bi-pin-map" aria-hidden="true"></i> + <a + class="site-footer__link" + href="https://maps.app.goo.gl/s1AFqfKvUKgXDCrq5" + target="_blank" + rel="noopener noreferrer" + > + <span>534 Trejo Street, Suite 200F</span><br /> + <span>Rexburg, ID, 83440</span> + </a> + </div> + </address> + </section> + + <section class="site-footer__col" aria-label="About"> + <h2 class="site-footer__heading">About</h2> + <div class="site-footer__item border-darkish"> + <a + class="site-footer__link" + href="https://docs.google.com/document/d/10S_-yfiaDZnuD-0tvXTEIIyZiErVwrTj0y_0JiRT5_U/edit?usp=sharing" + target="_blank" + rel="noopener noreferrer" + >Privacy Policy</a + > + </div> + + <p class="text-muted" style="margin-top: var(--space-4)"> + Copyright © 2024 Misty Mountains Therapy + </p> + <p class="text-muted">High quality therapy services for the greater Rexburg area.</p> + </section> + </div> </footer> + +<style> + .site-footer { + margin-top: auto; + background: #101413; + color: white; + padding-block: var(--space-6); + } + + .site-footer__inner { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-6); + } + + .site-footer__heading { + margin: 0 0 var(--space-3); + font-size: 1.6rem; + color: white; + } + + .site-footer__list { + margin: 0; + font-style: normal; + display: grid; + gap: var(--space-2); + } + + .site-footer__item { + display: flex; + align-items: flex-start; + gap: var(--space-3); + padding: var(--space-2); + } + + .site-footer__item i { + margin-top: 0.2rem; + color: rgba(255, 255, 255, 0.85); + } + + .site-footer__link { + color: inherit; + text-decoration: none; + } + + .site-footer__link:hover { + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 0.18em; + } + + @media (min-width: 768px) { + .site-footer__inner { + grid-template-columns: 1fr 1fr; + align-items: start; + } + } +</style> diff --git a/src/components/NavBar.svelte b/src/components/NavBar.svelte index 44118ba..2b64c88 100644 --- a/src/components/NavBar.svelte +++ b/src/components/NavBar.svelte @@ -1,34 +1,274 @@ -<nav class="navbar navbar-expand-md navbar-dark bg-dark"> - <div class="container"> - <a href="/" class="navbar-brand abs"> - <img height="100" src="/logo.png" alt="Misty Mountains Therapy"/> - </a> - <button class="navbar-toggler ms-auto" type="button" data-bs-toggle="collapse" data-bs-target="#toggle"> - <span class="navbar-toggler-icon"></span> - </button> - <div class="navbar-collapse collapse" id="toggle"> - <ul class="navbar-nav "> - <li class="nav-item active"> - <a class="nav-link" href="/">Home</a> - </li> - <li class="nav-item active"> - <a class="nav-link" href="/team">Our Team</a> - </li> - <li class="nav-item active"> - <a class="nav-link" href="/services">Services</a> - </li> - <li class="nav-item active"> - <a class="nav-link" href="/approach">Approach</a> - </li> - <li class="nav-item active"> - <a class="nav-link" href="https://joni-hunt.clientsecure.me/" target="_blank">Portal</a> - </li> - </ul> - <ul class="navbar-nav ms-auto"> - <li class="nav-item"> - <a class="btn btn-primary" href="/contact">Contact Us</a> - </li> - </ul> - </div> - </div> +<script> + import { resolveRoute } from '$app/paths'; + import { page } from '$app/stores'; + + let menuOpen = false; + const menuId = 'primary-navigation'; + let menuEl; + let toggleEl; + + const navLinks = [ + { href: '/', label: 'Home' }, + { href: '/team', label: 'Team' }, + { href: '/services', label: 'Services' }, + { href: '/approach', label: 'Approach' } + ]; + + function isActive(path, currentPath) { + if (path === '/') return currentPath === '/'; + return currentPath.startsWith(path); + } + + function onWindowPointerDown(event) { + if (!menuOpen) return; + const target = event.target; + if (menuEl?.contains(target) || toggleEl?.contains(target)) return; + closeMenu(); + } + + function toggleMenu() { + menuOpen = !menuOpen; + } + + function closeMenu() { + menuOpen = false; + } + + function onNavKeydown(event) { + if (event.key === 'Escape') { + menuOpen = false; + } + } +</script> + +<svelte:window on:keydown={onNavKeydown} on:pointerdown={onWindowPointerDown} /> + +{#if menuOpen} + <div class="site-nav__backdrop" aria-hidden="true" on:click={closeMenu}></div> +{/if} + +<nav class="site-nav" aria-label="Primary"> + <div class="container site-nav__inner"> + <a class="site-nav__brand" href={resolveRoute('/')} on:click={closeMenu}> + <img class="site-nav__logo" height="64" src="/logo.png" alt="Misty Mountains Therapy" /> + <span class="visually-hidden">Misty Mountains Therapy</span> + </a> + + <button + bind:this={toggleEl} + class="site-nav__toggle" + type="button" + aria-expanded={menuOpen} + aria-controls={menuId} + on:click={toggleMenu} + > + <span class="visually-hidden">{menuOpen ? 'Close menu' : 'Open menu'}</span> + <i class={'bi ' + (menuOpen ? 'bi-x-lg' : 'bi-list')} aria-hidden="true"></i> + </button> + + <div bind:this={menuEl} id={menuId} class="site-nav__menu" class:site-nav__menuOpen={menuOpen}> + <ul class="site-nav__links"> + {#each navLinks as link (link.href)} + <li> + <a + class="site-nav__link" + class:site-nav__link--active={isActive(link.href, $page.url.pathname)} + href={resolveRoute(link.href)} + aria-current={isActive(link.href, $page.url.pathname) ? 'page' : undefined} + on:click={closeMenu} + > + {link.label} + </a> + </li> + {/each} + <li> + <a + class="site-nav__link" + href="https://joni-hunt.clientsecure.me/" + target="_blank" + rel="noopener noreferrer" + on:click={closeMenu} + > + Portal + <i class="bi bi-box-arrow-up-right" aria-hidden="true"></i> + </a> + </li> + </ul> + + <div class="site-nav__cta"> + <a class="button button--primary" href={resolveRoute('/contact')} on:click={closeMenu}>Contact Us</a> + </div> + </div> + </div> </nav> + +<style> + .site-nav { + position: sticky; + top: 0; + z-index: 70; + background: rgba(247, 247, 243, 0.86); + backdrop-filter: blur(10px); + border-bottom: 1px solid var(--color-border); + } + + .site-nav__inner { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--space-4); + padding-block: var(--space-3); + } + + .site-nav__brand { + display: inline-flex; + align-items: center; + gap: var(--space-3); + text-decoration: none; + } + + .site-nav__logo { + display: block; + width: auto; + height: 64px; + } + + .site-nav__toggle { + margin-left: auto; + border: 1px solid var(--color-border); + border-radius: var(--radius-pill); + background: transparent; + padding: 0.75rem 0.9rem; + cursor: pointer; + line-height: 1; + } + + .site-nav__toggle:hover { + border-color: var(--color-accent); + } + + .site-nav__toggle i { + font-size: 1.25rem; + } + + .site-nav__backdrop { + position: fixed; + inset: 0; + background: rgba(16, 20, 19, 0.32); + backdrop-filter: blur(2px); + z-index: 40; + } + + .site-nav__menu { + z-index: 60; + position: absolute; + top: calc(100% + var(--space-2)); + left: 0; + right: 0; + width: 100%; + display: flex; + flex-direction: column; + gap: var(--space-4); + padding: var(--space-3); + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background: rgba(247, 247, 243, 0.95); + box-shadow: var(--shadow-sm); + + transform: translateX(0.75rem); + opacity: 0; + max-height: 0; + overflow: hidden; + pointer-events: none; + transition: + transform 180ms ease, + opacity 180ms ease, + max-height 240ms ease; + } + + .site-nav__menuOpen { + transform: translateX(0); + opacity: 1; + max-height: 70vh; + pointer-events: auto; + } + + .site-nav__links { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: var(--space-2); + } + + .site-nav__link { + position: relative; + display: inline-flex; + align-items: center; + gap: 0.5rem; + text-decoration: none; + padding: 0.75rem 0.9rem; + border-radius: var(--radius-pill); + font-weight: 600; + transition: background 150ms ease, color 150ms ease; + } + + .site-nav__link:hover { + background: color-mix(in srgb, var(--color-accent) 12%, transparent); + } + + .site-nav__link--active { + background: color-mix(in srgb, var(--color-accent) 15%, transparent); + color: var(--color-accent-strong); + } + + .site-nav__link--active:hover { + background: color-mix(in srgb, var(--color-accent) 20%, transparent); + } + + .site-nav__cta { + display: flex; + } + + @media (min-width: 768px) { + .site-nav__toggle { + display: none; + } + + .site-nav__backdrop { + display: none; + } + + .site-nav__menu { + z-index: auto; + position: static; + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; + gap: var(--space-4); + margin-top: 0; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; + width: auto; + margin-left: auto; + + transform: none; + opacity: 1; + max-height: none; + overflow: visible; + pointer-events: auto; + transition: none; + } + + .site-nav__links { + flex-direction: row; + align-items: center; + } + } +</style> diff --git a/src/lib/data/images.ts b/src/lib/data/images.ts index 3e787af..9ee4a6c 100644 --- a/src/lib/data/images.ts +++ b/src/lib/data/images.ts @@ -1,10 +1,10 @@ // Carousel images for homepage export const carouselImages = [ + '/images/carousel/mountains.png', '/images/carousel/Plaza-Logo-Door.jpeg', '/images/carousel/Entry-Way.jpeg', '/images/carousel/office-1-1.jpeg', - '/images/carousel/Inside-Office.jpeg', - '/images/carousel/mountains.png' + '/images/carousel/Inside-Office.jpeg' ]; // Services page images diff --git a/src/lib/data/people.ts b/src/lib/data/people.ts index 1ddd303..7b5d237 100644 --- a/src/lib/data/people.ts +++ b/src/lib/data/people.ts @@ -30,7 +30,7 @@ export const people: Person[] = [ email: 'contact@mistymountainstherapy.com', position: 'Office Assistant', image: '/images/team/Lily.jpeg', - bio: 'Having grown up in rural Idaho, I have always loved potatoes in every form. I also love cooking and baking just about anything, but especially sourdough bread and baked goods. I lived in the Swiss and Austrian alps for long enough to know I am in love with mountains as well as the German language and culture. I am currently close to graduating with my bachelor\'s degree in Marriage and Family Studies/Psychology at BYU-Idaho and will then go on to studying Professional Counseling at Liberty University. I hope to one day become a mental health therapist with an emphasis on marriage and relationship dynamics.' + bio: "Having grown up in rural Idaho, I have always loved potatoes in every form. I also love cooking and baking just about anything, but especially sourdough bread and baked goods. I lived in the Swiss and Austrian alps for long enough to know I am in love with mountains as well as the German language and culture. I am currently close to graduating with my bachelor's degree in Marriage and Family Studies/Psychology at BYU-Idaho and will then go on to studying Professional Counseling at Liberty University. I hope to one day become a mental health therapist with an emphasis on marriage and relationship dynamics." }, { id: 4, diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..651ef5f --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,40 @@ +<script> + import { page } from '$app/stores'; + import { fade } from 'svelte/transition'; + import NavBar from '../components/NavBar.svelte'; + import Footer from '../components/Footer.svelte'; + import '../app.css'; +</script> + +<svelte:head> + <meta + name="description" + content="Misty Mountains Therapy provides high quality therapy services for Rexburg and the surrounding area." + /> + <meta property="og:site_name" content="Misty Mountains Therapy" /> + <meta property="og:title" content="Misty Mountains Therapy" /> + <meta + property="og:description" + content="High quality therapy services for Rexburg and the surrounding area." + /> + <meta property="og:type" content="website" /> + <meta property="og:url" content="https://mistymountainstherapy.com/" /> + <meta property="og:image" content="https://mistymountainstherapy.com/logo.png" /> + <meta name="twitter:card" content="summary" /> + <meta name="twitter:title" content="Misty Mountains Therapy" /> + <meta + name="twitter:description" + content="High quality therapy services for Rexburg and the surrounding area." + /> +</svelte:head> + +<a class="skip-link" href="#main">Skip to content</a> +<NavBar /> +<main id="main" class="page" tabindex="-1"> + {#key $page.url.pathname} + <div class="container" in:fade={{ duration: 150, delay: 50 }}> + <slot /> + </div> + {/key} +</main> +<Footer /> diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..4c0265f --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,265 @@ +<script> + import { resolveRoute } from '$app/paths'; + import { carouselImages } from '$lib/data/images'; + import { onMount } from 'svelte'; + + const images = [ + { src: carouselImages[0], alt: 'Glass office door with the Misty Mountains Therapy logo' }, + { src: carouselImages[1], alt: 'Entry hall with a bench for waiting patients' }, + { src: carouselImages[2], alt: 'Office room with a rustic lamp and comfortable couch' }, + { src: carouselImages[3], alt: 'Welcoming therapy office interior' }, + { src: carouselImages[4], alt: 'Misty mountain landscape' } + ]; + + let currentIndex = 0; + let intervalId; + + function nextSlide() { + currentIndex = (currentIndex + 1) % images.length; + } + + function prevSlide() { + currentIndex = (currentIndex - 1 + images.length) % images.length; + } + + function goToSlide(index) { + currentIndex = index; + } + + onMount(() => { + intervalId = setInterval(nextSlide, 5000); + return () => clearInterval(intervalId); + }); + + function pauseAutoplay() { + clearInterval(intervalId); + } + + function resumeAutoplay() { + clearInterval(intervalId); + intervalId = setInterval(nextSlide, 5000); + } +</script> + +<section class="hero"> + <div + class="carousel" + role="region" + aria-label="Office photos" + aria-roledescription="carousel" + on:mouseenter={pauseAutoplay} + on:mouseleave={resumeAutoplay} + on:focusin={pauseAutoplay} + on:focusout={resumeAutoplay} + > + <div class="carousel__track" style="transform: translateX(-{currentIndex * 100}%)"> + {#each images as image, i (image.src)} + <div + class="carousel__slide" + role="group" + aria-roledescription="slide" + aria-label="{i + 1} of {images.length}" + aria-hidden={i !== currentIndex} + > + <img + class="carousel__image" + src={image.src} + alt={image.alt} + loading={i === 0 ? 'eager' : 'lazy'} + decoding="async" + /> + </div> + {/each} + </div> + + <button + class="carousel__btn carousel__btn--prev" + type="button" + aria-label="Previous slide" + on:click={prevSlide} + > + <i class="bi bi-chevron-left" aria-hidden="true"></i> + </button> + <button + class="carousel__btn carousel__btn--next" + type="button" + aria-label="Next slide" + on:click={nextSlide} + > + <i class="bi bi-chevron-right" aria-hidden="true"></i> + </button> + + <div class="carousel__dots" role="tablist" aria-label="Slide controls"> + {#each images as _, i (i)} + <button + class="carousel__dot" + class:carousel__dot--active={i === currentIndex} + type="button" + role="tab" + aria-selected={i === currentIndex} + aria-label="Go to slide {i + 1}" + on:click={() => goToSlide(i)} + ></button> + {/each} + </div> + </div> + + <div class="hero__content"> + <h1>Helping you conquer Mount Doom.</h1> + <a href={resolveRoute('/contact')} class="button button--primary"> + Free 15 Minute Consultation + </a> + </div> +</section> + +<section class="card home-intro"> + <blockquote> + "Darkness must pass, a new day will come, and when the sun shines, it will shine out the clearer." + <footer>Samwise Gamgee</footer> + </blockquote> + + <h2>Welcome to Misty Mountains Therapy</h2> + + <p> + Founded in 2020 by Jefferson Hunt, we are dedicated to providing high quality therapy services to children and adults throughout the Rexburg area. Our approach combines professional expertise and genuine care to help clients navigate life's challenges. + </p> + + <h3>We're Here to Help</h3> + <p> + We are currently accepting new clients. Whether you're seeking support for yourself or a loved one, we offer personalized treatment tailored to your unique needs and goals. + </p> + + <p> + <strong>Ready to take the first step?</strong> Schedule a <a href={resolveRoute('/contact')}>free 15-minute consultation</a> to see if we're the right fit for you. + </p> + + <aside class="notice notice--warn" aria-label="Crisis information"> + <p style="margin: 0"> + <strong>Crisis Support:</strong> We do not have a crisis line. If you or someone you know is in danger, please call 911, visit your nearest emergency room, call the National Suicide Prevention Lifeline at <a href="tel:18002738255">(800) 273-TALK</a>, or text HELLO to 741-741. + </p> + </aside> +</section> + +<style> + .hero { + display: grid; + gap: var(--space-5); + margin-bottom: var(--space-5); + } + + @media (min-width: 768px) { + .hero { + grid-template-columns: 1.4fr 1fr; + align-items: center; + } + } + + /* Carousel */ + .carousel { + position: relative; + border-radius: var(--radius-sm); + overflow: hidden; + box-shadow: var(--shadow-sm); + } + + .carousel__track { + display: flex; + transition: transform 400ms ease; + } + + .carousel__slide { + flex: 0 0 100%; + min-width: 0; + } + + .carousel__image { + display: block; + width: 100%; + height: auto; + aspect-ratio: 4 / 3; + object-fit: cover; + } + + .carousel__btn { + position: absolute; + top: 50%; + transform: translateY(-50%); + background: rgba(255, 255, 255, 0.85); + border: none; + border-radius: 50%; + width: 2.5rem; + height: 2.5rem; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + opacity: 0; + transition: opacity 150ms ease, background 150ms ease; + font-size: 1.1rem; + color: var(--color-ink); + } + + .carousel:hover .carousel__btn, + .carousel:focus-within .carousel__btn { + opacity: 1; + } + + .carousel__btn:hover { + background: white; + } + + .carousel__btn--prev { + left: var(--space-3); + } + + .carousel__btn--next { + right: var(--space-3); + } + + .carousel__dots { + position: absolute; + bottom: var(--space-3); + left: 50%; + transform: translateX(-50%); + display: flex; + gap: var(--space-2); + } + + .carousel__dot { + width: 0.5rem; + height: 0.5rem; + border-radius: 50%; + border: none; + background: rgba(255, 255, 255, 0.5); + cursor: pointer; + padding: 0; + transition: background 150ms ease, transform 150ms ease; + } + + .carousel__dot:hover { + background: rgba(255, 255, 255, 0.8); + } + + .carousel__dot--active { + background: white; + transform: scale(1.25); + } + + /* Hero content */ + .hero__content { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: var(--space-4); + } + + .hero__content h1 { + margin: 0; + } + + /* Intro card - removed max-width for wider feel */ + .home-intro .notice { + margin-top: var(--space-5); + margin-bottom: 0; + } +</style> diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte deleted file mode 100644 index ccb2fd5..0000000 --- a/src/routes/__layout.svelte +++ /dev/null @@ -1,13 +0,0 @@ -<script> - import NavBar from '../components/NavBar.svelte'; - import Footer from '../components/Footer.svelte'; - import '../app.css'; - import { SvelteToast } from '@zerodevx/svelte-toast'; -</script> - -<SvelteToast /> -<NavBar /> -<div class="container my-2"> - <slot /> -</div> -<Footer /> diff --git a/src/routes/approach/+page.svelte b/src/routes/approach/+page.svelte new file mode 100644 index 0000000..a103139 --- /dev/null +++ b/src/routes/approach/+page.svelte @@ -0,0 +1,188 @@ +<script> + import { slide } from 'svelte/transition'; + import { approachImage } from '$lib/data/images'; + + const approaches = [ + { + title: 'Solution Focused Therapy', + description: + "You are the expert of your own story. While we'll draw on the past for insight and understanding, our primary focus will be on the present and future. We'll work together to create new perspectives, explore possibilities, and develop concrete plans that help you move forward with confidence." + }, + { + title: 'Cognitive Behavioral Therapy', + description: + "CBT helps you identify and modify unhelpful patterns in your thoughts, emotions, and behaviors. Together, we'll examine whether these patterns accurately reflect reality, and when they don't, we'll develop strategies to challenge and change them." + }, + { + title: 'Narrative Therapy', + description: + 'Sometimes we become so entangled with our struggles that we define ourselves by them, as "an anxious person" or "a depressed person." Narrative therapy helps you separate your identity from your challenges. You\'ll learn to see your difficulties as something you\'re dealing with, not something that defines who you are. This distance creates space for you to discover your true self and recognize your full potential.' + }, + { + title: 'Emotion Focused Therapy', + description: + "Working as a team, we'll identify the emotional blocks that prevent vulnerability in your relationship. You'll learn to express emotions in ways that foster connection rather than create distance. Our goal is to help you build greater trust and a more secure bond with your partner." + }, + { + title: 'Gottman Method', + description: + "Research by Dr. John Gottman demonstrates how negativity affects the brain and creates distance between partners. We'll help you develop the skills to maintain a positive connection, even during challenging times. Couples therapy works best when both partners participate, allowing each person to share their perspective, history, and goals." + }, + { + title: 'Family Systems Theory', + description: + 'While families can be a source of profound joy, they can also be the origin of many psychological challenges. When one family member struggles with anxiety, addiction, abuse, faith transitions, depression, or health issues, it impacts the entire family system. Through effective mediation, we can help navigate even the most difficult situations, fostering respect and working toward resolution.' + }, + { + title: 'PPP - Positive Parenting Program', + description: + "This evidence-based program helps families address and prevent behavioral and emotional challenges in children and teens through a holistic approach that considers family, school, and community contexts. Rather than dictating how to parent, we'll provide you with practical tools and strategies to manage difficult behaviors, establish clear boundaries, encourage positive actions, and practice self-care—so you can parent with confidence and feel good about your approach." + } + ]; + + let openIndex = -1; + + function toggle(index) { + openIndex = openIndex === index ? -1 : index; + } +</script> + +<div class="approach"> + <h1 class="text-center">Our Approach.</h1> + + <section class="card approach__intro"> + <div class="approach__hero"> + <img + class="shadow rounded" + src={approachImage} + alt="boats in water" + loading="lazy" + decoding="async" + /> + </div> + <p> + We tailor each therapeutic journey to meet your unique needs, personality, and circumstances. + Below are some of the approaches we commonly integrate into our practice: + </p> + </section> + + <ul class="approach__list"> + {#each approaches as approach, i (approach.title)} + <li class="approach__item"> + <button + class="approach__trigger" + type="button" + aria-expanded={openIndex === i} + on:click={() => toggle(i)} + > + <span class="approach__title">{approach.title}</span> + <span class="approach__icon" aria-hidden="true"> + <i class={'bi ' + (openIndex === i ? 'bi-dash' : 'bi-plus')}></i> + </span> + </button> + {#if openIndex === i} + <div class="approach__content" transition:slide={{ duration: 200 }}> + <p>{approach.description}</p> + </div> + {/if} + </li> + {/each} + </ul> +</div> + +<style> + .approach { + display: grid; + gap: var(--space-5); + } + + .approach__intro { + display: grid; + gap: var(--space-4); + text-align: center; + } + + .approach__intro p { + max-width: 65ch; + margin-inline: auto; + } + + .approach__hero img { + max-width: min(100%, 420px); + height: auto; + } + + .approach__list { + list-style: none; + margin: 0; + padding: 0; + display: grid; + gap: var(--space-3); + } + + .approach__item { + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + overflow: hidden; + } + + .approach__trigger { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-3); + padding: var(--space-4); + background: transparent; + border: none; + cursor: pointer; + font: inherit; + text-align: left; + transition: background 150ms ease; + } + + .approach__trigger:hover { + background: rgba(0, 0, 0, 0.02); + } + + .approach__icon { + transition: transform 150ms ease; + } + + .approach__trigger[aria-expanded='true'] .approach__icon { + transform: rotate(180deg); + } + + .approach__title { + font-family: var(--font-serif); + font-size: 1.25rem; + font-weight: 600; + } + + .approach__icon { + flex-shrink: 0; + display: inline-flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; + border-radius: 999px; + background: color-mix(in srgb, var(--color-accent) 12%, transparent); + color: var(--color-accent-strong); + } + + .approach__icon i { + font-size: 1.1rem; + } + + .approach__content { + padding: 0 var(--space-4) var(--space-4); + } + + .approach__content p { + margin: 0; + color: var(--color-muted); + line-height: 1.6; + } +</style> diff --git a/src/routes/approach/index.svelte b/src/routes/approach/index.svelte deleted file mode 100644 index 3a4e963..0000000 --- a/src/routes/approach/index.svelte +++ /dev/null @@ -1,45 +0,0 @@ -<script> - import { approachImage } from '$lib/data/images'; -</script> - -<main> - <h1 class="text-center">Our Approach.</h1> - <div class="bg-light rounded p-4 shadow"> - <div class="row justify-content-center"> - <div class="col-sm-10"> - <div style="text-align:center"> - <img class="shadow" src={approachImage} alt="boats in water" style="width:60%;border-radius: 0.25em;"> - </div> - <br> - <p>We meet each client where they are at and customize their therapeutic journey to best fit their personality and issues. Some of the approaches we use most are listed below:</p> - </div> - </div> - <br> - <h2>Cognitive Behavioral Therapy</h2> - - <p>CBT focuses on modifying dysfunctional behaviors, thoughts, and emotions. In session, we will identify these harmful processes and determine if they are an accurate depiction of reality. If they are not, we will help you use strategies to challenge and overcome them. </p> - <h2>Solution Focused Therapy</h2> - - <p>We believe the client is the expert of their own story. If you are coming to see us, it’s possible that you’re overwhelmed with your present situation. We will use the past to provide understanding and reflection, but we will mostly focus on the present and future. We will empower you to create new perspective, possibilities, and plans to actualize your new story.</p> - <h2>Narrative Therapy</h2> - - <p>Sometimes we become our problems. We start to identify as “a depressed person” or “an anxious person”. We can help you learn to see your issues as something you have, but not something that defines you. We will teach you to put some distance between yourself and your issues and empower you to live your life so that you can work on finding out who you really are and what you are capable of.</p> - <h2>Emotion Focused Therapy</h2> - - <p>We will work together as a team to find blocked vulnerabilities in your relationship. We will teach you to express your emotions to help you connect rather than disconnect. The goal is to create more trust and a secure bond with your partner. It is a three-step process: - </p><ol> - <li>De-escalate negative interactions</li> - <li>Re-structure interactions and state fears as well as insecurities</li> - <li>Continue productive conversations outside of therapy.</li> - </ol> - <h2>Gottman Method</h2> - - <p>Gottman’s research has shown that negativity impacts the brain and can draw couples apart. We will help you take the steps necessary to maintain a positive orientation towards one another during difficult circumstances. Therapy is most effective when both partners are present so that each person can share their history, story, and goals.</p> - <h2>Family Systems Theory</h2> - - <p>It is true that profound joy can come from living in a family, but conversely many psychological issues stem from dysfunctional relationships with one another. For example, when one person is struggling with anxiety, pornography addiction, any form of abuse, faith crisis, depression, or health issues it affects the whole family unit. Effective mediation can help in the toughest situations and facilitate respect and resolution.</p> - <h2>PPP - Positive Parenting Program</h2> - - <p>We use this method to teach families how to treat and prevent behavioral and emotional problems in children and teens. We use a holistic approach and address concerns in the family, school, and community. We will not tell you how to parent, rather we will give you tools and ideas to manage misbehavior, set rules, encourage behavior you like, and take care of yourself so you can feel good and be confident in your parenting skills.</p> - </div> -</main> diff --git a/src/routes/contact/+page.svelte b/src/routes/contact/+page.svelte new file mode 100644 index 0000000..c66d9df --- /dev/null +++ b/src/routes/contact/+page.svelte @@ -0,0 +1,325 @@ +<script> + import { browser } from '$app/environment'; + import HCaptcha from 'svelte-hcaptcha'; + + const mapsUrl = 'https://maps.app.goo.gl/s1AFqfKvUKgXDCrq5'; + + let submission = { + name: '', + email: '', + message: '', + phone: '', + captchaToken: '' + }; + + let captcha; + let noticeType = 'info'; + let noticeMessage = ''; + + function setNotice(type, message) { + noticeType = type; + noticeMessage = message; + } + + function handleSubmit(e) { + e.preventDefault(); + + if (!browser) return; + if (!submission.captchaToken) { + setNotice('error', 'Please complete the captcha first.'); + return; + } + + setNotice('info', 'Sending...'); + fetch('/contact/submit', { + method: 'POST', + body: JSON.stringify(submission) + }) + .then((x) => x.json()) + .then((x) => { + if (x.success) { + setNotice('success', 'Success! Reloading...'); + setTimeout(() => window.location.reload(), 1000); + } else { + setNotice('error', x.error ?? 'Something went wrong. Please try again.'); + } + }) + .catch((err) => { + console.log(err); + setNotice('error', 'Network error. Please try again.'); + }); + } + + function onCaptchaError() { + setNotice('error', 'Failed to verify captcha, try again.'); + captcha?.reset(); + submission.captchaToken = ''; + } + + function onCaptchaSuccess({ detail: { token } }) { + submission.captchaToken = token; + } +</script> + +<div class="contact"> + <header class="contact__header"> + <h1>Contact</h1> + </header> + + {#if noticeMessage} + <div + class={'notice notice--' + noticeType} + role={noticeType === 'error' ? 'alert' : 'status'} + aria-live={noticeType === 'error' ? 'assertive' : 'polite'} + > + {noticeMessage} + </div> + {/if} + + <div class="contact__grid"> + <div class="contact__sidebar"> + <section class="card" aria-label="Contact details"> + <h2>Details</h2> + + <div class="contact__methods"> + <a class="contact__method" href={mapsUrl} target="_blank" rel="noopener noreferrer"> + <span class="contact__icon" aria-hidden="true"><i class="bi bi-pin-map"></i></span> + <span class="contact__methodBody"> + <span class="contact__methodTitle">Office</span> + <span class="contact__methodText"> + 534 Trejo Street, Suite 200F<br />Rexburg, ID 83440 + </span> + </span> + </a> + + <a class="contact__method" href="tel:12084994517"> + <span class="contact__icon" aria-hidden="true"><i class="bi bi-telephone"></i></span> + <span class="contact__methodBody"> + <span class="contact__methodTitle">Phone</span> + <span class="contact__methodText">(208) 499 - 4517</span> + </span> + </a> + + <a class="contact__method" href="mailto:contact@mistymountainstherapy.com"> + <span class="contact__icon" aria-hidden="true"><i class="bi bi-envelope"></i></span> + <span class="contact__methodBody"> + <span class="contact__methodTitle">Email</span> + <span class="contact__methodText">contact@mistymountainstherapy.com</span> + </span> + </a> + </div> + </section> + + <section class="card card--warn" aria-label="Crisis resources"> + <h2>In a crisis?</h2> + <p style="margin: 0"> + We do not have a crisis line. If you or someone you know is in danger please call 911, visit your + nearest emergency room, call the National Suicide Prevention Lifeline at + <a href="tel:18002738255">(800)273-TALK</a> (8255), or text HELLO to 741-741. + </p> + </section> + </div> + + <section class="card" aria-label="Send a message"> + <h2>Send a message</h2> + <form class="contact__form" on:submit|preventDefault={handleSubmit}> + <div class="contact__fields"> + <label class="field"> + <span class="field__label">Email *</span> + <input + type="email" + class="form-control" + bind:value={submission.email} + placeholder="johnnyappleseed@example.com" + required + /> + </label> + + <label class="field"> + <span class="field__label">Name *</span> + <input + type="text" + class="form-control" + bind:value={submission.name} + placeholder="Johnny Appleseed" + required + /> + </label> + + <label class="field"> + <span class="field__label">Phone</span> + <input + type="text" + class="form-control" + bind:value={submission.phone} + placeholder="(208) 123-4567" + /> + </label> + + <label class="field"> + <span class="field__label">Message *</span> + <textarea + class="form-control" + bind:value={submission.message} + rows="6" + placeholder="Hello! I would like to schedule a free 15-minute consultation..." + required + ></textarea> + </label> + </div> + + <div class="contact__captcha"> + <HCaptcha + sitekey={import.meta.env.VITE_HCAPTCHA_KEY} + bind:this={captcha} + on:success={onCaptchaSuccess} + on:error={onCaptchaError} + /> + </div> + + <div class="contact__actions"> + <button type="submit" class="button button--primary" disabled={!submission.captchaToken}> + Submit + </button> + <p class="text-muted" style="margin: 0"> + Please watch spam for replies. + </p> + </div> + </form> + </section> + </div> +</div> + +<style> + .contact { + display: grid; + gap: var(--space-5); + } + + .contact__header { + display: grid; + gap: var(--space-2); + } + + .contact__lede { + margin: 0; + color: var(--color-muted); + } + + .contact__grid { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-5); + align-items: start; + } + + @media (min-width: 900px) { + .contact__grid { + grid-template-columns: 1fr minmax(0, 320px); + } + + .contact__sidebar { + order: 2; + } + } + + .contact__sidebar { + display: grid; + gap: var(--space-5); + min-width: 0; + } + + .contact__methods { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-3); + } + + .contact__method { + display: grid; + grid-template-columns: 2.25rem 1fr; + gap: var(--space-3); + align-items: start; + padding: var(--space-3); + border-radius: var(--radius-sm); + border: 1px solid var(--color-border); + text-decoration: none; + background: rgba(255, 255, 255, 0.55); + } + + .contact__method:hover { + border-color: var(--color-accent); + } + + .contact__icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.25rem; + height: 2.25rem; + border-radius: 999px; + background: color-mix(in srgb, var(--color-accent) 10%, transparent); + color: var(--color-accent-strong); + } + + .contact__icon i { + font-size: 1.1rem; + } + + .contact__methodBody { + display: grid; + gap: 0.15rem; + } + + .contact__methodTitle { + font-weight: 700; + } + + .contact__methodText { + color: var(--color-muted); + line-height: 1.35; + word-break: break-word; + } + + .contact__form { + display: grid; + gap: var(--space-4); + } + + .contact__fields { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-3); + } + + @media (min-width: 700px) { + .contact__fields { + grid-template-columns: 1fr; + } + + .field--full { + grid-column: 1 / -1; + } + } + + .field { + display: grid; + gap: var(--space-2); + } + + .field__label { + font-weight: 650; + } + + .contact__captcha { + display: grid; + justify-content: start; + } + + .contact__actions { + display: flex; + flex-wrap: wrap; + gap: var(--space-3); + align-items: center; + } +</style> diff --git a/src/routes/contact/index.svelte b/src/routes/contact/index.svelte deleted file mode 100644 index 5bd68f1..0000000 --- a/src/routes/contact/index.svelte +++ /dev/null @@ -1,158 +0,0 @@ -<script context="module"> - import { browser } from '$app/env'; - import { toast } from '@zerodevx/svelte-toast' - import HCaptcha from 'svelte-hcaptcha'; - - let submission = { - name: '', - email: '', - message: '', - phone: '', - captchaToken: '', - }; - - let captcha; - - function handleSubmit (e) { - e.preventDefault(); - - if (browser) { - const sendToast = toast.push('Sending...', { - duration: 300, - initial: 0, - next: 0.2, - dismissable: false - }); - fetch('/contact/submit', { - method: "POST", - body: JSON.stringify(submission) - }) - .then((x) => x.json()) - .then((x) => { - toast.set(sendToast, { next: 1 }); - - if (x.success) { - toast.push('Success! Reloading...', { - theme: { - '--toastBackground': '#48BB78', - '--toastBarBackground': '#2F855A' - }, - duration: 1000, - onpop: () => { window.location.reload(); }, - }); - } else if (x.error) { - toast.push(x.error, { - theme: { - '--toastBackground': '#F56565', - '--toastBarBackground': '#C53030' - } - }); - } - }) - .catch((err) => console.log(err)); - } - } - - function onCaptchaError () { - toast.push('Failed to verify captcha, try again.', { - theme: { - '--toastBackground': '#F56565', - '--toastBarBackground': '#C53030' - } - }); - captcha.reset(); - } - - function onCaptchaSuccess ({ detail: { token } }) { - submission.captchaToken = token; - } -</script> - -<main> - <h1 class="text-center">Get in touch.</h1> - <div class="d-flex justify-content-center flex-row row"> - <div class="border shadow bg-light py-2 col-lg-3 d-flex align-items-center flex-column m-2"> - <h1><i class="bi bi-map-fill"></i></h1> - <p style="hyphens: auto;"> - <a href="https://maps.app.goo.gl/s1AFqfKvUKgXDCrq5">534 Trejo Street - <br> - Suite 200F - <br> - Rexburg, ID - <br> - 83440 - </a> - </p> - </div> - <div class="border shadow bg-light py-2 col-lg-3 d-flex align-items-center flex-column m-2"> - <div><h1><i class="bi bi-phone-fill"></i></h1></div> - <p style="hyphens: auto;"> - Scheduling and Other - <br> - <a href="tel:12084994517">(208) 499 - 4517</a> - </p> - </div> - <div class="border shadow bg-light py-2 col-lg-3 d-flex align-items-center flex-column m-2" style="word-break: break-all;"> - <h1><i class="bi bi-envelope-fill"></i></h1> - <div> - <p style="hyphens: auto;"> - Billing - <br> - <a href="mailto:billing@mistymountainstherapy.com">billing@mistymountainstherapy.com</a> - </p> - <p style="hyphens: auto;"> - Inquiries - <br> - <a href="mailto:contact@mistymountainsthreapy.com">contact@mistymountainstherapy.com</a> - </p> - <div><em>note: please watch spam for replies</em></div> - </div> - </div> - </div> - <br> - <hr> - <form class="bg-light border shadow p-4" on:submit|preventDefault={handleSubmit}> - <h2>Or send us a message.</h2> - <div class="row mb-2"> - <div class="form-group col-md-6"> - <label for="email">Email *</label> - <input id="email" type="email" class="form-control" bind:value={submission.email} placeholder="johnnyappleseed@example.com" required> - </div> - <div class="form-group col-md-6"> - <label for="name">Name *</label> - <input id="name" type="text" class="form-control" bind:value={submission.name} placeholder="Johnny Appleseed" required> - </div> - </div> - <div class="form-group"> - <label for="phone">Phone</label> - <input id="phone" type="text" class="form-control" bind:value={submission.phone} placeholder="(208) 123-4567"> - </div> - <div class="form-group"> - <label for="message">Message *</label> - <textarea id="message" class="form-control" bind:value={submission.message} rows="3" placeholder="Hello! I would like to schedule a free 15-minute consultation..." required></textarea> - </div> - <div class="pt-2"> - <HCaptcha - sitekey={import.meta.env.VITE_HCAPTCHA_KEY} - bind:this={captcha} - on:success={onCaptchaSuccess} - on:error={onCaptchaError} - /> - </div> - <button type="submit" class="btn btn-primary">Submit</button> - <div class="my-2"><em>note: please watch spam for any replies</em></div> - </form> - <br> - - <div class="bg-light border shadow p-4"> - <div class="d-flex justify-content-center"> - <div class="col-md-8 mt-2"> - <div> - We do not have a crisis line. If you or someone you know is in danger please call 911, visit - your nearest emergency room, call the National Suicide Prevention Lifeline for free crisis - counseling at <a href="tel:18002738255">(800)273-TALK</a> (8255), or text HELLO to 741-741. - </div> - </div> - </div> - </div> -</main> diff --git a/src/routes/contact/submit.js b/src/routes/contact/submit/+server.js index 0befd0e..eed95fe 100644 --- a/src/routes/contact/submit.js +++ b/src/routes/contact/submit/+server.js @@ -39,7 +39,7 @@ class MistyMountainsMailer { } } -export async function post({ request }) { +export async function POST({ request }) { const body = await request.json(); const { HCAPTCHA_SECRET, FORM_TO_EMAIL } = process.env; const mailer = new MistyMountainsMailer( @@ -61,12 +61,10 @@ export async function post({ request }) { .catch(() => false); if (!captchaVerified) { - return { - statusCode: 400, - body: { - error: 'Captcha verification failed' - } - }; + return new Response(JSON.stringify({ error: 'Captcha verification failed' }), { + status: 400, + headers: { 'Content-Type': 'application/json' } + }); } const text = `<p>New MMT Message</h1> @@ -86,17 +84,14 @@ export async function post({ request }) { }); if (!messageSent) { - return { - statusCode: 500, - body: { - error: 'Message could not be sent' - } - }; + return new Response(JSON.stringify({ error: 'Message could not be sent' }), { + status: 500, + headers: { 'Content-Type': 'application/json' } + }); } - return { - body: { - success: true - } - }; + return new Response(JSON.stringify({ success: true }), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); } diff --git a/src/routes/index.svelte b/src/routes/index.svelte deleted file mode 100644 index 1a701c7..0000000 --- a/src/routes/index.svelte +++ /dev/null @@ -1,84 +0,0 @@ -<script> - import { carouselImages } from '$lib/data/images'; - import { Navigation, Pagination, Scrollbar, A11y, Autoplay } from 'swiper'; - import { Swiper, SwiperSlide } from 'swiper/svelte'; - import 'swiper/css'; - import 'swiper/css/navigation'; - import 'swiper/css/pagination'; - import 'swiper/css/scrollbar'; - import 'swiper/css/autoplay'; - - const images = [ - { image: carouselImages[0], alt: 'Picture of the glass office door with the Misty Mountains Therapy logo in the center.' }, - { image: carouselImages[1], alt: 'The entry hall to the Misty Mountains Suite room, showing a bench for waiting patients to sit. The vibes are inviting.' }, - { image: carouselImages[2], alt: 'An office room with a rustic lamp and comfy looking couch.' }, - { image: carouselImages[3], alt: 'A second office room with good vibes.' }, - { image: carouselImages[4], alt: 'Cloudy mountains in a light sky' }, - ]; -</script> - -<div class="row align-items-center py-2"> - <div class="col-md-7"> - <Swiper - modules={[Navigation, Pagination, Scrollbar, A11y, Autoplay]} - class="rounded shadow" - spaceBetween={50} - slidesPerView={1} - autoHeight={true} - loop={true} - navigation - pagination={{ clickable: true }} - autoplay={true} - speed={1000} - on:slideChange={() => console.log('slide change')} - on:swiper={(e) => console.log(e.detail[0])} - > - {#each images as { image, alt } (image)} - <SwiperSlide> - <img class="image-fluid" style="width:100%" src={image} {alt} /> - </SwiperSlide> - {/each} - </Swiper> - </div> - <div class="col-md-5 my-2"> - <h2>Helping you conquer Mount Doom.</h2> - <p><a href="/contact" class="btn btn-success shadow">FREE 15 Minute Consultation</a></p> - </div> -</div> - -<div class="bg-light rounded p-3 shadow"> - <div class="d-flex justify-content-center"> - <div class="col-md-8 mt-2 border-bottom"> - <h3> - "Darkness must pass, a new day will come, and when the sun shines, it will shine out the - clearer." - </h3> - <div class="d-flex justify-content-end">- Samwise Gamgee</div> - <br /> - <p> - Misty Mountains Therapy is a privately owned, high quality, specialty therapy clinic, - founded in January 2020 by Jefferson Hunt. We are dedicated to providing comprehensive - therapy evaluation and treatment services to children and adults for a wide variety of - disorders in the most efficient and effective manner possible in the Rexburg area. We - believe that therapy should be fun, engaging, and most importantly, useful to our clients. - </p> - <p> - We are currently accepting new clients and offer a variety of services to <strong>help you live the - life you desire</strong>. To find the right fit for you, schedule a <a href="/contact" - >free 15-minute consultation</a - >. - </p> - </div> - </div> - - <div class="d-flex justify-content-center"> - <div class="col-md-8 mt-2"> - <div> - We do not have a crisis line. If you or someone you know is in danger please call 911, visit - your nearest emergency room, call the National Suicide Prevention Lifeline for free crisis - counseling at <a href="tel:18002738255">(800)273-TALK</a> (8255), or text HELLO to 741-741. - </div> - </div> - </div> - <br /> -</div> diff --git a/src/routes/services/index.svelte b/src/routes/services/+page.svelte index 1a02120..222e2ff 100644 --- a/src/routes/services/index.svelte +++ b/src/routes/services/+page.svelte @@ -3,7 +3,7 @@ import { servicesImages } from '$lib/data/images'; </script> -<main> +<div> <h1 class="text-center">Our Services.</h1> <DirectionCard direction="left" imageSpec={{image: servicesImages.girl, alt: "Girl looking at camera"}}> <h2>Therapy</h2> @@ -41,4 +41,4 @@ <li>Suicidal Ideation</li> </ul> </DirectionCard> -</main> +</div> diff --git a/src/routes/team/index.svelte b/src/routes/team/+page.svelte index 631645b..12e068f 100644 --- a/src/routes/team/index.svelte +++ b/src/routes/team/+page.svelte @@ -3,20 +3,20 @@ import { people } from '$lib/data/people'; </script> -<main> +<div> <h1 class="text-center">Our Team.</h1> {#if people.length} - {#each people as person, i} + {#each people as person, i (person.email)} <div class="row border-bottom"> <DirectionCard imageSpec={{ image: person.image, alt: person.name }} direction={i % 2 ? 'left' : 'right'} > <h2>{person.name}, {person.position}</h2> - <p style="white-space: pre-line">{person.bio}</p> <a href="mailto:{person.email}"><p>{person.email}</p></a> + <p style="white-space: pre-line">{person.bio}</p> </DirectionCard> </div> {/each} {/if} -</main> +</div> |
