Compare commits

...

6 Commits

Author SHA1 Message Date
5ce04ba694 own network 2026-06-16 11:33:19 -05:00
143ca13601 some changes 2026-06-14 18:14:16 -05:00
f36cb9ce51 neue Favicons 2026-06-13 17:03:41 -05:00
fa33ccbe72 favicon 2026-06-13 16:52:25 -05:00
d6f6c6480d layout fix 2026-06-13 16:38:38 -05:00
546d6fbba3 docker + hero change 2026-06-13 15:41:15 -05:00
19 changed files with 211 additions and 32 deletions

22
.dockerignore Normal file
View File

@@ -0,0 +1,22 @@
node_modules/
.next/
.git/
.gitignore
Dockerfile
.dockerignore
.env
.env.*
npm-debug.log*
.env.example
docker-compose.yml
README.md
PLAN.md
PRODUCT.md
PROJECT_BRIEF.md
SHAPE_BRIEF.md
DESIGN.md
research/
.DS_Store
Thumbs.db
.vscode/
.idea/

31
Dockerfile Normal file
View File

@@ -0,0 +1,31 @@
# ---- deps ----
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# ---- builder ----
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
# ---- runner ----
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]

View File

@@ -492,9 +492,17 @@ svg {
align-content: start;
gap: var(--space-3);
padding-block: clamp(0.4rem, 2vw, 1rem);
max-width: 56rem;
margin-inline: auto;
text-align: center;
min-width: 0;
}
.hero-copy h1 {
max-width: none;
margin-inline: auto;
}
.eyebrow {
margin: 0;
color: oklch(80% 0.1 246);
@@ -535,19 +543,23 @@ h3 {
}
.hero-lede {
max-width: 32rem;
max-width: 38rem;
margin-inline: auto;
margin-bottom: 0;
color: var(--text-muted);
font-size: clamp(1rem, 0.45vw + 0.93rem, 1.15rem);
line-height: 1.38;
text-align: center;
}
.hero-local-trust {
max-width: 32rem;
margin: var(--space-2) 0 0;
max-width: 38rem;
margin-inline: auto;
margin-top: var(--space-2);
color: var(--blue);
font-size: var(--step--1);
font-weight: 700;
text-align: center;
}
.trust-list,
@@ -561,6 +573,7 @@ h3 {
.trust-list {
display: grid;
gap: 0.45rem;
justify-items: center;
color: var(--text-muted);
}
@@ -621,6 +634,7 @@ h3 {
flex-wrap: wrap;
gap: var(--space-3);
align-items: center;
justify-content: center;
margin-top: var(--space-1);
}
@@ -2973,6 +2987,36 @@ details p {
padding: 0 var(--space-4) var(--space-4);
}
.faq-answer-body {
padding: 0 var(--space-4) var(--space-4);
}
.faq-answer-body p {
margin: 0;
padding: 0;
}
.faq-diagram-image {
display: block;
width: 100%;
max-width: 42rem;
height: auto;
margin-top: var(--space-4);
border-radius: var(--radius-sm);
}
.faq-diagram-light {
display: none;
}
:root[data-theme="light"] .faq-diagram-dark {
display: none;
}
:root[data-theme="light"] .faq-diagram-light {
display: block;
}
.assessment-section {
position: relative;
display: block;
@@ -3508,6 +3552,13 @@ textarea::placeholder {
}
@media (min-width: 760px) {
.trust-list {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--space-4);
}
.site-header {
grid-template-columns: auto 1fr auto;
}
@@ -3593,8 +3644,12 @@ textarea::placeholder {
}
.hero-grid {
grid-template-columns: minmax(18rem, 0.55fr) minmax(43rem, 1.65fr);
gap: clamp(2.5rem, 4vw, 5rem);
gap: clamp(2.5rem, 4vw, 4rem);
}
.architecture-panel {
max-width: 52rem;
margin-inline: auto;
}
.module-grid {
@@ -3840,10 +3895,6 @@ textarea::placeholder {
max-width: 100%;
}
.hero-lede {
max-width: 100%;
}
.hero-copy {
padding-right: 0;
}

View File

@@ -5,6 +5,17 @@ export const metadata: Metadata = {
title: "Business Email Hosting Corpus Christi | Bay Area Email Services",
description:
"Professional domain email hosting for Corpus Christi businesses. 25 GB mailboxes, Outlook and iPhone setup, SPF/DKIM/DMARC, migration, and local support.",
icons: {
icon: [
{ url: "/favicon.svg", type: "image/svg+xml" },
{ url: "/favicon.ico" },
{ url: "/favicon-16x16.png", sizes: "16x16", type: "image/png" },
{ url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" },
{ url: "/favicon-48x48.png", sizes: "48x48", type: "image/png" },
],
shortcut: "/favicon.ico",
apple: { url: "/apple-touch-icon.png", sizes: "180x180", type: "image/png" },
},
};
export default function RootLayout({

16
app/manifest.ts Normal file
View File

@@ -0,0 +1,16 @@
import type { MetadataRoute } from "next";
export default function manifest(): MetadataRoute.Manifest {
return {
name: "Bay Area Email Services",
short_name: "Bay Area Email",
start_url: "/",
display: "standalone",
background_color: "#05070d",
theme_color: "#000080",
icons: [
{ src: "/icon-192.png", sizes: "192x192", type: "image/png" },
{ src: "/icon-512.png", sizes: "512x512", type: "image/png" },
],
};
}

View File

@@ -18,6 +18,28 @@ export default function FaqSection() {
<summary>Can you migrate our existing email?</summary>
<p>Yes. The migration plan depends on your current provider, mailbox count, domain access, and preferred migration window.</p>
</details>
<details>
<summary>How does mail stay online if a server goes down?</summary>
<div className="faq-answer-body">
<p>Incoming mail is temporarily held in Amazon AWS if the primary server needs maintenance or encounters an issue your messages aren&rsquo;t lost. A standby backup environment is ready to take over, so your team can keep sending and receiving. Once things are back to normal, buffered mail is delivered to your inboxes automatically.</p>
<img
className="faq-diagram-image faq-diagram-dark"
src="/assets/mail-flow-panel.png"
alt="Diagram of Bay Area Email mail flow: inbound buffering, mailbox delivery, outbound via Amazon SES, and standby failover."
loading="lazy"
/>
<img
className="faq-diagram-image faq-diagram-light"
src="/assets/mail-flow-panel-light.png"
alt=""
aria-hidden="true"
loading="lazy"
/>
<p className="sr-only">
Inbound messages are collected from the internet, remote mail servers, and other providers, buffered and processed through Amazon S3, delivered to mailboxes, sent outbound through Amazon SES, and supported by standby infrastructure.
</p>
</div>
</details>
<details>
<summary>Does this work with Outlook, iPhone, and iPad?</summary>
<p>Yes. Outlook, iPhone, iPad, web, and desktop access are part of the setup conversation.</p>
@@ -28,7 +50,7 @@ export default function FaqSection() {
</details>
<details>
<summary>What does switching cost?</summary>
<p>Setup and migration are included at no extra charge for teams that switch by June 30, 2026. After that, the ongoing cost is $5 per mailbox per month. We confirm the final scope in your assessment before any work begins.</p>
<p>Setup and migration are included at no extra charge for teams that switch by July 31, 2026. After that, the ongoing cost is $5 per mailbox per month. We confirm the final scope in your assessment before any work begins.</p>
</details>
</div>
</section>

View File

@@ -35,24 +35,6 @@ export default function HeroSection() {
<a className="call-inline" href="tel:+13617658400">Call (361) 765-8400</a>
</div>
</div>
<section id="infrastructure" className="architecture-panel" aria-labelledby="architecture-title">
<h2 id="architecture-title" className="sr-only">Mail flow designed for continuity</h2>
<img
className="architecture-image architecture-image-dark"
src="/assets/mail-flow-panel.png"
alt="Mail flow diagram showing inbound email, Amazon S3 buffering, mailbox delivery, Amazon SES outbound sending, standby infrastructure, and operational system status."
/>
<img
className="architecture-image architecture-image-light"
src="/assets/mail-flow-panel-light.png"
alt=""
aria-hidden="true"
/>
<p className="sr-only">
Inbound messages are collected from the internet, remote mail servers, and other providers, buffered and processed through Amazon S3, delivered to mailboxes, sent outbound through Amazon SES, and supported by standby infrastructure.
</p>
</section>
</div>
<div className="module-grid" aria-label="Service highlights">

View File

@@ -39,10 +39,10 @@ export default function PricingSection({ mailboxes, onMailboxesChange }: Pricing
<div className="offer-banner">
<span className="offer-banner-icon" aria-hidden="true">&#9733;</span>
Free migration &amp; setup for teams that switch by June&nbsp;30,&nbsp;2026.
Free migration &amp; setup for teams that switch by July&nbsp;31,&nbsp;2026.
</div>
<p className="price-subtext">$5 per mailbox / month. Free migration if you switch by June 30, 2026. Final scope confirmed in your assessment.</p>
<p className="price-subtext">$5 per mailbox / month. Free migration if you switch by July 31, 2026. Final scope confirmed in your assessment.</p>
<a className="button button-primary" href="#assessment">Get a mailbox count quote</a>
</div>

View File

@@ -44,7 +44,7 @@ export default function SiteFooter() {
</div>
<div className="footer-bottom">
<p>Bay Area Email Services, part of Bay Area IT. All rights reserved.</p>
<p>Bay Area Email Services, part of <a href="https://bayarea-cc.com" target="_blank" rel="noopener noreferrer">Bay Area IT</a>. All rights reserved.</p>
<div>
<a href="#top" className="back-to-top">
Back to top

28
docker-compose.yml Normal file
View File

@@ -0,0 +1,28 @@
# docker compose up -d --build
#
# The host runs Caddy as a reverse proxy in its own compose project.
# Caddy forwards requests for email-cc.com to this container by name:
# reverse_proxy email-cc-web:3000
# For that to resolve, Caddy and this container must share a Docker network.
# The network "email-cc" is created by the Caddy compose project and joined
# here as external.
#
# Prerequisites: a .env file in the project directory with:
# SES_SMTP_USER=
# SES_SMTP_PASS= (SMTP credentials, not IAM access keys)
# SES_FROM_EMAIL= (must be an SES-verified identity)
services:
web:
build: .
container_name: email-cc-web
env_file:
- .env
restart: unless-stopped
networks:
- email-cc
networks:
email-cc:
external: true
name: email-cc

View File

@@ -1,4 +1,7 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
const nextConfig = {
output: "standalone",
outputFileTracingRoot: process.cwd(),
};
export default nextConfig;

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
public/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

BIN
public/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 B

BIN
public/favicon-48x48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

13
public/favicon.svg Normal file
View File

@@ -0,0 +1,13 @@
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="512" y2="512" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#000080"/>
<stop offset="1" stop-color="#0000FF"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="512" height="512" rx="112" fill="url(#bg)"/>
<rect x="104" y="150" width="304" height="212" rx="28" fill="#FFFFFF"/>
<path d="M132 182 L256 286 L380 182"
fill="none" stroke="#0000FF" stroke-width="30"
stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 629 B

BIN
public/icon-192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
public/icon-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB