/* /Components/Layout/MainLayout.razor.rz.scp.css */
.portal-shell[b-gxq09b5oau] {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--portal-bg, #050407);
}

.portal-main[b-gxq09b5oau] {
    flex: 1;
}

.portal-footer[b-gxq09b5oau] {
    padding: 32px 24px;
    text-align: center;
    color: var(--portal-text-muted, #98a09b);
    border-top: 1px solid var(--portal-border, #34383c);
}

.portal-footer a[b-gxq09b5oau] {
    color: inherit;
    text-decoration: none;
}

.portal-footer a:hover[b-gxq09b5oau] {
    text-decoration: underline;
}

/* Auto / Light / Dark (9 Sep 2026). Three buttons rather than one that flips, because "follow
   this device" is not the opposite of anything — and the one in force has to be readable at a
   glance or the group is a puzzle. */
.portal-footer .theme-switch[b-gxq09b5oau] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: 0.6rem;
}

.portal-footer .theme-switch-label[b-gxq09b5oau] {
    color: var(--portal-text-faint, #6a726d);
}

.portal-footer .theme-switch-btn[b-gxq09b5oau] {
    background: none;
    border: 1px solid var(--portal-border-card, #34383c);
    border-radius: 999px;
    color: inherit;
    font: inherit;
    font-size: 12px;
    padding: 4px 12px;
    cursor: pointer;
}

.portal-footer .theme-switch-btn:hover[b-gxq09b5oau] {
    color: var(--portal-text, #e3e6e4);
    border-color: var(--portal-text-muted, #98a09b);
}

.portal-footer .theme-switch-btn[aria-current][b-gxq09b5oau] {
    color: var(--portal-accent-text, #80d4c8);
    border-color: var(--portal-accent, #5fcabb);
    font-weight: 700;
}

.portal-footer .brand-mark[b-gxq09b5oau] {
    width: 16px;
    height: 16px;
    vertical-align: -3px;
    margin-right: 0.4rem;
}

#blazor-error-ui[b-gxq09b5oau] {
    background: #fff3cd;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss[b-gxq09b5oau] {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}
/* /Components/Layout/ReconnectModal.razor.rz.scp.css */
.components-reconnect-first-attempt-visible[b-9hyg142gkg],
.components-reconnect-repeated-attempt-visible[b-9hyg142gkg],
.components-reconnect-failed-visible[b-9hyg142gkg],
.components-pause-visible[b-9hyg142gkg],
.components-resume-failed-visible[b-9hyg142gkg],
.components-rejoining-animation[b-9hyg142gkg] {
    display: none;
}

#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible[b-9hyg142gkg],
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation[b-9hyg142gkg],
#components-reconnect-modal.components-reconnect-paused .components-pause-visible[b-9hyg142gkg],
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible[b-9hyg142gkg],
#components-reconnect-modal.components-reconnect-retrying[b-9hyg142gkg],
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible[b-9hyg142gkg],
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation[b-9hyg142gkg],
#components-reconnect-modal.components-reconnect-failed[b-9hyg142gkg],
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible[b-9hyg142gkg] {
    display: block;
}


#components-reconnect-modal[b-9hyg142gkg] {
    background-color: white;
    width: 20rem;
    margin: 20vh auto;
    padding: 2rem;
    border: 0;
    border-radius: 0.5rem;
    box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
    opacity: 0;
    transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
    animation: components-reconnect-modal-fadeOutOpacity-b-9hyg142gkg 0.5s both;
    &[open]

{
    animation: components-reconnect-modal-slideUp-b-9hyg142gkg 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity-b-9hyg142gkg 0.5s ease-in-out 0.3s;
    animation-fill-mode: both;
}

}

#components-reconnect-modal[b-9hyg142gkg]::backdrop {
    background-color: rgba(0, 0, 0, 0.4);
    animation: components-reconnect-modal-fadeInOpacity-b-9hyg142gkg 0.5s ease-in-out;
    opacity: 1;
}

@keyframes components-reconnect-modal-slideUp-b-9hyg142gkg {
    0% {
        transform: translateY(30px) scale(0.95);
    }

    100% {
        transform: translateY(0);
    }
}

@keyframes components-reconnect-modal-fadeInOpacity-b-9hyg142gkg {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

@keyframes components-reconnect-modal-fadeOutOpacity-b-9hyg142gkg {
    0% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.components-reconnect-container[b-9hyg142gkg] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
}

#components-reconnect-modal p[b-9hyg142gkg] {
    margin: 0;
    text-align: center;
}

#components-reconnect-modal button[b-9hyg142gkg] {
    border: 0;
    background-color: #6b9ed2;
    color: white;
    padding: 4px 24px;
    border-radius: 4px;
}

    #components-reconnect-modal button:hover[b-9hyg142gkg] {
        background-color: #3b6ea2;
    }

    #components-reconnect-modal button:active[b-9hyg142gkg] {
        background-color: #6b9ed2;
    }

.components-rejoining-animation[b-9hyg142gkg] {
    position: relative;
    width: 80px;
    height: 80px;
}

    .components-rejoining-animation div[b-9hyg142gkg] {
        position: absolute;
        border: 3px solid #0087ff;
        opacity: 1;
        border-radius: 50%;
        animation: components-rejoining-animation-b-9hyg142gkg 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
    }

        .components-rejoining-animation div:nth-child(2)[b-9hyg142gkg] {
            animation-delay: -0.5s;
        }

@keyframes components-rejoining-animation-b-9hyg142gkg {
    0% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    4.9% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    5% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 1;
    }

    100% {
        top: 0px;
        left: 0px;
        width: 80px;
        height: 80px;
        opacity: 0;
    }
}
/* /Components/Pages/Home.razor.rz.scp.css */
/* Booking home — the one page styled as the platform's own storefront. Salon pages
   (/{slug}) keep the salon's branding; everything here uses the portal tokens.

   .book-header / .book-brand are NOT here: this file is scoped to Home.razor (CSS isolation),
   and /account and /account/recover use the same header markup — with the rules in here they
   rendered the favicon at its natural size, a full-width Mi mark on a phone (found on
   production 18 Sep 2026). They live in app.css. */

.book-nav[b-wm3k2vrlgh] {
    display: flex;
    align-items: center;
    gap: 26px;
}

.book-nav a[b-wm3k2vrlgh] {
    color: var(--portal-text-body);
    text-decoration: none;
    font-weight: 500;
}

.book-nav a:hover[b-wm3k2vrlgh] { color: var(--portal-text); }

.book-nav .btn-book-now[b-wm3k2vrlgh] {
    background: var(--portal-accent);
    color: var(--portal-accent-contrast);
    font-weight: 700;
    padding: 10px 22px;
    border-radius: 12px;
}

.book-nav .btn-book-now:hover[b-wm3k2vrlgh] { background: var(--portal-accent-deep); color: var(--portal-accent-contrast); }

/* The page is exactly as wide as its row of cards, and centred (William, 25 Sep 2026, with three
   screenshots of 3, 4 and 5 salons: "this is what I want when the website has 3, 4, or 5 shops").

   This is a different question from the one this file asked all morning, and the difference is the
   whole rule. A stretching grid asks "how much room is there?" and spreads what it has across it.
   This asks "how many salons are there?" and makes the page that wide. So three salons on a 2554px
   monitor do not become three slabs under a heading floating somewhere above them: they stay 320px
   each, and the whole block -- heading, cards and feature strip -- sits in the middle of the screen
   with equal air either side. Measured off the screenshots: 1004 / 1346 / 1688px of cards inside
   773 / 601 / 428px of margin, symmetrical in all three.

   The width is the smaller of two counts, so a row is never wider than the salons to put in it and
   never wider than the window:
     --n     how many salons there are -- the n-1..n-5 class this div is stamped with
     --cols  how many the window can hold -- stepped by the media queries below

   Both counts have to be honest, because the HEADING takes its left edge from this box: if the box
   is wider than the cards that end up in it, the heading drifts off the first card. That is why
   --cols is a media query and not `width: fit-content`, which was the obvious answer and is wrong
   -- fit-content clamps to the space AVAILABLE, not to the space the cards took, so the moment a
   row wrapped the box stayed wide and the heading slid up to 170px left of the card beneath it. */
.book-container[b-wm3k2vrlgh] {
    /* Two card widths, and they do different jobs — mixing them up is the whole difficulty here.
       --card-max sizes THIS BOX: a row of cards at their happiest is what the page wants to be.
       --card-min decides HOW MANY go in it: that count belongs to the narrowest a card may get,
       or the row would break to a new line while there was still room to squeeze. So a 1512px
       window with five salons runs four cards at about 347px each rather than three at 350. */
    --card-min: 320px;
    --card-max: 350px;
    --gap: 22px;
    --gutter: 28px;
    /* No cards at all (the directory was unreachable, or there are none): three cards' worth is a
       readable measure for one sentence. The n-* class overrides it whenever there are cards. */
    --n: 3;
    --cols: 5;

    width: min(100%, calc(min(var(--n), var(--cols)) * (var(--card-max) + var(--gap))
                          - var(--gap) + 2 * var(--gutter)));
    margin-inline: auto;
    padding: 0 var(--gutter) 48px;
}

.book-container.n-1[b-wm3k2vrlgh] { --n: 1; }
.book-container.n-2[b-wm3k2vrlgh] { --n: 2; }
.book-container.n-3[b-wm3k2vrlgh] { --n: 3; }
.book-container.n-4[b-wm3k2vrlgh] { --n: 4; }
.book-container.n-5[b-wm3k2vrlgh] { --n: 5; }

/* How many cards the WINDOW holds: N cards and N-1 gaps plus the two gutters, then about 20px of
   headroom. The headroom is load-bearing, not rounding. A media query is measured against the
   viewport INCLUDING the classic scrollbar in Chrome and Firefox, while the 100% above excludes
   it, so a window sitting exactly on a bare threshold would claim a column it cannot actually
   draw -- the row would wrap and take the heading off the first card. Giving up that column 20px
   early is invisible; claiming one we cannot draw is not. */
@media (max-width: 1763px) { .book-container[b-wm3k2vrlgh] { --cols: 4; } }
@media (max-width: 1421px) { .book-container[b-wm3k2vrlgh] { --cols: 3; } }
@media (max-width: 1079px) { .book-container[b-wm3k2vrlgh] { --cols: 2; } }
@media (max-width:  737px) { .book-container[b-wm3k2vrlgh] { --cols: 1; } }

/* The Mi mark starts where the heading and the first card start (William, 25 Sep 2026). The rule
   is scoped to this page on purpose: .book-header lives in app.css because /account and
   /account/recover share the markup, and those pages sit in .container-narrow, so giving the bar
   this page's measure globally would misalign theirs.
   max() keeps the bar's rule full width while its CONTENTS take the same gutter .book-container
   computes: on a screen narrower than the cap the second operand goes negative and the plain 28px
   wins, which is exactly what the container does. */
.book-header[b-wm3k2vrlgh] {
    /* 1838px is five cards at their full width (5x350 + 4x22), the widest this page ever gets --
       so on a monitor showing five salons the Mi mark sits directly above the first card. With
       fewer salons, or a window too tight for five at 350, the block is narrower and the bar stays
       where it is, which is what William was looking at in all three screenshots. */
    padding-inline: max(28px, calc((100% - 1838px) / 2));
}

/* Left-aligned, flush with the first card. Centring the text was the wrong reading of "align this
   center to center" (25 Sep 2026): what is centred is the BLOCK, in the window -- the heading sits
   at its left edge, exactly as it does in all three screenshots. Getting this from the container's
   width rather than from a text-align is what lets the heading and the first card agree at every
   salon count. */
.book-hero[b-wm3k2vrlgh] {
    padding: 72px 0 40px;
}

.hero-eyebrow[b-wm3k2vrlgh] {
    color: var(--portal-accent-text);
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    margin: 0 0 18px;
}

.book-hero h1[b-wm3k2vrlgh] {
    max-width: 18ch;
    font-size: clamp(2.4rem, 6vw, 4.5rem);
    font-weight: 800;
    letter-spacing: -0.02em;
    margin: 0 0 18px;
    color: var(--portal-text);
}

.hero-sub[b-wm3k2vrlgh] {
    color: var(--portal-text-muted);
    font-size: 1.15rem;
    line-height: 1.6;
    max-width: 620px;
    margin: 0;
}

/* ── Salon cards ─────────────────────────────────────────────────────────── */

/* The tracks flex between the two widths, and the CONTAINER is what stops them at 350 —
   the card carries no width of its own (William, 25 Sep 2026: "the card always 350 and can be
   reduce to 320, and the width of browser is smaller the width of total card, then the shop card
   can go the next line").

   Read together with the box above, that gives one rule with two halves. The box is as wide as
   `cols` cards at 350, or as wide as the window, whichever is smaller. The tracks then divide
   that box by the 320 minimum. When the window is roomy the box wins, the division comes out at
   exactly 350 and there is no slack; when the window is tight the box is the window, the same
   division hands each card something between 320 and 350, and the card that no longer fits goes
   to the next line. Either way the row fills the box exactly, which is what keeps the heading on
   the first card — a max-width on the CARD would leave slack inside the track and break that.

   `1fr` as the maximum is still load-bearing and still the easiest thing here to get wrong. The
   repetition count uses a track's MAX sizing function whenever that is a definite length, so
   `minmax(320px, 350px)` would count in 350px steps and cost a column at most widths — which is
   the shape of the defect that gave three columns where five were meant (25 Sep 2026). A flexible
   max is not definite, so the count falls back to the 320px minimum, which is the count the
   --cols breakpoints were computed from. The two have to agree or the row wraps under the heading.

   A sixth salon lands in track 1 of the next row at the same width as the card above it, which is
   what the shared track sizing buys over a flex row: no odd one out. */
.salon-grid[b-wm3k2vrlgh] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--card-min, 320px)), 1fr));
    gap: var(--gap, 22px);
    scroll-margin-top: 24px;
}

/* No width here on purpose. The track is the width, and the card fills it — which is what lets
   the same card be 350px on a monitor and 322px on a laptop without a second rule knowing about
   it. A max-width: 350px would put the ceiling in the wrong place: the track would still be as
   wide as its share of the box, the card would stop short inside it, and the slack would land
   between the cards and under the heading. The ceiling belongs to the box. */
.salon-card[b-wm3k2vrlgh] {
    /* ONE height for every card on the page (William, 25 Sep 2026: "The shop card height is
       fix"). A row already levels the cards within it; this levels them between rows too, so a
       salon with one category and a salon with nine draw the same rectangle. Everything that
       could disagree about height is pinned below: the title and the address are clamped to two
       lines each, the category strip is a fixed window, and the status row keeps margin-top:auto
       so the slack collects above it and the button stays on the floor. */
    height: 470px;
}

.salon-card[b-wm3k2vrlgh] {
    background: var(--portal-panel);
    border: 1px solid var(--portal-border-card);
    border-radius: 14px;
    overflow: hidden;               /* the logo panel hugs the rounded top */
    display: flex;
    flex-direction: column;
}

/* The logo panel. A deliberately LIGHT surface in both themes — brand marks are drawn for
   pale stationery, and a dark card eats the dark half of most of them. Fixed 16:9-ish box so
   a row of cards keeps one shoulder line whatever shape the logos are. */
.salon-figure[b-wm3k2vrlgh] {
    position: relative;
    aspect-ratio: 4 / 1;
    overflow: hidden;
    background: #fbfaf7;
    border-bottom: 1px solid var(--portal-border-card);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 16px;
}

/* Sized at LAST. Until 25 Sep 2026 there was no rule at all here, so the booking site drew every
   brand mark at its natural size — 512x512 for both of production's — as a block wider than the
   card's content box, clipped by the card's own overflow.

   multiply is what lets the mark sit on the panel instead of in a box on it. Both of production's
   logos are opaque near-white (#fefcfd) rather than transparent, so on a warm panel they would
   otherwise show as a slightly-wrong-white rectangle with a visible seam; multiplied, their ground
   becomes the panel exactly and only the ink remains. A logo that really is transparent is
   unaffected, so this costs nothing to the brands that upload one.

   Sized by WIDTH, not height, and the panel clips what hangs out. Both of production's logos are
   SQUARE canvases holding a wide, short mark with blank bands above and below it; fitting the whole
   square inside a 4:1 panel would shrink the mark to the panel's height and leave it tiny. Sizing
   by width and letting the blank bands overflow spends the panel on the mark instead. A logo whose
   art really does fill its canvas is cropped rather than shrunk, which is why max-height bounds it
   — and a wide logo file never reaches the bound at all. */
.salon-logo[b-wm3k2vrlgh] {
    /* Sized in PIXELS, and taller than the panel on purpose. The card is only ever 320-350px
       wide, so a fixed figure is as responsive as a percentage here and cannot be mis-resolved:
       `max-height: 260%` was measured against the 4:1 panel MINUS its own vertical padding, which
       left the mark about 44px wide — far smaller than the mock-up, which is what William saw.
       170px is chosen from the artwork: the tallest mark of the two (MiLash, whose lash sweeps
       above the words) is about 45% of its square canvas, so 170px keeps it inside the panel
       while the blank bands above and below overflow and are clipped. */
    height: 170px;
    width: auto;
    max-width: 86%;
    flex: none;
    object-fit: contain;
    mix-blend-mode: multiply;
}

/* Shown when the brand has no logo yet. Ink on the light panel, never the theme's text colour —
   this panel does not flip with the theme. */
.salon-wordmark[b-wm3k2vrlgh] {
    color: #1a1d1f;
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    text-align: center;
}

/* Half the size it was (William, 25 Sep 2026). It is a state, not a headline — the salon's name
   and the Book button are what the card is for, and at full size the pill competed with the logo
   it sits beside. 0.62rem is the floor for a bold uppercase-height badge before it stops being
   readable at arm's length on a tablet. */
.salon-open[b-wm3k2vrlgh] {
    position: absolute;
    top: 7px;
    right: 7px;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    border-radius: 999px;
    padding: 3px 8px;
    font-size: 0.62rem;
    font-weight: 700;
    background: #14171a;
    color: #e3e6e4;
}

.salon-open .dot[b-wm3k2vrlgh] {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    flex: none;
}

.salon-open.is-open .dot[b-wm3k2vrlgh] { background: var(--portal-accent); }
.salon-open.is-open[b-wm3k2vrlgh] { color: var(--portal-accent); }
.salon-open.is-shut .dot[b-wm3k2vrlgh] { background: #8b9196; }
.salon-open.is-shut[b-wm3k2vrlgh] { color: #b9bfc3; }

.salon-body[b-wm3k2vrlgh] {
    padding: 22px 22px 20px;
    display: flex;
    flex-direction: column;
    flex: 1;                        /* buttons align across cards of unequal height */
}

/* The spring lives on .salon-status when there is one; this keeps the button on the floor for
   a card whose snapshot said nothing at all. */
.salon-body > .btn-book:not(:first-child)[b-wm3k2vrlgh] { margin-top: auto; }

.salon-eyebrow[b-wm3k2vrlgh] {
    color: var(--portal-text-faint);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    margin: 0 0 10px;
}

.salon-body h2[b-wm3k2vrlgh] {
    font-size: 1.45rem;
    font-weight: 700;
    color: var(--portal-text);
    margin: 0 0 8px;
    /* Two lines is enough for "Rouse Hill Town Center", the longest name on production. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
}

.salon-address[b-wm3k2vrlgh] {
    color: var(--portal-text-muted);
    line-height: 1.5;
    margin: 0 0 16px;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
}

/* A fixed window, two rows tall, with the categories drifting behind it when there are more than
   fit (William, 25 Sep 2026: "if the categories is too many, can you make them scroll up and down
   automatically?"). Fixed, because a card of one height cannot have a part of it that grows.
   --chips-h is named rather than inlined because the animation has to do arithmetic with it. */
.salon-chips[b-wm3k2vrlgh] {
    --chips-h: 58px;
    height: var(--chips-h);
    overflow: hidden;
    margin-bottom: 16px;
}

.salon-chips-run[b-wm3k2vrlgh] {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
    /* The whole trick is min(): 100% is the RUN's own height, so when the categories fit, the
       second operand is positive, min() picks 0px, and nothing moves. No script, no measuring, and
       a one-category salon sits perfectly still. */
    animation: chip-drift-b-wm3k2vrlgh 14s ease-in-out infinite alternate;
}

@keyframes chip-drift-b-wm3k2vrlgh {
    0%, 18%   { transform: translateY(0); }
    82%, 100% { transform: translateY(min(0px, calc(var(--chips-h) - 100%))); }
}

/* Anyone who has asked for less movement gets a strip they scroll themselves instead. */
@media (prefers-reduced-motion: reduce) {
    .salon-chips[b-wm3k2vrlgh] { overflow-y: auto; }
    .salon-chips-run[b-wm3k2vrlgh] { animation: none; }
}

.chip[b-wm3k2vrlgh] {
    border: 1px solid var(--portal-border-card);
    color: var(--portal-text-body);
    border-radius: 999px;
    padding: 4px 11px;
    font-size: 0.68rem;
    white-space: nowrap;
}

/* A rule, then the two answers to "can I walk in now?" on one line: what the queue is doing
   on the left, when the door shuts on the right. Pushed to the bottom of the body so the rule
   sits just above the button on every card, however many chips the salon has. */
.salon-status[b-wm3k2vrlgh] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    margin-top: auto;
    padding-top: 16px;
    border-top: 1px solid var(--portal-border);
    margin-bottom: 18px;
}

.status-live[b-wm3k2vrlgh] {
    color: var(--portal-accent-text);
    font-weight: 600;
    margin: 0;
}

.status-open[b-wm3k2vrlgh] {
    color: var(--portal-text-muted);
    margin: 0;
    white-space: nowrap;
}

.btn-book[b-wm3k2vrlgh] {
    background: var(--portal-accent);
    color: var(--portal-accent-contrast);
    border-radius: 12px;
    padding: 15px 18px;
    text-align: center;
    font-weight: 600;
    text-decoration: none;
}

.btn-book strong[b-wm3k2vrlgh] { font-weight: 800; }

.btn-book:hover[b-wm3k2vrlgh] { background: var(--portal-accent-deep); }

/* ── Feature strip ───────────────────────────────────────────────────────── */

/* The same tracks as the salons, so each column is exactly a card wide and sits UNDER one
   (William, 25 Sep 2026: "can you make the width exactly like the shop card"), which is also how
   it reads in both of his screenshots.
   auto-FILL here, not auto-fit, and this is the one place on the page where the difference still
   matters: there are three features and up to five tracks, and auto-fit would collapse the two
   nobody stands in and let the three share the whole block at about 600px each. auto-fill keeps
   them, so the three sit in tracks 1-3 at a card's width, under the first three cards. */
.book-features[b-wm3k2vrlgh] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--card-min, 320px)), 1fr));
    gap: var(--gap, 22px);
    border-top: 1px solid var(--portal-border);
    margin-top: 56px;
    padding-top: 36px;
}

.book-features > div[b-wm3k2vrlgh] {
    text-align: center;
}

.book-features h3[b-wm3k2vrlgh] {
    font-size: 1rem;
    font-weight: 700;
    color: var(--portal-text);
    margin: 0 0 8px;
}

.book-features p[b-wm3k2vrlgh] {
    color: var(--portal-text-muted);
    line-height: 1.55;
    margin: 0;
    margin-inline: auto;
}

@media (max-width: 640px) {
    /* The gutter is a token rather than a padding override because the container's WIDTH is
       computed from it -- setting padding alone would leave the box 20px wider than the card
       inside it. */
    .book-container[b-wm3k2vrlgh] { --gutter: 18px; padding-bottom: 40px; }
    .book-hero[b-wm3k2vrlgh] { padding: 44px 0 28px; }
}
