Salon — Salon Landing
A complete boutique-salon marketing landing for Maison Lumière, dressed in a rose-gold and matte-black editorial palette. It pairs a translucent sticky nav and smooth-scrolling anchor links with a serif hero, animated stat counters, a six-card services menu, a dark signature-treatments band, tappable stylist cards that pre-fill the booking form, a pull-quote, a CSS gallery row, a live open or closed indicator, and a validating appointment request form with elegant toast confirmations.
MCP
الكود
:root {
--serif: "Cormorant Garamond", Georgia, serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
--gold: #b08d57;
--gold-d: #8c6d3f;
--gold-soft: #efe2cf;
--rose: #c9a78f;
--rose-soft: #f3e6dc;
--ink: #1c1814;
--ink-2: #3d362f;
--muted: #8a7d70;
--cream: #f7f1e8;
--bg: #faf6ef;
--white: #ffffff;
--line: rgba(28, 24, 20, 0.1);
--line-2: rgba(28, 24, 20, 0.18);
--ok: #5f8a6b;
--warn: #c08a3e;
--danger: #b3503e;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 22px;
--sh-sm: 0 1px 2px rgba(28, 24, 20, 0.06), 0 1px 1px rgba(28, 24, 20, 0.04);
--sh-md: 0 14px 34px -18px rgba(28, 24, 20, 0.32);
--sh-lg: 0 38px 80px -42px rgba(28, 24, 20, 0.45);
--nav-h: 72px;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
scroll-padding-top: calc(var(--nav-h) + 12px);
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
font-family: var(--sans);
font-size: 16px;
line-height: 1.5;
color: var(--ink-2);
background: var(--bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
}
h1,
h2,
h3 {
font-family: var(--serif);
color: var(--ink);
font-weight: 600;
margin: 0;
line-height: 1.06;
}
img {
max-width: 100%;
display: block;
}
a {
color: inherit;
text-decoration: none;
}
.skip-link {
position: absolute;
left: -999px;
top: 0;
background: var(--ink);
color: var(--cream);
padding: 10px 16px;
border-radius: var(--r-sm);
z-index: 200;
}
.skip-link:focus {
left: 12px;
top: 12px;
}
:focus-visible {
outline: 2px solid var(--gold-d);
outline-offset: 2px;
border-radius: 4px;
}
/* ---------- shared bits ---------- */
.eyebrow {
font-family: var(--sans);
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--gold-d);
margin: 0 0 14px;
}
.eyebrow--light {
color: var(--gold-soft);
}
.btn {
--btn-bg: var(--ink);
--btn-fg: var(--cream);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
font-family: var(--sans);
font-size: 0.92rem;
font-weight: 600;
letter-spacing: 0.01em;
padding: 13px 24px;
border-radius: 999px;
border: 1px solid transparent;
background: var(--btn-bg);
color: var(--btn-fg);
cursor: pointer;
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease,
color 0.18s ease, border-color 0.18s ease;
white-space: nowrap;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: var(--sh-md);
}
.btn:active {
transform: translateY(0);
}
.btn--gold {
--btn-bg: linear-gradient(135deg, var(--gold) 0%, var(--gold-d) 100%);
--btn-fg: #fff;
}
.btn--outline {
--btn-bg: transparent;
--btn-fg: var(--ink);
border-color: var(--line-2);
}
.btn--outline:hover {
border-color: var(--gold);
color: var(--gold-d);
}
.btn--ghost {
--btn-bg: transparent;
--btn-fg: var(--ink-2);
}
.btn--ghost:hover {
--btn-fg: var(--gold-d);
box-shadow: none;
}
.btn--sm {
padding: 9px 16px;
font-size: 0.85rem;
}
.btn--block {
width: 100%;
}
/* ---------- nav ---------- */
.nav {
position: fixed;
inset: 0 0 auto 0;
height: var(--nav-h);
z-index: 100;
display: flex;
align-items: center;
background: rgba(250, 246, 239, 0.55);
backdrop-filter: saturate(140%) blur(14px);
-webkit-backdrop-filter: saturate(140%) blur(14px);
border-bottom: 1px solid transparent;
transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease,
height 0.3s ease;
}
.nav.is-scrolled {
background: rgba(250, 246, 239, 0.86);
border-bottom-color: var(--line);
box-shadow: var(--sh-sm);
}
.nav__inner {
width: min(1180px, 92vw);
margin: 0 auto;
display: flex;
align-items: center;
gap: 18px;
}
.brand {
display: flex;
align-items: center;
gap: 12px;
margin-right: auto;
}
.brand__mark {
display: grid;
place-items: center;
width: 40px;
height: 40px;
border-radius: 50%;
background: linear-gradient(140deg, var(--gold) 0%, var(--gold-d) 100%);
color: #fff;
font-family: var(--serif);
font-weight: 700;
font-size: 1.05rem;
letter-spacing: 0.02em;
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.25);
}
.brand__name {
font-family: var(--serif);
font-size: 1.32rem;
font-weight: 700;
color: var(--ink);
line-height: 1;
}
.brand__name small {
display: block;
font-family: var(--sans);
font-size: 0.6rem;
font-weight: 600;
letter-spacing: 0.26em;
text-transform: uppercase;
color: var(--muted);
margin-top: 4px;
}
.nav__links {
display: flex;
gap: 6px;
}
.nav__link {
position: relative;
padding: 8px 14px;
font-size: 0.9rem;
font-weight: 500;
color: var(--ink-2);
border-radius: var(--r-sm);
transition: color 0.18s ease;
}
.nav__link::after {
content: "";
position: absolute;
left: 14px;
right: 14px;
bottom: 4px;
height: 1px;
background: var(--gold);
transform: scaleX(0);
transform-origin: left;
transition: transform 0.22s ease;
}
.nav__link:hover,
.nav__link.is-active {
color: var(--gold-d);
}
.nav__link:hover::after,
.nav__link.is-active::after {
transform: scaleX(1);
}
.nav__actions {
display: flex;
align-items: center;
gap: 10px;
}
.nav__toggle {
display: none;
flex-direction: column;
justify-content: center;
gap: 5px;
width: 42px;
height: 42px;
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
background: transparent;
cursor: pointer;
}
.nav__toggle span {
display: block;
height: 1.6px;
width: 20px;
margin: 0 auto;
background: var(--ink);
transition: transform 0.25s ease, opacity 0.2s ease;
}
.nav__toggle[aria-expanded="true"] span:nth-child(1) {
transform: translateY(6.6px) rotate(45deg);
}
.nav__toggle[aria-expanded="true"] span:nth-child(2) {
opacity: 0;
}
.nav__toggle[aria-expanded="true"] span:nth-child(3) {
transform: translateY(-6.6px) rotate(-45deg);
}
/* ---------- hero ---------- */
.hero {
position: relative;
padding: calc(var(--nav-h) + 86px) 0 96px;
overflow: hidden;
}
.hero__media {
position: absolute;
inset: 0;
z-index: 0;
}
.hero__glow {
position: absolute;
top: -18%;
right: -8%;
width: 60vw;
height: 60vw;
max-width: 760px;
max-height: 760px;
background: radial-gradient(
circle at 50% 50%,
rgba(201, 167, 143, 0.5),
rgba(201, 167, 143, 0) 62%
);
filter: blur(8px);
}
.hero__photo {
position: absolute;
inset: 0;
background: radial-gradient(
120% 120% at 78% 8%,
rgba(239, 226, 207, 0.9) 0%,
rgba(239, 226, 207, 0) 48%
),
linear-gradient(180deg, var(--bg) 0%, var(--cream) 100%);
}
.hero__inner {
position: relative;
z-index: 1;
width: min(1180px, 92vw);
margin: 0 auto;
}
.hero__title {
font-size: clamp(2.9rem, 7.6vw, 5.6rem);
letter-spacing: -0.01em;
max-width: 16ch;
}
.hero__title em {
font-style: italic;
color: var(--gold-d);
}
.hero__lede {
margin: 26px 0 0;
max-width: 52ch;
font-size: 1.08rem;
color: var(--ink-2);
}
.hero__cta {
margin-top: 32px;
display: flex;
flex-wrap: wrap;
gap: 14px;
}
.hero__stats {
list-style: none;
margin: 56px 0 0;
padding: 26px 0 0;
border-top: 1px solid var(--line);
display: flex;
flex-wrap: wrap;
gap: 48px;
}
.hero__stats strong {
display: block;
font-family: var(--serif);
font-size: 2.6rem;
font-weight: 700;
color: var(--ink);
line-height: 1;
}
.hero__stats span {
display: block;
margin-top: 6px;
font-size: 0.78rem;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--muted);
}
/* ---------- section scaffolding ---------- */
.section {
width: min(1180px, 92vw);
margin: 0 auto;
padding: 104px 0;
}
.section--tight {
padding: 84px 0;
}
.section__head {
max-width: 56ch;
margin-bottom: 48px;
}
.section__title {
font-size: clamp(2.2rem, 4.6vw, 3.4rem);
letter-spacing: -0.01em;
}
.section__sub {
margin: 16px 0 0;
color: var(--muted);
font-size: 1.02rem;
}
/* ---------- services ---------- */
.services {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 22px;
}
.svc {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 30px 28px;
box-shadow: var(--sh-sm);
transition: transform 0.22s ease, box-shadow 0.22s ease,
border-color 0.22s ease;
position: relative;
}
.svc::after {
content: "";
position: absolute;
left: 28px;
right: 28px;
top: 0;
height: 2px;
background: linear-gradient(90deg, var(--gold), var(--rose));
transform: scaleX(0);
transform-origin: left;
transition: transform 0.3s ease;
border-radius: 2px;
}
.svc:hover {
transform: translateY(-6px);
box-shadow: var(--sh-md);
border-color: rgba(176, 141, 87, 0.45);
}
.svc:hover::after {
transform: scaleX(1);
}
.svc__icon {
width: 46px;
height: 46px;
display: grid;
place-items: center;
border-radius: 50%;
background: var(--gold-soft);
color: var(--gold-d);
font-size: 1.25rem;
margin-bottom: 18px;
}
.svc h3 {
font-size: 1.5rem;
}
.svc p {
margin: 10px 0 0;
color: var(--muted);
font-size: 0.95rem;
}
.svc__price {
margin-top: 18px !important;
color: var(--ink-2) !important;
font-size: 0.82rem !important;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.svc__price strong {
font-family: var(--serif);
font-size: 1.5rem;
color: var(--gold-d);
letter-spacing: 0;
}
/* ---------- why band ---------- */
.why {
background:
radial-gradient(120% 140% at 0% 0%, rgba(176, 141, 87, 0.16), transparent 55%),
var(--ink);
color: var(--cream);
padding: 104px 0;
}
.why__inner {
width: min(1180px, 92vw);
margin: 0 auto;
display: grid;
grid-template-columns: 0.85fr 1.15fr;
gap: 60px;
align-items: start;
}
.why__title {
color: var(--cream);
font-size: clamp(2.2rem, 4.4vw, 3.2rem);
}
.why__sub {
margin: 18px 0 0;
color: rgba(247, 241, 232, 0.66);
font-size: 1.04rem;
}
.why__grid {
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px 40px;
}
.why__item {
border-top: 1px solid rgba(239, 226, 207, 0.22);
padding-top: 20px;
}
.why__num {
font-family: var(--serif);
font-size: 1.3rem;
color: var(--gold);
}
.why__item h3 {
color: var(--cream);
font-size: 1.4rem;
margin: 8px 0 8px;
}
.why__item p {
margin: 0;
color: rgba(247, 241, 232, 0.6);
font-size: 0.92rem;
}
/* ---------- stylists ---------- */
.stylists {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 22px;
}
.stylist {
font: inherit;
text-align: center;
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 30px 18px;
cursor: pointer;
box-shadow: var(--sh-sm);
transition: transform 0.22s ease, box-shadow 0.22s ease,
border-color 0.22s ease;
}
.stylist:hover {
transform: translateY(-6px);
box-shadow: var(--sh-md);
border-color: rgba(176, 141, 87, 0.45);
}
.stylist__avatar {
width: 92px;
height: 92px;
margin: 0 auto 16px;
border-radius: 50%;
display: grid;
place-items: center;
background: linear-gradient(150deg, var(--a, var(--rose)), var(--b, var(--gold-soft)));
box-shadow: inset 0 0 0 4px rgba(255, 255, 255, 0.6);
position: relative;
}
.stylist__avatar::after {
content: attr(data-initials);
font-family: var(--serif);
font-weight: 700;
font-size: 1.8rem;
color: var(--ink);
opacity: 0.78;
}
.stylist__name {
display: block;
font-family: var(--serif);
font-size: 1.32rem;
font-weight: 600;
color: var(--ink);
}
.stylist__role {
display: block;
margin-top: 4px;
font-size: 0.78rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
}
/* ---------- testimonial ---------- */
.quote {
background: var(--cream);
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
padding: 96px 0;
}
.quote__inner {
width: min(840px, 92vw);
margin: 0 auto;
text-align: center;
position: relative;
}
.quote__mark {
font-family: var(--serif);
font-size: 6rem;
line-height: 0.5;
color: var(--gold);
opacity: 0.55;
margin: 0 0 8px;
}
.quote blockquote {
margin: 0;
font-family: var(--serif);
font-size: clamp(1.5rem, 3.6vw, 2.3rem);
font-weight: 500;
font-style: italic;
color: var(--ink);
line-height: 1.3;
}
.quote__by {
margin: 26px 0 0;
}
.quote__by strong {
font-family: var(--serif);
font-size: 1.2rem;
color: var(--ink);
}
.quote__by span {
display: block;
margin-top: 2px;
font-size: 0.78rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--muted);
}
.quote__stars {
margin: 14px 0 0;
color: var(--gold);
letter-spacing: 0.32em;
font-size: 0.95rem;
}
/* ---------- gallery ---------- */
.gallery {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 14px;
}
.tile {
aspect-ratio: 1;
border-radius: var(--r-md);
box-shadow: var(--sh-sm);
transition: transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease;
filter: saturate(0.96);
}
.tile:hover {
transform: translateY(-4px) scale(1.02);
box-shadow: var(--sh-md);
filter: saturate(1.06);
}
.tile--1 { background: linear-gradient(145deg, #d9bda3, #b08d57); }
.tile--2 { background: linear-gradient(145deg, #efe2cf, #c9a78f); }
.tile--3 { background: linear-gradient(145deg, #3d362f, #8a7d70); }
.tile--4 { background: linear-gradient(145deg, #f3e6dc, #b08d57); }
.tile--5 { background: linear-gradient(145deg, #c9a78f, #8c6d3f); }
.tile--6 { background: linear-gradient(145deg, #f7f1e8, #c9a78f); }
/* ---------- visit / book ---------- */
.visit {
background: var(--white);
border-top: 1px solid var(--line);
}
.visit__inner {
width: min(1180px, 92vw);
margin: 0 auto;
padding: 104px 0;
display: grid;
grid-template-columns: 1fr 0.9fr;
gap: 64px;
align-items: start;
}
.visit__title {
font-size: clamp(2.2rem, 4.4vw, 3.2rem);
}
.visit__addr {
margin: 18px 0 28px;
font-size: 1.05rem;
color: var(--ink-2);
}
.hours {
margin: 0;
border-top: 1px solid var(--line);
}
.hours > div {
display: flex;
justify-content: space-between;
align-items: baseline;
padding: 14px 0;
border-bottom: 1px solid var(--line);
}
.hours dt {
font-weight: 600;
color: var(--ink);
font-size: 0.92rem;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.hours dd {
margin: 0;
font-family: var(--serif);
font-size: 1.3rem;
color: var(--ink-2);
}
.hours--closed dd {
color: var(--danger);
font-size: 1rem;
font-style: italic;
}
.visit__note {
margin: 22px 0 0;
font-size: 0.92rem;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 8px;
}
.visit__note::before {
content: "";
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--muted);
}
.visit__note.is-open {
color: var(--ok);
}
.visit__note.is-open::before {
background: var(--ok);
box-shadow: 0 0 0 4px rgba(95, 138, 107, 0.18);
}
.visit__note.is-closed {
color: var(--danger);
}
.visit__note.is-closed::before {
background: var(--danger);
}
.book {
background: var(--cream);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 34px 32px;
box-shadow: var(--sh-md);
}
.book__title {
font-size: 1.9rem;
}
.book__sub {
margin: 6px 0 24px;
color: var(--muted);
font-size: 0.92rem;
}
.field {
margin-bottom: 16px;
}
.field label {
display: block;
margin-bottom: 7px;
font-size: 0.74rem;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--ink-2);
}
.field input,
.field select {
width: 100%;
font: inherit;
font-size: 0.95rem;
padding: 12px 14px;
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
background: var(--white);
color: var(--ink);
transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.field input::placeholder {
color: #b6aa9c;
}
.field input:focus,
.field select:focus {
outline: none;
border-color: var(--gold);
box-shadow: 0 0 0 3px rgba(176, 141, 87, 0.18);
}
.field input[aria-invalid="true"],
.field select[aria-invalid="true"] {
border-color: var(--danger);
box-shadow: 0 0 0 3px rgba(179, 80, 62, 0.16);
}
.book__fine {
margin: 14px 0 0;
font-size: 0.74rem;
color: var(--muted);
text-align: center;
}
/* ---------- footer ---------- */
.foot {
background: var(--ink);
color: rgba(247, 241, 232, 0.7);
}
.foot__inner {
width: min(1180px, 92vw);
margin: 0 auto;
padding: 56px 0;
display: flex;
flex-wrap: wrap;
gap: 28px 48px;
align-items: center;
}
.foot__brand {
display: flex;
align-items: center;
gap: 14px;
margin-right: auto;
}
.foot__brand p {
margin: 0;
font-family: var(--serif);
font-size: 1.15rem;
color: var(--cream);
line-height: 1.3;
}
.foot__brand span {
font-family: var(--sans);
font-size: 0.78rem;
color: rgba(247, 241, 232, 0.5);
}
.foot__links {
display: flex;
gap: 22px;
}
.foot__links a {
font-size: 0.9rem;
transition: color 0.18s ease;
}
.foot__links a:hover {
color: var(--gold);
}
.foot__copy {
width: 100%;
margin: 0;
padding-top: 24px;
border-top: 1px solid rgba(247, 241, 232, 0.12);
font-size: 0.8rem;
color: rgba(247, 241, 232, 0.45);
}
/* ---------- toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translate(-50%, 24px);
z-index: 300;
max-width: min(420px, 90vw);
padding: 14px 20px;
background: var(--ink);
color: var(--cream);
border-radius: 999px;
font-size: 0.9rem;
font-weight: 500;
box-shadow: var(--sh-lg);
border: 1px solid rgba(176, 141, 87, 0.4);
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease, transform 0.3s ease;
}
.toast.is-show {
opacity: 1;
transform: translate(-50%, 0);
}
.toast::before {
content: "✦ ";
color: var(--gold);
}
/* ---------- reveal ---------- */
.reveal {
opacity: 0;
transform: translateY(22px);
transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.reveal.is-in {
opacity: 1;
transform: none;
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
.reveal {
opacity: 1;
transform: none;
transition: none;
}
.btn:hover,
.svc:hover,
.stylist:hover,
.tile:hover {
transform: none;
}
}
/* ---------- responsive ---------- */
@media (max-width: 940px) {
.why__inner {
grid-template-columns: 1fr;
gap: 44px;
}
.services {
grid-template-columns: repeat(2, 1fr);
}
.stylists {
grid-template-columns: repeat(2, 1fr);
}
.visit__inner {
grid-template-columns: 1fr;
gap: 44px;
}
}
@media (max-width: 760px) {
.nav__links {
position: fixed;
inset: var(--nav-h) 0 auto 0;
flex-direction: column;
gap: 0;
background: rgba(250, 246, 239, 0.97);
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
border-bottom: 1px solid var(--line);
box-shadow: var(--sh-md);
padding: 10px 6vw 18px;
transform: translateY(-12px);
opacity: 0;
pointer-events: none;
transition: opacity 0.22s ease, transform 0.22s ease;
}
.nav__links.is-open {
transform: none;
opacity: 1;
pointer-events: auto;
}
.nav__link {
padding: 14px 6px;
border-bottom: 1px solid var(--line);
font-size: 1rem;
}
.nav__link::after {
display: none;
}
.nav__toggle {
display: flex;
}
.nav__call {
display: none;
}
}
@media (max-width: 520px) {
body {
font-size: 15px;
}
.hero {
padding: calc(var(--nav-h) + 54px) 0 70px;
}
.hero__stats {
gap: 28px 40px;
margin-top: 40px;
}
.hero__stats strong {
font-size: 2.1rem;
}
.section,
.why,
.quote {
padding-top: 70px;
padding-bottom: 70px;
}
.why,
.visit__inner {
padding-top: 70px;
padding-bottom: 70px;
}
.services,
.stylists {
grid-template-columns: 1fr;
}
.why__grid {
grid-template-columns: 1fr;
gap: 24px;
}
.gallery {
grid-template-columns: repeat(3, 1fr);
}
.hero__cta .btn,
.hero__cta {
width: 100%;
}
.book {
padding: 26px 22px;
}
.brand__name {
font-size: 1.16rem;
}
}(function () {
"use strict";
/* ---------- toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("is-show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-show");
}, 3200);
}
/* ---------- sticky nav shadow ---------- */
var nav = document.querySelector("[data-nav]");
function onScroll() {
if (!nav) return;
nav.classList.toggle("is-scrolled", window.scrollY > 12);
}
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
/* ---------- mobile nav toggle ---------- */
var toggle = document.getElementById("navToggle");
var navLinks = document.getElementById("navLinks");
function closeMenu() {
if (!toggle || !navLinks) return;
toggle.setAttribute("aria-expanded", "false");
toggle.setAttribute("aria-label", "Open menu");
navLinks.classList.remove("is-open");
}
if (toggle && navLinks) {
toggle.addEventListener("click", function () {
var open = toggle.getAttribute("aria-expanded") === "true";
toggle.setAttribute("aria-expanded", String(!open));
toggle.setAttribute("aria-label", open ? "Open menu" : "Close menu");
navLinks.classList.toggle("is-open", !open);
});
navLinks.addEventListener("click", function (e) {
if (e.target.closest(".nav__link")) closeMenu();
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape") closeMenu();
});
}
/* ---------- active link on scroll (scroll spy) ---------- */
var sections = ["services", "why", "stylists", "gallery", "visit"]
.map(function (id) {
return document.getElementById(id);
})
.filter(Boolean);
var linkFor = {};
document.querySelectorAll(".nav__link").forEach(function (a) {
var id = a.getAttribute("href").slice(1);
linkFor[id] = a;
});
if ("IntersectionObserver" in window && sections.length) {
var spy = new IntersectionObserver(
function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) {
Object.keys(linkFor).forEach(function (k) {
linkFor[k].classList.remove("is-active");
});
var id = en.target.id;
if (linkFor[id]) linkFor[id].classList.add("is-active");
}
});
},
{ rootMargin: "-45% 0px -50% 0px", threshold: 0 }
);
sections.forEach(function (s) {
spy.observe(s);
});
}
/* ---------- reveal-on-scroll ---------- */
var reveals = document.querySelectorAll(".reveal");
if ("IntersectionObserver" in window) {
var ro = new IntersectionObserver(
function (entries, obs) {
entries.forEach(function (en) {
if (en.isIntersecting) {
en.target.classList.add("is-in");
obs.unobserve(en.target);
}
});
},
{ threshold: 0.12, rootMargin: "0px 0px -8% 0px" }
);
reveals.forEach(function (el, i) {
el.style.transitionDelay = (i % 6) * 60 + "ms";
ro.observe(el);
});
} else {
reveals.forEach(function (el) {
el.classList.add("is-in");
});
}
/* ---------- animated hero stat counters ---------- */
var counters = document.querySelectorAll("[data-count]");
function animateCount(el) {
var target = parseFloat(el.getAttribute("data-count"));
var decimals = parseInt(el.getAttribute("data-decimals") || "0", 10);
var start = performance.now();
var dur = 1400;
function step(now) {
var p = Math.min((now - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
var val = target * eased;
el.textContent =
decimals > 0
? val.toFixed(decimals)
: Math.round(val).toLocaleString("en-US");
if (p < 1) requestAnimationFrame(step);
else
el.textContent =
decimals > 0
? target.toFixed(decimals)
: target.toLocaleString("en-US");
}
requestAnimationFrame(step);
}
if (counters.length && "IntersectionObserver" in window) {
var co = new IntersectionObserver(
function (entries, obs) {
entries.forEach(function (en) {
if (en.isIntersecting) {
animateCount(en.target);
obs.unobserve(en.target);
}
});
},
{ threshold: 0.6 }
);
counters.forEach(function (c) {
co.observe(c);
});
} else {
counters.forEach(function (c) {
c.textContent = c.getAttribute("data-count");
});
}
/* ---------- stylist quick-book ---------- */
var stylistSelect = document.getElementById("bk-stylist");
document.querySelectorAll(".stylist").forEach(function (btn) {
btn.addEventListener("click", function () {
var name = btn.getAttribute("data-stylist");
if (stylistSelect) {
var match = Array.prototype.find.call(
stylistSelect.options,
function (o) {
return o.value === name;
}
);
if (match) stylistSelect.value = name;
}
toast("Booking with " + name + " — finish the form below.");
var visit = document.getElementById("visit");
if (visit) visit.scrollIntoView({ behavior: "smooth", block: "start" });
});
});
/* ---------- "Book now" CTAs ---------- */
document.querySelectorAll("[data-book]").forEach(function (el) {
el.addEventListener("click", function () {
closeMenu();
setTimeout(function () {
var nameField = document.getElementById("bk-name");
if (nameField) nameField.focus({ preventScroll: true });
}, 600);
});
});
/* ---------- open / closed state ---------- */
// Tue–Fri 9–19, Sat 9–17, Sun 10–15, Mon closed. getDay(): 0=Sun..6=Sat
var schedule = {
0: [10, 15],
1: null,
2: [9, 19],
3: [9, 19],
4: [9, 19],
5: [9, 19],
6: [9, 17],
};
var openState = document.getElementById("openState");
function updateOpen() {
if (!openState) return;
var now = new Date();
var hours = schedule[now.getDay()];
var hourFloat = now.getHours() + now.getMinutes() / 60;
if (hours && hourFloat >= hours[0] && hourFloat < hours[1]) {
openState.textContent = "Open now · closes at " + hours[1] + ":00";
openState.className = "visit__note is-open";
} else {
openState.textContent = "Closed now · book online anytime";
openState.className = "visit__note is-closed";
}
}
updateOpen();
setInterval(updateOpen, 60000);
/* ---------- date min = today ---------- */
var dateField = document.getElementById("bk-date");
if (dateField) {
var t = new Date();
var iso = new Date(t.getTime() - t.getTimezoneOffset() * 60000)
.toISOString()
.slice(0, 10);
dateField.min = iso;
}
/* ---------- booking form ---------- */
var form = document.getElementById("bookForm");
if (form) {
var emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
form.addEventListener("submit", function (e) {
e.preventDefault();
var fields = [
{ el: form.elements["name"], ok: function (v) { return v.trim().length >= 2; } },
{ el: form.elements["email"], ok: function (v) { return emailRe.test(v.trim()); } },
{ el: form.elements["service"], ok: function (v) { return !!v; } },
{ el: form.elements["date"], ok: function (v) { return !!v; } },
];
var firstBad = null;
fields.forEach(function (f) {
var valid = f.ok(f.el.value);
f.el.setAttribute("aria-invalid", valid ? "false" : "true");
if (!valid && !firstBad) firstBad = f.el;
});
if (firstBad) {
firstBad.focus();
toast("Please complete the highlighted fields.");
return;
}
var name = form.elements["name"].value.trim().split(" ")[0];
var service = form.elements["service"].value;
var stylist = form.elements["stylist"].value || "the next available stylist";
var date = new Date(form.elements["date"].value + "T00:00:00");
var pretty = date.toLocaleDateString("en-US", {
weekday: "short",
month: "short",
day: "numeric",
});
toast(
"Thank you, " + name + " — " + service + " with " + stylist +
" requested for " + pretty + "."
);
form.reset();
fields.forEach(function (f) {
f.el.setAttribute("aria-invalid", "false");
});
if (dateField) dateField.min = iso;
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Salon Landing · Maison Lumière Salon</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Inter:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<!-- NAV -->
<header class="nav" id="nav" data-nav>
<div class="nav__inner">
<a class="brand" href="#top" aria-label="Maison Lumière Salon — home">
<span class="brand__mark" aria-hidden="true">ML</span>
<span class="brand__name">
Maison Lumière
<small>Salon & Atelier</small>
</span>
</a>
<nav class="nav__links" id="navLinks" aria-label="Primary">
<a href="#services" class="nav__link">Services</a>
<a href="#why" class="nav__link">Why Us</a>
<a href="#stylists" class="nav__link">Stylists</a>
<a href="#gallery" class="nav__link">Gallery</a>
<a href="#visit" class="nav__link">Visit</a>
</nav>
<div class="nav__actions">
<a href="tel:+13105550182" class="btn btn--ghost btn--sm nav__call">
<span aria-hidden="true">☎</span> Call
</a>
<a href="#visit" class="btn btn--gold btn--sm" data-book>Book now</a>
<button
class="nav__toggle"
id="navToggle"
aria-expanded="false"
aria-controls="navLinks"
aria-label="Open menu"
>
<span></span><span></span><span></span>
</button>
</div>
</div>
</header>
<main id="main">
<span id="top"></span>
<!-- HERO -->
<section class="hero" aria-label="Introduction">
<div class="hero__media" aria-hidden="true">
<div class="hero__glow"></div>
<div class="hero__photo"></div>
</div>
<div class="hero__inner">
<p class="eyebrow reveal">Est. 2014 · Downtown Atelier</p>
<h1 class="hero__title reveal">
Where light, <em>line</em><br />and luxury meet.
</h1>
<p class="hero__lede reveal">
Maison Lumière is a boutique salon for considered colour, precision
cutting, and quiet indulgence. Every appointment begins with a
consultation and a glass of something cold.
</p>
<div class="hero__cta reveal">
<a href="#visit" class="btn btn--gold" data-book>Book an appointment</a>
<a href="tel:+13105550182" class="btn btn--outline">
<span aria-hidden="true">☎</span> (310) 555-0182
</a>
</div>
<ul class="hero__stats reveal" aria-label="At a glance">
<li><strong data-count="11">0</strong><span>Master stylists</span></li>
<li><strong data-count="4200">0</strong><span>Guests a year</span></li>
<li><strong data-count="4.9" data-decimals="1">0</strong><span>Avg. rating</span></li>
</ul>
</div>
</section>
<!-- SERVICES -->
<section class="section" id="services" aria-labelledby="services-h">
<div class="section__head reveal">
<p class="eyebrow">The Menu</p>
<h2 id="services-h" class="section__title">Services & rituals</h2>
<p class="section__sub">
Pricing starts from the figures shown; a stylist confirms after your
consultation.
</p>
</div>
<div class="services">
<article class="svc reveal">
<div class="svc__icon" aria-hidden="true">✂</div>
<h3>Cut & Style</h3>
<p>Precision dry-cutting tailored to your hair's natural fall and lifestyle.</p>
<p class="svc__price">from <strong>$95</strong></p>
</article>
<article class="svc reveal">
<div class="svc__icon" aria-hidden="true">❋</div>
<h3>Colour & Balayage</h3>
<p>Hand-painted dimension, gloss, and lived-in tones that grow out gracefully.</p>
<p class="svc__price">from <strong>$185</strong></p>
</article>
<article class="svc reveal">
<div class="svc__icon" aria-hidden="true">♨</div>
<h3>Smoothing & Repair</h3>
<p>Bond-building treatments and keratin therapy for glass-like shine.</p>
<p class="svc__price">from <strong>$140</strong></p>
</article>
<article class="svc reveal">
<div class="svc__icon" aria-hidden="true">♛</div>
<h3>Bridal & Events</h3>
<p>Trials, day-of styling, and on-location service for the moments that matter.</p>
<p class="svc__price">from <strong>$220</strong></p>
</article>
<article class="svc reveal">
<div class="svc__icon" aria-hidden="true">♦</div>
<h3>Extensions</h3>
<p>Hand-tied wefts and tape-ins for seamless length and density.</p>
<p class="svc__price">from <strong>$320</strong></p>
</article>
<article class="svc reveal">
<div class="svc__icon" aria-hidden="true">✺</div>
<h3>Skin & Brow</h3>
<p>Brow architecture, lash lifts, and a restorative express facial.</p>
<p class="svc__price">from <strong>$70</strong></p>
</article>
</div>
</section>
<!-- WHY US BAND -->
<section class="why" id="why" aria-labelledby="why-h">
<div class="why__inner">
<div class="why__copy reveal">
<p class="eyebrow eyebrow--light">Signature treatments</p>
<h2 id="why-h" class="why__title">A salon built around the detail.</h2>
<p class="why__sub">
Small chairs, big intention. We keep the room calm and the
schedule unhurried so your stylist can do their best work.
</p>
</div>
<ul class="why__grid">
<li class="why__item reveal">
<span class="why__num">01</span>
<h3>Bespoke consultation</h3>
<p>Every visit opens with a face-framing and tone analysis — no two formulas alike.</p>
</li>
<li class="why__item reveal">
<span class="why__num">02</span>
<h3>Clean, vegan colour</h3>
<p>Ammonia-free, cruelty-free lines that protect the hair and the planet.</p>
</li>
<li class="why__item reveal">
<span class="why__num">03</span>
<h3>Lumière membership</h3>
<p>Priority booking, seasonal gloss, and a complimentary treatment each quarter.</p>
</li>
<li class="why__item reveal">
<span class="why__num">04</span>
<h3>Atelier comforts</h3>
<p>Heated chairs, espresso, and a curated playlist — stay as long as you like.</p>
</li>
</ul>
</div>
</section>
<!-- STYLISTS -->
<section class="section" id="stylists" aria-labelledby="stylists-h">
<div class="section__head reveal">
<p class="eyebrow">The Atelier</p>
<h2 id="stylists-h" class="section__title">Meet your stylists</h2>
<p class="section__sub">Tap a stylist to start a booking with them.</p>
</div>
<div class="stylists" id="stylists-strip">
<button class="stylist reveal" data-stylist="Aria Vance" data-role="Creative Director">
<span class="stylist__avatar" data-initials="AV" style="--a:#c9a78f;--b:#efe2cf"></span>
<span class="stylist__name">Aria Vance</span>
<span class="stylist__role">Creative Director · Colour</span>
</button>
<button class="stylist reveal" data-stylist="Theo Marchetti" data-role="Senior Cutting Stylist">
<span class="stylist__avatar" data-initials="TM" style="--a:#b08d57;--b:#f3e6dc"></span>
<span class="stylist__name">Theo Marchetti</span>
<span class="stylist__role">Senior Stylist · Cutting</span>
</button>
<button class="stylist reveal" data-stylist="Noor Haddad" data-role="Balayage Specialist">
<span class="stylist__avatar" data-initials="NH" style="--a:#8c6d3f;--b:#efe2cf"></span>
<span class="stylist__name">Noor Haddad</span>
<span class="stylist__role">Balayage Specialist</span>
</button>
<button class="stylist reveal" data-stylist="Elise Fontaine" data-role="Bridal & Editorial">
<span class="stylist__avatar" data-initials="EF" style="--a:#c9a78f;--b:#f7f1e8"></span>
<span class="stylist__name">Elise Fontaine</span>
<span class="stylist__role">Bridal & Editorial</span>
</button>
</div>
</section>
<!-- TESTIMONIAL -->
<section class="quote reveal" aria-label="Guest testimonial">
<div class="quote__inner">
<p class="quote__mark" aria-hidden="true">“</p>
<blockquote>
The most relaxed I've ever been in a salon chair — and the colour
still looks hand-painted three months on. Aria simply understands
hair.
</blockquote>
<p class="quote__by">
<strong>Camille Roux</strong>
<span>Member since 2019</span>
</p>
<p class="quote__stars" aria-label="Rated 5 out of 5">★★★★★</p>
</div>
</section>
<!-- GALLERY -->
<section class="section section--tight" id="gallery" aria-labelledby="gallery-h">
<div class="section__head reveal">
<p class="eyebrow">@maisonlumiere</p>
<h2 id="gallery-h" class="section__title">From the chair</h2>
</div>
<div class="gallery" aria-hidden="false">
<div class="tile tile--1 reveal"></div>
<div class="tile tile--2 reveal"></div>
<div class="tile tile--3 reveal"></div>
<div class="tile tile--4 reveal"></div>
<div class="tile tile--5 reveal"></div>
<div class="tile tile--6 reveal"></div>
</div>
</section>
<!-- VISIT / HOURS / BOOK -->
<section class="visit" id="visit" aria-labelledby="visit-h">
<div class="visit__inner">
<div class="visit__info reveal">
<p class="eyebrow">Visit us</p>
<h2 id="visit-h" class="visit__title">Hours & location</h2>
<p class="visit__addr">
418 Marlowe Avenue, Suite 2<br />
Los Angeles, CA 90013
</p>
<dl class="hours">
<div><dt>Tue – Fri</dt><dd>9:00 — 19:00</dd></div>
<div><dt>Saturday</dt><dd>9:00 — 17:00</dd></div>
<div><dt>Sunday</dt><dd>10:00 — 15:00</dd></div>
<div class="hours--closed"><dt>Monday</dt><dd>Closed</dd></div>
</dl>
<p class="visit__note" id="openState" aria-live="polite"></p>
</div>
<form class="book" id="bookForm" novalidate>
<h3 class="book__title">Request an appointment</h3>
<p class="book__sub">We'll confirm within one business hour.</p>
<div class="field">
<label for="bk-name">Full name</label>
<input id="bk-name" name="name" type="text" autocomplete="name" required placeholder="Camille Roux" />
</div>
<div class="field">
<label for="bk-email">Email</label>
<input id="bk-email" name="email" type="email" autocomplete="email" required placeholder="[email protected]" />
</div>
<div class="field">
<label for="bk-service">Service</label>
<select id="bk-service" name="service" required>
<option value="" disabled selected>Choose a service…</option>
<option>Cut & Style</option>
<option>Colour & Balayage</option>
<option>Smoothing & Repair</option>
<option>Bridal & Events</option>
<option>Extensions</option>
<option>Skin & Brow</option>
</select>
</div>
<div class="field">
<label for="bk-stylist">Preferred stylist</label>
<select id="bk-stylist" name="stylist">
<option value="">No preference</option>
<option>Aria Vance</option>
<option>Theo Marchetti</option>
<option>Noor Haddad</option>
<option>Elise Fontaine</option>
</select>
</div>
<div class="field">
<label for="bk-date">Preferred date</label>
<input id="bk-date" name="date" type="date" required />
</div>
<button type="submit" class="btn btn--gold btn--block">Request booking</button>
<p class="book__fine">By requesting you agree to our cancellation policy.</p>
</form>
</div>
</section>
</main>
<!-- FOOTER -->
<footer class="foot">
<div class="foot__inner">
<div class="foot__brand">
<span class="brand__mark" aria-hidden="true">ML</span>
<p>
Maison Lumière Salon & Atelier<br />
<span>418 Marlowe Avenue · Los Angeles</span>
</p>
</div>
<nav class="foot__links" aria-label="Footer">
<a href="#services">Services</a>
<a href="#stylists">Stylists</a>
<a href="#gallery">Gallery</a>
<a href="#visit">Book</a>
</nav>
<p class="foot__copy">© 2026 Maison Lumière. A fictional salon, lovingly made.</p>
</div>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Salon Landing
A full marketing landing for the fictional Maison Lumière Salon & Atelier, built around a luxe rose-gold, cream, and matte-black palette with Cormorant Garamond display type over an Inter UI. A translucent sticky nav blurs the page beneath it, gains a hairline and shadow on scroll, and highlights the active section as you move through the page. The editorial hero opens with a serif headline, a softly glowing gradient backdrop, Book and Call calls-to-action, and a row of stats that count up the first time they enter view.
Below the fold, a six-card services menu lifts and reveals a gold top-rule on hover, a dark signature-treatments band lays out the salon’s points of difference, and a strip of stylist cards is fully interactive — tapping any stylist pre-selects them in the booking form and smooth-scrolls you down to finish. A guest testimonial, an Instagram-style CSS gallery row, and an hours-and-location block with a live open or closed indicator round out the page.
Every section reveals on scroll via IntersectionObserver, the mobile nav collapses into an accessible toggle, and the appointment request form validates name, email, service, and date inline before confirming with a tasteful toast. It is pure vanilla HTML, CSS, and JavaScript — no frameworks, no build step, responsive down to 360px.