Contact Lab

Eight working contact page layouts plus best-practice guidance. Click a tab to preview, then view source to remix your own.

What to include on a contact page

Research from HubSpot, SaaSLandingPage, and industry UX studies points to these essentials.

Multiple contact methods

Email, phone, form, and social — let visitors pick how they reach you.

Hours & response time

Set expectations. "We reply within 24 hours" beats silence.

Physical address

Even if you're remote — adds trust. Embed a map if relevant.

Short form (3–5 fields)

Name, email, subject, message. Longer forms kill conversions.

FAQ or help links

Let visitors self-serve. Links to docs, support, status page.

Clear routing

Sales? Support? Press? Route inquiries to the right inbox.

Welcoming copy

Conversational over corporate. "We'd love to hear from you."

Thank-you / next steps

After submit, tell users what happens next and when.

Mobile-first layout

Most traffic is mobile. Big tap targets, single column, fast.

Sources: HubSpot — Best Contact Us Pages · SaaSLandingPage — 20 Contact Page Examples

1. Simple Form — minimal Apple-style
<header>
  <nav>
    <a href="#">Home</a>
    <a href="#">Work</a>
    <a href="#">Contact</a>
  </nav>
</header>

<main class="contact-wrapper">
  <h1>Get in touch</h1>
  <p class="intro">
    Have a question, collaboration idea, or need support? Fill out the form below and I'll get back to you as soon as possible.
  </p>

  <form action="https://formspree.io/f/YOUR_ID" method="POST">
    <label>Name:</label>
    <input type="text" name="name" required>

    <label>Email:</label>
    <input type="email" name="email" required>

    <label>Subject:</label>
    <input type="text" name="subject" required>

    <label>Message:</label>
    <textarea name="message" rows="5" required></textarea>

    <button type="submit">Send Message</button>
  </form>
</main>
/* Centered narrow column, Apple-style typography */
.contact-wrapper {
  max-width: 880px;
  margin: 0 auto;
  padding: 80px 20px;
}

h1 {
  font-size: 32px;
  font-weight: 600;
  letter-spacing: -0.015em;
}

.intro {
  max-width: 580px;
  color: #555;
  margin-bottom: 48px;
}

form {
  display: flex;
  flex-direction: column;
  gap: 18px;
  max-width: 540px;
}

input, textarea {
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid #e5e5e5;
}

button[type="submit"] {
  width: 180px;
  padding: 12px 18px;
  border-radius: 999px;
  border: 1px solid #111;
  background: transparent;
  cursor: pointer;
}
button:hover { background: #111; color: #fff; }

Get in touch

Have a question, collaboration idea, or need support? Fill out the form below and I'll get back to you as soon as possible.

2. Hero + Form — full-width image above form
<section class="hero">
  <div>
    <h1>Let's Work Together</h1>
    <p>Curious about collaboration, design work, development, or speaking engagements? Reach out and I'll respond shortly.</p>
  </div>
</section>

<main class="contact-wrapper">
  <form action="https://formspree.io/f/YOUR_ID" method="POST">
    <label>Name:</label>
    <input type="text" name="name" placeholder="Your Name" required>

    <label>Email:</label>
    <input type="email" name="email" placeholder="Your Email" required>

    <label>Subject:</label>
    <input type="text" name="subject" placeholder="Subject" required>

    <label>Message:</label>
    <textarea name="message" rows="5" placeholder="Your Message" required></textarea>

    <button type="submit">Send Message</button>
  </form>
</main>
/* Full-width hero image with bottom-aligned text */
.hero {
  width: 100%;
  min-height: 45vh;
  background: url("image.jpg") center/cover no-repeat;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 100px 20px 40px;
  text-align: center;
}

.hero h1 {
  font-size: 42px;
  font-weight: 600;
  letter-spacing: -0.025em;
}

.hero p {
  max-width: 600px;
  color: #555;
  font-size: 18px;
}

Let's Work Together

Curious about collaboration, design work, development, or speaking engagements? Reach out and I'll respond shortly.

3. Dark Theme — moody and minimal
<body>
  <header>
    <nav>
      <a href="#">Home</a>
      <a href="#">Contact</a>
    </nav>
  </header>

  <main class="wrapper">
    <h1>Contact</h1>
    <p class="intro">
      Questions, collaborations, project requests — reach out anytime.
    </p>
    <form action="https://formspree.io/f/YOUR_ID" method="POST">
      <label>Name:</label>
      <input type="text" name="name" placeholder="Your Name" required>

      <label>Email:</label>
      <input type="email" name="email" placeholder="Your Email" required>

      <label>Subject:</label>
      <input type="text" name="subject" placeholder="Subject" required>

      <label>Message:</label>
      <textarea name="message" rows="5" placeholder="Your Message" required></textarea>

      <button type="submit">Send Message</button>
    </form>
  </main>
</body>
/* Custom properties for a dark palette */
:root {
  --d-gray: #0c0c0c;
  --gray: #2a2a2a;
  --light-gray: #4a4a4a;
  --white: #eaeaea;
}

body {
  background: var(--d-gray);
  color: var(--white);
}

input, textarea {
  background: var(--gray);
  border: 1px solid #000;
  color: var(--white);
}

button[type="submit"] {
  border: 1px solid var(--white);
  color: var(--white);
  background: transparent;
}
button:hover { background: var(--white); color: #000; }

Contact

Questions, collaborations, project requests—reach out anytime.

4. Split Screen — image left, form right
<section class="split">
  <div class="left-img"></div>

  <div class="right-form">
    <h1>Let's Connect</h1>
    <p>I'd love to hear from you. Send a message and I'll respond soon.</p>

    <form action="https://formspree.io/f/YOUR_ID" method="POST">
      <label>Name:</label>
      <input type="text" name="name" placeholder="Your Name" required>

      <label>Email:</label>
      <input type="email" name="email" placeholder="Your Email" required>

      <label>Subject:</label>
      <input type="text" name="subject" placeholder="Subject" required>

      <label>Message:</label>
      <textarea name="message" rows="5" placeholder="Your Message" required></textarea>

      <button type="submit">Send Message</button>
    </form>
  </div>
</section>
/* Flexbox 50/50 split, stacks on mobile */
.split {
  display: flex;
  min-height: 100vh;
  flex-wrap: wrap;
}

.left-img {
  flex: 1;
  min-width: 300px;
  background: url("image.jpg") center/cover no-repeat;
}

.right-form {
  flex: 1;
  min-width: 300px;
  padding: 70px 40px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Stack into a single column below 850px */
@media (max-width: 850px) {
  .split { flex-direction: column; }
}

Let's Connect

I'd love to hear from you. Send a message and I'll respond soon.

5. Support Grid — resources, form, contact info
<main class="wrapper">
  <h1>Contact Support</h1>
  <p class="sub">Send a message or find answers using the resources below.</p>

  <div class="grid">
    <div class="panel">
      <strong>Resources</strong>
      FAQs<br>Documentation<br>Service Status
    </div>

    <div>
      <form action="https://formspree.io/f/YOUR_ID" method="POST">
        <label>Name:</label>
        <input type="text" name="name" placeholder="Your Name" required>

        <label>Email:</label>
        <input type="email" name="email" placeholder="Your Email" required>

        <label>Subject:</label>
        <input type="text" name="subject" placeholder="Subject" required>

        <label>Message:</label>
        <textarea name="message" rows="5" placeholder="Your Message" required></textarea>

        <button type="submit">Send Message</button>
      </form>
    </div>

    <div class="panel">
      <strong>Contact Info</strong>
      Email: support@example.com<br>
      Office Hours: M–F<br>9 AM–5 PM
    </div>
  </div>
</main>
/* 3-column grid: narrow - wide - narrow */
.grid {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  gap: 30px;
}

.panel {
  padding: 10px 0;
  font-size: 15px;
  color: #666;
}

.panel strong {
  color: #111;
  display: block;
  margin-bottom: 8px;
}

/* Collapse to single column on small screens */
@media (max-width: 900px) {
  .grid { grid-template-columns: 1fr; }
}

Contact Support

Send a message or find answers using the resources below.

Resources FAQs
Documentation
Service Status
Contact Info Email: support@example.com
Office Hours: M–F
9 AM–5 PM
6. Gradient Hero — colorful radial background + gradient text
<section class="grad-hero">
  <h1>Reach Out</h1>
  <p>Send questions, ideas, or project inquiries. Let's create something extraordinary.</p>
</section>

<main class="wrapper">
  <form action="https://formspree.io/f/YOUR_ID" method="POST">
    <label>Name:</label>
    <input type="text" name="name" placeholder="Your Name" required>

    <label>Email:</label>
    <input type="email" name="email" placeholder="Your Email" required>

    <label>Subject:</label>
    <input type="text" name="subject" placeholder="Subject" required>

    <label>Message:</label>
    <textarea name="message" rows="5" placeholder="Your Message" required></textarea>

    <button type="submit">Send Message</button>
  </form>
</main>
/* Layered radial gradients create a colorful glow */
.grad-hero {
  padding: 120px 20px 80px;
  text-align: center;
  background:
    radial-gradient(circle at 20% 20%, rgba(255,107,157,.35), transparent 55%),
    radial-gradient(circle at 80% 30%, rgba(108,99,255,.35), transparent 55%),
    radial-gradient(circle at 50% 90%, rgba(95,211,255,.35), transparent 55%),
    linear-gradient(135deg, #fef6fb 0%, #f0efff 50%, #eefaff 100%);
}

/* Gradient-filled text using background-clip */
.grad-hero h1 {
  font-size: 48px;
  font-weight: 700;
  letter-spacing: -0.02em;
  background: linear-gradient(135deg, #e55481, #6c63ff 60%, #00a9e0);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.grad-hero p {
  color: #555;
  max-width: 600px;
  margin: 0 auto;
  font-size: 18px;
}

Reach Out

Send questions, ideas, or project inquiries. Let's create something extraordinary.

7. Multi-Contact Grid — multiple ways to reach out (HubSpot best practice)
<main class="wrap">
  <h1>Get in Touch</h1>
  <p>Pick whichever channel works best — we reply within 24 hours.</p>

  <div class="contact-grid">
    <div class="contact-card">
      <span>✉️</span>
      <h3>Email</h3>
      <a href="mailto:hi@site.com">hi@site.com</a>
    </div>

    <div class="contact-card">
      <span>📞</span>
      <h3>Phone</h3>
      <a href="tel:+15551234567">(555) 123-4567</a>
    </div>

    <div class="contact-card">
      <span>💬</span>
      <h3>Live Chat</h3>
      <p>M–F 9am–5pm EST</p>
    </div>

    <div class="contact-card">
      <span>📍</span>
      <h3>Visit</h3>
      <p>123 Main St, VA</p>
    </div>
  </div>

  <form action="https://formspree.io/f/YOUR_ID" method="POST">
    <label>Name:</label>
    <input type="text" name="name" placeholder="Your Name" required>

    <label>Email:</label>
    <input type="email" name="email" placeholder="Your Email" required>

    <label>Message:</label>
    <textarea name="message" rows="5" placeholder="How can we help?" required></textarea>

    <button type="submit">Send Message</button>
  </form>
</main>
/* Responsive auto-fit grid of contact cards */
.contact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 20px;
  margin-bottom: 50px;
}

.contact-card {
  background: #f7f7f8;
  border: 1px solid #eee;
  border-radius: 12px;
  padding: 24px;
  text-align: center;
  transition: all 0.2s;
}

.contact-card:hover {
  background: #fff;
  box-shadow: 0 6px 18px rgba(0,0,0,.06);
  transform: translateY(-2px);
}

.contact-card .emoji {
  font-size: 2rem;
  display: block;
  margin-bottom: 10px;
}

Get in Touch

Pick whichever channel works best — we reply within 24 hours.

Live Chat

M–F 9am–5pm EST

Visit

123 Main St
Lakewood, VA

8. FAQ + Form — self-service alongside contact (SaaS best practice)
<main class="wrap">
  <h1>We're here to help</h1>
  <p>Check the FAQ first — or send us a message and we'll reply within 24 hours.</p>

  <div class="two-col">

    <section class="faq">
      <h3>Frequent Questions</h3>

      <div class="faq-item">
        <div class="faq-q">How fast do you respond?</div>
        <div class="faq-a">
          We reply to all messages within 24 hours on business days (M–F).
        </div>
      </div>

      <div class="faq-item">
        <div class="faq-q">Where can I find pricing?</div>
        <div class="faq-a">
          Visit our pricing page for a full breakdown of plans and features.
        </div>
      </div>

      <div class="faq-item">
        <div class="faq-q">Do you offer student discounts?</div>
        <div class="faq-a">
          Yes — email us with a .edu address for 50% off.
        </div>
      </div>

      <div class="faq-item">
        <div class="faq-q">Can I schedule a demo?</div>
        <div class="faq-a">
          Absolutely. Use the form and mention "demo" in the subject.
        </div>
      </div>
    </section>

    <section>
      <form action="https://formspree.io/f/YOUR_ID" method="POST">
        <label>Name:</label>
        <input type="text" name="name" placeholder="Your Name" required>

        <label>Email:</label>
        <input type="email" name="email" placeholder="Your Email" required>

        <label>Subject:</label>
        <input type="text" name="subject" placeholder="Subject" required>

        <label>Message:</label>
        <textarea name="message" rows="5" placeholder="Your Message" required></textarea>

        <button type="submit">Send Message</button>
      </form>
    </section>

  </div>
</main>
/* Two-column layout: FAQ | form */
.two-col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 50px;
}

/* FAQ accordion — toggled with .open class */
.faq-item {
  padding: 14px 0;
  border-bottom: 1px solid #f0f0f0;
  cursor: pointer;
}

.faq-q {
  font-weight: 500;
  display: flex;
  justify-content: space-between;
}

.faq-q::after { content: "+"; color: #999; }
.faq-item.open .faq-q::after { content: "−"; }

.faq-a { display: none; padding-top: 10px; }
.faq-item.open .faq-a { display: block; }

@media (max-width: 800px) {
  .two-col { grid-template-columns: 1fr; }
}

We're here to help

Check the FAQ first — or send us a message and we'll reply within 24 hours.

Frequent Questions

How fast do you respond?
We reply to all messages within 24 hours on business days (M–F).
Where can I find pricing?
Visit our pricing page for a full breakdown of plans and features.
Do you offer student discounts?
Yes — email us with a .edu address for 50% off.
Can I schedule a demo?
Absolutely. Use the form and mention "demo" in the subject.
9. Floating Labels + Validation — animated labels, inline errors, in-page success
<!-- placeholder=" " (a space) makes the float trick work -->
<form class="contact" novalidate>
  <div class="float-field">
    <input type="text" id="name" placeholder=" ">
    <label for="name">Your name</label>
    <div class="err">Please enter your name.</div>
  </div>
  <div class="float-field">
    <input type="email" id="email" placeholder=" ">
    <label for="email">Email address</label>
    <div class="err">Please enter a valid email.</div>
  </div>
  <div class="float-field">
    <textarea id="msg" placeholder=" "></textarea>
    <label for="msg">Message</label>
    <div class="err">Please write a short message.</div>
  </div>
  <button type="submit">Send Message</button>
</form>

<div class="form-success">Thanks! Your message has been sent — we'll be in touch soon.</div>
/* Label sits inside, floats up on focus or when filled */
.float-field { position: relative; }
.float-field label {
  position: absolute; left: 11px; top: 14px;
  background: #fff; padding: 0 5px;
  color: #999; transition: all .16s ease;
}
.float-field input:focus + label,
.float-field input:not(:placeholder-shown) + label {
  top: -8px; font-size: 12px; color: #111;
}
/* Error state — toggled by JS */
.err { display: none; color: #e0245e; font-size: 12px; }
.float-field.invalid input { border-color: #e0245e; }
.float-field.invalid .err { display: block; }
// Validate on submit, show errors inline, then a success message
const form = document.querySelector('.contact');
form.addEventListener('submit', (e) => {
  e.preventDefault();
  let ok = true;
  form.querySelectorAll('.float-field').forEach((field) => {
    const input = field.querySelector('input, textarea');
    const valid = input.type === 'email'
      ? /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(input.value)
      : input.value.trim() !== '';
    field.classList.toggle('invalid', !valid);
    if (!valid) ok = false;
  });
  if (ok) {
    form.style.display = 'none';
    document.querySelector('.form-success').style.display = 'flex';
  }
});

Send a message

The label sits inside the field, then floats up when you type. Errors appear inline, and the success note shows right here — no page reload.

Please enter your name.
Please enter a valid email.
Please write a short message.
Thanks! Your message has been sent — we'll be in touch soon.
10. Click-to-Copy Details — tap any detail to copy, with confirmation + socials
<!-- The value to copy lives in data-copy -->
<div class="copy-row" data-copy="hello@studio.com">
  <span class="copy-ic"></span>
  <div class="copy-meta">
    <div class="lbl">Email</div>
    <div class="val">hello@studio.com</div>
  </div>
  <span class="copy-hint">Click to copy</span>
</div>

<div class="copy-row" data-copy="+1 (703) 555-0142">
  <span class="copy-ic"></span>
  <div class="copy-meta">
    <div class="lbl">Phone</div>
    <div class="val">+1 (703) 555-0142</div>
  </div>
  <span class="copy-hint">Click to copy</span>
</div>

<div class="copy-row" data-copy="123 Web St, Annandale, VA">
  <span class="copy-ic">📍</span>
  <div class="copy-meta">
    <div class="lbl">Address</div>
    <div class="val">123 Web St, Annandale, VA</div>
  </div>
  <span class="copy-hint">Click to copy</span>
</div>

<div class="socials">
  <a href="#" aria-label="GitHub">GitHub</a>
  <a href="#" aria-label="LinkedIn">LinkedIn</a>
  <a href="#" aria-label="Instagram">Instagram</a>
</div>
.copy-row {
  display: flex; align-items: center; gap: 16px;
  padding: 16px 18px; border: 1px solid #e5e5e5;
  border-radius: 14px; cursor: pointer; transition: all .18s;
}
.copy-row:hover { border-color: #111; transform: translateY(-2px); }
/* .copied class is added by JS after a successful copy */
.copy-row.copied { border-color: #1a7f43; }
.copy-row.copied .copy-hint { color: #1a7f43; font-weight: 600; }
// Copy the data-copy value, then show "Copied!" for 1.5s
document.querySelectorAll('.copy-row').forEach((row) => {
  row.addEventListener('click', () => {
    navigator.clipboard.writeText(row.dataset.copy).then(() => {
      const hint = row.querySelector('.copy-hint');
      const original = hint.textContent;
      row.classList.add('copied');
      hint.textContent = 'Copied!';
      setTimeout(() => {
        row.classList.remove('copied');
        hint.textContent = original;
      }, 1500);
    });
  });
});

Reach us directly

Tap any detail below to copy it to your clipboard.

Email
hello@studio.com
Click to copy
Phone
+1 (703) 555-0142
Click to copy
Address
123 Web St, Annandale, VA
Click to copy
11. Map + Form Split — embedded map beside the form
<div class="split">
  <div class="map-side">
    <!-- Free Google Maps embed: ?q=PLACE&output=embed -->
    <iframe src="https://www.google.com/maps?q=Annandale,VA&output=embed"
            title="Map" loading="lazy"></iframe>
  </div>
  <div class="form-side">
    <h2>Visit or write</h2>
    <p class="intro">Find us on the map, or drop a message and we'll reply within a day.</p>
    <form action="#" method="POST">
      <input type="text" placeholder="Your name" required>
      <input type="email" placeholder="Your email" required>
      <textarea rows="4" placeholder="Your message" required></textarea>
      <button type="submit">Send</button>
    </form>
  </div>
</div>
/* Two equal columns; map fills its side */
.split { display: grid; grid-template-columns: 1fr 1fr; min-height: 520px; }
.map-side { position: relative; }
.map-side iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.form-side { padding: 56px 48px; }

/* Stack on small screens */
@media (max-width: 800px) { .split { grid-template-columns: 1fr; } }

Visit or write

Find us on the map, or drop a message and we'll reply within a day.

12. Micro-interactions — animated send button + live character counter
<form>
  <label for="name">Name</label>
  <input type="text" id="name" placeholder="Your name">

  <label for="msg">Message</label>
  <textarea id="msg" maxlength="300" placeholder="Up to 300 characters"></textarea>
  <div class="count-row"><span class="counter"><b id="count">0</b>/300</span></div>

  <button class="send-btn">
    <span class="btn-label">Send Message</span>
    <span class="spinner"></span>
    <span class="check"></span>
  </button>
</form>
/* Button swaps label → spinner → check via classes */
.spinner {
  width: 18px; height: 18px; display: none;
  border: 2px solid rgba(255,255,255,.4); border-top-color: #fff;
  border-radius: 50%; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.send-btn.loading .btn-label { display: none; }
.send-btn.loading .spinner { display: block; }
.send-btn.done { background: #1a7f43; }
.counter.warn { color: #e0245e; }
// Live counter + animated button (loading → done)
const msg = document.getElementById('msg');
const count = document.getElementById('count');
msg.addEventListener('input', () => {
  count.textContent = msg.value.length;
  count.parentElement.classList.toggle('warn', msg.value.length > 280);
});

document.querySelector('.send-btn').addEventListener('click', (e) => {
  e.preventDefault();
  const btn = e.currentTarget;
  btn.classList.add('loading');
  setTimeout(() => {
    btn.classList.remove('loading');
    btn.classList.add('done');
  }, 1400);
});

Say hello

Watch the button animate on send, and the counter update live as you type.

0/300
Full Page — one complete contact page you can copy into a single .html file

Live preview

Complete HTML — copy this whole file

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Contact Us</title>
  <style>
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
      background: #f5f5f7; color: #111; line-height: 1.55;
    }

    /* ===== Top nav ===== */
    .site-header {
      display: flex; align-items: center; justify-content: space-between;
      padding: 16px 40px; background: #fff; border-bottom: 1px solid #e5e5e5;
    }
    .site-header .logo { font-weight: 700; font-size: 1.1rem; text-decoration: none; color: #111; }
    .site-header nav { display: flex; gap: 28px; }
    .site-header nav a { text-decoration: none; color: #555; font-size: 15px; }
    .site-header nav a:hover { color: #111; }

    /* ===== Gradient hero ===== */
    .grad-hero {
      padding: 90px 20px 70px; text-align: center;
      background:
        radial-gradient(circle at 20% 20%, rgba(255, 107, 157, 0.35), transparent 55%),
        radial-gradient(circle at 80% 30%, rgba(108, 99, 255, 0.35), transparent 55%),
        radial-gradient(circle at 50% 90%, rgba(95, 211, 255, 0.35), transparent 55%),
        linear-gradient(135deg, #fef6fb 0%, #f0efff 50%, #eefaff 100%);
    }
    .grad-hero h1 {
      font-size: 44px; font-weight: 700; margin-bottom: 14px; letter-spacing: -0.02em;
      background: linear-gradient(135deg, #e55481, #6c63ff 60%, #00a9e0);
      -webkit-background-clip: text; background-clip: text; color: transparent;
    }
    .grad-hero p { color: #555; max-width: 560px; margin: 0 auto; font-size: 18px; }

    /* ===== Layout wrapper ===== */
    .wrap { max-width: 1000px; margin: 0 auto; padding: 60px 20px; }

    /* ===== Multi-contact cards ===== */
    .contact-grid {
      display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
      gap: 20px; margin-bottom: 60px;
    }
    .contact-card {
      background: #fff; border: 1px solid #eee; border-radius: 12px;
      padding: 24px; text-align: center; transition: all 0.2s;
    }
    .contact-card:hover {
      box-shadow: 0 6px 18px rgba(0,0,0,0.06); transform: translateY(-2px);
    }
    .contact-card .emoji {
      font-size: 1.6rem; display: inline-flex; width: 56px; height: 56px;
      align-items: center; justify-content: center;
      background: linear-gradient(135deg, #e55481, #bf5af2); color: #fff;
      border-radius: 14px; margin-bottom: 14px;
    }
    .contact-card h3 { font-size: 1rem; margin-bottom: 6px; }
    .contact-card p { font-size: 0.9rem; color: #666; }
    .contact-card a { color: #111; text-decoration: none; font-weight: 500; }
    .contact-card a:hover { text-decoration: underline; }

    /* ===== Form card with floating labels ===== */
    .form-card {
      max-width: 560px; margin: 0 auto; background: #fff;
      border: 1px solid #e5e5e5; border-radius: 16px;
      padding: 40px 36px; box-shadow: 0 4px 18px rgba(0,0,0,0.05);
    }
    .form-card h2 { font-size: 28px; font-weight: 600; margin-bottom: 8px; letter-spacing: -0.015em; }
    .form-card .intro { color: #555; margin-bottom: 34px; }

    .float-field { position: relative; margin-bottom: 26px; }
    .float-field input, .float-field textarea {
      width: 100%; padding: 18px 14px 7px; font-size: 15px; font-family: inherit;
      border: 1px solid #d1d1d6; border-radius: 10px; background: #fff;
    }
    .float-field textarea { min-height: 120px; resize: vertical; }
    .float-field label {
      position: absolute; left: 11px; top: 14px; color: #999; font-size: 15px;
      pointer-events: none; background: #fff; padding: 0 5px; transition: all .16s ease;
    }
    .float-field input:focus + label,
    .float-field input:not(:placeholder-shown) + label,
    .float-field textarea:focus + label,
    .float-field textarea:not(:placeholder-shown) + label {
      top: -8px; font-size: 12px; color: #111; font-weight: 500;
    }
    .float-field input:focus, .float-field textarea:focus { outline: none; border-color: #111; }
    .float-field.invalid input, .float-field.invalid textarea { border-color: #e0245e; }
    .float-field.invalid label { color: #e0245e; }
    .err { color: #e0245e; font-size: 12.5px; margin-top: 6px; display: none; }
    .float-field.invalid .err { display: block; }

    .count-row { display: flex; justify-content: flex-end; margin: -18px 0 22px; }
    .counter { font-size: 12.5px; color: #999; }
    .counter.warn { color: #e0245e; }

    .send-btn {
      position: relative; width: 200px; height: 48px; border: none; border-radius: 999px;
      background: #111; color: #fff; font-size: 15px; cursor: pointer; overflow: hidden;
      display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    }
    .send-btn:hover { opacity: .9; }
    .send-btn .spinner {
      width: 18px; height: 18px; border: 2px solid rgba(255,255,255,.4); border-top-color: #fff;
      border-radius: 50%; display: none; animation: spin .7s linear infinite;
    }
    @keyframes spin { to { transform: rotate(360deg); } }
    .send-btn.loading .btn-label { display: none; }
    .send-btn.loading .spinner { display: block; }
    .send-btn.done { background: #1a7f43; }
    .send-btn.done .btn-label, .send-btn.done .spinner { display: none; }
    .send-btn .check { display: none; }
    .send-btn.done .check { display: inline; }

    .form-success {
      display: none; align-items: center; gap: 12px;
      background: #e8f7ee; border: 1px solid #b6e3c6; color: #1a7f43;
      padding: 18px 20px; border-radius: 12px; font-size: 15px; font-weight: 500;
    }
    .form-card.show-success #contactForm { display: none; }
    .form-card.show-success .form-success { display: flex; }

    @media (max-width: 600px) {
      .site-header { padding: 14px 20px; }
      .grad-hero h1 { font-size: 34px; }
      .form-card { padding: 30px 22px; }
    }
  </style>
</head>
<body>

  <header class="site-header">
    <a href="#" class="logo">Studio</a>
    <nav>
      <a href="#">Home</a>
      <a href="#">Work</a>
      <a href="#">Contact</a>
    </nav>
  </header>

  <section class="grad-hero">
    <h1>Get in touch</h1>
    <p>Have a question, a project idea, or just want to say hello? Pick a method below or send us a message — we reply within 24 hours.</p>
  </section>

  <main class="wrap">

    <!-- Multiple contact methods -->
    <div class="contact-grid">
      <div class="contact-card">
        <div class="emoji">✉</div>
        <h3>Email</h3>
        <p><a href="mailto:hello@studio.com">hello@studio.com</a></p>
      </div>
      <div class="contact-card">
        <div class="emoji">☎</div>
        <h3>Phone</h3>
        <p><a href="tel:+15551234567">(555) 123-4567</a></p>
      </div>
      <div class="contact-card">
        <div class="emoji">📍</div>
        <h3>Visit</h3>
        <p>123 Main St, Annandale, VA</p>
      </div>
      <div class="contact-card">
        <div class="emoji">🕑</div>
        <h3>Hours</h3>
        <p>Mon–Fri, 9am–5pm ET</p>
      </div>
    </div>

    <!-- Form with floating labels, validation, live counter, animated button -->
    <div class="form-card" id="formCard">
      <h2>Send a message</h2>
      <p class="intro">Fill out the short form below and we’ll get back to you as soon as we can.</p>

      <form id="contactForm" novalidate>
        <div class="float-field">
          <input type="text" id="name" name="name" placeholder=" ">
          <label for="name">Your name</label>
          <div class="err">Please enter your name.</div>
        </div>

        <div class="float-field">
          <input type="email" id="email" name="email" placeholder=" ">
          <label for="email">Email address</label>
          <div class="err">Please enter a valid email.</div>
        </div>

        <div class="float-field">
          <textarea id="message" name="message" maxlength="300" placeholder=" "></textarea>
          <label for="message">Your message</label>
          <div class="err">Please write a short message.</div>
        </div>
        <div class="count-row"><span class="counter"><b id="count">0</b>/300</span></div>

        <button type="submit" class="send-btn">
          <span class="btn-label">Send Message</span>
          <span class="spinner"></span>
          <span class="check">✓ Sent</span>
        </button>
      </form>

      <div class="form-success">
        <span>✓</span>
        <span>Thanks! Your message is on its way — we’ll reply within 24 hours.</span>
      </div>
    </div>

  </main>

  <script>
    var form  = document.getElementById('contactForm');
    var card  = document.getElementById('formCard');
    var msg   = document.getElementById('message');
    var count = document.getElementById('count');
    var btn   = form.querySelector('.send-btn');

    // Live character counter
    msg.addEventListener('input', function () {
      count.textContent = msg.value.length;
      count.parentElement.classList.toggle('warn', msg.value.length > 280);
    });

    // Validate, animate button, then show success
    form.addEventListener('submit', function (e) {
      e.preventDefault();
      var ok = true;

      form.querySelectorAll('.float-field').forEach(function (field) {
        var input = field.querySelector('input, textarea');
        var valid = input.type === 'email'
          ? /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(input.value)
          : input.value.trim() !== '';
        field.classList.toggle('invalid', !valid);
        if (!valid) ok = false;
      });

      if (!ok) return;

      btn.classList.add('loading');
      setTimeout(function () {
        btn.classList.remove('loading');
        btn.classList.add('done');
        setTimeout(function () { card.classList.add('show-success'); }, 500);
      }, 1300);
    });
  </script>

</body>
</html>