diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.css | 466 | ||||
| -rw-r--r-- | src/app.html | 25 | ||||
| -rw-r--r-- | src/components/DirectionCard.svelte | 80 | ||||
| -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 | 266 | ||||
| -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, 1750 insertions, 449 deletions
diff --git a/src/app.css b/src/app.css index cd98343..8edf450 100644 --- a/src/app.css +++ b/src/app.css @@ -1,7 +1,473 @@ +@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; +} + +p { + margin: 0 0 var(--space-4); + max-width: 70ch; +} + +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..7f9d76f 100644 --- a/src/components/DirectionCard.svelte +++ b/src/components/DirectionCard.svelte @@ -1,26 +1,60 @@ <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"> + {#if 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> + {:else} + <div class="direction-card__row row my-2"> + <div class="direction-card__content col-md-8 card"> + <slot /> + </div> + <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> + {/if} +</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; + } + } + + .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..71b9920 --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,266 @@ +<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> + + <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={resolveRoute('/contact')} + >free 15-minute consultation</a + >. + </p> + + <aside class="notice notice--warn" aria-label="Crisis information"> + <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 for free crisis counseling at + <a href="tel:18002738255">(800)273-TALK</a> (8255), 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..75c9620 --- /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: 'Cognitive Behavioral Therapy', + description: + '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.' + }, + { + title: 'Solution Focused Therapy', + description: + "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." + }, + { + title: 'Narrative Therapy', + description: + '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.' + }, + { + title: 'Emotion Focused Therapy', + description: + '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.' + }, + { + title: 'Gottman Method', + description: + "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." + }, + { + title: 'Family Systems Theory', + description: + '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.' + }, + { + title: 'PPP - Positive Parenting Program', + description: + '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.' + } + ]; + + 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 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> + </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> |
