/* ============================================================
   SERVE CLEANING — Main Stylesheet
   Mobile-first. No frameworks. Vanilla CSS.
   ============================================================ */

/* --- Reset & Base --- */
*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* Ensure the [hidden] attribute always wins over class-level display rules */
[hidden] {
    display: none !important;
}

:root {
    --color-dark:         #1f2937;
    --color-dark-alt:     #111827;
    --color-accent:       #4b5563;
    --color-accent-hover: #6b7280;
    --color-bg:           #ffffff;
    --color-bg-light:     #f9fafb;
    --color-text:         #374151;
    --color-text-muted:   #6b7280;
    --color-border:       #e5e7eb;
    --font:               'Inter', sans-serif;
    --radius:             8px;
    --shadow:             0 1px 3px rgba(0,0,0,0.10), 0 1px 2px rgba(0,0,0,0.06);
    --shadow-md:          0 4px 6px -1px rgba(0,0,0,0.10), 0 2px 4px -1px rgba(0,0,0,0.06);
}

html {
    scroll-behavior: smooth;
}

body {
    font-family: var(--font);
    color: var(--color-text);
    background: var(--color-bg);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

img {
    max-width: 100%;
    height: auto;
    display: block;
}

a {
    color: inherit;
    text-decoration: none;
}

/* --- Shared Layout Utilities --- */
.section-inner {
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 1.25rem;
}

.section-title {
    font-size: 1.75rem;
    font-weight: 800;
    color: var(--color-dark);
    margin-bottom: 0.5rem;
    text-align: center;
}

.section-sub {
    font-size: 1rem;
    color: var(--color-text-muted);
    text-align: center;
    margin-bottom: 2.5rem;
    max-width: 560px;
    margin-left: auto;
    margin-right: auto;
}

/* --- Buttons --- */
.btn {
    display: inline-block;
    padding: 0.75rem 1.75rem;
    border-radius: var(--radius);
    font-family: var(--font);
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    border: none;
    transition: background 0.2s ease, box-shadow 0.2s ease, transform 0.1s ease;
    text-align: center;
}

.btn--primary {
    background: linear-gradient(180deg, #566070 0%, #414852 100%);
    color: #fff;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.10),
        inset 0 -1px 0 rgba(0, 0, 0, 0.15),
        0 2px 8px rgba(0, 0, 0, 0.25);
}

.btn--primary:hover {
    background: linear-gradient(180deg, #626c7d 0%, #4b5563 100%);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.14),
        inset 0 -1px 0 rgba(0, 0, 0, 0.15),
        0 4px 14px rgba(0, 0, 0, 0.30);
}

.btn--primary:active {
    transform: translateY(1px) scale(0.99);
    box-shadow:
        inset 0 1px 2px rgba(0, 0, 0, 0.20),
        0 1px 3px rgba(0, 0, 0, 0.20);
}

.btn--full {
    width: 100%;
    padding: 1rem;
    font-size: 1.05rem;
}


/* ============================================================
   NAV
   ============================================================ */
.nav {
    background: var(--color-dark);
    position: sticky;
    top: 0;
    z-index: 100;
    box-shadow: var(--shadow-md);
}

.nav__inner {
    max-width: 1100px;
    margin: 0 auto;
    padding: 1rem 1.25rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.nav__logo {
    font-size: 1.8rem;
    font-weight: 800;
    color: #fff;
    letter-spacing: -0.5px;
    font-family: 'Times New Roman', Times, serif;
}

.nav__links {
    display: none;
    list-style: none;
    gap: 2rem;
    align-items: center;
}

.nav__links a {
    color: #d1d5db;
    font-size: 0.95rem;
    font-weight: 500;
    transition: color 0.15s;
}

.nav__links a:hover {
    color: #fff;
}

.nav__cta {
    background: linear-gradient(180deg, #566070 0%, #414852 100%);
    color: #fff !important;
    padding: 0.5rem 1.25rem;
    border-radius: var(--radius);
    font-weight: 600 !important;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.10),
        inset 0 -1px 0 rgba(0, 0, 0, 0.15),
        0 2px 6px rgba(0, 0, 0, 0.25);
    transition: background 0.2s ease, box-shadow 0.2s ease;
}

.nav__cta:hover {
    background: linear-gradient(180deg, #626c7d 0%, #4b5563 100%) !important;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.14),
        inset 0 -1px 0 rgba(0, 0, 0, 0.15),
        0 4px 12px rgba(0, 0, 0, 0.30);
}

/* Hamburger */
.nav__hamburger {
    display: flex;
    flex-direction: column;
    gap: 5px;
    background: none;
    border: none;
    cursor: pointer;
    padding: 4px;
}

.nav__hamburger span {
    display: block;
    width: 24px;
    height: 2px;
    background: #fff;
    border-radius: 2px;
}

/* Mobile dropdown */
.nav__mobile-menu {
    display: none;
    background: var(--color-dark-alt);
    padding: 1rem 1.25rem;
}

.nav__mobile-menu.open {
    display: block;
}

.nav__mobile-menu ul {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.nav__mobile-menu a {
    color: #d1d5db;
    font-size: 1rem;
    font-weight: 500;
}


/* ============================================================
   HERO
   ============================================================ */
.hero {
    background: var(--color-dark);
    color: #fff;
    padding: 3rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 2rem;
    /* Both are for the skyline below: position anchors it, overflow keeps it
       from bleeding into the trust bar, isolate keeps its negative z-index
       inside this section rather than escaping behind the page. */
    position: relative;
    overflow: hidden;
    isolation: isolate;
}

/* Boston skyline along the bottom of the hero.
   ---------------------------------------------------------------
   BLACK at low opacity, so it lands a shade DARKER than the section it sits on
   rather than lighter. That reads as depth — a city receding into the dark —
   instead of as a picture pasted on top, and it keeps the artwork from ever
   competing with the headline or the button for attention.

   The SVG is inline in index.html rather than a background-image because CSS
   cannot reach inside a background to animate part of it. The tint therefore
   comes from `color` here, feeding fill="currentColor" on the markup: with no
   colour set it would inherit #fff from .hero and paint a WHITE skyline.

   preserveAspectRatio="none" means the drawing stretches to whatever band it
   is given, so the heights step up with the breakpoints. One fixed height
   would squash the buildings on a phone and smear them on a wide desktop.

   z-index: -1 puts it above .hero's own background but below everything in
   normal flow, so no hero content needs a stacking rule of its own. */
.hero__skyline {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 130px;
    color: #000;
    opacity: 0.3;
    z-index: -1;
    pointer-events: none;
}

.hero__skyline svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* Sails. Drift carries each hull a full viewBox width to the right; its twin,
   parked one width to the left in the markup, lands exactly where it began, so
   the loop is seamless. Inside a transformed SVG element a `px` in translate
   is a USER UNIT, not a screen pixel — 1200px here means one viewBox width at
   any rendered size, which is why the loop holds on every screen.

   Bob nests inside drift, so the two transforms compose into a slow diagonal
   rather than fighting each other.

   The three durations are deliberately non-harmonic. Round multiples would
   re-sync the boats into a visible formation every few minutes. */
@keyframes serve-sail-drift {
    from { transform: translateX(0); }
    to   { transform: translateX(1200px); }
}

@keyframes serve-sail-bob {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-2.5px); }
}

/* Sails catch a little light; everything else stays on currentColor (black).
   At 0.3 container x 0.42 group opacity the sails were rendering 5 points of
   luminance BELOW the hero — a dark shape on a dark ground, which is why they
   read as barely there. A light fill puts them ~12 above it instead: still
   quiet, but on the correct side of the background to be seen. White sails
   over dark water is also the right way round physically.

   Set on the drift group rather than the sail group so the waterline below
   keeps the darker tone — water should not catch light the way canvas does.
   Inheritance does the rest: these <g> elements sit between the <svg> that
   carries fill="currentColor" and the polygons, so the nearer value wins. */
.skyline__drift { fill: #78859a; }

.skyline__drift--a { animation: serve-sail-drift  96s linear infinite; }
.skyline__drift--b { animation: serve-sail-drift 132s linear infinite; }
.skyline__drift--c { animation: serve-sail-drift  78s linear infinite; }

.skyline__bob--a { animation: serve-sail-bob 7.5s ease-in-out infinite; }
.skyline__bob--b { animation: serve-sail-bob 9s   ease-in-out infinite; }
.skyline__bob--c { animation: serve-sail-bob 8.2s ease-in-out infinite; }

/* The skyline still renders — only the sailing stops. */
@media (prefers-reduced-motion: reduce) {
    .skyline__drift,
    .skyline__bob {
        animation: none;
    }
}

.hero__content {
    max-width: 1100px;
    margin: 0 auto;
    width: 100%;
    text-align: center;
}

/* Times New Roman again (2026-09-14, founder's call), matching the logo
   wordmark. It was Inter for a day at 0.875x these sizes, because Inter runs
   about 14% wider; the sizes here and in the 640px / 1024px blocks are the
   Times ones. */
.hero__headline {
    font-family: 'Times New Roman', Times, serif;
    font-size: 2rem;
    font-weight: 800;
    line-height: 1.2;
    margin-bottom: 1rem;
    color: #f9fafb;
}

/* The tagline now sits BELOW this, so the big gap moved down with it: these
   two lines belong together and the air belongs before the button. */
.hero__sub {
    font-size: 1.05rem;
    color: #9ca3af;
    margin: 0 auto 0.6rem;
    max-width: 520px;
    line-height: 1.7;
}

/* The signature line. It sat under the CTA until 2026-09-07 and now sits
   between the headline and the subhead, so the margins flipped: it needs to
   read as part of the headline's breath rather than as a sign-off. Still
   quieter than .hero__sub — it is a phrase, not information. Set like
   .hero__lead since 2026-09-14: the headline's Times and white, its own size. */
.hero__tagline {
    font-family: 'Times New Roman', Times, serif;
    font-size: 1.15rem;
    color: #f9fafb;
    margin: 0 0 1.75rem;
}

.hero__tagline strong {
    font-weight: 700;
    color: #fff;
}

.hero__image {
    max-width: 1100px;
    margin: 0 auto;
    width: 100%;
    display: flex;
    justify-content: center;
}

.hero__image img:not(.hero__logo) {
    width: 100%;
    max-height: 280px;
    object-fit: cover;
    border-radius: var(--radius);
    border: 2px solid #374151;
}

.hero__logo {
    width: 100%;
    max-width: 280px;
    height: auto;
    display: block;
    border: none;
    outline: none;
    background: transparent;
    box-shadow: none;
}

/* --- ZIP-check hero body (2026-09-14) ----------------------------------
   Shown in place of the default body for the move-in and pro skins; see
   HERO_SKINS in script.js. It sits on the dark hero, so the greens are the LIGHT ones —
   Build's #166534 would all but disappear against #1f2937. */
.hero-zip {
    max-width: 320px;
    margin: 0.5rem auto 0;
}

.hero-zip__label {
    display: block;
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    font-weight: 600;
    color: #e5e7eb;
    margin-bottom: 0.55rem;
}

.hero-zip__field {
    position: relative;
}

/* Padded equally on both sides so the digits stay centred with the check
   sitting on the right. Scoped under .hero-zip on purpose: .flow-input is
   defined LATER in this file at the same weight, and would otherwise win. */
.hero-zip .hero-zip__input {
    text-align: center;
    font-size: 1.15rem;
    line-height: 1.3;
    letter-spacing: 0.06em;
    /* Letter-spacing trails the last glyph, which nudges centred text left;
       an equal indent puts it back on the true centre. */
    text-indent: 0.06em;
    padding-left: 2.75rem;
    padding-right: 2.75rem;
}
.hero-zip .hero-zip__input:focus {
    border-color: #fff;
    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.25);
}
.hero-zip .hero-zip__input.error {
    border-color: #ef4444;
    box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.35);
}

.hero-zip__check {
    position: absolute;
    right: 0.65rem;
    top: 50%;
    width: 1.65rem;
    height: 1.65rem;
    margin-top: -0.825rem;
    border-radius: 50%;
    background: #16a34a;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.95rem;
    font-weight: 700;
    opacity: 0;
    transform: scale(0.6);
    transition: opacity 0.2s, transform 0.2s;
    pointer-events: none;
}

.hero-zip.is-ok .hero-zip__input {
    border-color: #22c55e;
    box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.35);
}
.hero-zip.is-ok .hero-zip__check {
    opacity: 1;
    transform: scale(1);
}

.hero-zip__status {
    font-family: var(--flow-sans);
    font-size: 0.9rem;
    font-weight: 600;
    min-height: 1.35rem;
    margin-top: 0.5rem;
    color: #cbd5e1;
}
.hero-zip.is-ok .hero-zip__status { color: #4ade80; }

.hero-zip__waitlist {
    font-size: 0.9rem;
    font-weight: 600;
    color: #fff;
    text-decoration: underline;
    text-underline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
    .hero-zip__check { transition: none; }
}

.hero-zip + .hero__note {
    margin-top: 0.35rem;
}

/* On the authored hero the sub sits above the ZIP check; give the check the
   air the old tagline gave the button. */
.hero__body--default .hero__sub {
    margin-bottom: 1.25rem;
}

/* "Serve handles the rest…" — the lead-in to the sub beneath it, so it sits
   close to what it introduces. Same size as .hero__tagline, but the face and
   colour of .hero__headline (Times, #f9fafb), at the founder's request
   (2026-09-14). The gap above it was 2.25rem, then 1.1rem, each time the
   founder found it too wide under "Instantly quote & book…". */
.hero__lead {
    font-family: 'Times New Roman', Times, serif;
    font-size: 1.15rem;
    color: #f9fafb;
    margin: 0.6rem 0 0.35rem;
}

.hero__body--zip .hero__tagline {
    margin-bottom: 0;
}


/* ============================================================
   TRUST BAR
   ============================================================ */
.trust-bar {
    background: var(--color-bg-light);
    border-top: 1px solid var(--color-border);
    border-bottom: 1px solid var(--color-border);
    padding: 1rem 1.25rem;
}

/* Same type as the checkmark items below it, just centred and without one.
   It doubles as the fold's handle, so it also carries the caret and the
   pointer; the default disclosure triangle is removed in both dialects
   because it would sit off to the left and break the centring. */
.trust-bar__lead {
    max-width: 1100px;
    margin: 0 auto;
    text-align: center;
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--color-dark);
    cursor: pointer;
    list-style: none;
}

.trust-bar__lead::-webkit-details-marker {
    display: none;
}

/* Inline rather than absolutely placed, so on a narrow screen the caret
   follows the last word down instead of floating beside a two-line claim.
   Accent-coloured to match the checks it opens — it is an affordance, not
   a fifth check. */
.trust-bar__lead::after {
    content: "";
    display: inline-block;
    width: 0.4em;
    height: 0.4em;
    margin-left: 0.5em;
    border-right: 2px solid var(--color-accent);
    border-bottom: 2px solid var(--color-accent);
    transform: translateY(-0.15em) rotate(45deg);
    transition: transform 0.15s ease;
}

.trust-bar__fold[open] .trust-bar__lead::after {
    transform: translateY(0.1em) rotate(-135deg);
}

.trust-bar__lead:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 3px;
    border-radius: 3px;
}

/* The gap belongs to the open state. Closed, the bar is one line tall. */
.trust-bar__inner {
    max-width: 1100px;
    margin: 0.75rem auto 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 2rem;
    justify-content: center;
}

.trust-bar__item {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--color-dark);
}

.trust-bar__icon {
    color: var(--color-accent);
    font-weight: 700;
}


/* ============================================================
   HOW IT WORKS
   ============================================================ */
/* Bottom padding is shorter than the top on purpose: the handset is the
   last thing in this section and the calculator is the next thing after it,
   so the two want to read as one move rather than two separate blocks. */
.how-it-works {
    padding: 4rem 1.25rem 2rem;
    background: var(--color-bg);
}

.steps {
    display: flex;
    flex-direction: column;
    gap: 2rem;
}

.step {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.step__number {
    width: 2.5rem;
    height: 2.5rem;
    background: var(--color-accent);
    color: #fff;
    font-weight: 800;
    font-size: 1rem;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    margin-bottom: 0.25rem;
}

.step h3 {
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--color-dark);
}

.step p {
    color: var(--color-text-muted);
    font-size: 0.95rem;
    line-height: 1.6;
}

.step__link {
    margin-top: 0.35rem;
}

.step__link a {
    color: var(--color-accent);
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.step__link a:hover { color: var(--color-dark); }

.step__link a:focus-visible {
    outline: 2px solid var(--color-dark);
    outline-offset: 3px;
    border-radius: 2px;
}


/* ============================================================
   WORK EXAMPLES
   ============================================================ */
.work {
    padding: 4rem 1.25rem;
    background: var(--color-bg-light);
}

/* The before/after photos open on demand, so the quote form sits higher.
   Only the SUMMARY is width-capped — capping the <details> itself would
   squeeze the grid inside it down to button width when opened.

   The margin carries the whole gap under the heading now. It used to come
   from .section-sub's 2.5rem bottom margin; with the subhead gone, this
   collapsed against .section-title's 0.5rem and left the button crowding
   the heading. */
.work-acc {
    margin-top: 2rem;
}

.work-acc__summary {
    max-width: 340px;
    margin: 0 auto;
    padding: 0.85rem 1.4rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: var(--color-bg);
    cursor: pointer;
    font-weight: 600;
    color: var(--color-dark);
    /* list-style plus the webkit rule below: between them they remove the
       default disclosure triangle in every browser that ships one. */
    list-style: none;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 0.55rem;
    transition: background 0.2s ease, border-color 0.2s ease;
}

.work-acc__summary::-webkit-details-marker { display: none; }
.work-acc__summary::after { content: "+"; color: var(--color-text-muted); font-size: 1.15rem; }
.work-acc[open] .work-acc__summary::after { content: "\2013"; }

.work-acc__summary:hover {
    background: var(--color-bg-light);
    border-color: var(--color-text-muted);
}

/* <summary> is focusable, so the ring has to be deliberate — this is the only
   way to reach the photos from the keyboard. */
.work-acc__summary:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

.ba-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 2.5rem;
    margin-top: 2rem;
}

.ba-item {
    display: flex;
    flex-direction: column;
}

.ba-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
    border-radius: var(--radius);
    overflow: hidden;
}

.ba-photo {
    position: relative;
    margin: 0;
    aspect-ratio: 3 / 4;
    overflow: hidden;
    border-radius: var(--radius);
    border: 1px solid var(--color-border);
    background: var(--color-bg);
}

.ba-photo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.ba-tag {
    position: absolute;
    top: 0.625rem;
    left: 0.625rem;
    padding: 0.25rem 0.625rem;
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #fff;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

.ba-tag--before {
    background: rgba(31, 41, 55, 0.78);
}

.ba-tag--after {
    background: rgba(37, 99, 235, 0.92);
}

.ba-caption {
    margin-top: 0.9rem;
    font-size: 0.92rem;
    color: var(--color-text-muted);
    line-height: 1.6;
    text-align: center;
}

/* ============================================================
   RECENT WORK STRIP
   Its own section at the foot of the page, below #after-you-book.
   Shares --color-bg-light with the section above it, so the border-top
   is what separates them — same trick .svmA-section uses.
   ============================================================ */
.work-strip {
    padding: 3.5rem 1.25rem;
    background: var(--color-bg-light);
    border-top: 1px solid var(--color-border);
}

.work-strip__title {
    margin-bottom: 1.25rem;
    text-align: center;
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--color-dark);
}

.work-strip__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.75rem;
}

.work-strip__item {
    margin: 0;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    border-radius: var(--radius);
    border: 1px solid var(--color-border);
}

.work-strip__item img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}


/* ============================================================
   BOOKING FLOW — Build → Price → Reserve
   One typeface: Inter, the same as the rest of the page. Until 2026-09-14
   the flow was set in Times New Roman through a --flow-serif token; the
   founder asked for the front page to read in one font, and that token is
   gone. --flow-sans is only defined here, so outside the flow a rule that
   uses it simply inherits the page's Inter.
   ============================================================ */
.quote-section {
    padding: 4rem 1.25rem;
    background: var(--color-bg);
    --flow-sans:  'Inter', system-ui, sans-serif;
    font-family: var(--flow-sans);
}

/* Set explicitly: a <button> does not inherit the page font on its own. */
.quote-section .btn {
    font-family: var(--flow-sans);
    font-size: 1.05rem;
    letter-spacing: 0.01em;
}

/* --- Progress --- */
.flow-progress {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    max-width: 600px;
    margin: 0 auto 2.25rem;
    padding: 0;
    list-style: none;
}

.flow-progress__step {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-family: var(--flow-sans);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.flow-progress__num {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    border: 2px solid var(--color-border);
    font-size: 0.8rem;
    flex-shrink: 0;
}

.flow-progress__step.is-active { color: var(--color-dark); }
.flow-progress__step.is-active .flow-progress__num {
    border-color: var(--color-dark);
    background: var(--color-dark);
    color: #fff;
}
.flow-progress__step.is-done .flow-progress__num {
    border-color: var(--color-dark);
    background: var(--color-dark);
    color: #fff;
    opacity: 0.5;
}

.flow-progress__sep {
    flex: 1;
    max-width: 2rem;
    height: 2px;
    background: var(--color-border);
}

/* --- Step head --- */
.step-head {
    text-align: center;
    max-width: 600px;
    margin: 0 auto 1.75rem;
}

/* 0.875x its Times New Roman size, for the same reason as .hero__headline. */
.flow-title {
    font-family: var(--flow-sans);
    font-size: 1.75rem;
    font-weight: 700;
    color: var(--color-dark);
    line-height: 1.12;
    letter-spacing: -0.01em;
    margin-bottom: 0.6rem;
}

.flow-sub {
    font-family: var(--flow-sans);
    font-size: 1.1rem;
    color: var(--color-text-muted);
    line-height: 1.55;
}

/* --- Card shell --- */
.flow-card {
    background: var(--color-bg-light);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 1.5rem 1.25rem;
    max-width: 600px;
    margin: 0 auto 1rem;
}

/* --- Fields --- */
.field-block { margin-bottom: 1.5rem; }
.field-block:last-child { margin-bottom: 0; }

.field-label {
    display: block;
    font-family: var(--flow-sans);
    font-size: 0.76rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-dark);
    margin-bottom: 0.65rem;
}

.field-label .opt {
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    color: var(--color-text-muted);
}

.flow-input {
    font-family: var(--flow-sans);
    font-size: 1rem;
    padding: 0.8rem 0.9rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: #fff;
    color: var(--color-text);
    width: 100%;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.flow-input:focus {
    outline: none;
    border-color: var(--color-dark);
    box-shadow: 0 0 0 3px rgba(31, 41, 55, 0.12);
}
.flow-input.error {
    border-color: #ef4444;
    box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
}

/* The "anything we should know?" box, on the confirmation screen.
   ---------------------------------------------------------------
   It sat in the reserve step until 2026-09-01. Same question, moved past the
   commitment: a textarea before the spot is claimed is a reason to hesitate,
   and after it is claimed hesitating costs nothing.

   Separated by a rule and set quieter than the confirmation above it, so it
   reads as a bonus rather than as one more thing standing between them and
   being finished. They are already done. */
.postbook {
    margin-top: 1.75rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--color-border);
    text-align: left;
    max-width: 460px;
    margin-left: auto;
    margin-right: auto;
}

.postbook__title {
    font-family: var(--flow-sans);
    font-size: 1.08rem;
    font-weight: 700;
    color: var(--color-dark);
    margin: 0 0 0.3rem;
}

.postbook__sub {
    font-family: var(--flow-sans);
    font-size: 0.88rem;
    color: var(--color-text-muted);
    line-height: 1.5;
    margin: 0 0 0.8rem;
}

.postbook__box {
    display: block;
    resize: vertical;
    min-height: 5.25rem;
    line-height: 1.55;
    margin-bottom: 0.7rem;
}

.postbook__status {
    font-family: var(--flow-sans);
    font-size: 0.85rem;
    color: var(--color-text-muted);
    line-height: 1.5;
    margin: 0.7rem 0 0;
    min-height: 1.2em;   /* holds its space so the panel does not jump on send */
}

/* Visually hidden, still announced. The textarea's placeholder is an example,
   not a label, and a placeholder alone leaves a screen reader with an unnamed
   field. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.field-hint {
    font-family: var(--flow-sans);
    font-size: 0.8rem;
    color: var(--color-text-muted);
    line-height: 1.5;
    margin-top: 0.45rem;
}

.form-error {
    color: #ef4444;
    font-family: var(--flow-sans);
    font-size: 0.85rem;
    margin: 0.75rem 0;
    font-weight: 500;
    min-height: 1.1rem;
    text-align: center;
}

.zip-status {
    margin-top: 0.5rem;
    font-family: var(--flow-sans);
    font-size: 0.9rem;
    font-weight: 600;
    min-height: 1.2rem;
    color: var(--color-text-muted);
}
.zip-status.is-ok { color: #166534; }

/* --- Stepper --- */
.stepper {
    display: inline-flex;
    align-items: center;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: #fff;
    overflow: hidden;
}

.stepper__btn {
    width: 3rem;
    height: 3rem;
    font-size: 1.5rem;
    line-height: 1;
    background: #fff;
    border: none;
    color: var(--color-dark);
    cursor: pointer;
    font-family: var(--flow-sans);
    transition: background 0.15s;
}
.stepper__btn:hover:not(:disabled) { background: var(--color-bg-light); }
.stepper__btn:active:not(:disabled) { background: var(--color-border); }
.stepper__btn:disabled { color: #d1d5db; cursor: not-allowed; }

.stepper__value {
    min-width: 5rem;
    text-align: center;
    font-family: var(--flow-sans);
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--color-dark);
}

/* --- Address suggestions ----------------------------------------------
   The list floats over whatever is beneath it rather than pushing the form
   down — a field that grows by 200px while you type moves the button out from
   under your thumb. `.ac` is the positioning context and nothing else. */
.ac { position: relative; }

.ac__list {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(100% + 4px);
    z-index: 20;
    margin: 0;
    padding: 0.25rem;
    list-style: none;
    background: #fff;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    box-shadow: 0 12px 28px -8px rgba(0, 0, 0, 0.28);
    max-height: 15rem;
    overflow-y: auto;
}
.ac__list[hidden] { display: none; }

.ac__opt {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.6rem 0.7rem;
    border-radius: 6px;
    cursor: pointer;
}
/* One highlight for both routes in. `.is-active` is set by the arrow keys and
   by hover alike, so the keyboard and the mouse cannot disagree about which
   row is selected — two separate highlights on screen at once is the bug this
   avoids. */
.ac__opt.is-active { background: var(--color-bg-light); }

.ac__line {
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--color-dark);
}
.ac__where {
    font-family: var(--flow-sans);
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

/* Attribution, required by Geoapify's free tier. Hidden until the first
   suggestion actually renders — crediting a service that has not yet done
   anything is noise on a form that is trying to be short. */
.ac__credit {
    font-family: var(--flow-sans);
    font-size: 0.72rem;
    color: var(--color-text-muted);
    margin-top: 0.4rem;
}
.ac__credit[hidden] { display: none; }
.ac__credit a { color: var(--color-text-muted); text-decoration: underline; }
.ac__credit a:hover { color: var(--color-accent); }

/* --- Choice cards (service, condition, time) --- */
.choice-cards {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.choice-card {
    position: relative;
    cursor: pointer;
    display: block;
    flex: 1;
}

.choice-card input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.choice-card__body {
    display: block;
    height: 100%;
    border: 2px solid var(--color-border);
    border-radius: var(--radius);
    padding: 1rem 1.15rem;
    background: #fff;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.choice-card:hover .choice-card__body { border-color: #cbd5e1; }

.choice-card input:checked + .choice-card__body {
    border-color: var(--color-dark);
    box-shadow: 0 0 0 3px rgba(31, 41, 55, 0.12);
}

.choice-card input:focus-visible + .choice-card__body {
    outline: 2px solid var(--color-dark);
    outline-offset: 2px;
}

.choice-card__title {
    display: block;
    font-family: var(--flow-sans);
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--color-dark);
    margin-bottom: 0.25rem;
}

.choice-card__desc {
    display: block;
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    color: var(--color-text-muted);
    line-height: 1.5;
}

.choice-card--sm .choice-card__body { padding: 0.85rem 1rem; }
.choice-card--sm .choice-card__title { margin-bottom: 0; font-size: 1rem; }

/* --- Inline capture (waitlist / overflow) --- */
.inline-capture { margin-top: 1.25rem; }
.inline-capture__copy {
    font-family: var(--flow-sans);
    font-size: 1.05rem;
    color: var(--color-text);
    line-height: 1.6;
    margin-bottom: 1rem;
}
.inline-capture .flow-input { margin-bottom: 0.6rem; }
.inline-capture__row {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.inline-capture__row .flow-input { flex: 1; min-width: 0; margin-bottom: 0; }
.inline-capture__done {
    color: #166534;
    font-weight: 600;
    margin-top: 0.75rem;
}


/* ============================================================
   AFTER YOU BOOK  (#after-you-book)
   Self-contained block. Every selector is either .svmA- prefixed or
   scoped under .svmA-wrap, so nothing here bleeds into the rest of the
   page and nothing on the page bleeds in. Safe to lift out whole.
   ============================================================ */
.svmA-section {
    padding: 4rem 1.25rem;
    background: var(--color-bg-light);
    border-top: 1px solid var(--color-border);
}

.svmA-wrap {
    max-width: 1100px;
    margin: 0 auto;
}

.svmA-wrap h2 {
    font-size: 1.9rem;
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.01em;
    color: var(--color-dark);
    text-align: center;
    margin-bottom: 0.6rem;
}

.svmA-lede {
    font-size: 1.05rem;
    color: var(--color-text-muted);
    text-align: center;
    max-width: 580px;
    margin: 0 auto 2.5rem;
    line-height: 1.6;
}

.svmA-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
}

.svmA-bubble {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 1.35rem 1.25rem;
    box-shadow: var(--shadow);
}

.svmA-step {
    display: block;
    font-family: 'Inter', system-ui, sans-serif;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-accent);
    margin-bottom: 0.5rem;
}

.svmA-wrap h3 {
    font-size: 1.15rem;
    font-weight: 700;
    line-height: 1.25;
    color: var(--color-dark);
    margin-bottom: 0.4rem;
}

.svmA-bubble p {
    font-size: 1rem;
    color: var(--color-text);
    line-height: 1.6;
}

.svmA-foot {
    font-size: 1rem;
    color: var(--color-text-muted);
    text-align: center;
    line-height: 1.7;
    margin-top: 2.5rem;
}

.svmA-foot a {
    color: var(--color-dark);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.svmA-foot a:hover { color: var(--color-accent); }

.svmA-foot a:focus-visible {
    outline: 2px solid var(--color-dark);
    outline-offset: 3px;
    border-radius: 2px;
}

/* At 720px and below the grid stays single-column and every bubble runs
   full width — that is the base state above. From 721px up they pair off. */
@media (min-width: 721px) {
    .svmA-section { padding: 5rem 2rem; }
    .svmA-wrap h2 { font-size: 2.25rem; }
    .svmA-grid  { grid-template-columns: repeat(2, 1fr); gap: 1.25rem; }
}

@media (min-width: 1024px) {
    .svmA-grid { grid-template-columns: repeat(3, 1fr); }
}


/* ============================================================
   FOOTER
   ============================================================ */
.footer {
    background: var(--color-dark-alt);
    color: #9ca3af;
    padding: 2.5rem 1.25rem 1.5rem;
}

.footer__inner {
    max-width: 1100px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    margin-bottom: 2rem;
}

.footer__logo {
    font-size: 1.65rem;
    font-weight: 800;
    color: #fff;
    display: block;
    margin-bottom: 0.35rem;
    font-family: 'Times New Roman', Times, serif;
}

.footer__brand p,
.footer__contact p {
    font-size: 0.875rem;
    line-height: 1.6;
}

.footer__contact a {
    color: #d1d5db;
    font-size: 0.875rem;
    text-decoration: underline;
}

.footer__copy {
    max-width: 1100px;
    margin: 0 auto;
    font-size: 0.8rem;
    color: #4b5563;
    border-top: 1px solid #374151;
    padding-top: 1.25rem;
}


/* ============================================================
   BOOKING FLOW — Steps show/hide
   ============================================================ */
.quote-step {
    display: none;
    animation: stepFade 0.25s ease;
}

.quote-step.step-active {
    display: block;
}

@keyframes stepFade {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* --- Step actions (back / continue) --- */
.step-actions {
    display: flex;
    gap: 0.75rem;
    align-items: center;
    margin-top: 1.25rem;
    max-width: 600px;
    margin-left: auto;
    margin-right: auto;
}

.step-actions .btn--primary { flex: 1; }

.btn--ghost {
    background: transparent;
    color: var(--color-text-muted);
    border: 1px solid var(--color-border);
    padding: 0.75rem 1.25rem;
}

.btn--ghost:hover {
    background: var(--color-bg-light);
    color: var(--color-dark);
}


/* ============================================================
   STEP 2 — Price
   ============================================================ */
.price-card {
    text-align: center;
    padding: 2rem 1.25rem;
}

.price-badge {
    display: inline-block;
    background: var(--color-dark);
    color: #fff;
    font-family: var(--flow-sans);
    font-size: 0.68rem;
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 0.35rem 0.7rem;
    border-radius: 999px;
    margin-bottom: 1rem;
}

.price-strike {
    font-family: var(--flow-sans);
    font-size: 1.5rem;
    color: var(--color-text-muted);
    text-decoration: line-through;
    margin-bottom: 0.15rem;
}

.price-main {
    font-family: var(--flow-sans);
    font-size: 3.25rem;
    font-weight: 700;
    color: var(--color-dark);
    line-height: 1;
    letter-spacing: -0.01em;
}

.price-caption {
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    color: var(--color-text-muted);
    font-style: italic;
    margin-top: 0.65rem;
}

.price-note {
    font-family: var(--flow-sans);
    font-size: 0.85rem;
    color: var(--color-text);
    line-height: 1.5;
    background: #fff;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 0.85rem 0.95rem;
    margin-top: 1.25rem;
    text-align: left;
}

/* Add-on toggles */
.toggle-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.9rem 0;
    border-bottom: 1px solid var(--color-border);
    cursor: pointer;
}
.toggle-row:last-child { border-bottom: none; padding-bottom: 0; }

.toggle-row__title {
    font-family: var(--flow-sans);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--color-dark);
}
.toggle-row__price {
    font-family: var(--flow-sans);
    color: var(--color-text-muted);
    margin-left: 0.5rem;
}
.toggle-row input {
    width: 1.35rem;
    height: 1.35rem;
    flex-shrink: 0;
    accent-color: var(--color-dark);
    cursor: pointer;
}

/* Frequency chips */
.freq-options {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.6rem;
}

.freq-chip { position: relative; cursor: pointer; }
.freq-chip input { position: absolute; opacity: 0; pointer-events: none; }

.freq-chip__body {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.2rem;
    height: 100%;
    border: 2px solid var(--color-border);
    border-radius: var(--radius);
    padding: 0.75rem 0.5rem;
    background: #fff;
    text-align: center;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.freq-chip input:checked + .freq-chip__body {
    border-color: var(--color-dark);
    box-shadow: 0 0 0 3px rgba(31, 41, 55, 0.1);
}
.freq-chip input:focus-visible + .freq-chip__body {
    outline: 2px solid var(--color-dark);
    outline-offset: 2px;
}
.freq-chip__name {
    font-family: var(--flow-sans);
    font-weight: 700;
    font-size: 1rem;
    color: var(--color-dark);
}
.freq-chip__price {
    font-family: var(--flow-sans);
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

.freq-note {
    font-family: var(--flow-sans);
    font-size: 0.8rem;
    color: var(--color-text-muted);
    margin-top: 0.85rem;
}

/* What's included accordion */
.included-acc {
    max-width: 600px;
    margin: 0 auto 1rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: #fff;
    overflow: hidden;
}
.included-acc summary {
    cursor: pointer;
    padding: 1rem 1.15rem;
    font-family: var(--flow-sans);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--color-dark);
    list-style: none;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.included-acc summary::-webkit-details-marker { display: none; }
.included-acc summary::after { content: "+"; color: var(--color-text-muted); font-size: 1.2rem; }
.included-acc[open] summary::after { content: "\2013"; }

.included-acc__body {
    padding: 0 1.15rem 1.15rem;
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    color: var(--color-text);
    line-height: 1.6;
}
.included-acc__body ul { list-style: disc; padding-left: 1.2rem; }
.included-acc__body li { margin-bottom: 0.35rem; }

/* Trust strip */
.trust-strip {
    text-align: center;
    font-family: var(--flow-sans);
    font-size: 0.82rem;
    color: var(--color-text-muted);
    max-width: 600px;
    margin: 1.25rem auto;
    line-height: 1.5;
}

/* Ghost email capture */
.ghost-capture { text-align: center; margin-top: 1.5rem; }
.ghost-capture__trigger {
    background: none;
    border: none;
    color: var(--color-accent);
    text-decoration: underline;
    cursor: pointer;
    font-family: var(--flow-sans);
    font-size: 1rem;
}
.ghost-capture__form {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    max-width: 420px;
    margin: 1rem auto 0;
}
.ghost-capture__form .flow-input { flex: 1; min-width: 0; }
.ghost-capture__done {
    width: 100%;
    color: #166534;
    font-weight: 600;
    margin-top: 0.25rem;
    font-family: var(--flow-sans);
}


/* ============================================================
   STEP 3 — Live scheduler (open agenda)
   ============================================================ */
/* Broadcast-style LIVE pill beside "Pick your day and time".
   ---------------------------------------------------------------
   Honest by construction: script.js only unhides it in 'scheduler' mode, so it
   appears when real slots from the availability feed are on screen and never
   while loading or while the fallback radios are standing in. That is the
   whole licence for using this device — the times genuinely are live.

   #dc2626 rather than the #ef4444 used by .form-error and .flow-input.error.
   A red pill a few inches from a form field should not read as a validation
   failure, so the two reds are deliberately different weights of red. */
.live-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.38rem;
    margin-left: 0.5rem;
    padding: 0.14rem 0.48rem 0.14rem 0.4rem;
    border: 1px solid rgba(220, 38, 38, 0.32);
    border-radius: 999px;
    background: rgba(220, 38, 38, 0.09);
    color: #dc2626;
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 0.10em;
    /* .field-block sets uppercase and wide tracking on the label; the pill
       carries its own so it does not inherit the heading's spacing. */
    text-transform: uppercase;
    vertical-align: 1px;
}

.live-badge__dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: #dc2626;
    animation: serve-live-blink 2.4s ease-in-out infinite;
}

/* Dim-and-swell rather than an on/off blink. A hard blink is a warning light;
   this should read as a signal that is simply alive. The halo fades outward at
   the same time so the pulse is legible without the dot ever going dark. */
@keyframes serve-live-blink {
    0%, 100% { opacity: 1;    box-shadow: 0 0 0 0   rgba(220, 38, 38, 0.40); }
    50%      { opacity: 0.42; box-shadow: 0 0 0 4px rgba(220, 38, 38, 0); }
}

@media (prefers-reduced-motion: reduce) {
    .live-badge__dot { animation: none; }
}

.scheduler-sub {
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    color: var(--color-text-muted);
    line-height: 1.5;
    margin: -0.35rem 0 1rem;
}

.scheduler-loading {
    font-family: var(--flow-sans);
    font-style: italic;
    font-size: 0.95rem;
    color: var(--color-text-muted);
}

/* The agenda's wrapper, built by serve-slots.js for both pages that show it.
   LAYOUT-TRANSPARENT ON PURPOSE: the booking page used to append day blocks
   straight into #scheduler-days, and this wrapper must not become a box that
   any future rule on that container has to reckon with. */
.slotpick { display: contents; }

.scheduler-day { margin-bottom: 0.875rem; }
.scheduler-day:last-child { margin-bottom: 0; }

.scheduler-day__head {
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    font-weight: 400;
    color: var(--color-dark);
    margin-bottom: 6px;
}

.scheduler-day__times {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.slot-chip {
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    color: var(--color-dark);
    background: #fff;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 8px 14px;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.slot-chip:hover { border-color: #cbd5e1; }
.slot-chip:focus-visible {
    outline: 2px solid var(--color-dark);
    outline-offset: 2px;
}
.slot-chip.is-selected {
    background: var(--color-dark);
    border-color: var(--color-dark);
    color: #fff;
}

/* "Show N more days" — the folded half of the agenda, one click away.
   DASHED, AND NOT SHAPED LIKE A CHIP. It sits directly under a row of solid
   bordered chips that are all pressable times; if it read as one more chip,
   the first click would be somebody trying to book it. Full width and dashed
   says "the list continues past here" instead.

   Not an underlined text link either, the way .scheduler-fallback-link and
   .plan__other are. Those are escape hatches for a customer the agenda failed.
   This one is the rest of the agenda, and it has to be findable at a glance. */
.slotpick__more {
    display: block;
    width: 100%;
    margin-top: 0.875rem;
    padding: 10px 14px;
    font-family: var(--flow-sans);
    font-size: 0.9rem;
    color: var(--color-text-muted);
    background: #fff;
    border: 1px dashed var(--color-border);
    border-radius: var(--radius);
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, color 0.15s;
}

.slotpick__more:hover {
    border-color: #cbd5e1;
    background: #f8fafc;
    color: var(--color-dark);
}

.slotpick__more:focus-visible {
    outline: 2px solid var(--color-dark);
    outline-offset: 2px;
}

.scheduler-fallback-line {
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    color: var(--color-text-muted);
    margin-top: 1.1rem;
}

.scheduler-fallback-link {
    background: none;
    border: none;
    padding: 0;
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    color: var(--color-accent);
    text-decoration: underline;
    cursor: pointer;
}
.scheduler-fallback-link:hover { color: var(--color-dark); }

/* Link down to the "After you book" section */
.reserve-aftercare {
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    color: var(--color-text-muted);
    text-align: center;
    margin-top: 1.1rem;
}

.reserve-aftercare a {
    color: var(--color-accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.reserve-aftercare a:hover { color: var(--color-dark); }

.reserve-aftercare a:focus-visible {
    outline: 2px solid var(--color-dark);
    outline-offset: 3px;
    border-radius: 2px;
}


/* ============================================================
   Reservation confirmation
   ============================================================ */
.flow-confirm {
    max-width: 600px;
    margin: 1rem auto;
    text-align: center;
    background: #f0fdf4;
    border: 1px solid #86efac;
    border-radius: var(--radius);
    padding: 2.5rem 1.5rem;
}
.flow-confirm__mark {
    width: 3rem;
    height: 3rem;
    border-radius: 50%;
    background: #16a34a;
    color: #fff;
    font-size: 1.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto 1rem;
}
.flow-confirm h2 {
    font-family: var(--flow-sans);
    font-size: 1.7rem;
    font-weight: 700;
    color: #166534;
    margin-bottom: 0.5rem;
}
.flow-confirm p {
    font-family: var(--flow-sans);
    color: #15803d;
    font-size: 1.05rem;
    line-height: 1.6;
}

/* The 15-minute promise. Quieter than the confirmation itself, but it has to be
   readable — it is the only thing telling a customer that silence is a fault. */
.flow-confirm__promise {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid #86efac;
    font-size: 0.95rem !important;
}

.flow-confirm__promise a {
    color: #166534;
    text-decoration: underline;
    text-underline-offset: 2px;
    font-weight: 600;
}

.flow-confirm__promise a:focus-visible {
    outline: 2px solid #166534;
    outline-offset: 3px;
    border-radius: 2px;
}


/* ============================================================
   Booking could not be confirmed

   Deliberately not styled as a catastrophe. It is a setback with a one-tap
   route out, and the customer did nothing wrong — panic-red would make them
   abandon rather than send the email.
   ============================================================ */
.flow-failed {
    max-width: 600px;
    margin: 1rem auto;
    text-align: center;
    background: #fffbeb;
    border: 1px solid #fcd34d;
    border-radius: var(--radius);
    padding: 2.5rem 1.5rem;
}

.flow-failed__mark {
    width: 3rem;
    height: 3rem;
    border-radius: 50%;
    background: #d97706;
    color: #fff;
    font-family: var(--flow-sans);
    font-size: 1.6rem;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto 1rem;
}

.flow-failed h2 {
    font-family: var(--flow-sans);
    font-size: 1.7rem;
    font-weight: 700;
    color: #92400e;
    margin-bottom: 0.5rem;
}

.flow-failed p {
    font-family: var(--flow-sans);
    color: #78350f;
    font-size: 1.05rem;
    line-height: 1.6;
}

.flow-failed__action { margin: 1.5rem 0 0.75rem; }
.flow-failed__action .btn { width: 100%; max-width: 22rem; }

.flow-failed__alt {
    font-size: 0.95rem !important;
}
.flow-failed__alt a {
    color: #92400e;
    text-decoration: underline;
    text-underline-offset: 2px;
    font-weight: 600;
}

/* Quiet — it matters only if they get in touch. */
.flow-failed__ref {
    margin-top: 1.25rem;
    font-family: var(--flow-sans) !important;
    font-size: 0.75rem !important;
    color: #a16207 !important;
    word-break: break-all;
}


/* ============================================================
   BOOKING FLOW — Responsive (640px+)
   ============================================================ */
@media (min-width: 640px) {
    .choice-cards { flex-direction: row; }
    .choice-cards--condition { flex-direction: row; }
    .freq-options { grid-template-columns: repeat(4, 1fr); }

    .flow-title { font-size: 2.2rem; }
    .price-main { font-size: 3.75rem; }
}


/* ============================================================
   RESPONSIVE — Tablet (640px+)
   ============================================================ */
@media (min-width: 640px) {
    .section-title {
        font-size: 2rem;
    }

    /* Hero: side by side */
    .hero {
        flex-direction: row;
        align-items: center;
        padding: 4rem 2rem;
        gap: 3rem;
    }

    .hero__skyline {
        height: 200px;
    }

    .hero__content {
        flex: 1;
        max-width: none;
    }

    .hero__headline {
        font-size: 2.4rem;
    }

    .hero__image {
        flex: 1;
    }

    .hero__image img {
        max-height: 360px;
    }

    .hero__logo {
        max-width: 320px;
    }

    /* Steps: 02 across the top, the handset centre, 01 and 03 flanking it.

       Grid areas rather than DOM order, so the markup stays 01 → 02 → 03 →
       handset. A keyboard or screen-reader user gets the steps in their
       numbered order; only the painting is rearranged.

       COLUMNS ARE CAPPED, NOT 1fr. With 1fr the two outer columns swallowed
       every spare pixel and kept growing with the viewport, so the group never
       had a settled shape. Capped and centred, 01 and 03 are the same width as
       each other at every screen size and the gap alone decides how far they
       sit from the handset. */
    .steps {
        display: grid;
        grid-template-columns: minmax(0, 26rem) auto minmax(0, 26rem);
        grid-template-areas:
            "two  two   two"
            "one  ring  three";
        justify-content: center;
        /* START, not center: 01 and 03 begin at the same y, so their number
           badges and headings sit on one horizontal line. Centring them made
           the two headings drift apart whenever the copy below ran to a
           different number of lines. The handset opts back out below. */
        align-items: start;
        /* The column gap is what sets 01 and 03 back from the handset. It is
           one value for both sides, so the two distances cannot drift apart. */
        gap: 1.75rem 4rem;
        text-align: center;
    }
    /* The handset stays vertically centred against the steps beside it. */
    .steps__ring { align-self: center; }

    /* .step is a flex column; this centres the number badge and heading with
       the copy instead of leaving them hard against the left edge. */
    .step { align-items: center; }

    /* Headings and body both centre. width:100% so each line is centred
       against the full column rather than against the paragraph's own
       shrink-to-fit box — otherwise a short step and a long one sit on
       different axes and the row looks lopsided. */
    .step p,
    .step__link {
        width: 100%;
        text-align: center;
    }
    .step:nth-of-type(1) { grid-area: one; }
    /* 02 spans all three columns rather than being confined to the middle one
       and overflowing it — same centring, without relying on overflow. */
    .step:nth-of-type(2) {
        grid-area: two;
        max-width: 34rem;
        margin: 0 auto 0.25rem;
    }
    .step:nth-of-type(3) { grid-area: three; }
    .steps__ring         { grid-area: ring; }

    /* Before/After grid: two pairs per row on tablet+ */
    .ba-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 3rem 2rem;
    }

    .work-strip__grid {
        grid-template-columns: repeat(4, 1fr);
        gap: 1rem;
    }

    /* Footer: side by side */
    .footer__inner {
        flex-direction: row;
        justify-content: space-between;
        align-items: flex-start;
    }

    .footer__copy {
        text-align: center;
    }
}


/* ============================================================
   RESPONSIVE — Desktop (1024px+)
   ============================================================ */
@media (min-width: 1024px) {
    /* Show full nav, hide hamburger */
    .nav__links {
        display: flex;
    }

    .nav__hamburger {
        display: none;
    }

    /* Larger hero */
    .hero {
        padding: 5rem 2rem;
        gap: 5rem;
    }

    .hero__headline {
        font-size: 3rem;
    }

    .hero__skyline {
        height: 260px;
    }

    .hero__image img {
        max-height: 420px;
    }

    .hero__logo {
        max-width: 360px;
    }

    .section-title {
        font-size: 2.25rem;
    }
}


/* ============================================================
   SMS CONSENT — A2P 10DLC
   Sits inside .quote-section, so --flow-sans is in scope.

   Deliberately plain. This block is read by carrier reviewers as well as
   customers, so the disclosure text stays fully visible at all times: never
   collapsed behind a "show more", never shrunk below 0.8rem, and never
   restyled so an unchecked box is hard to distinguish from a checked one.
   ============================================================ */
.sms-consent {
    margin-bottom: 1.5rem;
    padding: 1.15rem 1.15rem 0.9rem;
    background: var(--color-bg-light);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
}

/* The benefit, above the boxes and outside every label. See the comment on the
   block in index.html for why that distinction is the whole point. */
.sms-consent__intro {
    margin-bottom: 1rem;
}

.sms-consent__title {
    margin: 0 0 0.3rem;
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--color-dark);
}

.sms-consent__lede {
    margin: 0;
    font-family: var(--flow-sans);
    font-size: 0.84rem;
    line-height: 1.55;
    color: var(--color-text);
}

.sms-consent__opt {
    display: flex;
    align-items: flex-start;
    gap: 0.7rem;
    font-family: var(--flow-sans);
    font-size: 0.82rem;
    line-height: 1.55;
    color: var(--color-text);
    cursor: pointer;
}

/* WEIGHT, NOT SIZE. The disclosure text may never drop below 0.8rem — a
   carrier reviewer reads the secondary box too — so the hierarchy is carried
   by the card, the border and the control, and the promotional label keeps the
   same 0.82rem it always had. */
.sms-consent__opt--primary {
    padding: 0.85rem 0.9rem;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    font-size: 0.86rem;
    color: var(--color-dark);
}

/* Checked is visibly different from unchecked at a glance, which the block
   comment requires — here it is the border and ground, not just the tick. */
.sms-consent__opt--primary:hover {
    border-color: var(--color-accent-hover);
}

.sms-consent__opt--primary:has(input:checked) {
    border-color: var(--color-dark);
    box-shadow: inset 0 0 0 1px var(--color-dark);
}

.sms-consent__opt--secondary {
    margin-top: 0.85rem;
    padding-top: 0.85rem;
    border-top: 1px solid var(--color-border);
    color: var(--color-text-muted);
}

/* 1.05rem box with padding around it clears the 44px touch target on mobile
   without making the control look oversized next to the body text. */
.sms-consent__opt input[type="checkbox"] {
    flex: 0 0 auto;
    width: 1.05rem;
    height: 1.05rem;
    margin: 0.18rem 0 0;
    accent-color: var(--color-dark);
    cursor: pointer;
}

.sms-consent__opt--primary input[type="checkbox"] {
    width: 1.25rem;
    height: 1.25rem;
    margin-top: 0.1rem;
}

.sms-consent-links {
    margin-top: 0.9rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--color-border);
    font-family: var(--flow-sans);
    font-size: 0.78rem;
    color: var(--color-text-muted);
}

.sms-consent-links a {
    color: var(--color-text-muted);
    text-decoration: underline;
}

.sms-consent-links a:hover {
    color: var(--color-dark);
}

.footer__legal {
    margin-bottom: 0.5rem;
}

.footer__legal a {
    color: #9ca3af;
    text-decoration: underline;
}

.footer__legal a:hover {
    color: #d1d5db;
}


/* ============================================================
   LEGAL PAGES — /privacy, /sms-terms
   Standalone pages: --flow-sans is NOT in scope here, so these rules use the
   global --font. One readable column, no ornament.
   ============================================================ */
.legal {
    max-width: 44rem;
    margin: 0 auto;
    padding: 3rem 1.25rem 4rem;
    font-family: var(--font);
}

.legal__title {
    font-size: 2rem;
    font-weight: 800;
    color: var(--color-dark);
    line-height: 1.2;
    margin-bottom: 0.4rem;
}

.legal__updated {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin-bottom: 2rem;
}

.legal__lede {
    font-size: 1.02rem;
    color: var(--color-text);
    margin-bottom: 2rem;
}

.legal h2 {
    font-size: 1.2rem;
    font-weight: 700;
    color: var(--color-dark);
    margin: 2.25rem 0 0.75rem;
}

.legal h3 {
    font-size: 1rem;
    font-weight: 600;
    color: var(--color-dark);
    margin: 1.5rem 0 0.5rem;
}

.legal p {
    margin-bottom: 1rem;
    color: var(--color-text);
}

.legal ul {
    margin: 0 0 1.25rem 1.25rem;
    color: var(--color-text);
}

.legal li {
    margin-bottom: 0.5rem;
}

.legal a {
    color: var(--color-dark);
    text-decoration: underline;
}

/* The mobile-information paragraph and the STOP instruction. Carrier reviewers
   look for these specifically; the callout keeps them findable at a glance. */
.legal__callout {
    padding: 1.1rem 1.25rem;
    background: var(--color-bg-light);
    border-left: 3px solid var(--color-dark);
    border-radius: 0 var(--radius) var(--radius) 0;
    margin-bottom: 1.25rem;
}

@media (min-width: 768px) {
    .legal {
        padding: 4rem 2rem 5rem;
    }

    .legal__title {
        font-size: 2.5rem;
    }
}


/* ============================================================
   JOB TICKET  (/j)
   ------------------------------------------------------------
   The only surface a contractor ever touches, and the customer's record of
   what they bought. Mobile-first and deliberately plain: it is read standing
   on a doorstep, one-handed, often on a bad connection.
   ============================================================ */
.ticket-body {
    background: var(--color-bg-light);
    min-height: 100vh;
}

.ticket {
    max-width: 460px;
    margin: 0 auto;
    padding: 1rem 1.15rem 3rem;
}

.ticket__bar {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.9rem 0;
    border-bottom: 1px solid var(--color-border);
    margin-bottom: 1.15rem;
}

.ticket__brand {
    font-family: 'Times New Roman', Times, serif;
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-dark);
    text-decoration: none;
}

.ticket__role {
    margin-left: auto;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.ticket__state {
    color: var(--color-text-muted);
    font-size: 0.95rem;
    line-height: 1.6;
    padding: 1.5rem 0;
}

/* Shown only when the trip to the server failed, never when the link itself is
   unknown — there is nothing to try again for a link that does not exist. */
.tk-retry {
    display: inline-block;
    margin-top: 1rem;
    font-size: 0.95rem;
}

/* The date and the time are the first thing anyone opens a ticket to find, so
   they lead — but one line should not shout over the whole page. The date used
   to be the smallest text on the ticket at 0.7rem while the time ran at
   1.75rem; now that the prose summary no longer repeats them, they carry the
   answer between them and are sized closer together. */
.tk-when { margin-bottom: 1.25rem; }
.tk-day {
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-accent);
    margin-bottom: 0.25rem;
}
.tk-time {
    font-size: 1.45rem;
    font-weight: 700;
    color: var(--color-dark);
    line-height: 1.2;
}
.tk-sub {
    font-size: 0.88rem;
    color: var(--color-text-muted);
    margin-top: 0.2rem;
}

.tk-blk {
    border-top: 1px solid var(--color-border);
    padding-top: 0.85rem;
    margin-top: 0.85rem;
}
.tk-blk--flag { border-top-color: #d97706; }

/* --- Ticket sections (r13) --------------------------------------------
   The client ticket became five sections rather than one list: booking, the
   home, instructions, access, partner. A section is a heading and a rule; the
   blocks inside it keep the styling they already had. */
.tk-sec {
    margin-top: 1.75rem;
    padding-top: 1.25rem;
    border-top: 2px solid var(--color-dark);
}
.tk-sec__h {
    font-family: var(--flow-sans);
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-dark);
    margin: 0 0 0.25rem;
}
.tk-sec__sub {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    line-height: 1.5;
    margin: 0 0 0.9rem;
}

/* Rooms. <details> rather than a JS accordion: keyboard-operable and
   screen-reader-announced for free, and it still opens if the script that
   renders the save button somehow fails. */
.tk-room {
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    margin-bottom: 0.5rem;
    background: #fff;
}
.tk-room[open] { border-color: var(--color-accent); }
.tk-room__sum {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.7rem 0.85rem;
    cursor: pointer;
    list-style: none;
}
.tk-room__sum::-webkit-details-marker { display: none; }
.tk-room__name {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--color-dark);
}
.tk-room__flag {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

/* The editable box. Present on every instruction the customer owns. */
.tk-edit { padding: 0 0.85rem 0.85rem; }
.tk-sec > .tk-edit { padding-left: 0; padding-right: 0; }
.tk-edit__lbl {
    display: block;
    font-size: 0.63rem;
    font-weight: 700;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin-bottom: 0.35rem;
}
.tk-edit__box {
    width: 100%;
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    line-height: 1.5;
    color: var(--color-text);
    padding: 0.6rem 0.7rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    resize: vertical;
}
.tk-edit__box:focus {
    outline: none;
    border-color: var(--color-dark);
    box-shadow: 0 0 0 3px rgba(31, 41, 55, 0.12);
}
.tk-edit__row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.5rem;
}
.tk-edit__save {
    font-family: var(--flow-sans);
    font-size: 0.85rem;
    font-weight: 600;
    color: #fff;
    background: var(--color-dark);
    border: none;
    border-radius: var(--radius);
    padding: 0.45rem 1rem;
    cursor: pointer;
}
.tk-edit__save:hover:not(:disabled) { background: var(--color-accent); }
.tk-edit__save:disabled { opacity: 0.55; cursor: default; }

/* Save state. Three of them, because "not saved yet" is the one that matters:
   a note typed and abandoned is the common failure, and silence would let the
   customer believe it was kept. */
.tk-save {
    font-size: 0.78rem;
    color: var(--color-text-muted);
}
.tk-save.is-ok    { color: #166534; font-weight: 600; }
.tk-save.is-bad   { color: #b91c1c; font-weight: 600; }
.tk-save.is-dirty { color: #b45309; }

/* Partner card, client side. Company only — no individual is named here; see
   the joint-employer note in webhook.gs. */
.tk-partner {
    background: var(--color-bg-light);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 0.9rem 1rem;
}
.tk-partner__name {
    font-family: var(--flow-sans);
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--color-dark);
    /* Logo and name centre as a masthead; the description and facts below
       stay left, because centring a paragraph that wraps costs more in
       readability than it gains in symmetry. */
    text-align: center;
}
.tk-partner__desc {
    font-size: 0.9rem;
    line-height: 1.55;
    color: var(--color-text);
    margin: 0.35rem 0 0;
}
.tk-partner__facts {
    font-size: 0.8rem;
    color: var(--color-text-muted);
    margin-top: 0.5rem;
}

/* The partner's own logo. Contain, never cover: a cropped logo is a worse
   look than a small one, and it is somebody else's brand. */
.tk-partner__logo {
    display: block;
    max-width: 140px;
    /* 56, not 44: a wide wordmark is bound by max-width and never
       reaches this, while a square-ish mark is bound by it and at 44 was
       rendering as a stamp. */
    max-height: 56px;
    width: auto;
    height: auto;
    object-fit: contain;
    margin: 0 auto 0.6rem;
}

/* ---- No partner assigned yet -------------------------------------------
   Every preference booking lands here, not just the odd one: a booking with
   no specific time has no availability row to consume, so there is nobody to
   consume it. The old copy was a passive sentence; this shows the work.

   Everything animated is decorative and aria-hidden in the markup, and all
   of it stops under prefers-reduced-motion. */
.tk-seek {
    background: var(--color-bg-light);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 1.5rem 1rem 1.2rem;
    text-align: center;
}
/* Used on THREE surfaces now: the partner card (/j and /h), How Serve Works,
   and the booking confirmation. The `tk-` prefix is where it was born, not a
   claim about where it belongs. Size travels through custom properties so
   there is one set of keyframes rather than three. */
.tk-seek__stage {
    --ring-w: 54px;
    --ring-h: 94px;
    --ring-mark: 32px;
    --ring-reach: 46px;
    position: relative;
    height: 108px;
    margin-bottom: 0.95rem;
    display: flex;
    align-items: center;
    justify-content: center;
}
/* Confirmation: a flourish under the tick, not a second headline. */
.tk-seek__stage--sm {
    --ring-w: 40px;
    --ring-h: 70px;
    --ring-mark: 24px;
    --ring-reach: 34px;
    height: 84px;
    margin: 1.1rem 0 0;
}
/* How Serve Works: the centrepiece of the three steps, still restrained. */
.tk-seek__stage--mid {
    --ring-w: 62px;
    --ring-h: 108px;
    --ring-mark: 36px;
    --ring-reach: 54px;
    height: 124px;
    margin: 0;
}

/* A rounded rect is enough. A drawn handset at this size reads as clip art. */
.tk-seek__phone {
    position: relative;
    z-index: 1;
    width: var(--ring-w);
    height: var(--ring-h);
    padding: 6px 5px;
    border-radius: 12px;
    background: var(--color-dark);
    box-shadow: 0 6px 18px rgba(31, 41, 55, 0.24);
    transform-origin: 50% 88%;
    animation: tk-ring 2.6s ease-in-out infinite;
}
.tk-seek__screen {
    width: 100%;
    height: 100%;
    border-radius: 7px;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}
.tk-seek__mark {
    display: block;
    width: var(--ring-mark);
    height: var(--ring-mark);
}

/* Sound leaving the handset: an arc each side, twice, staggered. */
.tk-seek__wave {
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--ring-reach);
    height: var(--ring-reach);
    margin: calc(var(--ring-reach) / -2) 0 0 calc(var(--ring-reach) / -2);
    border: 2px solid transparent;
    border-radius: 50%;
    opacity: 0;
    animation: tk-wave 2.6s ease-out infinite;
}
.tk-seek__wave--l { border-left-color:  var(--color-dark); }
.tk-seek__wave--r { border-right-color: var(--color-dark); }
.tk-seek__wave.is-2 { animation-delay: 0.3s; }

/* The negotiation, made visible. Green because it is money moving, and it is
   Serve paying the partner — not the customer paying again. */
.tk-seek__coin {
    position: absolute;
    top: 50%;
    left: 50%;
    font-family: var(--flow-sans);
    font-size: 0.85rem;
    font-weight: 700;
    color: #15803d;
    opacity: 0;
    animation: tk-coin 3.2s ease-out infinite;
}
.tk-seek__coin.is-1 { margin-left:  30px; animation-delay: 0.25s; }
.tk-seek__coin.is-2 { margin-left: -38px; animation-delay: 1.35s; }
.tk-seek__coin.is-3 { margin-left:  12px; animation-delay: 2.25s; }

.tk-seek__title {
    font-family: var(--flow-sans);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--color-dark);
}
.tk-seek__msg {
    font-size: 0.88rem;
    line-height: 1.55;
    color: var(--color-text);
    margin: 0.4rem auto 0;
    max-width: 30rem;
}

@keyframes tk-ring {
    0%, 60%, 90%, 100% { transform: rotate(0deg); }
    63% { transform: rotate(-8deg); }
    67% { transform: rotate(7deg); }
    71% { transform: rotate(-6deg); }
    75% { transform: rotate(5deg); }
    79% { transform: rotate(-3deg); }
    84% { transform: rotate(2deg); }
}
@keyframes tk-wave {
    0%, 58%   { transform: scale(0.55); opacity: 0; }
    66%       { opacity: 0.5; }
    92%, 100% { transform: scale(2.1);  opacity: 0; }
}
@keyframes tk-coin {
    0%        { transform: translateY(4px)   scale(0.8); opacity: 0; }
    18%       { opacity: 0.95; }
    100%      { transform: translateY(-52px) scale(1.05); opacity: 0; }
}

/* Static, not merely slower. A paused ring frame is not a design. */
@media (prefers-reduced-motion: reduce) {
    .tk-seek__phone { animation: none; }
    .tk-seek__wave,
    .tk-seek__coin  { display: none; }
}

/* Scope list, partner side. Read-only: the crew does the work, the customer
   decides what the work is. */
.tk-scope {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.55rem 0;
    border-bottom: 1px solid var(--color-border);
}
.tk-scope:last-child { border-bottom: none; }
.tk-scope__name {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--color-dark);
}
.tk-scope__ins {
    font-size: 0.88rem;
    line-height: 1.5;
    color: var(--color-text);
    white-space: pre-line;
}

.tk-lbl {
    font-size: 0.63rem;
    font-weight: 700;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin-bottom: 0.3rem;
}
.tk-val {
    font-size: 0.95rem;
    color: var(--color-text);
    line-height: 1.55;
    /* Values arrive from a hand-typed sheet and may carry real line breaks. */
    white-space: pre-line;
}
.tk-note {
    font-size: 0.8rem;
    color: var(--color-text-muted);
    line-height: 1.5;
    margin-top: 0.35rem;
}
.tk-money {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--color-dark);
    line-height: 1.15;
}

/* Big enough to hit on a doorstep in the cold. */
.tk-tap {
    display: block;
    margin-top: 0.6rem;
    padding: 0.75rem 0.9rem;
    min-height: 46px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: #fff;
    color: var(--color-dark);
    font-size: 0.92rem;
    font-weight: 600;
    text-decoration: none;
}

.ticket__foot {
    margin-top: 2rem;
    padding-top: 1rem;
    border-top: 1px solid var(--color-border);
    font-size: 0.85rem;
    color: var(--color-text-muted);
}
.ticket__foot a { color: var(--color-accent); }

/* ============================================================
   SERVE — PRICING & SERVICES
   Added with the published-pricing build.

   Everything here is namespaced `serve-*`. The site already carries three
   naming conventions (`.nav__`, `.flow-`, `.svmA-`), and a fourth prefix is
   cheaper than auditing which of them a bare `.card` would collide with.

   Shared by index.html, pricing.html and services.html.
   ============================================================ */

:root {
    /* The accent is the existing brand gray, not a new colour. Both forms are
       needed: the hex for text and borders, the raw channels for rgba() inside
       keyframes, which cannot read a hex custom property. */
    --serve-accent:     var(--color-accent);   /* #4b5563 */
    --serve-accent-rgb: 75, 85, 99;
}

/* Visually hidden, still announced. Used for the matrix legend on each mark,
   where the glyph alone means nothing to a screen reader. */
.serve-sr {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* Every anchor target clears the sticky header. */
[id] { scroll-margin-top: 80px; }


/* ------------------------------------------------------------
   Inline links — annotation, not button

   Used inside How It Works prose. Italic and dotted so it reads as a footnote
   the eye can skip; the funnel's job there is still the form below it.
   ------------------------------------------------------------ */
.serve-inline-link {
    font-style: italic;
    color: var(--serve-accent);
    text-decoration: none;
    border-bottom: 1px dotted currentColor;
    padding-bottom: 1px;
    transition: border-bottom-color 0.18s ease, color 0.18s ease;
}

.serve-inline-link:hover {
    border-bottom-style: solid;
    color: var(--color-dark);
}

.serve-inline-link:focus-visible {
    outline: 2px solid var(--serve-accent);
    outline-offset: 3px;
    border-radius: 2px;
}


/* ------------------------------------------------------------
   CTA pulse

   A halo that expands and fades — never `transform: scale`. Scaling a button
   that carries a text label shifts it off the pixel grid, and the label goes
   visibly soft halfway through every cycle. The halo moves no type at all.

   Both CTAs that carry this sit on the dark navy (the nav bar and the hero),
   so the halo is light. `--on-light` is here for a pulse placed on a pale
   section later; a gray halo on a gray button on white is invisible.

   The existing box-shadows are repeated inside both keyframes. `box-shadow` is
   one property: animating it without the insets would drop the button's own
   bevel for the whole cycle.
   ------------------------------------------------------------ */
@keyframes serve-breathe-hero {
    0%, 100% {
        box-shadow:
            inset 0 1px 0 rgba(255, 255, 255, 0.10),
            inset 0 -1px 0 rgba(0, 0, 0, 0.15),
            0 2px 8px rgba(0, 0, 0, 0.25),
            0 0 0 0 rgba(255, 255, 255, 0.42);
    }
    50% {
        box-shadow:
            inset 0 1px 0 rgba(255, 255, 255, 0.10),
            inset 0 -1px 0 rgba(0, 0, 0, 0.15),
            0 2px 8px rgba(0, 0, 0, 0.25),
            0 0 0 14px rgba(255, 255, 255, 0);
    }
}

@keyframes serve-breathe-nav {
    0%, 100% {
        box-shadow:
            inset 0 1px 0 rgba(255, 255, 255, 0.10),
            inset 0 -1px 0 rgba(0, 0, 0, 0.15),
            0 2px 6px rgba(0, 0, 0, 0.25),
            0 0 0 0 rgba(255, 255, 255, 0.38);
    }
    50% {
        box-shadow:
            inset 0 1px 0 rgba(255, 255, 255, 0.10),
            inset 0 -1px 0 rgba(0, 0, 0, 0.15),
            0 2px 6px rgba(0, 0, 0, 0.25),
            0 0 0 10px rgba(255, 255, 255, 0);
    }
}

@keyframes serve-breathe-light {
    0%, 100% { box-shadow: 0 0 0 0  rgba(var(--serve-accent-rgb), 0.38); }
    50%      { box-shadow: 0 0 0 12px rgba(var(--serve-accent-rgb), 0); }
}

/* 1.2s delay: the page should be still when someone arrives, then move once. */
.serve-pulse             { animation: serve-breathe-hero  3.2s ease-in-out 1.2s infinite; }
.nav__cta.serve-pulse    { animation: serve-breathe-nav   3.2s ease-in-out 1.2s infinite; }
.serve-pulse--on-light   { animation: serve-breathe-light 3.2s ease-in-out 1.2s infinite; }

/* Stop conditions. Listed per selector rather than with !important, because
   `.nav__cta.serve-pulse` outweighs a bare `.serve-pulse:hover`.

   Hover and focus stop it because a button still pulsing under the pointer
   reads as unresponsive — the ambient motion fights the hover state.
   `.is-retired` is set by serveRetirePulse() once the visitor touches the
   quote form: the pulse means "notice me", and it has been noticed. */
.serve-pulse:hover,
.serve-pulse:focus-visible,
.serve-pulse.is-retired,
.nav__cta.serve-pulse:hover,
.nav__cta.serve-pulse:focus-visible,
.nav__cta.serve-pulse.is-retired,
.serve-pulse--on-light:hover,
.serve-pulse--on-light:focus-visible,
.serve-pulse--on-light.is-retired {
    animation: none;
}

@media (prefers-reduced-motion: reduce) {
    .serve-pulse,
    .nav__cta.serve-pulse,
    .serve-pulse--on-light { animation: none; }
}


/* ------------------------------------------------------------
   Arrival cue — why the page just moved
   ------------------------------------------------------------ */
@keyframes serve-arrive-fade {
    0%   { background-size: 100% 100%; }
    100% { background-size: 100% 0%; }
}

.serve-arrive {
    background-image: linear-gradient(rgba(var(--serve-accent-rgb), 0.16),
                                      rgba(var(--serve-accent-rgb), 0.16));
    background-repeat: no-repeat;
    background-position: 0 100%;
    animation: serve-arrive-fade 1.4s ease-out forwards;
    border-radius: 3px;
}

@media (prefers-reduced-motion: reduce) {
    .serve-arrive { animation: none; background-image: none; }
}


/* ------------------------------------------------------------
   Shared price table

   One set of rules for the homepage teaser and the full /pricing table. Below
   640px both collapse to stacked cards — a four-column table of dollar ranges
   is unreadable on a phone, and this site is mostly read on phones.
   ------------------------------------------------------------ */
.serve-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.serve-table {
    width: 100%;
    border-collapse: collapse;
    font-family: var(--font);
    text-align: left;
}

.serve-table thead th {
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    padding: 0 0.5rem 0.75rem;
    border-bottom: 1px solid var(--color-border);
    vertical-align: bottom;
}

.serve-table thead th span {
    display: block;
    font-size: 0.72rem;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: var(--color-text-muted);
}

.serve-table tbody tr + tr { border-top: 1px solid var(--color-border); }

.serve-size {
    font-size: 0.98rem;
    font-weight: 600;
    color: var(--color-dark);
    padding: 0.9rem 0.75rem 0.9rem 0;
    white-space: nowrap;
}

.serve-cell { padding: 0.4rem 0.25rem; }

.serve-cell--empty {
    padding: 0.9rem 0.75rem;
    color: var(--color-text-muted);
}

/* Every price is a real button: keyboard-reachable, announced as actionable,
   and it loads that exact configuration into the calculator. */
.serve-cell__btn {
    display: block;
    width: 100%;
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius);
    padding: 0.5rem 0.65rem;
    font-family: var(--font);
    text-align: left;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.serve-cell__price {
    display: block;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--color-dark);
    white-space: nowrap;
}

.serve-cell__go {
    display: block;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--serve-accent);
    opacity: 0;
    transition: opacity 0.15s ease;
}

.serve-cell__btn:hover {
    background: var(--color-bg-light);
    border-color: var(--color-border);
}

.serve-cell__btn:hover .serve-cell__go { opacity: 1; }

.serve-cell__btn:focus-visible {
    outline: 2px solid var(--serve-accent);
    outline-offset: 2px;
    background: var(--color-bg-light);
}

.serve-cell__btn:focus-visible .serve-cell__go { opacity: 1; }

/* The recurring column is the pitch, so it gets the emphasis. */
.serve-table td.serve-cell--feature { background: rgba(var(--serve-accent-rgb), 0.05); }
.serve-table th.serve-col--feature  { color: var(--color-dark); }

.serve-table-note {
    margin-top: 0.9rem;
    font-size: 0.85rem;
    color: var(--color-text-muted);
    font-family: var(--font);
}

/* The recurring-vs-one-time comparison. It is the whole recurring pitch and
   used to surface only after a visitor had finished the calculator. */
.serve-callout {
    margin-top: 2rem;
    padding: 1.5rem;
    background: var(--color-bg-light);
    border-left: 3px solid var(--serve-accent);
    border-radius: 0 var(--radius) var(--radius) 0;
}

.serve-callout h3 {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--color-dark);
    margin-bottom: 0.5rem;
}

.serve-callout p {
    font-size: 0.95rem;
    line-height: 1.7;
    margin: 0;
}

.serve-callout strong { color: var(--color-dark); }


/* ------------------------------------------------------------
   Homepage teasers
   ------------------------------------------------------------ */
.serve-teaser {
    padding: 3.5rem 1.25rem;
    border-top: 1px solid var(--color-border);
}

.serve-teaser--pricing  { background: var(--color-bg); }
.serve-teaser--services { background: var(--color-bg-light); }

.serve-teaser__inner {
    max-width: 1100px;
    margin: 0 auto;
}

.serve-teaser__title {
    font-size: 1.75rem;
    font-weight: 800;
    color: var(--color-dark);
    text-align: center;
    margin-bottom: 0.5rem;
}

.serve-teaser__sub {
    font-size: 1rem;
    color: var(--color-text-muted);
    text-align: center;
    max-width: 560px;
    margin: 0 auto 2rem;
}

.serve-teaser__table {
    max-width: 460px;
    margin: 0 auto;
}

.serve-teaser .serve-table-note { text-align: center; }

.serve-teaser__cta {
    margin-top: 1.75rem;
    text-align: center;
}

.serve-teaser__cta a {
    font-weight: 600;
    color: var(--color-dark);
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 2px;
    transition: border-color 0.15s ease;
}

.serve-teaser__cta a:hover { border-bottom-color: var(--color-dark); }

.serve-teaser__cta a:focus-visible {
    outline: 2px solid var(--serve-accent);
    outline-offset: 3px;
    border-radius: 2px;
}


/* ------------------------------------------------------------
   Service cards — teaser and /services share these
   ------------------------------------------------------------ */
.serve-cards {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
}

.serve-card {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 1.5rem;
    display: flex;
    flex-direction: column;
}

.serve-card__badge {
    align-self: flex-start;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #fff;
    background: var(--color-dark);
    border-radius: 999px;
    padding: 0.25rem 0.7rem;
    margin-bottom: 0.75rem;
}

/* Move In / Move Out carries no discount. An outlined badge naming who it is
   for keeps the three cards aligned and says something true, which an empty
   spacer would not. */
.serve-card__badge--quiet {
    color: var(--color-text-muted);
    background: none;
    border: 1px solid var(--color-border);
}

.serve-card__title {
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-dark);
    margin-bottom: 0.5rem;
}

.serve-card__lede {
    font-size: 0.92rem;
    color: var(--color-text-muted);
    margin-bottom: 1rem;
}

.serve-card__list {
    list-style: none;
    margin: 0 0 1.25rem;
    padding: 0;
    font-size: 0.92rem;
}

.serve-card__list li {
    position: relative;
    padding-left: 1.4rem;
    margin-bottom: 0.5rem;
    line-height: 1.55;
}

.serve-card__list li::before {
    content: "\2713";
    position: absolute;
    left: 0;
    color: var(--serve-accent);
    font-weight: 700;
}

.serve-card__foot {
    margin-top: auto;
    padding-top: 1rem;
    border-top: 1px solid var(--color-border);
}

.serve-card__range {
    display: block;
    font-size: 0.82rem;
    color: var(--color-text-muted);
    margin-bottom: 0.75rem;
}

.serve-card__range strong {
    display: block;
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-dark);
}


/* ------------------------------------------------------------
   Sub-page furniture — /pricing and /services
   ------------------------------------------------------------ */
.serve-page-head {
    background: var(--color-dark);
    color: #fff;
    padding: 3rem 1.25rem 2.5rem;
    text-align: center;
}

.serve-page-head h1 {
    font-family: 'Times New Roman', Times, serif;
    font-size: 2rem;
    font-weight: 800;
    color: #f9fafb;
    line-height: 1.2;
    margin-bottom: 0.75rem;
}

.serve-page-head p {
    color: #9ca3af;
    max-width: 560px;
    margin: 0 auto;
    line-height: 1.7;
}

/* The answer, before anything else. Someone who clicked "See full pricing"
   gets the number without reading a word of preamble. */
.serve-band {
    background: var(--color-bg-light);
    border-bottom: 1px solid var(--color-border);
    padding: 2rem 1.25rem;
    text-align: center;
}

.serve-band__label {
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    display: block;
    margin-bottom: 0.4rem;
}

.serve-band__figure {
    font-size: 2.6rem;
    font-weight: 800;
    color: var(--color-dark);
    line-height: 1.1;
    display: block;
}

.serve-band__note {
    font-size: 0.9rem;
    color: var(--color-text-muted);
    margin-top: 0.6rem;
}

.serve-section {
    padding: 3rem 1.25rem;
    border-top: 1px solid var(--color-border);
}

.serve-section--alt { background: var(--color-bg-light); }

.serve-section__inner {
    max-width: 1100px;
    margin: 0 auto;
}

.serve-section h2 {
    font-size: 1.5rem;
    font-weight: 800;
    color: var(--color-dark);
    margin-bottom: 0.5rem;
}

.serve-section__sub {
    color: var(--color-text-muted);
    margin-bottom: 1.75rem;
    max-width: 620px;
}

/* Plain definition lists — "what moves your price", add-ons, exclusions. */
.serve-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.serve-list li {
    position: relative;
    padding-left: 1.4rem;
    margin-bottom: 0.75rem;
    line-height: 1.6;
}

.serve-list li::before {
    content: "\2013";
    position: absolute;
    left: 0;
    color: var(--serve-accent);
}

.serve-list strong { color: var(--color-dark); }

.serve-list--excluded li::before {
    content: "\00d7";
    font-weight: 700;
}

.serve-note {
    margin-top: 1.25rem;
    font-size: 0.9rem;
    color: var(--color-text-muted);
}

/* FAQ */
.serve-faq details {
    border-bottom: 1px solid var(--color-border);
    padding: 1rem 0;
}

.serve-faq summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--color-dark);
    list-style: none;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
}

.serve-faq summary::-webkit-details-marker { display: none; }

.serve-faq summary::after {
    content: "+";
    font-size: 1.3rem;
    font-weight: 400;
    color: var(--serve-accent);
    flex-shrink: 0;
}

.serve-faq details[open] summary::after { content: "\2212"; }

.serve-faq summary:focus-visible {
    outline: 2px solid var(--serve-accent);
    outline-offset: 3px;
    border-radius: 2px;
}

.serve-faq p {
    margin-top: 0.75rem;
    color: var(--color-text);
    line-height: 1.7;
}


/* ------------------------------------------------------------
   Room-by-room matrix — collapsed by default

   Behind a <details> on purpose. It answers a real objection but nobody
   chooses a cleaner by reading an 18-row grid, and open by default it would
   push the CTA off the page.
   ------------------------------------------------------------ */
.serve-matrix-acc {
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: var(--color-bg);
    padding: 1rem 1.25rem;
}

.serve-matrix-acc summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--color-dark);
}

.serve-matrix-acc summary:focus-visible {
    outline: 2px solid var(--serve-accent);
    outline-offset: 3px;
    border-radius: 2px;
}

.serve-matrix-legend {
    margin: 1rem 0;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.serve-matrix {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.88rem;
    text-align: left;
}

.serve-matrix thead th {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    padding: 0 0.4rem 0.6rem;
    border-bottom: 1px solid var(--color-border);
}

.serve-matrix thead th:not(:first-child),
.serve-matrix__mark { text-align: center; }

.serve-matrix__group th {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--color-dark);
    background: var(--color-bg-light);
    padding: 0.6rem 0.5rem;
}

.serve-matrix__task {
    font-weight: 400;
    color: var(--color-text);
    padding: 0.55rem 0.5rem 0.55rem 0;
    line-height: 1.4;
}

.serve-matrix__mark {
    padding: 0.55rem 0.4rem;
    font-weight: 700;
}

.serve-matrix tbody tr + tr:not(.serve-matrix__group) { border-top: 1px solid var(--color-border); }

.serve-matrix__mark.is-yes   { color: var(--color-dark); }
.serve-matrix__mark.is-addon { color: var(--serve-accent); }
.serve-matrix__mark.is-no    { color: #d1d5db; }


/* ------------------------------------------------------------
   Sticky CTA — one tap back to the calculator from any scroll position
   ------------------------------------------------------------ */
.serve-sticky-cta {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 90;
    background: rgba(31, 41, 55, 0.97);
    border-top: 1px solid rgba(255, 255, 255, 0.12);
    padding: 0.75rem 1.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    flex-wrap: wrap;
}

.serve-sticky-cta__text {
    color: #d1d5db;
    font-size: 0.9rem;
    margin: 0;
}

.serve-sticky-cta .btn { padding: 0.6rem 1.5rem; }

/* Reserve the bar's height so it never covers the footer's last line. */
body.serve-has-sticky { padding-bottom: 5.5rem; }


/* ------------------------------------------------------------
   Responsive
   ------------------------------------------------------------ */

/* Phones: the price tables become stacked cards. The <thead> is hidden and
   each cell carries its column name in data-label. */
@media (max-width: 639px) {
    .serve-table thead { display: none; }

    .serve-table tbody tr {
        display: block;
        border: 1px solid var(--color-border);
        border-radius: var(--radius);
        padding: 0.75rem 0.9rem;
        margin-bottom: 0.75rem;
    }

    .serve-table tbody tr + tr { border-top: 1px solid var(--color-border); }

    .serve-size {
        display: block;
        padding: 0 0 0.5rem;
        font-size: 1.05rem;
        border-bottom: 1px solid var(--color-border);
        margin-bottom: 0.5rem;
    }

    .serve-cell { display: block; padding: 0.15rem 0; }

    .serve-cell::before {
        content: attr(data-label);
        display: block;
        font-size: 0.72rem;
        font-weight: 700;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        color: var(--color-text-muted);
        margin-bottom: 0.1rem;
    }

    .serve-cell__btn { padding: 0.35rem 0.5rem; }

    /* The affordance cannot rely on hover on a touch screen. */
    .serve-cell__go { opacity: 1; }

    .serve-matrix { font-size: 0.82rem; }
    .serve-matrix__task { padding-right: 0.25rem; }
    .serve-band__figure { font-size: 2.1rem; }
}

/* 380px — the narrowest phone worth supporting. */
@media (max-width: 400px) {
    .serve-page-head h1     { font-size: 1.65rem; }
    .serve-teaser__title    { font-size: 1.45rem; }
    .serve-band__figure     { font-size: 1.85rem; }
    .serve-teaser, .serve-section { padding-left: 1rem; padding-right: 1rem; }
    .serve-card             { padding: 1.15rem; }
    .serve-sticky-cta__text { display: none; }
}

@media (min-width: 640px) {
    .serve-teaser__title { font-size: 2rem; }
    .serve-section h2    { font-size: 1.75rem; }
    .serve-page-head h1  { font-size: 2.5rem; }
    .serve-band__figure  { font-size: 3rem; }
    .serve-size          { padding-left: 0.25rem; }
}

@media (min-width: 768px) {
    .serve-cards { grid-template-columns: repeat(3, 1fr); }
    .serve-page-head { padding: 4rem 1.25rem 3rem; }
    .serve-teaser, .serve-section { padding-top: 4rem; padding-bottom: 4rem; }
}


/* ============================================================
   SERVE AS A HOME COMPANY  ·  2026-09-07
   ============================================================
   The repositioning: Serve is the company, cleaning is its first service.
   Everything below is new surface for that story — the hero home card, the
   three ideas, the before/after argument, the job record, the service
   catalogue, the network, and the closer.

   No new palette. The existing grays carry it; what changes is the amount of
   air between things. The one recurring device is a thin architectural line —
   the roofline in the home card, the rules under the pillars, the connectors
   in the network diagram — because a home is drawn, not photographed, and a
   line drawing is the one visual that scales from a hero to a favicon without
   ever looking like stock photography.
   ============================================================ */

/* The line under the booking button. Quieter than .hero__sub — it is a
   reassurance about the click, not a second thing to read. */
.hero__note {
    font-size: 0.9rem;
    color: #9ca3af;
    margin: 0.85rem auto 0;
    max-width: 520px;
}

/* --- Explainer bubbles in How Serve Works ------------------------------
   The three-pillar block used to sit above this section repeating it. Two of
   those ideas were folded into the steps; the third needed more room than a
   step can spare, so it hides behind the phrase it explains.

   THE TRIGGER IS A BUTTON, NOT A SPAN. It is operable from the keyboard for
   free, it announces itself, and aria-expanded tells a screen reader what
   just happened. A dotted underline says "this does something" without
   borrowing the solid underline that means "this navigates" — the two sit in
   the same sentence here, so they must not look alike. */
.pop-trigger {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--color-dark);
    font-weight: 700;
    cursor: pointer;
    border-bottom: 2px dotted var(--color-accent);
    line-height: inherit;
}
.pop-trigger:hover { border-bottom-color: var(--color-dark); }
.pop-trigger[aria-expanded="true"] {
    background: var(--color-bg-light);
    border-bottom-style: solid;
}

/* Absolutely positioned so opening it does not shove the other two steps
   down the page, and stretched to the step's own width so it can never be
   the thing that causes a horizontal scrollbar on a phone. */
.step { position: relative; }
.pop-bubble {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    z-index: 5;
    margin-top: 0.5rem;
    background: var(--color-dark);
    color: #f9fafb;
    border-radius: 10px;
    padding: 1rem 2.25rem 1rem 1.1rem;
    box-shadow: 0 14px 30px -10px rgba(0, 0, 0, 0.45);
    text-align: left;
}
.pop-bubble[hidden] { display: none; }

/* The tail. Drawn with a rotated square rather than a border triangle so it
   inherits the bubble's background and stays one colour with it. */
.pop-bubble::before {
    content: "";
    position: absolute;
    top: -5px;
    left: 2rem;
    width: 12px;
    height: 12px;
    background: var(--color-dark);
    transform: rotate(45deg);
    border-radius: 2px;
}

.pop-bubble__title {
    font-family: var(--flow-sans);
    font-size: 1.1rem;
    font-weight: 700;
    margin: 0 0 0.4rem;
    color: #fff;
}
.pop-bubble__body {
    font-size: 0.92rem;
    line-height: 1.6;
    margin: 0;
    color: #d1d5db;
}
.pop-bubble__close {
    position: absolute;
    top: 0.5rem;
    right: 0.6rem;
    background: none;
    border: none;
    color: #9ca3af;
    font-size: 1.35rem;
    line-height: 1;
    padding: 0.15rem 0.35rem;
    cursor: pointer;
    border-radius: 4px;
}
.pop-bubble__close:hover { color: #fff; background: rgba(255, 255, 255, 0.08); }

/* The line under How Serve Works. It is the thesis of the whole page, so it
   gets its own rule and its own air rather than being a fourth paragraph. */
/* Mobile: the handset is the last thing in the flex column, a flourish after
   the three steps rather than an interruption between them. */
.steps__ring {
    display: flex;
    justify-content: center;
    align-items: center;
}


/* --- What Serve removes ----------------------------------------------- */
.swap {
    background: var(--color-bg-light);
    border-top: 1px solid var(--color-border);
    border-bottom: 1px solid var(--color-border);
    padding: 3.25rem 0;
}
.swap__grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 2.25rem;
    align-items: center;
}
.swap__label {
    font-family: var(--flow-sans);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin: 0 0 1rem;
}

/* Struck through, not crossed out with an icon. The line through the words is
   the whole graphic — it says "this list stops existing" without a single
   red X, which would read as a comparison chart. */
.swap__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.swap__list li {
    font-family: var(--flow-sans);
    font-size: 1rem;
    color: var(--color-text-muted);
    text-decoration: line-through;
    text-decoration-color: rgba(107, 114, 128, 0.55);
    text-decoration-thickness: 1px;
}

.swap__col--after { text-align: left; }
.swap__line {
    font-family: var(--flow-sans);
    font-size: 1.6rem;
    font-weight: 700;
    line-height: 1.25;
    color: var(--color-dark);
    margin: 0;
}
.swap__line--strong { font-size: 2rem; }
.swap__arrow {
    font-size: 1.35rem;
    color: var(--color-accent);
    margin: 0.5rem 0;
    line-height: 1;
}
.swap__cta { margin-top: 1.5rem; text-decoration: none; }

/* --- The job record --------------------------------------------------- */
.record { padding: 3.5rem 0; }
.record__grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 2.25rem;
    align-items: start;
}
.record__title { text-align: left; }
.record__lede {
    color: var(--color-text);
    line-height: 1.7;
    margin: 0 0 1.25rem;
}
.record__pull {
    font-family: var(--flow-sans);
    font-size: 1.35rem;
    font-weight: 700;
    line-height: 1.35;
    color: var(--color-dark);
    border-left: 3px solid var(--color-dark);
    padding-left: 1rem;
    margin: 0 0 1.25rem;
}
.record__note {
    font-size: 0.9rem;
    color: var(--color-text-muted);
    line-height: 1.6;
    margin: 0;
}

/* The ticket. Every field on it is one /j renders today, so it is styled as a
   document rather than as an illustration — a real thing shown at rest. */
.ticket {
    background: #fff;
    border: 1px solid var(--color-border);
    border-radius: 12px;
    padding: 1.25rem;
    box-shadow: var(--shadow-md);
}
.ticket__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-bottom: 0.85rem;
    border-bottom: 1px solid var(--color-border);
}
.ticket__brand {
    font-family: var(--flow-sans);
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-dark);
}
.ticket__id {
    font-family: var(--flow-sans);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}
.ticket__service {
    font-family: var(--flow-sans);
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--color-dark);
    margin: 1rem 0 0.15rem;
}
.ticket__when {
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    color: var(--color-text);
    margin: 0 0 1rem;
}
.ticket__addr {
    background: var(--color-bg-light);
    border-radius: var(--radius);
    padding: 0.75rem 0.85rem;
    margin-bottom: 1rem;
}
.ticket__street {
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--color-dark);
    margin: 0;
}
.ticket__specs {
    font-family: var(--flow-sans);
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin: 0.2rem 0 0;
}
.ticket__rows {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
}
.ticket__row {
    display: grid;
    grid-template-columns: 5.5rem 1fr;
    gap: 0.75rem;
    align-items: baseline;
}
.ticket__row dt {
    font-family: var(--flow-sans);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}
.ticket__row dd {
    margin: 0;
    font-family: var(--flow-sans);
    font-size: 0.9rem;
    color: var(--color-text);
    line-height: 1.55;
}
.ticket__row--notes dd { font-style: italic; }
.ticket__pill {
    display: inline-block;
    background: #ecfdf3;
    color: #166534;
    font-weight: 700;
    font-size: 0.8rem;
    border-radius: 999px;
    padding: 0.18rem 0.6rem;
}
.ticket__foot {
    margin: 1.1rem 0 0;
    padding-top: 0.85rem;
    border-top: 1px solid var(--color-border);
    font-family: var(--flow-sans);
    font-size: 0.78rem;
    color: var(--color-text-muted);
}

/* Held visually apart from the ticket above it, because one is real and one is
   not, and a reader must never have to work out which. */
.record__soon {
    margin-top: 1rem;
    border: 1px dashed var(--color-border);
    border-radius: 12px;
    padding: 0.9rem 1.1rem;
}
.record__soon-label {
    font-family: var(--flow-sans);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin: 0 0 0.5rem;
}
.record__soon-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 1.25rem;
}
.record__soon-list li {
    font-family: var(--flow-sans);
    font-size: 0.88rem;
    color: var(--color-text-muted);
}

/* --- The service catalogue -------------------------------------------- */
.catalogue {
    background: var(--color-bg-light);
    border-top: 1px solid var(--color-border);
    padding: 3.5rem 0;
}
.cat-grid {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
}
.cat-card {
    background: #fff;
    border: 1px solid var(--color-border);
    border-radius: 12px;
    padding: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

/* A "coming" card must not look pressable. No pointer, no hover, no link —
   the styling is the promise, and a card that invites a click it cannot
   honour is worse than no card. */
.cat-card__link {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    text-decoration: none;
    color: inherit;
}
.cat-card--live {
    border-color: var(--color-dark);
    box-shadow: var(--shadow);
}
.cat-card--live:hover { box-shadow: var(--shadow-md); }

.cat-card__icon { display: block; width: 30px; height: 30px; margin-bottom: 0.2rem; }
.cat-card__icon svg {
    width: 100%;
    height: 100%;
    stroke: var(--color-text-muted);
    stroke-linecap: round;
}
.cat-card--live .cat-card__icon svg { stroke: var(--color-dark); }

.cat-card__name {
    font-family: var(--flow-sans);
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-text-muted);
}
.cat-card--live .cat-card__name { color: var(--color-dark); }

.cat-card__status {
    font-family: var(--flow-sans);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}
.cat-card__status--live { color: #166534; }

.cat-card__blurb {
    font-family: var(--flow-sans);
    font-size: 0.88rem;
    line-height: 1.55;
    color: var(--color-text-muted);
}
.cat-card__go {
    font-family: var(--flow-sans);
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--color-accent);
    margin-top: 0.2rem;
}
.cat-card__link:hover .cat-card__go { color: var(--color-dark); }

.catalogue__foot {
    text-align: center;
    margin: 1.75rem auto 0;
    max-width: 560px;
    font-size: 0.92rem;
    color: var(--color-text-muted);
}
.catalogue__foot a { color: var(--color-accent); text-decoration: underline; }
.catalogue__foot a:hover { color: var(--color-dark); }

/* --- The network ------------------------------------------------------ */
.network { padding: 3.5rem 0; }
.network__grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 2rem;
    align-items: center;
}
.network__title { text-align: left; }
.network p {
    color: var(--color-text);
    line-height: 1.7;
    margin: 0 0 1rem;
}
.network__note {
    font-family: var(--flow-sans);
    font-size: 1.2rem;
    font-weight: 700;
    color: var(--color-dark);
}
.network__diagram { display: flex; justify-content: center; }
.network__diagram svg { width: 100%; max-width: 340px; height: auto; }
.nw-node {
    fill: #fff;
    stroke: var(--color-border);
    stroke-width: 1.5;
}
.nw-node--hub {
    fill: var(--color-dark);
    stroke: var(--color-dark);
}
.nw-hub-label {
    font-family: var(--flow-sans);
    font-size: 17px;
    font-weight: 700;
    fill: #fff;
}
.nw-line { stroke: var(--color-accent); stroke-width: 1.5; }
.nw-line--soft { stroke: var(--color-border); stroke-width: 1.5; }
.nw-foot {
    font-family: var(--flow-sans);
    font-size: 10px;
    font-weight: 600;
    fill: var(--color-text-muted);
}

/* --- The closer ------------------------------------------------------- */
.closer {
    background: var(--color-dark);
    color: #fff;
    padding: 4rem 1.25rem;
    text-align: center;
}
.closer__inner { max-width: 640px; margin: 0 auto; }
.closer__title {
    font-family: var(--flow-sans);
    font-size: 1.9rem;
    font-weight: 700;
    line-height: 1.25;
    color: #f9fafb;
    margin: 0 0 0.85rem;
    text-wrap: balance;
}
.closer__sub {
    font-size: 1.05rem;
    color: #9ca3af;
    margin: 0 0 1.75rem;
    line-height: 1.7;
}
.closer__cta { text-decoration: none; }
.closer__where {
    margin: 1.25rem 0 0;
    font-size: 0.9rem;
    color: #9ca3af;
}

/* --- Wider screens ---------------------------------------------------- */
@media (min-width: 720px) {
    .swap__grid     { grid-template-columns: 1.05fr 1fr; gap: 3.5rem; }
    .record__grid   { grid-template-columns: 1fr 1fr; gap: 3rem; }
    .network__grid  { grid-template-columns: 1.15fr 1fr; gap: 3rem; }
    .closer__title  { font-size: 2.4rem; }
    .swap__line     { font-size: 1.9rem; }
    .swap__line--strong { font-size: 2.4rem; }
}

/* NO HERO RULES HERE — deliberately.
   ---------------------------------------------------------------
   The hero's two-column layout is the ORIGINAL one, and it already lives in
   the Tablet (640px+) and Desktop (1024px+) blocks further up this file:
   `.hero` becomes a flex row, `.hero__image` and `.hero__content` are each
   `flex: 1`, so the logo centres in the left half and the copy centres in the
   right. That is the layout this section had before 2026-09-07 and the one it
   has again.

   A grid override sat here for a day and did two things wrong: it broke the
   centring by forcing `text-align: left`, and being later in the file it beat
   the original rules it was duplicating. Anything hero-shaped that needs
   changing belongs in those two blocks, not in a third one down here — a
   second source of truth for the same component is what caused this. */

/* The category line above the H1 on /services and /pricing. Those two pages
   still lead with cleaning because that is what they rank for; this is what
   places them inside Serve rather than beside it. */
.serve-page-head__eyebrow {
    font-family: var(--flow-sans);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin: 0 0 0.6rem;
}

/* ============================================================
   CLIENT PORTAL  ·  2026-09-08
   ============================================================
   Tabs on a phone, a sidebar from 720px — ONE nav, not two sets of markup.
   Everything else reuses the ticket's blocks, editors and save states, because
   a portal that looked like a different product from the ticket it replaced
   would be a second design to maintain.
   ============================================================ */
.portal {
    max-width: 820px;
    margin: 0 auto;
    padding: 1.25rem 1.1rem 3rem;
}

/* "Serve for Uriel" — the name sits beside the wordmark rather than under it,
   so the bar reads as one phrase. */
.portal__for {
    font-family: var(--flow-sans);
    font-size: 1.05rem;
    color: var(--color-text-muted);
    margin-left: 0.4rem;
}

.portal__nav {
    display: flex;
    gap: 0.4rem;
    margin: 1.1rem 0 1.5rem;
    border-bottom: 1px solid var(--color-border);
}
.portal__tab {
    font-family: var(--flow-sans);
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--color-text-muted);
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    padding: 0.55rem 0.7rem;
    margin-bottom: -1px;
    cursor: pointer;
}
.portal__tab:hover { color: var(--color-dark); }
.portal__access-flag {
    display: block;
    width: max-content;
    max-width: 100%;
    margin-top: 0.35rem;
    padding: 0.15rem 0.45rem;
    border-radius: 0.25rem;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1.4;
}
.portal__access-flag.is-incomplete { color: #b91c1c; background: #fee2e2; }
.portal__access-flag.is-complete { color: #166534; background: #dcfce7; }
.portal__tab.is-on {
    color: var(--color-dark);
    border-bottom-color: var(--color-dark);
}

.portal__h {
    font-family: var(--flow-sans);
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-dark);
    margin: 1.5rem 0 0.7rem;
}
.portal__h:first-child { margin-top: 0; }

/* --- a booking row --- */
.pb {
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: #fff;
    margin-bottom: 0.6rem;
}
.pb[open] { border-color: var(--color-accent); }
/* A finished booking is a record. Quieter, and it carries no controls. */
.pb--done { background: var(--color-bg-light); }

.pb__sum {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.2rem 0.7rem;
    padding: 0.8rem 0.95rem;
    cursor: pointer;
    list-style: none;
}
.pb__sum::-webkit-details-marker { display: none; }
.pb__title {
    font-family: var(--flow-sans);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--color-dark);
}
.pb__when {
    font-family: var(--flow-sans);
    font-size: 0.88rem;
    color: var(--color-text-muted);
}
.pb__price {
    font-family: var(--flow-sans);
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--color-text);
    margin-left: auto;
    font-variant-numeric: tabular-nums;
}
.pb__body { padding: 0 0.95rem 0.95rem; }
/* Sub-heading inside an open booking — smaller than the portal's own h2,
   since it labels a block within a row rather than a section of the page. */
.pb__h {
    font-family: var(--flow-sans);
    font-size: 0.76rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin: 1.1rem 0 0.5rem;
}

/* --- authorize payment ---------------------------------------------------
   Deliberately quieter than a booking CTA. By the time somebody sees this they
   have already decided to use Serve; the job of the button is to be findable
   and unambiguous, not to persuade. A loud button here reads as a demand. */
.pay__wrap { margin: 0.9rem 0 0.2rem; }

.pay__btn {
    font-family: var(--flow-sans);
    font-size: 0.86rem;
    font-weight: 600;
    color: #fff;
    background: var(--color-accent);
    border: 1px solid var(--color-accent);
    border-radius: 7px;
    padding: 0.62rem 1.05rem;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}
.pay__btn:hover:not(:disabled) {
    background: var(--color-accent-hover);
    border-color: var(--color-accent-hover);
}
.pay__btn:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}
/* Disabled means "already working on it", so it must not look broken —
   dimmed and not-allowed would suggest the click failed. */
.pay__btn:disabled { opacity: 0.62; cursor: progress; }

.pay__wrap .tk-note { margin: 0.5rem 0 0; }

/* Empty until something goes wrong, so it takes no space in the normal case. */
.pay__err:empty { display: none; }
.pay__err {
    font-family: var(--flow-sans);
    font-size: 0.83rem;
    line-height: 1.45;
    color: #b91c1c;
    margin: 0.55rem 0 0;
}

/* Returning from Stripe. The success tint is the same green the saved-answer
   line uses, so the portal has one vocabulary for "that worked". */
.portal__note {
    font-family: var(--flow-sans);
    font-size: 0.87rem;
    line-height: 1.5;
    color: var(--color-text);
    background: var(--color-surface-muted, #f6f6f5);
    border: 1px solid var(--color-border);
    border-radius: 8px;
    padding: 0.8rem 0.95rem;
    margin: 0 0 1.1rem;
}
.portal__note--ok {
    color: #166534;
    background: #f0fdf4;
    border-color: #bbf7d0;
}

/* --- one access question --- */
.pq {
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 0.9rem 1rem 1rem;
    margin: 0 0 0.85rem;
    background: #fff;
}
.pq__legend {
    font-family: var(--flow-sans);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    padding: 0 0.35rem;
}

/* Whole-row targets. A radio on a phone is a 13px circle; the label around it
   is the actual hit area and it needs to be one. */
.pq__opt {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.55rem 0.4rem;
    border-radius: 6px;
    cursor: pointer;
}
.pq__opt:hover { background: var(--color-bg-light); }
.pq__opt input { width: 1.05rem; height: 1.05rem; accent-color: var(--color-dark); flex: none; }
.pq__opt-label {
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    color: var(--color-dark);
}

/* The box only exists while the choice that owns it is selected. */
.pq__detail { margin-top: 0.5rem; }
.pq__detail .tk-edit { padding: 0; }

.pq__saved {
    font-family: var(--flow-sans);
    font-size: 0.85rem;
    font-weight: 600;
    color: #166534;
    margin: 0 0 0.4rem;
}

@media (min-width: 720px) {
    /* The nav becomes a sidebar. Same buttons, same handlers — only the box
       they sit in changes. */
    .portal {
        display: grid;
        grid-template-columns: 12rem 1fr;
        grid-template-areas:
            "bar  bar"
            "nav  body"
            "nav  foot";
        column-gap: 2rem;
        align-items: start;
        max-width: 980px;
    }
    .portal > .ticket__bar   { grid-area: bar; }
    .portal > .ticket__state { grid-column: 1 / -1; }
    .portal__nav {
        grid-area: nav;
        position: sticky;
        top: 1.5rem;
        flex-direction: column;
        gap: 0.15rem;
        border-bottom: none;
        border-right: 1px solid var(--color-border);
        margin: 0;
        padding-right: 0.75rem;
    }
    .portal__tab {
        text-align: left;
        border-bottom: none;
        border-left: 2px solid transparent;
        margin-bottom: 0;
        padding: 0.5rem 0.6rem;
    }
    .portal__tab.is-on {
        border-bottom-color: transparent;
        border-left-color: var(--color-dark);
        background: var(--color-bg-light);
    }
    .portal__body { grid-area: body; }
    .portal > .ticket__foot { grid-area: foot; }
}

/* --- Provider portal ------------------------------------------------------
   Reuses the customer portal's nav, booking rows, editors and save states.
   Only the three things a provider has and a customer does not need rules of
   their own: a logo, a service checklist, and a preference selector. */

.vp__logo {
    display: block;
    max-width: 200px;
    max-height: 90px;
    width: auto;
    height: auto;
    margin: 0 0 0.5rem;
    /* A partner's logo arrives at whatever size they had. Contain rather than
       cover: a cropped logo is a worse look than a small one. */
    object-fit: contain;
}

.vp__svc {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem 0.9rem;
    padding: 0.7rem 0.85rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: #fff;
    margin-bottom: 0.5rem;
}
.vp__svc-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    cursor: pointer;
    flex: 1 1 12rem;
}
.vp__svc-head input {
    width: 1.05rem;
    height: 1.05rem;
    accent-color: var(--color-dark);
    flex: none;
}
.vp__svc-name {
    font-family: var(--flow-sans);
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--color-dark);
}

.vp__pref {
    font-family: var(--flow-sans);
    font-size: 0.88rem;
    color: var(--color-text);
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: #fff;
}
.vp__pref:focus {
    outline: none;
    border-color: var(--color-dark);
    box-shadow: 0 0 0 3px rgba(31, 41, 55, 0.12);
}

/* Shared booking updates and private room photos. */
.booking-live { color: #68766f; font-size: .8rem; margin: 0 0 16px; }
.room-photos { margin-top: 14px; width: 100%; }
.room-photos__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.room-photos__card { min-width: 0; }
.room-photos__view { display: block; width: 100%; aspect-ratio: 4/3; overflow: hidden; border: 1px solid #dae4dd; border-radius: 10px; background: #f3f6f3; cursor: pointer; color: #355444; }
.room-photos__view img { display: block; width: 100%; height: 100%; object-fit: cover; }
.room-photos__remove { border: 0; background: none; color: #795252; min-height: 44px; padding: 8px 0; cursor: pointer; font: inherit; font-size: .8rem; }
.room-photos__actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.room-photos__actions button { min-height: 44px; }
.room-photos__note, .room-photos__status { color: #65736a; font-size: .8rem; line-height: 1.5; margin: 8px 0 0; }
.room-photos__status:empty { display: none; }
.room-photos__dialog { width: min(900px, 94vw); max-height: 94vh; border: 0; border-radius: 14px; padding: 16px; }
.room-photos__dialog::backdrop { background: rgb(12 28 21 / 75%); }
.room-photos__dialog img { display: block; max-width: 100%; max-height: 78vh; margin: 12px auto 0; object-fit: contain; }
@media (max-width: 480px) { .room-photos__grid { gap: 6px; } }
.tk-room > .room-photos { padding: 0 .85rem .85rem; box-sizing: border-box; margin-top: 0; }
/* The cleaner's room box: what the customer asked for, one Add photos row with
   its reminder beside the button, then the optional note. */
.tk-room__from { padding: 0 .85rem .85rem; }
.tk-room__from .room-photos { margin-top: 8px; }
.room-photos__grid:empty + .room-photos__actions { margin-top: 0; }
.room-photos__actions { align-items: center; }
.room-photos__actions .room-photos__note { margin: 0; }


/* ---- The partner's work, and what people said about it (r35) ------------ */

/* A scrolling strip rather than a grid: on a phone a grid of four turns into
   four stacked full-width photos and pushes everything else off the screen. */
.tk-partner__photos {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.75rem;
    overflow-x: auto;
    padding-bottom: 0.25rem;
    -webkit-overflow-scrolling: touch;
}
.tk-partner__shot {
    margin: 0;
    flex: 0 0 auto;
    width: 8.5rem;
}
.tk-partner__shot img {
    display: block;
    width: 100%;
    height: 6rem;
    object-fit: cover;
    border-radius: calc(var(--radius) * 0.6);
    background: var(--color-bg-light);
}
.tk-partner__shot figcaption {
    font-size: 0.72rem;
    line-height: 1.35;
    color: var(--color-text-muted);
    margin-top: 0.3rem;
}

.tk-partner__reviews {
    margin-top: 0.85rem;
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
}
.tk-review {
    margin: 0;
    padding: 0.6rem 0.75rem;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: calc(var(--radius) * 0.6);
}
.tk-review__stars {
    font-size: 0.78rem;
    color: var(--color-accent);
    font-weight: 700;
}
.tk-review__quote {
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--color-text);
    margin: 0.15rem 0 0;
}
/* The attribution is quiet but never hidden — a quote whose source is hard to
   find is a quote pretending to be ours. */
.tk-review__by {
    font-size: 0.75rem;
    color: var(--color-text-muted);
    margin-top: 0.35rem;
}
.tk-review__by a {
    color: var(--color-text-muted);
    text-decoration: underline;
}

/* ---- "What's included", on the portal and the client ticket (r37) -------- */
.sc {
    margin-top: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: calc(var(--radius) * 0.6);
    background: var(--color-bg-light);
}
.sc__sum {
    cursor: pointer;
    padding: 0.55rem 0.75rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-dark);
    list-style: none;
}
.sc__sum::-webkit-details-marker { display: none; }
/* A caret the summary owns, so the row reads as openable without a library. */
.sc__sum::after {
    content: '⌄';
    float: right;
    transition: transform 0.15s ease;
}
.sc[open] .sc__sum::after { transform: rotate(180deg); }
.sc__body { padding: 0 0.75rem 0.7rem; }
.sc__h {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin-top: 0.6rem;
}
.sc__list {
    margin: 0.35rem 0 0;
    padding-left: 1.1rem;
    font-size: 0.84rem;
    line-height: 1.55;
    color: var(--color-text);
}
.sc__list--addon { color: var(--color-text-muted); }

/* ---- The recurring plan: an offer, not a booking (r38) ------------------ */
/* Deliberately not styled like a booking row. Someone scanning this page should
   be able to tell at a glance which of these is happening and which is waiting
   on them — hence the dashed edge and the tag, not a card that looks booked. */
.plan {
    margin-top: 0.9rem;
    padding: 0.85rem 1rem;
    border: 1px dashed var(--color-border);
    border-radius: var(--radius);
    background: var(--color-bg-light);
}
.plan__tag {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-accent);
}
.plan__title {
    font-family: var(--flow-sans);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--color-dark);
    margin-top: 0.15rem;
    text-transform: capitalize;
}
.plan__price {
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-dark);
    margin-top: 0.2rem;
}
.plan__body {
    font-size: 0.86rem;
    line-height: 1.55;
    color: var(--color-text);
    margin: 0.4rem 0 0;
}
.plan__when {
    font-size: 0.78rem;
    color: var(--color-text-muted);
    margin-top: 0.4rem;
}

/* ---- Confirming the plan: choosing a time, from the portal (r42) -------- */
/* The card keeps its dashed, not-yet-booked look right up until the moment it
   becomes a booking. Styling these controls like a confirmed appointment would
   tell someone their visit was scheduled while they were still choosing. */
.plan__confirm { margin-top: 0.75rem; }

/* "Choose a time" is exploratory — pick one, pick another, change your mind —
   so it is the quiet button. */
.plan__cta {
    font-family: var(--flow-sans);
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-dark);
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 0.5rem 1.05rem;
    cursor: pointer;
}
.plan__cta:hover:not(:disabled) { border-color: var(--color-dark); }
.plan__cta:disabled { opacity: 0.55; cursor: default; }

/* This one books a visit. It should look like the thing that does. */
.plan__go {
    color: #fff;
    background: var(--color-dark);
    border-color: var(--color-dark);
}
.plan__go:hover:not(:disabled) {
    background: var(--color-accent);
    border-color: var(--color-accent);
}

.plan__pick { margin-top: 0.75rem; }

/* The chosen time, said back to them in full before they commit to it. */
.plan__chosen {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-dark);
    margin-top: 0.6rem;
}

/* The same-company request. A REQUEST — the note under it says so, and that
   note is not decoration: Serve assigns the visit, and promising a particular
   company is a promise we cannot keep from here. */
/* The checkbox and its caveat reveal together, so they are spaced together —
   the note is part of the question, not a remark underneath it. */
.plan__samewrap { margin-top: 0.7rem; }
.plan__same {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--color-text);
    cursor: pointer;
}
.plan__same input { margin-top: 0.2rem; flex: 0 0 auto; }
.plan__samenote {
    display: block;
    font-size: 0.76rem;
    color: var(--color-text-muted);
    margin-top: 0.25rem;
}

.plan__row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin-top: 0.8rem;
}

/* ---- A plan after a move (r45) ------------------------------------------ */
/* While choosing, it keeps r38's dashed not-yet-booked edge. Once chosen it
   turns solid: that box is now a standing arrangement, not a question. */
.mplan {
    margin-top: 0.9rem;
    padding: 0.95rem 1rem;
    border: 1px dashed var(--color-border);
    border-radius: var(--radius);
    background: var(--color-bg-light);
}
.mplan--saved {
    border-style: solid;
    background: #fff;
}
.mplan__tag {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-accent);
}
.mplan__title {
    font-family: var(--flow-sans);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--color-dark);
    margin-top: 0.15rem;
}
.mplan__price {
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-dark);
    margin-top: 0.2rem;
}
.mplan__home,
.mplan__next {
    font-size: 0.86rem;
    color: var(--color-text);
    margin-top: 0.3rem;
}
.mplan__next { font-weight: 600; color: var(--color-dark); }
.mplan__note,
.mplan__label {
    font-size: 0.78rem;
    color: var(--color-text-muted);
    margin-top: 0.4rem;
}
.mplan__label { margin: 0.9rem 0 0.4rem; font-weight: 600; }
.mplan__body {
    font-size: 0.86rem;
    line-height: 1.55;
    color: var(--color-text);
    margin: 0.4rem 0 0;
}

/* Three plans side by side where there is room, stacked on a phone. Whole-tile
   buttons: a radio on a phone is a 13px target. */
.mplan__time { margin-top: 0.4rem; }
.mplan__slots { margin-top: 0.25rem; font-size: 0.85rem; color: var(--color-text-muted); }
.mplan__plans {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
    gap: 0.5rem;
    margin-top: 0.75rem;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}
.plan__prefs,
.mplan__prefs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0.5rem;
    margin-top: 0.5rem;
}
.plan__pref,
.mplan__opt,
.mplan__pref,
.mplan__keep {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.15rem;
    width: 100%;
    text-align: left;
    font-family: var(--flow-sans);
    color: var(--color-dark);
    background: #fff;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 0.65rem 0.8rem;
    cursor: pointer;
}
.mplan__keep { font-size: 0.85rem; font-weight: 600; margin-top: 0.75rem; }
.plan__pref:hover,
.mplan__opt:hover,
.mplan__pref:hover,
.mplan__keep:hover { border-color: var(--color-dark); }
.plan__pref.is-selected,
.mplan__opt.is-selected,
.mplan__pref.is-selected,
.mplan__keep.is-selected {
    border-color: var(--color-dark);
    box-shadow: inset 0 0 0 1px var(--color-dark);
}
.plan__optname,
.mplan__optname { font-size: 0.88rem; font-weight: 700; }
.plan__optprice,
.mplan__optprice { font-size: 0.8rem; color: var(--color-text-muted); }

.plan__other,
.mplan__other,
.mplan__cancel,
.mplan__retry {
    font-family: var(--flow-sans);
    font-size: 0.8rem;
    color: var(--color-text-muted);
    background: none;
    border: 0;
    padding: 0;
    margin-top: 0.6rem;
    text-decoration: underline;
    cursor: pointer;
}
.plan__other:hover,
.mplan__other:hover,
.mplan__cancel:hover,
.mplan__retry:hover { color: var(--color-dark); }

.mplan__chosen {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-dark);
    margin-top: 0.75rem;
}
.mplan__chosen:empty { display: none; }
.mplan__row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.6rem 1rem;
    margin-top: 0.8rem;
}
.mplan__row .mplan__cancel { margin-top: 0; }

/* Books a visit, or changes one — it looks like the thing that does. */
.mplan__go,
.mplan__edit {
    font-family: var(--flow-sans);
    font-size: 0.85rem;
    font-weight: 600;
    color: #fff;
    background: var(--color-dark);
    border: 1px solid var(--color-dark);
    border-radius: var(--radius);
    padding: 0.55rem 1.1rem;
    cursor: pointer;
}
.mplan__edit {
    color: var(--color-dark);
    background: transparent;
    border-color: var(--color-border);
    margin-top: 0.8rem;
}
.mplan__go:hover:not(:disabled) { background: var(--color-accent); border-color: var(--color-accent); }
.mplan__edit:hover { border-color: var(--color-dark); }
.mplan__go:disabled { opacity: 0.55; cursor: default; }

/* ---- Changing or cancelling one visit, from the portal (r43) ----------- */
/* These sit at the BOTTOM of an open booking panel, under everything the
   customer might add to the visit, because they are the two controls that
   change whether it happens at all. */
.sched {
    margin-top: 1rem;
    padding-top: 0.85rem;
    border-top: 1px solid var(--color-border);
}
.sched__row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.6rem;
}
.sched__btn {
    font-family: var(--flow-sans);
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--color-dark);
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    padding: 0.4rem 0.9rem;
    cursor: pointer;
}
.sched__btn:hover:not(:disabled) { border-color: var(--color-dark); }
.sched__btn:disabled { opacity: 0.55; cursor: default; }
/* Cancelling is not an equal-weight choice with changing the time, and the
   button should not look like one. */
.sched__btn--quiet { color: var(--color-text-muted); }
.sched__pick { margin-top: 0.75rem; }
.sched__go   { margin-top: 0.75rem; }
/* The button that actually moves the visit. It carries its own name rather
   than borrowing the plan card's: two elements answering to plan__cta put a
   second "the plan's button" into every open booking, above the card, and
   whichever came first won. Same weight as confirming a plan, because it is
   the same kind of commitment. */
.sched__move {
    font-family: var(--flow-sans);
    font-size: 0.85rem;
    font-weight: 600;
    color: #fff;
    background: var(--color-dark);
    border: 1px solid var(--color-dark);
    border-radius: 999px;
    padding: 0.45rem 1rem;
    cursor: pointer;
}
.sched__move:hover:not(:disabled) {
    background: var(--color-accent);
    border-color: var(--color-accent);
}
.sched__move:disabled { opacity: 0.55; cursor: default; }
.sched__confirm {
    margin-top: 0.75rem;
    padding: 0.75rem 0.85rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: var(--color-bg-light);
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.6rem;
}
.sched__warn {
    flex: 1 0 100%;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--color-text);
    margin: 0;
}
/* The only destructive button on the page. It is allowed to look like it. */
.sched__yes {
    font-family: var(--flow-sans);
    font-size: 0.82rem;
    font-weight: 700;
    color: #fff;
    background: #b91c1c;
    border: 1px solid #b91c1c;
    border-radius: 999px;
    padding: 0.4rem 0.95rem;
    cursor: pointer;
}
.sched__yes:hover:not(:disabled) { background: #991b1b; border-color: #991b1b; }
.sched__yes:disabled { opacity: 0.55; cursor: default; }

/* A cancelled booking is a record, and reads as one: muted, and labelled in
   the summary line so it is legible without opening the panel. */
.pb--cancelled .pb__title,
.pb--cancelled .pb__when,
.pb--cancelled .pb__price { color: var(--color-text-muted); }
.pb--cancelled .pb__title { text-decoration: line-through; }
.pb__tag {
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #b91c1c;
    border: 1px solid currentColor;
    border-radius: 999px;
    padding: 0.1rem 0.45rem;
    white-space: nowrap;
}

/* ============================================================
   LOADING SKELETON  —  r44
   The shape of the page, painted with the first frame while its data is
   fetched: tabs and cards on /h and /v, the when-line and blocks on /j. A
   slow pulse, never a sweep, and still for anyone who has asked for less
   motion. It lives inside the state element, so the error message and the
   loaded page both replace it — nothing here can outlast the load.
   ============================================================ */
.skel {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
}
.skel__bar {
    display: block;
    height: 0.8rem;
    border-radius: 4px;
    background: var(--color-border);
    animation: skel-pulse 1.6s ease-in-out infinite;
}
.skel__bar--tab     { width: 5.5rem; height: 1.1rem; }
.skel__bar--heading { width: 9rem; height: 1rem; margin-top: 0.35rem; }
.skel__bar--eyebrow { width: 7rem; height: 0.7rem; }
.skel__bar--time    { width: 12rem; max-width: 80%; height: 1.45rem; }
.skel__bar--line    { width: 72%; }
.skel__bar--short   { width: 42%; }
.skel__tabs {
    display: flex;
    gap: 0.6rem;
    padding-bottom: 0.7rem;
    border-bottom: 1px solid var(--color-border);
    margin-bottom: 0.35rem;
}
.skel__card {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: #fff;
    padding: 1rem 1.1rem;
}
.skel__block {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    border-top: 1px solid var(--color-border);
    padding-top: 1rem;
    margin-top: 0.6rem;
}
/* Words only once the wait runs long. A server waking from idle can take
   several seconds, and a pulse with nothing said for that long reads as stuck. */
.skel__slow {
    margin: 0.35rem 0 0;
    font-size: 0.85rem;
    color: var(--color-text-muted);
    opacity: 0;
    animation: skel-reveal 0s linear 6s forwards;
}
/* From 720px the portal's tabs become a sidebar (see .portal__nav above). The
   skeleton takes the same shape, so nothing jumps sideways when the page lands. */
@media (min-width: 720px) {
    .portal .skel {
        display: grid;
        grid-template-columns: 12rem 1fr;
        column-gap: 2rem;
        row-gap: 0.55rem;
        align-items: start;
    }
    .portal .skel__tabs {
        grid-column: 1;
        grid-row: 1 / span 6;
        flex-direction: column;
        border-bottom: none;
        border-right: 1px solid var(--color-border);
        padding: 0.5rem 0.75rem 0.5rem 0.6rem;
        margin: 0;
    }
    .portal .skel > :not(.skel__tabs) { grid-column: 2; }
}
@keyframes skel-pulse  { 50% { opacity: 0.45; } }
@keyframes skel-reveal { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .skel__bar { animation: none; }
}
