/* RedTulip v2 — Overrides & Additions
   Everything else (page transitions, gradient canvas, loading screen,
   spinner, etc.) comes from v1's style.css.
   This file loads LAST to patch font paths and add v2-only utilities. */


/* ─────────────────────────────────────────────────────
   FONT FACES — v2 path override
   v1's style.css declares these with url('fonts/...') relative to css/.
   The fonts also exist at assets/fonts/ for the playground.
   ───────────────────────────────────────────────────── */

@font-face {
    font-family: 'soehne-buch';
    src: url('../fonts/soehne-buch.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'soehne-halbfett';
    src: url('../fonts/soehne-halbfett.woff2') format('woff2');
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'soehne-fett';
    src: url('../fonts/soehne-fett.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'soehne-mono';
    src: url('../fonts/soehne-mono-buch.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}


/* ─────────────────────────────────────────────────────
   BOLD FONT SWAP — Bootstrap sets `font-weight: bolder`
   on <b>/<strong>, but soehne-buch has no bold weight.
   Swap to soehne-halbfett for true bold rendering.
   ───────────────────────────────────────────────────── */

b, strong {
    font-family: 'soehne-halbfett', sans-serif;
    font-weight: normal;
}


/* ─────────────────────────────────────────────────────
   GRADIENT UTILITIES (v2 additions)
   ───────────────────────────────────────────────────── */

.bg-gradient-primary {
    background: linear-gradient(10deg, #1D4E91 0%, #2c75de 100%);
}

.bg-gradient-brand {
    background: linear-gradient(10deg, #b22d5a 0%, #eb5d8f 100%);
}

.bg-gradient-keeper {
    background: linear-gradient(10deg, #6b38d7 0%, #916df6 100%);
}


/* ─────────────────────────────────────────────────────
   UTILITY — 
   ───────────────────────────────────────────────────── */

.scrollbar-hide {
    -ms-overflow-style: none;
    scrollbar-width: none;
}

.scrollbar-hide::-webkit-scrollbar {
    display: none;
}

.form-control.no-border {
    border: none;
}

#content-main {
    min-height: calc(100vh - 4.125rem);
}

.dropdown-menu {
    text-transform: capitalize;
}


/* ─────────────────────────────────────────────────────
   DISCOVERY MODE STYLES
   ───────────────────────────────────────────────────── */

.discovery-mode-title {
    font-size: 3.6rem;
    line-height: 1.1;
    letter-spacing: -0.04em;
    margin-bottom: 1.5rem;
    text-shadow: 0 4px 30px rgba(0, 0, 0, 0.5);
    color: white;
}

.discovery-mode-container {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    margin-bottom: 1rem;
}
.discovery-mode-item {
    height: 2rem;
    border-radius: .5rem;
    border: 2px solid white;
    flex: 1;
    text-align: center;
    background: transparent;
    line-height: 1;
    color:white;
    transition: all 400ms ease-out;
    cursor: pointer;
    align-content: center;
}
.discovery-mode-item .dm-label {
    display: block;
    font-family: 'soehne-fett', sans-serif;
    text-transform: uppercase;
    font-size: .7rem;
    letter-spacing: 0.2rem;
}
.discovery-mode-item .dm-subtitle {
    display: block;
    font-family: 'soehne-buch', sans-serif;
    font-size: .6rem;
    letter-spacing: 0.05rem;
    opacity: 0.7;
    margin-top: .15rem;
}

.discovery-mode-item.dm-experiences {
    border-top-right-radius:0;
    border-bottom-right-radius:0;
    border-right-width: 1px;
}
.discovery-mode-item.dm-services {
    border-top-left-radius:0;
    border-bottom-left-radius:0;
    border-left-width: 1px;
}
.discovery-mode-item.dm-experiences.dm-active {
    background-color:white;
    color: var(--primary-color);
    box-shadow: inset 0 0 1rem rgba(44, 117, 222, 0.25);
}
.discovery-mode-item.dm-services.dm-active {
    background-color:white;
    color: var(--warn-color);
    box-shadow: inset 0 0 1rem rgba(145, 109, 246, 0.25);
}
@media (hover: hover) {
    .discovery-mode-item.dm-experiences:hover {
        background-color:white;
        color: var(--primary-color);
        box-shadow: inset 0 0 1rem rgba(44, 117, 222, 0.25);
    }
    .discovery-mode-item.dm-services:hover {
        background-color:white;
        color: var(--warn-color);
        box-shadow: inset 0 0 1rem rgba(145, 109, 246, 0.25);
    }
}

/* Discovery mode — hero text visibility (outside stage) */
.dm-hero { display: none; }
.dm-hero.dm-visible { display: block; }

/* Discovery mode — hero stage (grid stacking, no height management needed) */
.dm-hero-stage {
    display: grid;
    width: 100%;
    overflow: visible;
    align-items: start;
}
/* All heroes overlap in the same grid cell */
.dm-hero-stage > .dm-hero {
    grid-area: 1 / 1;
    visibility: hidden;
    pointer-events: none;
    display: block;
}
.dm-hero-stage > .dm-hero.dm-explainer {
    display: flex;
}
.dm-hero-stage > .dm-hero.dm-visible {
    visibility: visible;
    pointer-events: auto;
}

/* Discovery mode — category visibility */
.dm-cat { display: none; }
.dm-cat.dm-visible { display: flex; }

/* Discovery mode — tile visibility */
.dm-tile { display: none; }
.dm-tile.dm-visible { display: block; }

/* Discovery mode — explainer cards (unset state) */
.dm-explainer {
    flex-wrap: wrap;
    gap: 1rem;
    width: 100%;
    padding: 0;
}
.dm-explainer-card-container {
    flex: 1;
}
.dm-explainer-card {
    width: 100%;
    background: rgba(255, 255, 255, 0.12);
    border: 2px solid rgba(255, 255, 255, 0.45);
    backdrop-filter: saturate(180%) blur(20px) brightness(0.35);
    border-radius: .75rem;
    padding: 3rem .5rem .5rem .5rem;
    color: white;
    cursor: pointer;
    transition: all 400ms cubic-bezier(0.77, 0, 0, 1);
    text-align: center;
    /* aspect-ratio: 1 / 1.2; */
    margin-top: -2rem;
}
.dm-explainer-experiences {
    transform: rotate(-2deg);
}
.dm-explainer-services {
    transform: rotate(2deg);
}
.dm-explainer-card:active {
    transform: scale(0.97);
}
.dm-explainer-card .dm-explainer-icon {
    font-size: 2rem;
    margin-bottom: .75rem;
    opacity: 0.8;
}
.dm-explainer-card h3 {
    font-family: 'soehne-fett', sans-serif;
    font-size: 1.4rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-bottom: .5rem;
}
.dm-exploreItem .dm-explainer-headline {
    font-family: 'soehne-halbfett', sans-serif;
    color: rgba(255, 255, 255, 0.65);
    line-height: 1.3;
    margin-bottom: .5rem;
}
.dm-exploreItem .dm-explainer-headline span {
    color: white;
    display: block;
}
.dm-explainer-card .dm-explainer-desc {
    font-size: .75rem;
    line-height: 1.4;
    margin-bottom: .75rem;
}
.dm-explainer-card .dm-explainer-cta {
    display: inline-block;
    font-family: 'soehne-halbfett', sans-serif;
    font-size: .85rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    transition: opacity 300ms ease;
}

.dm-exploreItem {
    width: calc(100% - 1rem);
    min-height: 5rem;
    background-color: rgba(255, 255, 255, .2);
    outline: 4px solid rgba(255, 255, 255, .5);
    border-radius: 10px;
    box-shadow: var(--box-shadow-cont-small);
    transition: all 800ms cubic-bezier(0.77, 0, 0, 1);
    position: relative;
    overflow: hidden;
    backdrop-filter: saturate(180%) blur(20px);
    background: center center;
    background-size: cover;
    aspect-ratio: 1 / 1.2;
    align-content: center;
    padding: 0 1rem;
}

.dm-exploreItem>div:nth-child(1) {
    display: flex;
    flex-direction: column;
    align-self: center;
    text-align: center;
    justify-content: center;
    i {
        font-size: 4rem;
        margin: 1rem 0;
        align-self: center;
    }
}

#Routes-Tile {
    background: linear-gradient(-20deg, var(--primary-color-dk) 0%, var(--primary-color) 100%);
    outline-color: var(--primary-color-lt);
    transform: rotate(-2deg);
    margin-left: .75rem;
    z-index:10;
}
#Roots-Tile {
    background: linear-gradient(-20deg, var(--warn-color-dk) 0%, var(--warn-color) 100%);
    outline-color: var(--warn-color-lt);
    transform: rotate(2deg);
    margin-left: .25rem;
    z-index:10;
}
/* ─────────────────────────────────────────────────────
   HOME PAGE STYLES
   ───────────────────────────────────────────────────── */

.home-sections {
    min-height: calc(100vh - 4.125rem - 5rem);
}
.home-section-1 {
    position: relative;
}
.home-section-1-content {
    min-height: 40vh;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 1rem 0;
}
.home-section-1-scroll-to-explore {
    position: absolute;
    bottom: 1rem;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    align-content: center;
    text-transform: uppercase;
    font-size: 0.75rem;
    letter-spacing: 0.1em;
    animation: home-section-1-scroll-to-explore-bounce 2s infinite;
    transition: opacity 400ms cubic-bezier(0.77, 0, 0, 1);
}
.home-section-1-scroll-to-explore.inactive {
    opacity: 0;
    animation: none;
    pointer-events: none;
}
@keyframes home-section-1-scroll-to-explore-bounce {
    0%, 20%, 50%, 80%, 100% { transform: translateX(-50%) translateY(0); opacity: 1; }
    40% { transform: translateX(-50%) translateY(-10px); opacity: 0.25; }
    60% { transform: translateX(-50%) translateY(-5px); opacity: 0.75; }
}


.home-section-2-bg {
    background: linear-gradient(10deg, rgba(255,255,255,.7) 0%, rgba(255,255,255,0) 50%, rgba(255,255,255,.7) 100%) !important;
    backdrop-filter: saturate(180%) blur(20px);
}
.explore-title-container h2 {
    font-size: 3.6rem;
    line-height: 1.1;
    letter-spacing: -0.04em;
    margin-bottom: 1.5rem;
    text-shadow: 0 4px 30px rgba(0, 0, 0, 0.5);
    color: white;
}

.home-section-2 .exploreItem {
    width: calc(50% - 1rem);
    min-height: 5rem;
    background-color: rgba(255, 255, 255, .2);
    outline: 4px solid rgba(255, 255, 255, .5);
    border-radius: 10px;
    margin-bottom: 1.5rem;
    box-shadow: var(--box-shadow-cont-small);
    transition: all 800ms cubic-bezier(0.77, 0, 0, 1);
    position: relative;
    overflow: hidden;
    float: left;
    margin-right: 2rem;
    backdrop-filter: saturate(180%) blur(20px);
}

.home-section-2 .exploreItem.even {
    transform: rotate(-2deg);
}

.home-section-2 .exploreItem.odd {
    transform: translateY(20%) rotate(2deg);
    margin-right: 0;
}

.home-section-2 .exploreItem>div:nth-child(1) {
    background: center center;
    background-size: cover;
    width: 100%;
    aspect-ratio: 1 / 1.2;
}

.home-section-2 .exploreItem>div:nth-child(2) {
    background: transparent;
    bottom: .75rem;
    position: absolute;
    left: 0;
    right: 0;
    padding: 2rem .5rem .5rem .5rem;
    align-self: center;
}

.home-section-2 .exploreItem>div:nth-child(3) {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -75%);
    font-size: 4rem;
}

.home-section-2 #Search-BTN {
    background: linear-gradient(-20deg, var(--brand-color-dk) 0%, var(--brand-color) 100%);
    outline-color: var(--brand-color-lt);
}

.home-section-2 #Swipe-BTN {
    background: linear-gradient(-20deg, var(--primary-color-dk) 0%, var(--primary-color) 100%);
    outline-color: var(--primary-color-lt);
}

.home-section-2 #Nearby-BTN {
    background: linear-gradient(-20deg, var(--accent-color-dk) 0%, var(--accent-color) 100%);
    outline-color: var(--accent-color-lt);
}

.home-section-2 #Live-BTN {
    background: linear-gradient(-20deg, var(--warn-color-dk) 0%, var(--warn-color) 100%);
    outline-color: var(--warn-color-lt);
}


/* Horizontal-scroll snap — modifier classes for .rbox / .row */
.rbox-hscroll {
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    -ms-overflow-style: none;
    scrollbar-width: none;
}
.rbox-hscroll::-webkit-scrollbar {
    display: none;
}
.row-hscroll {
    flex-wrap: nowrap;
}
.row-hscroll > .col-12 {
    flex: 0 0 100%;
    scroll-snap-align: center;
    margin-right: 1rem;
}
.hscroll-dots {
    display: flex;
    justify-content: center;
    gap: .5rem;
    padding: .75rem 0;
    margin-top: -2rem;
}
.hscroll-dots .dot {
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background: rgba(0,0,0,.15);
    transition: background 300ms ease, transform 300ms ease;
    z-index: 1;
}
.hscroll-dots .dot.active {
    background: var(--brand-color);
    transform: scale(1.25);
}

.home-section-4 {
    min-height: auto;
}

.home-section-3-bg,
.home-section-5-bg {
    /* background: linear-gradient(10deg, rgba(255,255,255,.7) 0%, rgba(255,255,255,0) 50%, rgba(255,255,255,.7) 100%) !important; */
    /* backdrop-filter: saturate(180%) blur(20px); */
    background: transparent;
}


/* ─────────────────────────────────────────────────────
   CARDS PAGE — Services Category Panel
   ───────────────────────────────────────────────────── */

.services-category-panel {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 900;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background:rgba(0, 0, 10, .65);
    backdrop-filter: blur(5px);
    animation: scp-fade-in 400ms cubic-bezier(0.77, 0, 0, 1) both;
}

@keyframes scp-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.scp-inner {
    width: 100%;
    max-width: 360px;
    max-height: calc(100vh - 6rem);
    overflow-y: auto;
    padding: 1.5rem 1rem;
    border-radius: 1rem;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.2);
    backdrop-filter: saturate(180%) blur(20px);
    -ms-overflow-style: none;
    scrollbar-width: none;
}
.scp-inner::-webkit-scrollbar {
    display: none;
}

.cat-show-all {
    display: block;
    width: 100%;
    padding: .6rem 1rem;
    margin-bottom: 1rem;
    border-radius: .5rem;
    border: 2px solid rgba(0, 0, 0, 0.4);
    background: transparent;
    color: var(--warn-color);
    font-family: 'soehne-halbfett', sans-serif;
    font-size: .85rem;
    letter-spacing: .05em;
    text-align: center;
    cursor: pointer;
    transition: all 300ms ease;
}
.cat-show-all.active {
    background: var(--warn-color-lt);
    color: var(--warn-color-dk);
    border-color: var(--warn-color-dk);
}

.cat-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .6rem;
    margin-bottom: 1.25rem;
}

.cat-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    padding: .75rem .5rem;
    border-radius: .6rem;
    border: 2px solid rgba(255, 255, 255, 0.25);
    background: rgba(255, 255, 255, 0.06);
    color: rgba(255, 255, 255, 0.5);
    font-family: 'soehne-buch', sans-serif;
    font-size: .7rem;
    line-height: 1.2;
    text-align: center;
    cursor: pointer;
    transition: all 300ms ease;
}
.cat-tile i {
    font-size: 1.4rem;
    transition: color 300ms ease;
}
.cat-tile span {
    display: block;
}
.cat-tile.selected {
    border-color: var(--warn-color-dk);
    background: var(--warn-color-lt);
    color: var(--warn-color-dk);
}
.cat-tile.selected i {
    color: var(--warn-color-dk);
}

.cat-confirm-btn {
    display: block;
    width: 100%;
    padding: .75rem 1rem;
    border-radius: 2rem;
    border: none;
    background: var(--warn-color-dk);
    color: var(--warn-color-lt);
    font-family: 'soehne-halbfett', sans-serif;
    font-size: .95rem;
    letter-spacing: .05em;
    text-align: center;
    cursor: pointer;
    transition: transform 200ms ease, opacity 200ms ease;
}
.cat-confirm-btn:active {
    transform: scale(0.97);
    opacity: .85;
}

/* ─────────────────────────────────────────────────────
   CARDS PAGE — Services Preference Cards (gradient BGs)
   ───────────────────────────────────────────────────── */

.tinder--card[data-id="Opt-Urgency"] .tinder--img {
    background: linear-gradient(-20deg, #1a3a5c 0%, #2c75de 50%, #69e2e1 100%) !important;
}
.tinder--card[data-id="Opt-Location"] .tinder--img {
    background: linear-gradient(-20deg, #4a1942 0%, #916df6 50%, #eb5d8f 100%) !important;
}
.tinder--card[data-id="Opt-Loading-Services"] .tinder--img {
    background: linear-gradient(-20deg, var(--warn-color-dk) 0%, var(--warn-color) 100%) !important;
}


/* ─────────────────────────────────────────────────────
   ORBITAL LOADER — white variant (.pl)
   The app's custom spinner (.pl / .pl__dot from v1's style.css) defaults to
   brand/success/primary dots. This variant renders them solid white for inline
   use over the gradient / dark backgrounds. Pair with an inline --pl-size for
   per-usage sizing, e.g. <div class="pl pl--white" style="--pl-size: 5em;">.
   ───────────────────────────────────────────────────── */

.pl--white {
    --pl-color1: #fff;
    --pl-color2: #fff;
    --pl-color3: #fff;
}


/* ─────────────────────────────────────────────────────
   REVIEW CHIPS + PILLS + SEGMENTED TABS (Details-All-Reviews)
   Designed for a white .rbox background.
   .rt-chip — tappable sort/filter + theme chips
   .rt-pill — static info pill (rating label, % stats)
   .rt-seg  — Overview / All Reviews segmented toggle
   ───────────────────────────────────────────────────── */

.rt-chip {
    flex: 0 0 auto;
    background: #eef0f4;
    /* color: #3c4257; */
    border: 0;
    border-radius: 2rem;
    padding: .35rem .85rem;
    font-family: 'soehne-halbfett', sans-serif; /* Soehne has no CSS weights — swap faces, never font-weight */
    font-size: .8rem;
    line-height: 1.2;
    transition: background 150ms ease, color 150ms ease;
}
.rt-chip .chip-count {
    opacity: .5;
    margin-left: .1rem;
}
.rt-chip.active {
    background: var(--primary-color);
    color: #fff;
}
.rt-chip.active .chip-count {
    opacity: .8;
}

.rt-pill {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    background: #eef0f4;
    color: #13182c;
    border-radius: 2rem;
    padding: .35rem .7rem;
    font-family: 'soehne-halbfett', sans-serif;
    font-size: .8rem;
    line-height: 1.2;
}

.rt-seg {
    display: flex;
    width: 100%;
    background: #eef0f4;
    border-radius: 2rem;
    padding: .25rem;
    gap: .25rem;
}
.rt-seg-btn {
    flex: 1 1 0;
    border: 0;
    background: transparent;
    border-radius: 2rem;
    padding: .55rem .75rem;
    font-family: 'soehne-halbfett', sans-serif;
    font-size: .9rem;
    line-height: 1.2;
    color: #5a6072;
    transition: background 200ms ease, color 200ms ease, box-shadow 200ms ease;
}
.rt-seg-btn.active {
    background: #fff;
    color: #13182c;
    box-shadow: 0 1px 6px rgba(19, 24, 44, .14);
}
.rt-seg.rt-seg-primary .rt-seg-btn.active {
	background: linear-gradient(10deg, var(--primary-color-dk) 0%, var(--primary-color) 100%) !important;
	color: white !important;
}
/* Warn variant (flexible-schedule segments) — multi-select. The gradient lives
   on a ::before underlay so select/unselect can fade (gradients themselves
   can't transition), and two adjacent actives bleed into one pill: shared
   corners square off and the track gap closes, both animated. */
.rt-seg.rt-seg-warn .rt-seg-btn {
	position: relative;
	z-index: 0;
	transition: color 200ms ease, border-radius 200ms ease, transform 200ms ease;
}
.rt-seg.rt-seg-warn .rt-seg-btn::before {
	content: '';
	position: absolute;
	inset: 0;
	border-radius: inherit;
	background: linear-gradient(0deg, var(--warn-color-dk) 0%, var(--warn-color) 100%);
	box-shadow: 0 1px 6px rgba(145, 109, 246, .35);
	opacity: 0;
	transition: opacity 200ms ease, left 200ms ease;
	z-index: -1;
}
.rt-seg.rt-seg-warn .rt-seg-btn:active {
	transform: scale(.96);
}
.rt-seg.rt-seg-warn .rt-seg-btn.active {
	background: transparent !important;
	box-shadow: none;
	color: white !important;
}
.rt-seg.rt-seg-warn .rt-seg-btn.active::before {
	opacity: 1;
}
/* Neighbors both selected → one continuous pill */
.rt-seg.rt-seg-warn .rt-seg-btn.active:has(+ .rt-seg-btn.active) {
	border-top-right-radius: 0;
	border-bottom-right-radius: 0;
}
.rt-seg.rt-seg-warn .rt-seg-btn.active + .rt-seg-btn.active {
	border-top-left-radius: 0;
	border-bottom-left-radius: 0;
}
/* The underlay (not the button) stretches left across the track gap, so the
   pill merges without changing flex layout — no text shift. */
.rt-seg.rt-seg-warn .rt-seg-btn.active + .rt-seg-btn.active::before {
	left: calc(-.25rem - 1px); /* track gap + 1px against seams */
}
/* Optional second line inside a segment (e.g. "8a–12p" under "Morning") */
.rt-seg-btn .rt-seg-hint {
	display: block;
	font-family: 'soehne-buch', sans-serif;
	font-size: .6875rem;
	line-height: 1.2;
	opacity: .6;
}
/* A segment the day's schedule can't offer (Details flex picker — e.g. a Monday
   donut 08–12 + 17–21 greys out Afternoon). Quietly dimmed, not tappable. */
.rt-seg-btn.rt-seg-off {
	opacity: .35;
	pointer-events: none;
}

/* ── Booking-Details fact grid — sibling .selection-box cells must stay
      equal-height when one wraps (e.g. Preferred time "Morning or Evening"),
      and all cells top-align their labels. Same fix as booking/preview.php. */
.bd-fact-grid .selection-box {
	height: 100%;
	align-items: flex-start;
}

/* ── Day-editor time row — native <input type="time"> pair, side by side.
      Lifted from the exception sheet's .rt-ex-sheet__times (Joel prefers the
      native picker over the stacked hour/minute/AM-PM selects). Used by the
      Calendar + Listing-Edit-Availability weekly editors and the
      Express-Booking-Create start/end time pair. */
.rt-time-row {
	display: flex;
	gap: .75rem;
	align-items: center;
	text-align: left;
}
.rt-time-row > div {
	flex: 1;
}
.rt-time-row label {
	font-size: .75rem;
	color: #777;
	text-transform: uppercase;
	margin-bottom: .25rem;
	display: block;
	font-family: 'soehne-halbfett', sans-serif;
}
.rt-time-row input[type="time"],
.rt-time-row input[type="date"],
.rt-time-row input[type="number"] {
	width: 100%;
	padding: .55rem .75rem;
	border-radius: 8px;
	border: 1px solid #ddd;
	font-size: 1rem;
	font-family: 'soehne-halbfett', sans-serif;
	background: #fff;
	-webkit-appearance: none;
	appearance: none;
}
/* Multiple start–end blocks per day (donuts render as 2+ blocks) */
.rt-time-block + .rt-time-block {
	margin-top: .625rem;
}
/* Remove sits inline with the Close label — same size, right-aligned */
.rt-time-label-row {
	display: flex;
	align-items: baseline;
}
.rt-time-label-row .remove-hours-btn {
	margin-left: auto;
	font-size: .75rem;
	text-transform: uppercase;
	font-family: 'soehne-halbfett', sans-serif;
	color: var(--brand-color) !important;
	line-height: 1;
	/* it's a <button> now — strip the UA chrome down to the label look */
	background: none;
	border: 0;
	padding: 0;
	cursor: pointer;
}
.rt-time-label-row .remove-hours-btn:active {
	transform: scale(.94);
}


/* ─────────────────────────────────────────────────────
   MESSAGES VIEW THEME & CHAT STYLES
   ───────────────────────────────────────────────────── */
    	/* ===========================================
    	   TWO-PANEL STAGE — info slide / chat slide
    	   (messaging-chat-redesign.md §2.1; the proven
    	   #Home-Stage scroll-snap pattern)
    	   =========================================== */
    	#MV-Stage {
    	    width: 100vw;
    	    height: 100vh;
    	    overflow-x: hidden;
    	    overflow-y: scroll;
    	    scroll-snap-type: y mandatory;
    	    overscroll-behavior-y: contain;
    	    -webkit-overflow-scrolling: touch;
    	    scrollbar-width: none;
    	    position: fixed;
    	    top: 0;
    	    left: 0;
    	    bottom: 0;
    	    right: 0;
    	}
    
    	#MV-Stage::-webkit-scrollbar {
    	    display: none;
    	}
    
    	.mv-slide {
    	    width: 100vw;
    	    position: relative;
    	    scroll-snap-align: start;
    	    scroll-snap-stop: always;
    	}
    
    	/* Slide 1 — Who · What · When · How much. Min one viewport, grows with content
    	   (scrolls within the snap flow, like .home-slide's fit-content). */
    	#MV-Info {
    	    min-height: 100vh;
    	    height: fit-content;
    	    display: flex;
    	    flex-direction: column;
    	    padding-top: calc(4.125rem + env(safe-area-inset-top));
    	}
    
    	/* Slide 2 — the chat. Exactly one viewport; flex column so the composer is a
    	   normal in-flow child at the bottom (no position:fixed — kills the old
    	   .chat-form float-over and most keyboard weirdness). */
    	#MV-Chat {
    	    height: 100vh;
    	    display: flex;
    	    flex-direction: column;
    	    overflow: hidden;
    	}
    
    	/* The chat scroller — history scrolls HERE only. overscroll-behavior:contain so
    	   hitting the top of history never chains into the stage and yanks the user to
    	   the info slide; the return handle is the way back up. */
    	#Chat-Messages {
    	    flex: 1 1 auto;
    	    min-height: 0;
    	    overflow-y: auto;
    	    overscroll-behavior: contain;
    	    -webkit-overflow-scrolling: touch;
    	    /* Bottom: the composer FLOATS (position:absolute) at ~3.5rem + safe-area tall, so reserve
    		   its height PLUS a ~1.5rem gap — otherwise the newest message rests flush against (or
    		   behind) the composer when the thread pins to the bottom. */
    	    padding: calc(env(safe-area-inset-top) + 7rem) 1rem calc(env(safe-area-inset-bottom) + 5rem);
    	    scrollbar-width: none;
    	    /* Anchor short threads to the bottom (iMessage-style) — the first run's
    		   margin-top:auto below does the work; this just makes the scroller a flex column. */
    	    display: flex;
    	    flex-direction: column;
    	}
    
    	#Chat-Messages::-webkit-scrollbar {
    	    display: none;
    	}
    
    	/* Runs keep their natural height (content overflows and SCROLLS, never compresses).
    	   The first run gets margin-top:auto: it absorbs free space when the thread is short
    	   (pushing everything down to just above the composer) and collapses to 0 once content
    	   overflows — so scrolling up and reaching the top still work, which a
    	   justify-content:flex-end would break in the WKWebView. */
    	#Chat-Messages>* {
    	    flex: 0 0 auto;
    	}
    
    	#Chat-Messages> :first-child {
    	    margin-top: auto;
    	}

    	/* ===========================================
    	   LALEH-VIEW — single-panel chat (laleh-chat-surfaces.md B2).
    	   Class-scoped equivalents of the id-scoped layout trio above
    	   (#MV-Stage/#MV-Chat/#Chat-Messages) so Laleh's thread reuses
    	   the chat system additively — Messages-View untouched.
    	   =========================================== */
    	/* No two-panel stage: one fixed 100vh flex column (the #MV-Chat
    	   recipe carrying #MV-Stage's fixed positioning). */
    	.laleh-chat {
    	    position: fixed;
    	    top: 0;
    	    left: 0;
    	    bottom: 0;
    	    right: 0;
    	    height: 100vh;
    	    display: flex;
    	    flex-direction: column;
    	    overflow: hidden;
    	}

    	/* The scroller — the #Chat-Messages recipe verbatim, incl. the
    	   bottom-anchor trick for short threads. */
    	.laleh-list {
    	    flex: 1 1 auto;
    	    min-height: 0;
    	    overflow-y: auto;
    	    overscroll-behavior: contain;
    	    -webkit-overflow-scrolling: touch;
    	    padding: calc(env(safe-area-inset-top) + 7rem) 1rem calc(env(safe-area-inset-bottom) + 5rem);
    	    scrollbar-width: none;
    	    display: flex;
    	    flex-direction: column;
    	}

    	.laleh-list::-webkit-scrollbar {
    	    display: none;
    	}

    	.laleh-list>* {
    	    flex: 0 0 auto;
    	}

    	.laleh-list> :first-child {
    	    margin-top: auto;
    	}

    	/* Laleh's "avatar" — brand-gradient circle + the comment-sparkle glyph
    	   (the Home input's icon; no image asset). Rides .mv-ch-avatar's geometry. */
    	.lv-avatar {
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	    background: linear-gradient(135deg, var(--brand-color) 0%, var(--primary-color) 100%);
    	    color: #fff;
    	    font-size: 1.25rem;
    	}

    	/* Typing-dots bubble — Laleh is "thinking" while the request runs
    	   (client-built; removed when the reply fills in). */
    	.lv-typing .mvb-bubble {
    	    display: inline-flex;
    	    align-items: center;
    	    gap: .3rem;
    	}

    	.lv-dot {
    	    width: .5rem;
    	    height: .5rem;
    	    border-radius: 50%;
    	    background: currentColor;
    	    opacity: .35;
    	    animation: lv-dot 1.2s infinite ease-in-out;
    	}

    	.lv-dot:nth-child(2) {
    	    animation-delay: .15s;
    	}

    	.lv-dot:nth-child(3) {
    	    animation-delay: .3s;
    	}

    	@keyframes lv-dot {

    	    0%,
    	    60%,
    	    100% {
    	        opacity: .35;
    	        transform: translateY(0);
    	    }

    	    30% {
    	        opacity: 1;
    	        transform: translateY(-3px);
    	    }
    	}

    	/* Result strip under a Laleh reply (WS-D, positioning per D2.1) — a
    	   SIBLING of the run in .laleh-list (never inside the side-aligned run),
    	   so it spans the container minus the list's 1rem side padding. ≤3
    	   results render full RT.ResultCard cards; more render the .lv-tiles
    	   scroll row. Ends with the "View search results" hand-off button. */
    	.lv-cards {
    	    width: 100%;
    	    margin: .25rem 0 .5rem;
    	}

    	.lv-cards .searchResult {
    	    padding: 0;
    	}

    	/* v1's style.css gives .tinder--card a hard 90vw (deck sizing) — inside
    	   the chat strip the card fills its container instead. */
    	.lv-cards .tinder--card {
    	    width: 100%;
    	}

    	/* >3 results: a horizontally scrolling row of photo tiles, title only. */
    	.lv-tiles {
    	    display: flex;
    	    gap: .5rem;
    	    overflow-x: auto;
    	    -webkit-overflow-scrolling: touch;
    	    scrollbar-width: none;
    	    padding-bottom: .25rem;
    	}

    	.lv-tiles::-webkit-scrollbar {
    	    display: none;
    	}

    	.lv-tile {
    	    flex: 0 0 auto;
    	    width: 9rem;
    	    height: 6.5rem;
    	    border-radius: .75rem;
    	    background-size: cover;
    	    background-position: center;
    	    background-color: rgba(255, 255, 255, .15);
    	    position: relative;
    	    overflow: hidden;
    	    display: flex;
    	    align-items: flex-end;
    	}

    	/* Legibility gradient behind the title. */
    	.lv-tile::after {
    	    content: '';
    	    position: absolute;
    	    inset: 0;
    	    background: linear-gradient(180deg, rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, .65) 100%);
    	}

    	.lv-tile-title {
    	    position: relative;
    	    z-index: 1;
    	    color: #fff;
    	    font-family: soehne-halbfett;
    	    font-size: .75rem;
    	    line-height: 1.2;
    	    padding: .4rem .5rem;
    	    display: -webkit-box;
    	    -webkit-line-clamp: 2;
    	    line-clamp: 2;
    	    -webkit-box-orient: vertical;
    	    overflow: hidden;
    	}

    	/* The strip's closing hand-off button (paint = BUTTON SYSTEM ghost-primary). */
    	.lv-cards .lv-see-all {
    	    margin-top: .25rem;
    	}

    	/* Reply chips under a Laleh bubble (seed → AI-Search, signin nudge).
    	   Paint comes from .mvc-btn--primary (BUTTON SYSTEM) — this only
    	   un-blocks the block-level button for an inline chip row. */
    	.lv-chips {
    	    margin-top: .375rem;
    	    display: flex;
    	    gap: .5rem;
    	    flex-wrap: wrap;
    	}

    	.lv-chips .lv-chip {
    	    display: inline-block;
    	    width: auto;
    	}

    	/* ── Laleh list: swipe-to-delete rows (WS-J follow-up) ─────────
    	   The anchor slides left; the delete button rides in from off-row-
    	   right in sync (both transformed by Laleh.js, so no background
    	   dependency — nothing shows until the row actually moves). */
    	.lv-row {
    	    position: relative;
    	    overflow: hidden;
    	}

    	.lv-row-slide {
    	    display: block;
    	    will-change: transform;
    	}

    	/* Geometry only — paint lives in the BUTTON SYSTEM section at file end. */
    	.lv-row-del {
    	    position: absolute;
    	    top: 0;
    	    bottom: 0;
    	    right: 0;
    	    width: 88px;
    	    border: 0;
    	    font-size: 1.15rem;
    	    transform: translateX(100%);
    	    will-change: transform;
    	}

    	.lv-row-slide.lv-snap,
    	.lv-row-del.lv-snap {
    	    transition: transform .22s ease;
    	}

    	.lv-row-gone {
    	    transition: height .25s ease, opacity .25s ease;
    	    opacity: 0;
    	}

    	/* Laleh-View top bar: right-side action pair (armed-state paint lives
    	   in the BUTTON SYSTEM section at file end). */
    	.lv-ch-actions {
    	    display: flex;
    	    gap: .5rem;
    	}

    	/* Two buttons right vs one left breaks space-between's centering —
    	   pin Laleh's identity to the true center instead (top mirrors the
    	   header's padding-top so vertical alignment is untouched). Scoped
    	   to .laleh-chat; Messages-View keeps its balanced flanks. */
    	.laleh-chat .mv-ch-contact {
    	    position: absolute;
    	    left: 50%;
    	    top: calc(env(safe-area-inset-top) + .625rem);
    	    transform: translateX(-50%);
    	}

    	/* Retired answer chips (single-use: chosen, or the conversation moved
    	   on) — dimmed and inert; the picked one keeps its full read. Shared by
    	   Laleh-View rows and Snap's refine chat (both use .lv-chips in-thread). */
    	.lv-chips .lv-chip.lv-chip-done {
    	    opacity: .45;
    	    pointer-events: none;
    	}

    	.lv-chips .lv-chip.lv-chip-picked {
    	    opacity: .85;
    	}

    	/* Error pill — the #AlertBox idiom: floats above the composer, JS
    	   mirrors the keyboard-lift transform. */
    	.lv-alert {
    	    position: absolute;
    	    left: 0;
    	    right: 0;
    	    bottom: calc(3.6rem + env(safe-area-inset-bottom));
    	    margin: 0 .75rem;
    	    z-index: 150;
    	}

    	/* Sticky handle at the end of the info slide — the way down to the chat. */
    	.mv-chat-handle {
    	    position: sticky;
    	    bottom: 0;
    	    margin-top: auto;
    	    z-index: 5;
    	    padding: 1.5rem 1rem max(.75rem, env(safe-area-inset-bottom));
    	    cursor: pointer;
    	    /* background-color: var(--default-background-color); */
    	    /* backdrop-filter: var(--default-backdrop-filter);
    		filter: drop-shadow(0 -5px 15px rgba(0,0,0,0.1)); */
    	    background: linear-gradient(0deg, rgba(0, 0, 0, .55) 0%, rgba(0, 0, 0, .34) 45%, rgba(0, 0, 0, .14) 75%, rgba(0, 0, 0, 0) 100%);
    	    /* background: linear-gradient(0deg, rgba(255, 255, 255, .55) 0%, rgba(255, 255, 255, .34) 45%, rgba(255, 255, 255, .14) 75%, rgba(255, 255, 255, 0) 100%); */
    	}
    
    	.mv-handle-bar {
    	    background: #fff;
    	    border-radius: 1.25rem;
    	    padding: .625rem .75rem .75rem;
    	    text-align: center;
    	    box-shadow: 0 .25rem 1.25rem rgba(0, 0, 0, .18);
    	}
    
    	.mv-handle-grip {
    	    width: 2.5rem;
    	    height: .25rem;
    	    border-radius: .125rem;
    	    background: rgba(0, 0, 0, .15);
    	    margin: 0 auto .375rem;
    	}
    
    	/* ===========================================
    	   STAGE B — chat header · bubbles · composer
    	   (messaging-chat-redesign.md §3, B-G1–B-G5; styles inline for now,
    	   destined for app.css once the look settles)
    	   =========================================== */
    
    	/* ── B-G1: the chat header — back circle · contact avatar+name · details circle,
    	   over a subtle black→transparent gradient from the top of the screen. The strip
    	   itself passes taps through (pointer-events) so only the controls catch them. */
    	.mv-chat-header {
    	    position: absolute;
    	    top: 0;
    	    left: 0;
    	    right: 0;
    	    z-index: 7;
    	    display: flex;
    	    align-items: flex-start;
    	    justify-content: space-between;
    	    padding: calc(env(safe-area-inset-top) + .625rem) 1rem 2.25rem;
    	    background: linear-gradient(180deg, rgba(0, 0, 0, .55) 0%, rgba(0, 0, 0, .34) 45%, rgba(0, 0, 0, .14) 75%, rgba(0, 0, 0, 0) 100%);
    	    pointer-events: none;
    	}
    
    	.mv-ch-btn {
    	    pointer-events: auto;
    	    width: 2.5rem;
    	    height: 2.5rem;
    	    margin-top: .5rem;
    	    /* centers the circle on the avatar's midline */
    	    border-radius: 50%;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	    background: rgba(255, 255, 255, .88);
    	    /* -webkit-backdrop-filter: blur(10px); */
    	    /* backdrop-filter: blur(10px); */
    	    color: #1c2430;
    	    font-size: 1.05rem;
    	    box-shadow: 0 .25rem .875rem rgba(0, 0, 0, .22);
    	    outline: var(--default-outline);
    	    border: 2px solid white;
    	    cursor: pointer;
    	}
    
    	.mv-ch-contact {
    	    pointer-events: auto;
    	    display: flex;
    	    flex-direction: column;
    	    align-items: center;
    	    gap: .3125rem;
    	}
    
    	.mv-ch-avatar {
    	    width: 3.5rem;
    	    height: 3.5rem;
    	    border-radius: 50%;
    	    background-size: cover;
    	    background-position: center;
    	    outline: var(--default-outline);
    	    border: 2px solid white;
    	    box-shadow: 0 .25rem 1rem rgba(0, 0, 0, .3);
    	}
    
    	.mv-ch-name {
    	    color: #fff;
    	    font-size: .8125rem;
    	    font-family: soehne-halbfett;
    	    text-shadow: 0 1px 3px rgba(0, 0, 0, .65), 0 1px 10px rgba(0, 0, 0, .5);
    	    max-width: 11rem;
    	    overflow: hidden;
    	    text-overflow: ellipsis;
    	    white-space: nowrap;
    	}
    
    	/* ── B-G2: the bubbles — iMessage-like, wider (no inline avatars), glassy + glows.
    	   Mine = blues · theirs = light/clear glass · stamps live OUTSIDE the bubble. */
    	.mvb-run {
    	    display: flex;
    	    flex-direction: column;
    	    margin: .5rem 0;
    	}
    
    	.mvb-mine {
    	    align-items: flex-end;
    	}
    
    	.mvb-theirs {
    	    align-items: flex-start;
    	}
    
    	.mvb-center {
    	    align-items: center;
    	    text-align: center;
    	}
    
    	.mvb-bubble {
    	    max-width: 78%;
    	    padding: .625rem .9375rem;
    	    border-radius: 1.25rem;
    	    font-size: .9375rem;
    	    line-height: 1.35;
    	    overflow-wrap: break-word;
    	    margin-top: .1875rem;
    	}
    
    	.mvb-mine .mvb-bubble {
    	    background: linear-gradient(160deg, var(--primary-color-lt) -10%, var(--primary-color) 55%, var(--primary-color-dk) 140%);
    	    color: #fff;
    	    box-shadow: 0 .375rem 1rem rgba(44, 117, 222, .35), 0 .125rem .375rem rgba(44, 117, 222, .22);
    	}
    
    	.mvb-mine .mvb-bubble.mvb-last {
    	    border-bottom-right-radius: .375rem;
    	}
    
    	.mvb-theirs .mvb-bubble {
    	    background: rgba(255, 255, 255, .72);
    	    -webkit-backdrop-filter: blur(14px);
    	    backdrop-filter: blur(14px);
    	    border: 1px solid rgba(255, 255, 255, .65);
    	    color: #1c2430;
    	    box-shadow: 0 .375rem 1rem rgba(21, 56, 105, .12);
    	}
    
    	.mvb-theirs .mvb-bubble.mvb-last {
    	    border-bottom-left-radius: .375rem;
    	}
    
    	/* C-D7 — the "👋 I'm Here!" co-presence bubble: success green + white text on BOTH sides
    	   (overrides the mine-blue / theirs-glass fills), still side-aligned to the asserting party. */
    	.mvb-mine .mvb-bubble.mvb-here,
    	.mvb-theirs .mvb-bubble.mvb-here {
    	    background: linear-gradient(160deg, var(--success-color-lt) -10%, var(--success-color) 55%, var(--success-color-dk) 140%);
    	    color: #fff;
    	    border: none;
    	    -webkit-backdrop-filter: none;
    	    backdrop-filter: none;
    	    box-shadow: 0 .375rem 1rem rgba(34, 197, 94, .32);
    	    font-family: soehne-halbfett;
    	}
    
    	.mvb-stamp {
    	    font-size: .6875rem;
    	    color: rgba(60, 70, 85, .65);
    	    background: rgba(255, 255, 255, .45);
    	    border-radius: 1rem;
    	    padding: .125rem .625rem;
    	    margin: .375rem calc(1px + .75rem) 0;
    	}
    
    	/* ── B-G3: system messages = center-aligned timeline events, three loudnesses. */
    	/* L1 — subtle status line weaving through the conversation. */
    	.mvb-status {
    	    display: inline-block;
    	    max-width: 86%;
    	    font-size: .75rem;
    	    color: rgba(28, 36, 48, .55);
    	    background: rgba(255, 255, 255, .55);
    	    -webkit-backdrop-filter: blur(10px);
    	    backdrop-filter: blur(10px);
    	    border: 1px solid rgba(255, 255, 255, .5);
    	    padding: .4375rem .9375rem;
    	    border-radius: 1rem;
    	    box-shadow: 0 .25rem .75rem rgba(21, 56, 105, .08);
    	}
    
    	/* C-D7 follow-on — the Report a Problem action rides as a subdued muted ghost;
    	   paints via the BUTTON SYSTEM section at file end (.btn-ghost-muted group). */
    
    	/* L2 — the louder CTA card: event text + action buttons. */
    	.mvb-cta {
    	    width: 100%;
    	    background: rgba(255, 255, 255, .8);
    	    -webkit-backdrop-filter: blur(16px);
    	    backdrop-filter: blur(16px);
    	    border: 1px solid rgba(255, 255, 255, .7);
    	    border-radius: 1.125rem;
    	    padding: .875rem 1rem;
    	    color: #1c2430;
    	    font-size: .875rem;
    	    line-height: 1.4;
    	    box-shadow: 0 .5rem 1.5rem rgba(21, 56, 105, .16);
    	}
    
    	.mvb-cta-actions {
    	    display: flex;
    	    gap: .5rem;
    	    justify-content: center;
    	    flex-wrap: wrap;
    	    margin-top: .75rem;
    	}
    
    	.mvb-cta-btn {
    	    display: inline-block;
    	    padding: .5rem 1.125rem;
    	    border-radius: 2rem;
    	    font-size: .8125rem;
    	    font-family: soehne-halbfett;
    	    background: linear-gradient(160deg, var(--primary-color-lt), var(--primary-color));
    	    color: #fff !important;
    	    box-shadow: 0 .375rem .875rem rgba(44, 117, 222, .35);
    	}
    
    	.mvb-cta-btn--ghost {
    	    background: rgba(255, 255, 255, .6);
    	    color: #1c2430 !important;
    	    border: 1px solid rgba(28, 36, 48, .12);
    	    box-shadow: none;
    	}
    
    	/* ── C-D1: live booking card — the #KeeperBoost rbox layout repurposed.
    	   Gradient strip ("New Booking Request"), dark header = lite booking details
    	   (date cube + key lines), white body = the panel's role×status buttons. */
    	.mvb-card {
    	    width: 100%;
    	    text-align: left;
    	    margin-top: .1875rem;
    	}
    
    	.mvb-card .mvc-strip {
    	    padding-top: .25rem;
    	    text-transform: uppercase;
    	}
    
    	/* Context color — the strip reads as the event's status family (pending → warn). */
    	.mvb-card .mvc-strip--warn {
    	    background: linear-gradient(260deg, var(--warn-color-dk) 25%, var(--warn-color-lt) 100%);
    	}
    
    	.mvb-card .mvc-header {
    	    background: linear-gradient(135deg, #0a1428 0%, #0f2a47 45%, #0e3a5f 100%);
    	    padding: .5rem;
    	    position: relative;
    	    overflow: hidden;
    	    color: #fff;
    	    border-radius: 1rem;
    	}
    
    	.mvb-card .mvc-header::before {
    	    content: "";
    	    position: absolute;
    	    inset: 0;
    	    background:
    	        radial-gradient(120% 80% at 100% 0%, rgba(34, 211, 238, .10), transparent 55%),
    	        radial-gradient(80% 60% at 0% 100%, rgba(251, 191, 36, .08), transparent 60%);
    	    pointer-events: none;
    	}
    
    	.mvb-card .mvc-row {
    	    position: relative;
    	    align-items: center;
    	}
    
    	.mvb-card .mvc-cube {
    	    max-width: 5rem;
    	    align-self: center;
    	    text-align: center;
    	}
    
    	.mvb-card .mvc-cube h3 {
    	    color: #fff;
    	    margin-top: -.25rem;
    	}
    
    	.mvb-card .mvc-cube p {
    	    color: rgba(255, 255, 255, .85);
    	}
    
    	.mvb-card .mvc-lines {
    	    font-size: .8125rem;
    	    color: rgba(255, 255, 255, .92);
    	}
    
    	.mvb-card .mvc-lines p {
    	    line-height: 1.5;
    	}
    
    	.mvb-card .mvc-lines i {
    	    width: 1.25rem;
    	    text-align: center;
    	    color: rgba(255, 255, 255, .55);
    	}
    
    	/* The details header deep-links to Booking-Details — block anchor + a trailing
    	   disclosure chevron (the action buttons below stay their own links). */
    	.mvc-details-link {
    	    display: block;
    	    text-decoration: none;
    	}
    
    	.mvc-go {
    	    color: rgba(255, 255, 255, .5);
    	    font-size: .8125rem;
    	    padding-left: .375rem;
    	}
    
    	/* Booking-action buttons — themed to the context family (warn = pending).
    	   Shared by the chat booking card AND the info panel's pending actions. */
    	.mvc-btn {
    	    display: block;
    	    width: 100%;
    	    padding: .5rem 1rem;
    	    border-radius: 2rem;
    	    font-size: .8125rem;
    	    font-family: soehne-halbfett;
    	    border: 0;
    	    text-align: center;
    	}
		.mvc-btn--lg {
			padding: .75rem 1rem;
			font-size: 1rem;
		}

		/* Tap to Pay wave mark — the genuine SF Symbol wave.3.right.circle.fill, inlined
		   FontAwesome-style: sized to the button text and inheriting its color via
		   fill="currentColor". Apple req 5.5 permits only this symbol in the button.
		   See docs/plans/stripe-tap-to-pay.md §9.6. (Nudge size/valign to taste.) */
		.ttp-icon {
			/* display: inline-block; */
			width: 2rem;
			height: 2rem;
			/* margin-right: .4em; */
			/* vertical-align: -.2em; */
			position: absolute;
			left: .5rem;
			top: 50%;
			transform: translateY(-50%);
		}

    	/* .mvc-btn--warn / --success / --primary / --brand / --warning / --dark and the
    	   --ghost family now paint via the BUTTON SYSTEM section at the end of this file
    	   (shared with .j-button and .btn-ghost); only geometry lives here. */
    
    	/* Accepted / awaiting-payment context — green success family (the accept card). */
    	.mvb-card .mvc-strip--success {
    	    background: linear-gradient(260deg, var(--success-color-dk) 25%, var(--success-color-lt) 100%);
    	}
    
    	/* Confirmed / paid context — primary blue family (the confirmed countdown card, C-D6). */
    	.mvb-card .mvc-strip--primary {
    	    background: linear-gradient(260deg, var(--primary-color-dk) 25%, var(--primary-color-lt) 100%);
    	}
    
    	/* Completion / review context — brand (pink) family (the "Leave a Review" card, C-D7). */
    	.mvb-card .mvc-strip--brand {
    	    background: linear-gradient(260deg, var(--brand-color-dk) 25%, var(--brand-color-lt) 100%);
    	}
    
    	.mvc-confirmed {
    	    padding: .25rem 0;
    	}
    
    	.mvc-countdown {
    	    font-family: soehne-halbfett;
    	    font-size: 1.0625rem;
    	    color: var(--primary-color-dk);
    	    margin-top: .0625rem;
    	}
    
    	/* (--ghost-brand = the walk-away ghost; --ghost-success = the "I'm Here!" secondary;
    	   --ghost-primary = the Booking Details ghost beside Pay Invoice — all in the
    	   BUTTON SYSTEM section at file end.) */
    
    	/* ── C-D2: a responded card reduces to one minimal full-width outcome line. */
    	.mvb-resolved {
    	    width: 100%;
    	    font-size: .75rem;
    	    color: rgba(28, 36, 48, .6);
    	    background: rgba(255, 255, 255, .5);
    	    -webkit-backdrop-filter: blur(10px);
    	    backdrop-filter: blur(10px);
    	    border: 1px solid rgba(255, 255, 255, .45);
    	    border-radius: 1rem;
    	    padding: .375rem .5rem;
    	    display: flex;
    	    align-items: center;
    	    text-align: left;
    	}
    
    	.mvb-resolved .mvb-stamp {
    	    margin: 0 !important;
    	}
    
    	.mvb-resolved--warn {
    	    background: linear-gradient(160deg, rgba(255, 255, 255, .5) 35%, rgba(145, 109, 246, .65) 125%);
    	    color: var(--warn-color-dk);
    	}
    
    	.mvb-resolved--brand {
    	    background: linear-gradient(160deg, rgba(255, 255, 255, .5) 35%, rgba(235, 93, 143, .65) 125%);
    	    color: var(--brand-color-dk);
    	}
    
    	.mvb-resolved--success {
    	    background: linear-gradient(160deg, rgba(255, 255, 255, .5) 35%, rgba(34, 197, 94, .55) 125%);
    	    color: var(--success-color-dk);
    	}
    
    	/* Confirmed (paid) reduced pill — primary blue ghost (C-D6). */
    	.mvb-resolved--primary {
    	    background: linear-gradient(160deg, rgba(255, 255, 255, .5) 35%, rgba(44, 117, 222, .5) 125%);
    	    color: var(--primary-color-dk);
    	}
    
    	/* L3 — RedTulip speaking: loudest, brand colors, glowing. */
    	.mvb-admin {
    	    max-width: 86%;
    	    background: linear-gradient(150deg, var(--brand-color-lt) -20%, var(--brand-color) 55%, var(--brand-color-dk) 140%);
    	    color: #fff;
    	    border-radius: 1.125rem;
    	    padding: .875rem 1rem;
    	    font-size: .9375rem;
    	    line-height: 1.4;
    	    box-shadow: 0 .5rem 1.75rem rgba(235, 93, 143, .45), 0 .125rem .5rem rgba(167, 58, 84, .3);
    	}
    
    	.mvb-admin-label {
    	    display: block;
    	    font-size: .6875rem;
    	    letter-spacing: .08em;
    	    text-transform: uppercase;
    	    opacity: .85;
    	    font-family: soehne-halbfett;
    	    margin-bottom: .25rem;
    	}
    
    	/* ── B-G5: the iMessage composer — "+" (attachments dropup) · growing input ·
    	   mic-in-ring ↔ send-on-text (restructured 2026-07-07, chat-attachments.md Phase A).
    	   z 121 floats the bar (and its child dropup) above the z-120 tap-to-close backdrop. */
    	.mv-composer-bar {
    	    display: flex;
    	    align-items: flex-end;
    	    gap: .5rem;
    	    padding: .5rem .75rem calc(.75rem + env(safe-area-inset-bottom));
    	    position: absolute;
    	    bottom: 0;
    	    left: 0;
    	    right: 0;
    	    z-index: 121;
    	    /* background: linear-gradient(0deg, rgba(0, 0, 0, .55) 0%, rgba(0, 0, 0, .34) 45%, rgba(0, 0, 0, .14) 75%, rgba(0, 0, 0, 0) 100%); */
    	    background: linear-gradient(0deg, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, .64) 45%, rgba(255, 255, 255, .24) 75%, rgba(255, 255, 255, 0) 100%);
    	}
    
    	/* While the keyboard is up it covers the home-indicator zone — drop the safe-area
    	   padding so the lifted bar sits flush on the keyboard (JS toggles this). */
    	.mv-composer-bar.kb-open {
    	    padding-bottom: .75rem;
    	}
    
    	/* Error pill floats ABOVE the composer (not in the flow below it) and above the recorder
    	   sheet; JS mirrors the composer's transform so it lifts with the keyboard. */
    	#AlertBox {
    	    position: absolute;
    	    left: 0;
    	    right: 0;
    	    bottom: calc(3.6rem + env(safe-area-inset-bottom));
    	    margin: 0 .75rem;
    	    z-index: 150;
    	}
    
    	/* The left circle — was the mic, now the "+" (#MV-PlusBTN); same geometry. */
    	.mv-comp-mic {
    	    flex: 0 0 auto;
    	    width: 2.25rem;
    	    height: 2.25rem;
    	    border-radius: 50%;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	    background: rgba(28, 36, 48, .06);
    	    color: #5a6675;
    	    font-size: 1rem;
    	    cursor: pointer;
    	}
    
    	.mv-comp-form {
    	    flex: 1;
    	    display: flex;
    	    align-items: flex-end;
    	    position: relative;
    	    min-width: 0;
    	}
    
    	.mv-comp-input {
    	    flex: 1;
    	    resize: none;
    	    border: 1px solid rgba(28, 36, 48, .16);
    	    border-radius: 1.25rem;
    	    background: rgba(255, 255, 255, .92);
    	    padding: .4375rem 2.625rem .4375rem .9375rem;
    	    font-size: .9375rem;
    	    line-height: 1.3;
    	    min-height: 2.25rem;
    	    /* rest height = the mic's 36px, so the bar rows align */
    	    max-height: 7.5rem;
    	    overflow-y: auto;
    	    outline: none;
    	}
    
    	.mv-comp-send {
    	    position: absolute;
    	    right: .25rem;
    	    bottom: .25rem;
    	    /* 4 + 28 + 4 = the 36px rest height — dead center */
    	    width: 1.75rem;
    	    height: 1.75rem;
    	    border-radius: 50%;
    	    border: 0;
    	    padding: 0;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	    background: linear-gradient(160deg, var(--primary-color-lt), var(--primary-color));
    	    color: #fff;
    	    font-size: .875rem;
    	    box-shadow: 0 .25rem .625rem rgba(44, 117, 222, .4);
    	    transform: scale(0);
    	    opacity: 0;
    	    transition: transform 160ms ease, opacity 160ms ease;
    	}
    
    	/* Mic↔send sequencing (chat-attachments.md §6.3): the 70ms delay lives ONLY on
    	   .is-visible, so the send waits for the mic's 120ms exit on the way IN but
    	   leaves immediately (base rule, no delay) the moment the input empties. */
    	.mv-comp-send.is-visible {
    	    transform: scale(1);
    	    opacity: 1;
    	    transition-delay: 70ms;
    	}

    	/* The in-ring mic (#MV-MicBTN) — occupies the send button's slot while the input
    	   is empty; _composerSync() swaps them. Faster 120ms exit so the send's delayed
    	   entrance reads as a sequence, not a collision. */
    	.mv-comp-micring {
    	    position: absolute;
    	    right: .25rem;
    	    bottom: .25rem;
    	    width: 1.75rem;
    	    height: 1.75rem;
    	    border-radius: 50%;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	    background: rgba(28, 36, 48, .06);
    	    color: #5a6675;
    	    font-size: .8125rem;
    	    cursor: pointer;
    	    transition: transform 120ms ease, opacity 120ms ease;
    	}

    	.mv-comp-micring.is-hidden {
    	    transform: scale(0);
    	    opacity: 0;
    	    pointer-events: none;
    	}
    
    	/* Send-in-flight spinner — mic-sized, right of the input; display-toggled so the
    	   idle bar stays tight (a flex gap would still count a zero-width item). */
    	.mv-comp-spin {
    	    flex: 0 0 auto;
    	    display: none;
    	    width: 2.25rem;
    	    height: 2.25rem;
    	    align-items: center;
    	    justify-content: center;
    	    color: #5a6675;
    	    font-size: 1rem;
    	}
    
    	.mv-comp-spin.is-active {
    	    display: flex;
    	}

    	/* ── "+" dropup (chat-attachments.md Phase A) — a small glass card anchored above
    	   the "+" INSIDE the composer bar, so _syncKeyboard()'s translateY carries it. The
    	   invisible backdrop is the bar's SIBLING at z 120 (under the bar's 121): any tap
    	   outside the bar closes the menu, the "+"/rows stay tappable. */
    	.mv-plus-backdrop {
    	    position: absolute;
    	    inset: 0;
    	    z-index: 120;
    	    background: transparent;
    	}

    	.mv-plus-backdrop[hidden] {
    	    display: none;
    	}

    	.mv-plus-menu {
    	    position: absolute;
    	    left: .625rem;
    	    bottom: calc(100% - .125rem);
    	    min-width: 14rem;
    	    padding: .375rem;
    	    border-radius: 1.125rem;
    	    background: rgba(255, 255, 255, .92);
    	    -webkit-backdrop-filter: blur(16px);
    	    backdrop-filter: blur(16px);
    	    border: 1px solid rgba(255, 255, 255, .7);
    	    box-shadow: 0 .75rem 2rem rgba(21, 56, 105, .22);
    	    transform-origin: bottom left;
    	    transform: scale(.7);
    	    opacity: 0;
    	    transition: transform 180ms cubic-bezier(.32, .72, 0, 1), opacity 140ms ease;
    	    pointer-events: none;
    	}

    	.mv-plus-menu.open {
    	    transform: scale(1);
    	    opacity: 1;
    	    pointer-events: auto;
    	}

    	.mv-plus-menu[hidden] {
    	    display: none;
    	}

    	.mv-plus-row {
    	    display: flex;
    	    align-items: center;
    	    gap: .75rem;
    	    width: 100%;
    	    min-height: 2.75rem;
    	    padding: .375rem .75rem;
    	    border: 0;
    	    border-radius: .875rem;
    	    background: transparent;
    	    color: #1c2430;
    	    font-size: .9375rem;
    	    text-align: left;
    	    -webkit-touch-callout: none;
    	    -webkit-user-select: none;
    	    user-select: none;
    	}

    	.mv-plus-row:active {
    	    background: rgba(28, 36, 48, .07);
    	}

    	.mv-plus-ico {
    	    flex: 0 0 auto;
    	    width: 2rem;
    	    height: 2rem;
    	    border-radius: 50%;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	    background: rgba(44, 117, 222, .12);
    	    color: var(--primary-color);
    	    font-size: .875rem;
    	}

    	.mv-plus-txt {
    	    display: flex;
    	    flex-direction: column;
    	    min-width: 0;
    	}

    	.mv-plus-sub {
    	    font-size: .6875rem;
    	    color: rgba(28, 36, 48, .45);
    	}

    	/* Pre-booking lock (§6.1) — greyed + non-interactive; the sub-line does the teaching. */
    	.mv-plus-row.is-locked {
    	    opacity: .45;
    	    pointer-events: none;
    	}

    	.mv-plus-row.is-locked .mv-plus-ico {
    	    background: rgba(28, 36, 48, .08);
    	    color: #5a6675;
    	}

    	/* In-row busy (Current Location while the OS resolves the fix) — the pin swaps
    	   for the row's own spinner; the row stops taking taps. */
    	.mv-plus-busy {
    	    display: none;
    	}

    	.mv-plus-row.is-busy {
    	    pointer-events: none;
    	}

    	.mv-plus-row.is-busy .mv-plus-ico i {
    	    display: none;
    	}

    	.mv-plus-row.is-busy .mv-plus-ico .mv-plus-busy {
    	    display: inline-block;
    	}
    
    	#rtm-report-open {
    	    position: absolute;
    	    top: calc(env(safe-area-inset-top) + 1.25rem);
    	    right: 1rem;
    	    color: white;
    	    font-size: 1.125rem;
    	    line-height: 1.2;
    	    z-index: 999;
    	}
    
    	/* ── Voice messages (voice-messages.md §4): recorder sheet + note player ── */
    	.mva-rec[hidden] {
    	    display: none;
    	}
    
    	.mva-rec {
    	    position: absolute;
    	    inset: 0;
    	    z-index: 130;
    	    display: flex;
    	    align-items: flex-end;
    	}
    
    	.mva-rec-backdrop {
    	    position: absolute;
    	    inset: 0;
    	    background: rgba(10, 14, 20, .45);
    	}
    
    	.mva-rec-sheet {
    	    position: relative;
    	    width: 100%;
    	    background: #fff;
    	    border-radius: 1.25rem 1.25rem 0 0;
    	    box-shadow: 0 -.5rem 1.75rem rgba(28, 36, 48, .22);
    	    padding: 3rem 1.25rem calc(1.5rem + env(safe-area-inset-bottom));
    	    display: flex;
    	    flex-direction: column;
    	    align-items: center;
    	    gap: 1.1rem;
    	}
    
    	.mva-rec-close {
    	    position: absolute;
    	    top: .7rem;
    	    right: .8rem;
    	    width: 2rem;
    	    height: 2rem;
    	    border: 0;
    	    border-radius: 50%;
    	    background: rgba(28, 36, 48, .06);
    	    color: #5a6675;
    	    font-size: 1rem;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	}
    
    	.mva-rec-stage {
    	    width: 100%;
    	    min-height: 4rem;
    	    display: flex;
    	    flex-direction: column;
    	    align-items: center;
    	    justify-content: center;
    	}
    
    	.mva-rec-live,
    	.mva-rec-review,
    	.mva-rec-denied,
    	.mva-rec-idlehint {
    	    display: none;
    	}
    
    	.mva-rec[data-state="recording"] .mva-rec-live {
    	    display: block;
    	    width: 100%;
    	}
    
    	.mva-rec[data-state="review"] .mva-rec-review {
    	    display: flex;
    	    align-items: center;
    	    gap: .6rem;
    	    width: 100%;
    	}
    
    	.mva-rec[data-state="denied"] .mva-rec-denied {
    	    display: flex;
    	    align-items: center;
    	    gap: .6rem;
    	    color: #b3261e;
    	}
    
    	.mva-rec[data-state="idle"] .mva-rec-idlehint {
    	    display: block;
    	}
    
    	.mva-rec[data-state="review"] .mva-rec-btn,
    	.mva-rec[data-state="denied"] .mva-rec-btn {
    	    display: none;
    	}
    
    	.mva-rec-idlehint {
    	    text-align: center;
    	    font-size: .85rem;
    	    color: #8a94a2;
    	    margin: 0;
    	}
    
    	.mva-rec-meter {
    	    width: 100%;
    	    max-width: 18rem;
    	    height: 56px;
    	    display: block;
    	    margin: 0 auto;
    	}
    
    	.mva-rec-row {
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	    gap: .4rem;
    	    margin-top: .4rem;
    	}
    
    	.mva-rec-dot {
    	    width: .55rem;
    	    height: .55rem;
    	    border-radius: 50%;
    	    background: #e23b3b;
    	    animation: mvaPulse 1s infinite;
    	}
    
    	@keyframes mvaPulse {
    
    	    0%,
    	    100% {
    	        opacity: 1;
    	    }
    
    	    50% {
    	        opacity: .25;
    	    }
    	}
    
    	.mva-rec-time {
    	    font-family: soehne-mono, monospace;
    	    font-size: .95rem;
    	    color: #1c2430;
    	}
    
    	.mva-rec-cap {
    	    color: #8a94a2;
    	}
    
    	.mva-rec-denied i {
    	    font-size: 1.1rem;
    	}
    
    	/* the big red record button — morphs circle -> stop square while recording */
    	.mva-rec-btn {
    	    flex: 0 0 auto;
    	    width: 4.75rem;
    	    height: 4.75rem;
    	    border-radius: 50%;
    	    border: 4px solid rgba(226, 59, 59, .25);
    	    background: #fff;
    	    padding: 0;
    	    cursor: pointer;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	}
    
    	.mva-rec-btn-inner {
    	    width: 3.4rem;
    	    height: 3.4rem;
    	    border-radius: 50%;
    	    background: #e23b3b;
    	    transition: width .18s ease, height .18s ease, border-radius .18s ease;
    	}
    
    	.mva-rec[data-state="recording"] .mva-rec-btn {
    	    border-color: rgba(226, 59, 59, .5);
    	    animation: mvaRecPulse 1.5s infinite;
    	}
    
    	.mva-rec[data-state="recording"] .mva-rec-btn-inner {
    	    width: 1.9rem;
    	    height: 1.9rem;
    	    border-radius: .45rem;
    	}
    
    	@keyframes mvaRecPulse {
    
    	    0%,
    	    100% {
    	        box-shadow: 0 0 0 0 rgba(226, 59, 59, .3);
    	    }
    
    	    50% {
    	        box-shadow: 0 0 0 .6rem rgba(226, 59, 59, 0);
    	    }
    	}
    
    	.mva-rec-play,
    	.mva-rec-del {
    	    flex: 0 0 auto;
    	    width: 2.25rem;
    	    height: 2.25rem;
    	    border-radius: 50%;
    	    border: 0;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	}
    
    	.mva-rec-play {
    	    background: linear-gradient(160deg, var(--primary-color-lt), var(--primary-color));
    	    color: #fff;
    	}
    
    	.mva-rec-del {
    	    background: rgba(28, 36, 48, .06);
    	    color: #5a6675;
    	}
    
    	.mva-rec-track {
    	    flex: 1;
    	    height: .35rem;
    	    border-radius: 1rem;
    	    background: rgba(28, 36, 48, .12);
    	    overflow: hidden;
    	}
    
    	.mva-rec-fill {
    	    height: 100%;
    	    width: 0;
    	    background: var(--primary-color);
    	    border-radius: 1rem;
    	}
    
    	.mva-rec-dur {
    	    flex: 0 0 auto;
    	    font-family: soehne-mono, monospace;
    	    font-size: .8rem;
    	    color: #5a6675;
    	    min-width: 2.4rem;
    	    text-align: right;
    	}
    
    	.mva-rec-acts {
    	    flex: 0 0 auto;
    	    display: flex;
    	    align-items: center;
    	    gap: .5rem;
    	}
    
    	.mva-rec-send {
    	    border: 0;
    	    border-radius: 1.25rem;
    	    padding: .45rem .9rem;
    	    background: linear-gradient(160deg, var(--primary-color-lt), var(--primary-color));
    	    color: #fff;
    	    font-family: soehne-halbfett;
    	    font-size: .85rem;
    	}
    
    	.mva-rec-send[disabled] {
    	    opacity: .6;
    	}

    	/* ── DOCUMENT TRAY (chat-attachments Phase C follow-up, 2026-07-08) — PDF cards
    	   open in this recorder-style sheet, rendered by PDF.js (the wrappers can't show
    	   a PDF any other way: no tabs on iOS, page-1-only frames in WKWebView, no inline
    	   PDFs in Android Chrome). Near-fullscreen: documents want height. ── */
    	.mv-doc {
    	    position: absolute;
    	    inset: 0;
    	    z-index: 130;
    	    display: flex;
    	    align-items: flex-end;
    	}

    	.mv-doc-backdrop {
    	    position: absolute;
    	    inset: 0;
    	    background: rgba(10, 14, 20, .45);
    	}

    	.mv-doc-sheet {
    	    position: relative;
    	    width: 100%;
    	    height: calc(100% - 3rem - env(safe-area-inset-top));
    	    background: #fff;
    	    border-radius: 1.25rem 1.25rem 0 0;
    	    box-shadow: 0 -.5rem 1.75rem rgba(28, 36, 48, .22);
    	    display: flex;
    	    flex-direction: column;
    	    overflow: hidden;
    	}

    	.mv-doc-head {
    	    flex: 0 0 auto;
    	    position: relative;
    	    display: flex;
    	    align-items: center;
    	    padding: .9rem 3.25rem .9rem 1.1rem;
    	    border-bottom: 1px solid rgba(28, 36, 48, .08);
    	}

    	.mv-doc-title {
    	    font-family: soehne-halbfett;
    	    font-size: .95rem;
    	    color: #1c2430;
    	    white-space: nowrap;
    	    overflow: hidden;
    	    text-overflow: ellipsis;
    	}

    	/* Same affordance as the recorder's close chip. */
    	.mv-doc-close {
    	    position: absolute;
    	    top: .7rem;
    	    right: .8rem;
    	    width: 2rem;
    	    height: 2rem;
    	    border: 0;
    	    border-radius: 50%;
    	    background: rgba(28, 36, 48, .06);
    	    color: #5a6675;
    	    font-size: 1rem;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	}

    	.mv-doc-body {
    	    flex: 1 1 auto;
    	    min-height: 0;
    	    overflow-y: auto;
    	    -webkit-overflow-scrolling: touch;
    	    overscroll-behavior: contain;
    	    background: #eceff3;
    	    padding: .75rem .75rem calc(.75rem + env(safe-area-inset-bottom));
    	}

    	/* One canvas per page — device-pixel sized by JS, CSS-scaled to the tray width. */
    	.mv-doc-page {
    	    display: block;
    	    width: 100%;
    	    height: auto;
    	    background: #fff;
    	    border-radius: .35rem;
    	    box-shadow: 0 1px 3px rgba(28, 36, 48, .12);
    	    margin: 0 0 .75rem;
    	}

    	.mv-doc-status {
    	    padding: 2.25rem 1rem;
    	    text-align: center;
    	    color: #5a6675;
    	    font-size: .9rem;
    	}

    	/* The mic (now in-ring, chat-attachments.md) + the "+": suppress the iOS
    	   long-press menu / text selection. */
    	.mv-comp-mic,
    	.mv-comp-micring {
    	    -webkit-touch-callout: none;
    	    -webkit-user-select: none;
    	    user-select: none;
    	    touch-action: none;
    	}
    
    	.mv-comp-micring.is-recording {
    	    background: #e23b3b;
    	    color: #fff;
    	}
    
    	/* Voice-note bubble player — compact play/pause · non-scrub progress · duration. */
    	.mvb-audio {
    	    display: flex;
    	    align-items: center;
    	    gap: .55rem;
    	    min-width: 11rem;
    	}
    
    	.mva-play {
    	    flex: 0 0 auto;
    	    width: 2rem;
    	    height: 2rem;
    	    border-radius: 50%;
    	    border: 0;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	    background: rgba(44, 117, 222, .14);
    	    color: var(--primary-color);
    	    font-size: .8rem;
    	    cursor: pointer;
    	}
    
    	.mvb-mine .mva-play {
    	    background: rgba(255, 255, 255, .92);
    	    color: var(--primary-color);
    	}
    
    	.mva-track {
    	    flex: 1;
    	    height: .3rem;
    	    border-radius: 1rem;
    	    background: rgba(0, 0, 0, .16);
    	    overflow: hidden;
    	    pointer-events: none;
    	}
    
    	.mvb-mine .mva-track {
    	    background: rgba(255, 255, 255, .4);
    	}
    
    	.mva-fill {
    	    height: 100%;
    	    width: 0;
    	    border-radius: 1rem;
    	    background: currentColor;
    	    opacity: .85;
    	}
    
    	.mva-time {
    	    flex: 0 0 auto;
    	    font-family: soehne-mono, monospace;
    	    font-size: .72rem;
    	    opacity: .8;
    	    min-width: 2.2rem;
    	}
    
    	.mva-spin,
    	.mva-ico {
    	    flex: 0 0 auto;
    	}
    
    	.mva-label {
    	    font-size: .85rem;
    	}
    
    	.mva-time--muted {
    	    opacity: .6;
    	}
    
    	.mva-transcript {
    	    font-size: .7rem;
    	    /* opacity: .6;  */
    	    margin-top: .25rem;
    	    max-width: 18rem;
    	    background: rgba(255, 255, 255, .72);
    	    backdrop-filter: blur(10px);
    	    border: 1px solid rgba(255, 255, 255, .65);
    	    box-shadow: 0 .375rem 1rem rgba(21, 56, 105, .12);
    	    padding: .625rem .9375rem;
    	    border-radius: 1.25rem;
    	}
    
    	/* The transcript lives INSIDE the audio note's run, so the side comes free from the run
    	   wrapper (.mvb-mine / .mvb-theirs) — no class needed on the transcript itself. */
    	.mvb-theirs .mva-transcript {
    	    border-top-left-radius: .375rem;
    	}
    
    	.mvb-mine .mva-transcript {
    	    border-top-right-radius: .375rem;
    	}
    
    	.mvb-mine .mva-transcript .d-block {
    	    text-align: right;
    	}
    
    	.mva-transcript--pending {
    	    opacity: .6;
    	}

    	/* ── Attachment bubbles (chat-attachments.md) — photo / PDF / location, rendered
    	   by _message.php; these class names are the renderer contract. Cards are neutral
    	   glass on BOTH sides (the run wrapper still sets the side) — a red PDF icon or a
    	   map pin reads better on glass than on the mine-blue gradient. */

    	/* Photo — the bubble IS the image: rounded + clipped, bubble-width convention
    	   (78%), aspect held by the img's width/height attrs so a poll-appended photo
    	   never reflows the scroller. PhotoSwipe opens from the anchor's data-pswp-*. */
    	.mvb-photo {
    	    display: block;
    	    max-width: 78%;
    	    margin-top: .1875rem;
    	    border-radius: 1.1rem;
    	    overflow: hidden;
    	    box-shadow: 0 .375rem 1rem rgba(21, 56, 105, .18);
    	}

    	.mvb-photo-img {
    	    display: block;
    	    width: 100%;
    	    height: auto;
    	}

    	/* PDF file card — icon chip + name/size; the anchor opens the CDN URL. */
    	.mvb-file,
    	.mvb-loc {
    	    display: flex;
    	    align-items: center;
    	    gap: .625rem;
    	    max-width: 78%;
    	    margin-top: .1875rem;
    	    padding: .625rem .9375rem;
    	    border-radius: 1.1rem;
    	    background: rgba(255, 255, 255, .85);
    	    -webkit-backdrop-filter: blur(14px);
    	    backdrop-filter: blur(14px);
    	    border: 1px solid rgba(255, 255, 255, .65);
    	    color: #1c2430;
    	    text-decoration: none;
    	    box-shadow: 0 .375rem 1rem rgba(21, 56, 105, .12);
    	}

    	.mvb-file-icon {
    	    flex: 0 0 auto;
    	    width: 2.25rem;
    	    height: 2.25rem;
    	    border-radius: .75rem;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	    background: rgba(226, 59, 59, .12);
    	    color: #e23b3b;
    	    font-size: 1.125rem;
    	}

    	.mvb-file-meta {
    	    display: flex;
    	    flex-direction: column;
    	    min-width: 0;
    	}

    	.mvb-file-name {
    	    font-family: soehne-halbfett;
    	    font-size: .875rem;
    	    max-width: 12rem;
    	    overflow: hidden;
    	    text-overflow: ellipsis;
    	    white-space: nowrap;
    	}

    	.mvb-file-size {
    	    font-size: .71875rem;
    	    opacity: .6;
    	}

    	/* Location card — pin chip + "Current location"/accuracy; taps out to the maps app. */
    	.mvb-loc-icon {
    	    flex: 0 0 auto;
    	    width: 2.25rem;
    	    height: 2.25rem;
    	    border-radius: 50%;
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	    background: rgba(44, 117, 222, .12);
    	    color: var(--primary-color);
    	    font-size: 1rem;
    	}

    	.mvb-loc-meta {
    	    display: flex;
    	    flex-direction: column;
    	    min-width: 0;
    	}

    	.mvb-loc-title {
    	    font-family: soehne-halbfett;
    	    font-size: .875rem;
    	}

    	.mvb-loc-sub {
    	    font-size: .71875rem;
    	    opacity: .6;
    	}

    	/* Optimistic temp bubble while an upload is in flight (data-temp-id run, JS-built) —
    	   rides the normal .mvb-mine .mvb-bubble blue, spinner centered; photos get an
    	   image-shaped block so the swap-in doesn't jump. */
    	.mvb-uploading {
    	    display: flex;
    	    align-items: center;
    	    justify-content: center;
    	    min-width: 8rem;
    	    min-height: 2.5rem;
    	}

    	.mvb-uploading.mvb-uploading--photo {
    	    width: 12rem;
    	    height: 9rem;
    	    border-radius: 1.1rem;
    	}


/* ─────────────────────────────────────────────────────
   RESULT CARDS — shared list container (AI-Search + Saved List view)
   Full-width result cards (RT.ResultCard) render inside #results-cont.
   Moved here from AI-Search.php so the Saved page's List view shares them.
   .tinder--card defaults to width:90vw/max-width:500px, so the 100% override
   is what makes each card fill its column.
   ───────────────────────────────────────────────────── */
#results-cont {
    margin-top: 1.5rem;
}
#results-cont .searchResult {
    width: 100% !important;
}
#results-cont .img-fav {
    pointer-events: all;
    cursor: pointer;
}

/* Saved List view — slide-up-from-transparent as each card scrolls into view.
   Mirrors the grid tiles' IntersectionObserver fade-in, using the app's default
   easing (cubic-bezier(0.77, 0, 0, 1)) at the same 800ms. The .inactive start
   state is added/removed by Saved.js; AI-Search never gets .saved-list-card. */
.saved-list-card {
    transition: opacity 800ms cubic-bezier(0.77, 0, 0, 1),
                transform 800ms cubic-bezier(0.77, 0, 0, 1);
    will-change: opacity, transform;
}
.saved-list-card.inactive {
    opacity: 0;
    transform: translateY(2rem);
}


/* ─────────────────────────────────────────────────────
   CREATE-INVOICE — itemized line builder
   Speaks the Option Sets design language. OS_scope sits on the builder's
   rbox-body (mirroring Listing-Edit-OptionSets), so inputs get the scoped
   bare-input styling for free. These rules add the per-line layout the way
   .option-set / .ov-row do for option sets: gap'd flex rows and an
   absolutely-positioned action — no Bootstrap col-# grid, no inline box.
   ───────────────────────────────────────────────────── */

/* Each line is a relative block separated by a hairline, so the remove
   control can hang top-right the way .option-set hangs .option-values. */
.OS_scope .ci-line {
    position: relative;
    padding-bottom: 1.25rem;
    margin-bottom: 1.25rem;
    border-bottom: 1px solid #eee;
}
.OS_scope .ci-line:last-child {
    padding-bottom: 0;
    margin-bottom: .25rem;
    border-bottom: 0;
}

/* Description spans the row but leaves room for the × . A charge description
   is a sentence, so opt out of .OS_scope input's capitalize. */
.OS_scope .ci-line .ci-desc {
    width: calc(100% - 3rem);
    margin-bottom: .75rem;
    text-transform: none;
}

/* Category + treatment share one gap'd flex row (replaces col-6 / col-6). */
.OS_scope .ci-fields {
    display: flex;
    gap: .75rem;
    margin-bottom: .75rem;
}
.OS_scope .ci-fields select {
    flex: 1 1 0;
    min-width: 0;          /* let each select shrink + ellipsize in its half */
    width: auto;
}
/* Required selects read as "unfilled" while their placeholder (value="") is the
   selection — :invalid matches until the Keeper picks a real category / treatment. */
.OS_scope .ci-fields select:invalid {
    color: #9a9a9a;
}
.OS_scope .ci-fields select option {
    color: #222;
}
.OS_scope .ci-fields select option[value=""] {
    color: #9a9a9a;
}

/* Amount: full width with a leading currency glyph (replaces col-9 + col-3). */
.OS_scope .ci-amount-wrap {
    position: relative;
}
.OS_scope .ci-amount-wrap .ci-amount-symbol {
    position: absolute;
    left: .75rem;
    top: 50%;
    transform: translateY(-50%);
    color: #9a9a9a;
    pointer-events: none;
}
.OS_scope .ci-line .ci-amount {
    width: 100%;
    padding-left: 1.85rem;   /* clear the symbol */
    text-transform: none;
}

/* Remove: a quiet ghost × top-right. Overrides .OS_scope button's accent fill
   and 20% width. No hover dependence (touch-only app); :active gives feedback. */
.OS_scope .ci-line .ci-remove {
    position: absolute;
    top: 0;
    right: 0;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    line-height: 1;
    font-size: 1.6rem;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: #c0c0c0;
}
.OS_scope .ci-line .ci-remove:active {
    color: var(--danger-color-lt);
    background: rgba(0, 0, 0, .04);
}

/* "Add line item": full-width dashed pill — a quiet secondary to the green
   "Send invoice". Overrides .OS_scope button's 20%-width accent default. */
.OS_scope .ci-add-line {
    width: 100%;
    margin-top: .5rem;
    padding: .7rem 0;
    border: 2px dashed #d6d6d6;
    border-radius: 100px;
    background: transparent;
    color: #6b6b6b;
}
.OS_scope .ci-add-line:active {
    border-color: var(--accent-color);
    color: var(--accent-color-dk);
}

/* Invoice Preview — itemized rows above the totals, so the box reads like a real receipt.
   (Outside .OS_scope — the preview isn't part of the line builder.) Description flexes and
   wraps; the amount stays on its own, right-aligned and mono like the totals below it. */
#ci-preview-lines .ci-pv-line {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: .6rem;
}
#ci-preview-lines .ci-pv-desc {
    min-width: 0;            /* let a long description wrap instead of shoving the amount off-row */
}
#ci-preview-lines .ci-pv-title {
    word-break: break-word;
}
#ci-preview-lines .ci-pv-cat {
    font-size: .8rem;
    line-height: 1.2;
}
#ci-preview-lines .ci-pv-amount {
    white-space: nowrap;
    flex-shrink: 0;
}


/* ─────────────────────────────────────────────────────
   LISTING-EDIT-ADD-ONS — add-on row builder
   Speaks the Option Sets design language, same as v1's Add-ons page: AO_scope
   rides with OS_scope on the editor wrapper so the bare inputs, os-suggest
   dropdown and .submit-btn come from style.css for free. These rules add the
   per-row layout (v1 carried them as inline cssText + a page <style> block).
   ───────────────────────────────────────────────────── */

/* Hairline-separated rows, like v1's .addon-row borders. */
.AO_scope .addon-row {
    padding-top: 1rem;
    padding-bottom: .5rem;
    border-bottom: 1px solid rgba(0, 0, 0, .25);
}
.AO_scope .addon-row:first-of-type {
    padding-top: 0;
}
.AO_scope .addon-row:last-of-type {
    padding-bottom: 0;
    border-bottom: none;
}

/* Name + delete on one gap'd flex row; the name flexes past the 80% input default. */
.AO_scope .ao-head {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .5rem;
}
.AO_scope .ao-head .ao-name {
    flex: 1;
    width: auto;
    min-width: 0;    /* same flex-shrink guard as .ao-qty */
}

/* Delete: v1's brand-gradient trash, on the .OS_scope button base (20% width). */
.AO_scope .ao-delete {
    background: linear-gradient(10deg, var(--brand-color-dk) 0%, var(--brand-color) 100%);
    border-color: var(--brand-color-lt);
}

/* Rate type + price share one flex row (select flexes, price stays compact). */
.AO_scope .ao-cfg {
    display: flex;
    gap: .5rem;
    margin-bottom: .5rem;
}
.AO_scope .ao-cfg .ao-rateType {
    flex: 1;
    min-width: 0;
    font-size: 1rem;
}
.AO_scope .ao-cfg .ao-priceDelta {
    width: 7rem;
    text-transform: none;
}

/* Min/max qty — only shown for per-item rate types (JS toggles display). */
.AO_scope .ao-qty-row {
    display: flex;
    gap: .5rem;
    margin-bottom: .5rem;
}
.AO_scope .ao-qty-row .ao-qty {
    flex: 1;
    width: auto;
    min-width: 0;    /* a text input won't flex-shrink below its intrinsic width without this */
    text-transform: none;
}

/* Description is a sentence: full width, small, no capitalize (v1's .ao-description). */
.AO_scope .ao-description {
    width: 100%;
    margin-bottom: .5rem;
    font-size: .8rem;
    text-transform: none;
}

/* ─────────────────────────────────────────────────────
   TOM SELECT — dropdown vs. card stacking
   .rbox carries backdrop-filter (style.css), which makes every card its own
   stacking context — so an open .ts-dropdown inside one card paints BEHIND
   the sibling cards below it, no matter the dropdown's own z-index. Lift the
   card that owns the open dropdown above its siblings for as long as it's
   open. (Pages still opt their .rbox into `overflow: visible` — e.g.
   .lep-page / .lec-page — so the dropdown can escape the card at all.)
   ───────────────────────────────────────────────────── */
.rbox:has(.ts-wrapper.dropdown-active) {
    position: relative;
    z-index: 6; /* one above .categories-cont/.search-cont (z-index 5, style.css) — an equal value loses the tie on DOM order */
}

/* ─────────────────────────────────────────────────────
   BLOOM POS — dark "register" mode
   Toggled by Point-of-Sale.js (init adds `pos-dark` to <body>, cleanup removes it),
   which also pauses window.gradient so the canvas isn't burning battery while faded.
   A glare-free black terminal for a Keeper working a physical counter.
   ───────────────────────────────────────────────────── */

body.pos-dark {
    background: #000;
}
/* Hard-hide the gradient with display:none, NOT an opacity fade. The canvas is a live
   WebGL animation (Gradient.js: a rAF loop + an IntersectionObserver that re-plays it
   whenever it's on screen), so a timed `opacity:0` fade loses a race on the FIRST slide
   into POS — Safari reveals the white body, iOS freezes a colorful frame, Chrome strobes
   the full-width margins. display:none can't race a transition, stops the compositing,
   and drops the canvas from its observer so Gradient.js auto-pauses its own loop. */
body.pos-dark #gradient-canvas {
    display: none;
}
/* Belt-and-suspenders: the register column paints its own opaque black, so the page can
   never reveal the white body behind it even for a frame. (#content is the 650px column;
   the margins outside it sit on body, already #000 above.) */
body.pos-dark #content {
    background: #000;
}

/* ─────────────────────────────────────────────────────
   CARDS2 — split-panel drag-to-fill filter cards
   Plan: docs/plans/cards2-split-swipe.md
   Two card-sized halves clipped via clip-path so future per-panel photos
   reveal (not squish) as the winning side grows. JS (Cards2.js) drives
   --a-share plus the label opacity/scale vars on the card element.
   ───────────────────────────────────────────────────── */

.tinder--card.split-card {
    --a-share: 50%;
    --label-a-o: 1;
    --label-b-o: 1;
    --label-a-s: 1;
    --label-b-s: 1;
}

/* The two halves: full-card layers, each showing its share via clip-path.
   Panel A = swipe-LEFT option (left half on h-splits, top half on v-splits). */
.split-card .tc-half {
    position: absolute;
    inset: 0;
    background-color: var(--panel-color, var(--text-dark));
    transition: clip-path .38s cubic-bezier(.25,.8,.3,1);
}
.split-card[data-split="h"] .tc-half.tc-a {
    clip-path: inset(0 calc(100% - var(--a-share)) 0 0);
}
.split-card[data-split="h"] .tc-half.tc-b {
    clip-path: inset(0 0 0 var(--a-share));
}
.split-card[data-split="v"] .tc-half.tc-a {
    clip-path: inset(0 0 calc(100% - var(--a-share)) 0);
}
.split-card[data-split="v"] .tc-half.tc-b {
    clip-path: inset(var(--a-share) 0 0 0);
}

/* Card-sized image layer inside each half — territory photos drop in here
   (images/cards2/{territory}/{cardKey}-{a|b}.jpg). Color shows through until then. */
.split-card .tc-img {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center center;
}

/* Label zones track the same share so each label stays centered in its
   visible half while it grows/shrinks. */
.split-card .tc-label-zone {
    position: absolute;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: visible;
    transition: width .38s cubic-bezier(.25,.8,.3,1), height .38s cubic-bezier(.25,.8,.3,1);
}
.split-card[data-split="h"] .tc-label-zone.tc-a-zone {
    left: 0; top: 0; bottom: 0;
    width: var(--a-share);
}
.split-card[data-split="h"] .tc-label-zone.tc-b-zone {
    right: 0; top: 0; bottom: 0;
    width: calc(100% - var(--a-share));
}
.split-card[data-split="v"] .tc-label-zone.tc-a-zone {
    left: 0; right: 0; top: 0;
    height: var(--a-share);
}
.split-card[data-split="v"] .tc-label-zone.tc-b-zone {
    left: 0; right: 0; bottom: 0;
    height: calc(100% - var(--a-share));
}

/* Seam — the line between the two halves. Pinned to the A-zone's trailing
   edge, which IS the --a-share boundary, so it rides the drag and the
   spring-back for free. Pseudo-element (not a border): it can be centered
   on the boundary (-2px) and carry a glow that melts into the photos. */
.split-card .tc-a-zone::after {
    content: '';
    position: absolute;
    background: #000;
    box-shadow: 0 0 10px rgba(0, 0, 0, .65);
}
.split-card[data-split="h"] .tc-a-zone::after {
    top: 0;
    bottom: 0;
    right: -2px;
    width: 4px;
}
.split-card[data-split="v"] .tc-a-zone::after {
    left: 0;
    right: 0;
    bottom: -2px;
    height: 4px;
}

/* "OR" badge — a black chip dead-center on the seam. Anchored to the same
   A-zone edge as the line, so it rides the drag/spring-back identically. */
.split-card .tc-a-zone::before {
    content: 'OR';
    position: absolute;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: #000;
    color: #fff;
    font-family: soehne-halbfett, sans-serif;
    font-size: .7rem;
    letter-spacing: .04em;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 0 10px rgba(0, 0, 0, .65);
    z-index: 1;
    /* Fade with the losing label: whichever side is collapsing has the lower
       label-opacity var, so min() tracks it for either drag direction. */
    opacity: min(var(--label-a-o), var(--label-b-o));
    transition: opacity .3s;
}
.split-card[data-split="h"] .tc-a-zone::before {
    top: 50%;
    right: -17px;
    transform: translateY(-50%);
}
.split-card[data-split="v"] .tc-a-zone::before {
    left: 50%;
    bottom: -17px;
    transform: translateX(-50%);
}

.split-card .tc-label {
    text-align: center;
    white-space: nowrap;
    color: #fff;
    transition: opacity .3s, transform .3s;
    filter: drop-shadow(0 2px 4px black);
}
.split-card .tc-a-zone .tc-label {
    opacity: var(--label-a-o);
    transform: scale(var(--label-a-s));
}
.split-card .tc-b-zone .tc-label {
    opacity: var(--label-b-o);
    transform: scale(var(--label-b-s));
}
.split-card .tc-label h3 {
    color: #fff;
    margin: 0;
}
.split-card .tc-label .tc-sub {
    opacity: .7;
    font-size: .85rem;
    margin: .25rem 0 0;
}
.split-card .tc-chev {
    opacity: .85;
    font-size:75%;
}

/* Kill geometry/label transitions while the finger is down — drag follows 1:1. */
.moving.tinder--card.split-card .tc-half,
.moving.tinder--card.split-card .tc-label-zone,
.moving.tinder--card.split-card .tc-label,
.moving.tinder--card.split-card .tc-a-zone::before {
    transition: none;
}

/* Progressive result feedback: Cards2.js drives the center ✕/♥ inline while
   dragging; suspend the base .2s transition so it tracks the finger. */
.tinder.tc-dragging .tinder--status i {
    transition: none;
}

/* Result-card drag veil (from the design artifact): a full-card color wash
   whose alpha tracks drag progress — brand pink + ♥ dragging right (save),
   dark ink + ✕ dragging left (skip). JS drives background/opacity inline. */
.tc-result-overlay {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    opacity: 0;
    transition: opacity .3s, background .3s;
}
.tc-result-overlay i {
    font-size: 96px;
    color: #fff;
    display: none;
    filter: drop-shadow(0 4px 14px rgba(0,0,0,.3));
    transition: transform .3s;
}
.tc-result-overlay.love .fa-heart {
    display: block;
}
.tc-result-overlay.nope .fa-xmark {
    display: block;
}
.moving.tinder--card .tc-result-overlay,
.moving.tinder--card .tc-result-overlay i {
    transition: none;
}

/* Undo circle (Cards2 only — .tc-undo): always on screen at .6 the size of
   the ✕/♥ circles, warning-yellow icon, greyed out when nothing to undo. */
.tinder--buttons div#undo.tc-undo {
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* tops of all three circles share the line box — pushing down by half the
       size difference ((60 − 36) / 2) centers this one against the big pair */
    vertical-align: top;
    margin-top: 12px;
    transition: opacity .3s, filter .3s;
}
.tinder--buttons div#undo.tc-undo i {
    font-size: 15px !important;
    color: var(--warning-color);
}
.tinder--buttons div#undo.tc-undo.disabled {
    opacity: .35;
    filter: grayscale(1);
    pointer-events: none;
}

/* Picks recap chips on the loading card */
.tc-chips {
    position: absolute;
    bottom: 8%;
    left: 0;
    right: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: center;
    padding: 0 30px;
}
.tc-chip {
    padding: 5px 13px;
    border-radius: 99px;
    border: 2px solid rgba(255,255,255,.5);
    background: rgba(30,40,53,.25);
    color: #fff;
    font-size: 13px;
    font-family: soehne-halbfett, sans-serif;
}

/* ── CARDS2: Services Interest Picker ──────────────────────────
   Plan: docs/plans/cards2-services-picker.md
   Full-screen Apple-TV-style grid of poster tiles (gradient art, parent
   FA icon, subcategory name). Hue classes band subcategories by parent.
   Cards (v1) keeps its old .services-category-panel — these are additive. */
.svc-picker {
    position: fixed;
    inset: 0;
    z-index: 1100;
    display: flex;
    flex-direction: column;
    background: #17171b;
    opacity: 0;
    pointer-events: none;
    /* visibility (not just pointer-events) — children that set their own
       pointer-events:auto (the Go button) stayed tappable through the closed
       overlay and sat invisibly on top of the deck's Reset pill. */
    visibility: hidden;
    transition: opacity .35s ease-out, visibility 0s linear .35s;
}
.svc-picker.open {
    opacity: 1;
    pointer-events: auto;
    visibility: visible;
    transition: opacity .35s ease-out;
}
.svc-picker-header {
    padding: calc(env(safe-area-inset-top) + 1.1rem) 1.25rem .65rem;
    color: #fff;
    transform: translateY(16px);
    transition: transform .45s cubic-bezier(.25,.8,.3,1);
}
.svc-picker.open .svc-picker-header {
    transform: translateY(0);
}
.svc-back {
    background: none;
    border: none;
    color: #fff;
    font-size: 1rem;
    /* padding: .25rem .75rem .25rem 0; */
    /* margin-bottom: .35rem; */
    cursor: pointer;
    display: flex;
    float: right;
    align-items: center;
    justify-content: center;
    background-color: rgba(255,255,255,.15);
    border-radius: 2rem;
    padding: .25rem;
    width: 2.5rem;
    height: 2.5rem;
}
.svc-title {
    /* font-family: soehne-fett, sans-serif; */
    font-size: 2rem;
    line-height: 1.05;
    letter-spacing: -.03em;
    color: var(--warn-color-lt);
    margin: 0;
}
.svc-sub {
    color: white;
    /* font-size: .9rem; */
    line-height: 1;
    margin: 0;
}
.svc-grid {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    touch-action: pan-y;          /* vertical-only: no x-drift rubber-banding */
    overscroll-behavior-x: none;
    -webkit-overflow-scrolling: touch;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .75rem;
    align-content: start;
    padding: .5rem 1rem calc(env(safe-area-inset-bottom) + 7.5rem);
}
/* Tile anatomy lifted from the POS register (Point-of-Sale.php inline styles):
   photo layer + bottom gradient overlay + white title, aspect 1/0.85.
   div (not button) — button boxes mangle aspect-ratio/spacer geometry. */
.svc-tile {
    position: relative;
    display: block;
    /* aspect-ratio: 1 / 0.85; */
    aspect-ratio: 1 / 1.2;
    cursor: pointer;
    transition: transform .12s ease;
}
.svc-tile:active {
    transform: scale(.96);
}
.svc-tile-photo {
    position: relative;
    width: 100%;
    height: 100%;
    border-radius: 12px;
    overflow: hidden;
    background: #1a1a1a;
    outline: 3px solid transparent;
    outline-offset: -3px;
    transition: outline-color .2s;
}
.svc-tile-photo-img {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    border-radius: 8px;
    /* Unselected = recessed: image scaled into the dark tile and faded.
       Text/overlay live on the container, so only the image recedes. */
    transform: scale(.85);
    opacity: .45;
    transition: transform .25s cubic-bezier(.25, .8, .3, 1), opacity .25s, border-radius .25s;
	/* filter: blur(1px); */
}
/* Photoless category — translucent fill instead of a stock placeholder (POS pattern) */
.svc-tile-photo-empty {
    background: rgba(255,255,255,.1);
}
/* Parent-category color wash — one family color (categories.c1, a brand
   token) arrives as --c1-color on .svc-tile-photo; the gradient's outer stops
   are that hue rotated ±45deg. Lives INSIDE the transformed img so it tracks
   the sunken/popped states and, because the img forms its own stacking
   context (transform+opacity), hard-light blends against the photo only —
   never the dark tile edges or the legibility gradient above. */
.svc-tile-photo-img:not(.svc-tile-photo-empty)::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(160deg,
        hsl(from var(--c1-color) calc(h - 45) s l) 0%,
        var(--c1-color) 35%,
        hsl(from var(--c1-color) calc(h + 45) s l) 100%);
    mix-blend-mode: hard-light;
    opacity: .5;
    transition: opacity .25s;
}
.svc-tile-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(0,0,0,0) 45%, rgba(0,0,0,.8) 100%);
    pointer-events: none;
}
.svc-tile-info {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: .5rem .6rem;
    pointer-events: none;
}
.svc-tile-title {
    color: white;
    font-size: .72rem;
    margin: 0;
    line-height: 1.2;
    text-shadow: 0 1px 3px rgba(0,0,0,.55);
    display: -webkit-box;
	line-clamp: 3;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* Parent-category caption under the title — muted grey, single line */
.svc-tile-parent {
    color: rgba(255,255,255,.55);
    font-size: .6rem;
    line-height: 1.2;
    margin-top: .1rem;
    text-shadow: 0 1px 3px rgba(0,0,0,.55);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.svc-check {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--c1-color, var(--warn-color));
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: .7rem;
    box-shadow: 0 2px 8px rgba(0,0,0,.45);
    transform: scale(0);
    transition: transform .2s cubic-bezier(.34, 1.56, .64, 1);
    z-index: 1;
}
/* Selected = the pop: image expands to full bleed at full strength, ring +
   glow + check badge in the category's family color (warn-purple fallback). */
.svc-tile.selected .svc-tile-photo {
    outline-color: var(--c1-color, var(--warn-color));
    box-shadow: 0 0 20px color-mix(in srgb, var(--c1-color, var(--warn-color)) 55%, transparent);
}
.svc-tile.selected .svc-tile-photo-img {
    transform: scale(1);
    opacity: 1;
    border-radius: 0;
	/* filter: blur(0); */
}
/* Selected tile shows the photo nearly clean — wash recedes to a tint */
.svc-tile.selected .svc-tile-photo-img::after {
    opacity: .2;
}
.svc-tile.selected .svc-check {
    transform: scale(1);
}
.svc-go-bar {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 3rem 1.25rem calc(env(safe-area-inset-bottom) + 1.25rem);
    background: linear-gradient(180deg, rgba(23,23,27,0) 0%, #17171b 55%);
    pointer-events: none;
}
/* Any button in the bar — the bar itself stays pointer-events: none so the
   grid scrolls/taps through its gradient, and buttons must opt back in
   regardless of how they're styled. */
.svc-picker.open .svc-go-bar button {
    pointer-events: auto;
}

/* ─────────────────────────────────────────────────────
   DETAILS — per-hour timeslot strip (v2 overrides)
   Two fixes over the v1 rules in style.css:

   1. Seams. v1 butted the 2px slot borders with margin-right:-4px, which
      was calibrated against the ~4px whitespace text node its template
      literal emitted between the inline-block slots. v2 renders the slots
      with no whitespace between them, so -4px dragged each slot over its
      neighbor and the seams landed on sub-pixel boundaries. Overlap by
      exactly one border width instead — a single crisp 2px seam at any
      viewport — and lift highlighted slots so their border color wins
      the paint order over the next sibling.

   2. Min-duration gaps (.dashGap, painted by Details.js paintSlotStates):
      slots that can only ever be the interior of a range — e.g. the
      10:00am between a 9:00am start and the 11:00am first-valid-end on a
      2h-minimum listing — collapse to a narrow grey '- - -' cell so the
      eye lands on the first tappable end time. The time label (a bare
      text node) stays in the layout but paints transparent — it defines
      the exact same line box and baseline as every neighboring slot —
      and the dashes overlay it via an absolutely-positioned ::before
      that adds no height of its own.
   ───────────────────────────────────────────────────── */
.timeSlot-Wrapper.perHour .timeSlot.perHour {
    margin: 0 -2px 1.5rem 0;
    position: relative;
    /* bottom-edge alignment, not baseline: overflow:hidden moves an
       inline-block's baseline to its bottom edge, so a baseline-aligned
       .dashGap would ride up relative to its text-baseline neighbors.
       All slots are the same height, so bottom == aligned. */
    vertical-align: bottom;
}
.timeSlot-Wrapper.perHour .timeSlot.perHour.selected,
.timeSlot-Wrapper.perHour .timeSlot.perHour.range {
    z-index: 1;
}
.timeSlot-Wrapper.perHour .timeSlot.perHour.dashGap {
    width: calc(100vw / 7.5);
    color: transparent;
    overflow: hidden; /* the (invisible) time text no longer fits the narrow cell */
    pointer-events: none;
}
.timeSlot-Wrapper.perHour .timeSlot.perHour.dashGap::before {
    content: '- - -';
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: lightgrey;
}
.timeSlot-Wrapper.perHour .timeSlot.perHour.dashGap > * {
    display: none;
}

/* ─────────────────────────────────────────────────────
   DETAILS — add-on qty stepper
   The stepper borrows the .partySizeSlot pill, but inside an add-on row it
   sits ml-auto (shrink-to-fit), so its width tracked the digit count and the
   ± buttons walked sideways on every tap. Pin the pill's width and split it
   into even thirds (bigger ± tap zones than the shared 1:3:1 split); the
   count reads as a value, not a headline — body size, default ink.
   ───────────────────────────────────────────────────── */
.addon-row .partySizeSlot {
    width: 6.5rem;
    flex: none;
    margin: 0;   /* the shared pill's side/bottom margins skew it inside the centered flex row */
}
.addon-row .partySizeCount {
    flex: 1;
}
.addon-row .addon-qty-num {
    font-size: 1rem;
}

/* ─────────────────────────────────────────────────────
   DETAILS — hidden QR share frame (carousel slide 0)
   Sits before the photos in #img-cont; the carousel starts on slide 1
   and the Share button scrolls back to it. qr.php renders white modules
   on transparency, so the slide supplies the brand gradient backdrop.
   ───────────────────────────────────────────────────── */
.img-item-qr .img-qr-inner {
    color: white;
}
.img-item-qr .img-qr-inner img {
    width: 52vw;
    max-width: 280px;
    height: auto;
}
.img-item-qr .img-qr-inner p {
    margin-top: 1.25rem;
    color: rgba(255, 255, 255, .9);
}

/* ═════════════════════════════════════════════════════════════════════
   BUTTON SYSTEM — one paint language for every button (2026-07-02)
   Plan: docs/plans/unified-button-system.md

   Entry points share declarations; geometry stays per-suite:
     .j-button + theme word ......... page CTAs (v1 markup, style.css geometry)
     .mvc-btn--* .................... chat suite (block/13px geometry above)
     .btn-outline-* ................. secondaries — rest as ghosts by default
                                      (colorway glass + 2px family border)
     .btn-ghost[-*] ................. glass secondaries, new for .btn markup

   Interaction model (mobile-first): one "engaged" look, three triggers —
     :active ........................ finger down right now
     .hover / .active / .daySlot-selected ... JS-driven persistent selection
     :hover ......................... desktop only, inside @media (hover:hover)
   v1's :hover/:focus rules leak on touch as sticky-tap states, so §3 pins
   them back to resting; the desktop block at the end re-adds hover.

   Cascade rules of the road: always the full `background:` shorthand (the
   fills are background-image — a background-color never overrides them);
   !important only where a v1 rule forces the tie (app.css loads last, so
   equal-specificity !important resolves our way).
   NEVER stack text-* utilities on colorway buttons: the variant supplies the
   text color, and v1's `a.text-brand:hover` (style.css) outguns the
   engaged-white at higher specificity — anchor buttons stay pink on hover.
   Outline buttons need the BARE .btn-outline class too (the icon-flip rules
   key on it).
   JS-action buttons are <button type="button">, never href-less <a> —
   bootstrap's `a:not([href]):not([tabindex])` forces color:inherit on those
   and breaks the variant text color (found on Revert Changes 2026-07-09).
   Canonical recipe: btn btn-sm btn-rounded btn-outline btn-outline-{family} b-2x _600
   ═════════════════════════════════════════════════════════════════════ */

/* ── §0 Bootstrap state noise off (before the variant paint, so variant
       glows still win the tie on sticky :focus). */
.btn:focus,
.btn.focus {
    box-shadow: none;
}

/* ── §1 Solid fills — one recipe per variant, both suites listed.
       (The .mvc-btn--* bodies these absorbed are tombstoned in the
       Messages-View section above.) */
.j-button,
.j-button.primary,
.mvc-btn--primary {
    background: linear-gradient(160deg, var(--primary-color-lt), var(--primary-color));
    box-shadow: 0 .375rem .875rem rgba(44, 117, 222, .35);
    outline: var(--default-outline) !important;
    color: #fff;
    font-family: soehne-halbfett;
}
.j-button.brand,
.mvc-btn--brand {
    background: linear-gradient(160deg, var(--brand-color-lt), var(--brand-color-dk));
    box-shadow: 0 .375rem .875rem rgba(235, 93, 143, .32);
    outline: var(--default-outline) !important;
    color: #fff;
}
.j-button.success,
.mvc-btn--success {
    background: linear-gradient(160deg, var(--success-color-lt), var(--success-color-dk));
    box-shadow: 0 .375rem .875rem rgba(34, 197, 94, .3);
    outline: var(--default-outline) !important;
    color: #fff;
}
.j-button.warn,
.mvc-btn--warn {
    background: linear-gradient(160deg, var(--warn-color-lt), var(--warn-color-dk));
    box-shadow: 0 .375rem .875rem rgba(145, 109, 246, .35);
    outline: var(--default-outline) !important;
    color: #fff;
}
/* warning keeps the chat amber (the --warning-color-dk var is salmon, off-family) */
.j-button.warning,
.mvc-btn--warning {
    background: linear-gradient(160deg, #fbbf24, #f59e0b);
    box-shadow: 0 .375rem .875rem rgba(245, 158, 11, .35);
    outline: var(--default-outline) !important;
    color: #fff;
}
.j-button.dark,
.mvc-btn--dark {
    background: linear-gradient(160deg, #0a1428 0%, #0f2a47 45%, #0e3a5f 100%);
    box-shadow: 0 .375rem .875rem rgba(15, 42, 71, .32);
    outline: var(--default-outline) !important;
    color: #fff;
}

/* Locked/disabled — v1's rule is background-color-only, which would sit
   invisibly under our gradient; re-kill the image and the glow. */
.j-button.locked {
    background: grey !important;
    box-shadow: none;
    outline: var(--default-outline) !important;
}

/* ── §2 Ghost family — the quiet glass secondary: page-usable on the .btn
       structure (btn btn-rounded [btn-block] btn-ghost btn-ghost-brand _600),
       shared with the chat ghosts, AND the default resting look for all
       btn-outline buttons (approved on Booking-Details 2026-07-02). Outline
       buttons keep their 2px family border over the glass; the ghost classes
       themselves are unchanged. Tints live in vars so resting (§2) and the
       sticky-tap pins (§3) can never drift apart. */
:root {
    --ghost-glass: rgba(255, 255, 255, .6);
    --ghost-brand: linear-gradient(160deg, rgba(255, 255, 255, .6) 35%, rgba(235, 93, 143, .18) 125%);
    --ghost-success: linear-gradient(160deg, rgba(255, 255, 255, .6) 35%, rgba(34, 197, 94, .18) 125%);
    --ghost-primary: linear-gradient(160deg, rgba(255, 255, 255, .6) 35%, rgba(44, 117, 222, .18) 125%);
    --ghost-warn: linear-gradient(160deg, rgba(255, 255, 255, .6) 35%, rgba(145, 109, 246, .18) 125%);
    --ghost-warning: linear-gradient(160deg, rgba(255, 255, 255, .6) 35%, rgba(245, 158, 11, .18) 125%);
    --ghost-accent: linear-gradient(160deg, rgba(255, 255, 255, .6) 35%, rgba(105, 226, 225, .18) 125%);
    --ghost-accent-dk: linear-gradient(160deg, rgba(255, 255, 255, .6) 35%, rgba(69, 150, 150, .18) 125%);
    --ghost-danger: linear-gradient(160deg, rgba(255, 255, 255, .6) 35%, rgba(220, 53, 69, .18) 125%);
    --ghost-dark: linear-gradient(160deg, rgba(255, 255, 255, .6) 35%, rgba(15, 42, 71, .18) 125%);
}
.btn-ghost,
.mvc-btn--ghost {
    background: var(--ghost-glass);
    outline: var(--default-outline);
    color: var(--brand-color);
    font-family: soehne-halbfett;
}
/* Outline buttons rest as ghosts by default — glass base here, colorway tint +
   text in the family groups below. Bare .btn-outline (no colorway) gets the
   glass only; its text keeps coming from text-* utilities / the .btn default. */
[class*="btn-outline"] {
    background: var(--ghost-glass);
    outline: var(--default-outline);
    font-family: soehne-halbfett;
}
/* Outline paint on chat-suite geometry: .mvc-btn's `border: 0` outguns
   .btn-outline/.b-2x in the cascade, so restore the 2px border here; the
   family class (.btn-outline-{family}) still supplies its color. */
.mvc-btn[class*="btn-outline"] {
    border-style: solid;
    border-width: 2px;
}
.btn-ghost-brand,
.mvc-btn--ghost.mvc-btn--ghost-brand,
.btn-outline-brand {
    background: var(--ghost-brand);
    color: var(--brand-color);
}
.btn-ghost-success,
.mvc-btn--ghost.mvc-btn--ghost-success,
.btn-outline-success {
    background: var(--ghost-success);
    color: var(--success-color-dk);
}
.btn-ghost-primary,
.mvc-btn--ghost.mvc-btn--ghost-primary,
.btn-outline-primary,
.btn-outline.b-primary {
    background: var(--ghost-primary);
    color: var(--primary-color);
}
.btn-ghost-warn,
.btn-outline-warn {
    background: var(--ghost-warn);
    color: var(--warn-color-dk);
}
/* warning text steps the fill's amber scale down one (#d97706) — the
   --warning-color-dk var is salmon, and #f59e0b is too light on the glass. */
.btn-ghost-warning,
.btn-outline-warning {
    background: var(--ghost-warning);
    color: #d97706;
}
.btn-ghost-accent,
.btn-outline-accent {
    background: var(--ghost-accent);
    color: var(--accent-color-dk);
}
.btn-ghost-accent-dk,
.btn-outline-accent-dk {
    background: var(--ghost-accent-dk);
    color: var(--accent-color-bl);
}
.btn-ghost-danger,
.btn-outline-danger {
    background: var(--ghost-danger);
    color: #dc3545;
}
.btn-ghost-dark,
.btn-outline-dark {
    background: var(--ghost-dark);
    color: #0f2a47;
}
/* (No -secondary: the palette has no --secondary-color; -muted is the neutral/quiet one.) */
.btn-ghost-muted,
.mvc-btn--ghost.mvc-btn--report {
    color: rgba(28, 36, 48, .55);
}
/* Pin sticky-tap states to resting (bootstrap's .btn:hover flips text dark). */
.btn-ghost:hover,
.btn-ghost:focus {
    color: var(--brand-color);
}
.btn-ghost-success:hover,
.btn-ghost-success:focus {
    color: var(--success-color-dk);
}
.btn-ghost-primary:hover,
.btn-ghost-primary:focus {
    color: var(--primary-color);
}
.btn-ghost-warn:hover,
.btn-ghost-warn:focus {
    color: var(--warn-color-dk);
}
.btn-ghost-warning:hover,
.btn-ghost-warning:focus {
    color: #d97706;
}
.btn-ghost-accent:hover,
.btn-ghost-accent:focus {
    color: var(--accent-color-dk);
}
.btn-ghost-accent-dk:hover,
.btn-ghost-accent-dk:focus {
    color: var(--accent-color-bl);
}
.btn-ghost-danger:hover,
.btn-ghost-danger:focus {
    color: #dc3545;
}
.btn-ghost-dark:hover,
.btn-ghost-dark:focus {
    color: #0f2a47;
}
.btn-ghost-muted:hover,
.btn-ghost-muted:focus {
    color: rgba(28, 36, 48, .55);
}

/* ── §3 Sticky-tap neutralization — v1/bootstrap :hover and :focus fire on
       touch and stick after release; pin every family back to its resting
       look. Desktop hover returns in §5. */
.btn-outline-primary:hover, .btn-outline-primary:focus {
    background: var(--ghost-primary) !important;
    color: var(--primary-color) !important;
}
.btn-outline-brand:hover, .btn-outline-brand:focus {
    background: var(--ghost-brand) !important;
    color: var(--brand-color) !important;
}
.btn-outline-success:hover, .btn-outline-success:focus {
    background: var(--ghost-success) !important;
    color: var(--success-color-dk) !important;
}
.btn-outline-warn:hover, .btn-outline-warn:focus {
    background: var(--ghost-warn) !important;
    color: var(--warn-color-dk) !important;
}
.btn-outline-warning:hover, .btn-outline-warning:focus {
    background: var(--ghost-warning) !important;
    color: #d97706 !important;
}
.btn-outline-accent:hover, .btn-outline-accent:focus {
    background: var(--ghost-accent) !important;
    color: var(--accent-color-dk) !important;
}
.btn-outline-accent-dk:hover, .btn-outline-accent-dk:focus {
    background: var(--ghost-accent-dk) !important;
    color: var(--accent-color-bl) !important;
}
.btn-outline-dark:hover, .btn-outline-dark:focus {
    background: var(--ghost-dark) !important;
    color: #0f2a47 !important;
    border-color: #1e2835 !important;
}
.btn-outline-danger:hover, .btn-outline-danger:focus {
    background: var(--ghost-danger) !important;
    color: #dc3545 !important;
    border-color: #dc3545 !important;
}
.btn-outline.b-primary:hover, .btn-outline.b-primary:focus {
    background: var(--ghost-primary) !important;
    color: var(--primary-color) !important;
}
.j-button-outline:hover, .j-button-outline:focus {
    background: white !important;
}
.j-button-light:hover, .j-button-light:focus {
    color: var(--brand-color) !important;
}
.btn-light.b-dark:hover, .btn-light.b-dark:focus {
    background: #f8f9fa !important;
    color: #212529 !important;
    box-shadow: none;
}
/* v1 forces icons white on hover — resting icons follow the button again. */
.btn-outline:hover:not(.b-light) i,
.btn-outline:focus:not(.b-light) i {
    color: inherit !important;
}

/* ── §4 Engaged — press (:active) or JS-selected (.hover/.active/
       .daySlot-selected): the variant's solid 160deg fill, white text.
       Replaces v1's 10deg hover gradients.
       The resting border is deliberately KEPT under the fill: gradients paint
       to the border-box, and border-color: transparent over one at pill radii
       makes Safari mis-clip the corners (seen on-device 2026-07-02) — the
       family-color ring over the family gradient reads fine. */
.btn-outline-primary:active, .btn-outline-primary.hover, .btn-outline-primary.active,
.btn-outline.b-primary:active, .btn-outline.b-primary.hover, .btn-outline.b-primary.active {
    background: linear-gradient(160deg, var(--primary-color-lt), var(--primary-color)) !important;
    color: #fff !important;
}
.btn-outline-brand:active, .btn-outline-brand.hover, .btn-outline-brand.active,
.btn-outline-brand.armed {
    background: linear-gradient(160deg, var(--brand-color-lt), var(--brand-color-dk)) !important;
    color: #fff !important;
}
.btn-outline-success:active, .btn-outline-success.hover, .btn-outline-success.active {
    background: linear-gradient(160deg, var(--success-color-lt), var(--success-color-dk)) !important;
    color: #fff !important;
}
.btn-outline-warn:active, .btn-outline-warn.hover, .btn-outline-warn.active,
.btn-outline-warn.daySlot-selected {
    background: linear-gradient(160deg, var(--warn-color-lt), var(--warn-color-dk)) !important;
    color: #fff !important;
}
.btn-outline-warning:active, .btn-outline-warning.hover, .btn-outline-warning.active {
    background: linear-gradient(160deg, #fbbf24, #f59e0b) !important;
    color: #fff !important;
}
.btn-outline-accent:active, .btn-outline-accent.hover, .btn-outline-accent.active {
    background: linear-gradient(160deg, var(--accent-color-lt), var(--accent-color-dk)) !important;
    color: #fff !important;
}
.btn-outline-accent-dk:active, .btn-outline-accent-dk.hover, .btn-outline-accent-dk.active {
    background: linear-gradient(160deg, var(--accent-color), var(--accent-color-bl)) !important;
    color: #fff !important;
}
.btn-outline-dark:active, .btn-outline-dark.hover, .btn-outline-dark.active,
.btn-light.b-dark:active, .btn-light.b-dark.hover, .btn-light.b-dark.active {
    background: linear-gradient(160deg, #0a1428 0%, #0f2a47 45%, #0e3a5f 100%) !important;
    color: #fff !important;
}
.btn-outline-danger:active, .btn-outline-danger.hover, .btn-outline-danger.active {
    background: linear-gradient(160deg, var(--danger-color-lt), var(--danger-color)) !important;
    color: #fff !important;
}
.j-button-outline:active {
    background: linear-gradient(160deg, var(--primary-color-lt), var(--primary-color)) !important;
    color: #fff !important;
}
/* Engaged icons go white with the fill (mirror of v1's hover icon rule).
   .armed = the two-tap confirm stage (Express-Booking-Share / POS cancel);
   .daySlot-selected = a JS-selected schedule control (#CalBTN / #NowSlot). */
.btn-outline:active:not(.b-light) i,
.btn-outline.hover:not(.b-light) i,
.btn-outline.active:not(.b-light) i,
.btn-outline.daySlot-selected:not(.b-light) i,
.btn-outline.armed:not(.b-light) i {
    color: #fff !important;
}
/* Muted sub-labels (e.g. #CalBTN-label "( more dates )") follow the fill too —
   bootstrap's grey-!important is unreadable on the engaged gradient. */
.btn-outline:active:not(.b-light) .text-muted,
.btn-outline.hover:not(.b-light) .text-muted,
.btn-outline.active:not(.b-light) .text-muted,
.btn-outline.daySlot-selected:not(.b-light) .text-muted,
.btn-outline.armed:not(.b-light) .text-muted {
    color: rgba(255, 255, 255, .75) !important;
}

/* ── §5 Press feedback (solids, ghosts + outlines) and desktop hover. */
.mvc-btn,
.btn-ghost {
    transition: transform 250ms cubic-bezier(0.77, 0, 0, 1), filter 250ms cubic-bezier(0.77, 0, 0, 1);
}
/* Outline (+ light-dark) buttons keep bootstrap's color/border transitions
   and add the press pair on top. */
[class*="btn-outline"],
.btn-light.b-dark {
    transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out, transform 250ms cubic-bezier(0.77, 0, 0, 1), filter 250ms cubic-bezier(0.77, 0, 0, 1);
}
.j-button:active,
.j-button-outline:active,
.mvc-btn:active,
.btn-ghost:active,
[class*="btn-outline"]:active,
.btn-light.b-dark:active {
    transform: scale(.97);
    filter: brightness(.94);
    transition-duration: 100ms; /* fast press-in; release returns at each suite's pace */
}
/* Bootstrap's inset press shadow and press focus ring, off — the pressed
   look is scale + brightness (glow dropping while pressed reads correctly). */
.btn:not(:disabled):not(.disabled):active,
.btn:not(:disabled):not(.disabled).active {
    box-shadow: none;
}
.btn:not(:disabled):not(.disabled):active:focus,
.btn:not(:disabled):not(.disabled).active:focus {
    box-shadow: none;
}

/* Desktop dev convenience only — real pointers get the engaged look on
   hover; never fires as sticky-tap in the app. */
@media (hover: hover) and (pointer: fine) {
    .btn-outline-primary:hover,
    .btn-outline.b-primary:hover {
        background: linear-gradient(160deg, var(--primary-color-lt), var(--primary-color)) !important;
        color: #fff !important;
    }
    .btn-outline-brand:hover {
        background: linear-gradient(160deg, var(--brand-color-lt), var(--brand-color-dk)) !important;
        color: #fff !important;
    }
    .btn-outline-success:hover {
        background: linear-gradient(160deg, var(--success-color-lt), var(--success-color-dk)) !important;
        color: #fff !important;
    }
    .btn-outline-warn:hover {
        background: linear-gradient(160deg, var(--warn-color-lt), var(--warn-color-dk)) !important;
        color: #fff !important;
    }
    .btn-outline-warning:hover {
        background: linear-gradient(160deg, #fbbf24, #f59e0b) !important;
        color: #fff !important;
    }
    .btn-outline-accent:hover {
        background: linear-gradient(160deg, var(--accent-color-lt), var(--accent-color-dk)) !important;
        color: #fff !important;
    }
    .btn-outline-accent-dk:hover {
        background: linear-gradient(160deg, var(--accent-color), var(--accent-color-bl)) !important;
        color: #fff !important;
    }
    .btn-outline-dark:hover,
    .btn-light.b-dark:hover {
        background: linear-gradient(160deg, #0a1428 0%, #0f2a47 45%, #0e3a5f 100%) !important;
        color: #fff !important;
    }
    .btn-outline-danger:hover {
        background: linear-gradient(160deg, var(--danger-color-lt), var(--danger-color)) !important;
        color: #fff !important;
    }
    .btn-outline:hover:not(.b-light) i {
        color: #fff !important;
    }
}

/* ── §6 On-dark variants — buttons resting on a DARK surface (the POS
       terminal sheets). The light glass base (§2) is white-based
       (rgba(255,255,255,.6) 35%) and reads as a solid white slab on dark, so
       .btn-on-dark swaps that stop to dark glass (same 160deg geometry, same
       family tint at 125%) and lifts the resting text to the light end of the
       family's palette. Engaged (§4) and desktop hover (§5) keep the solid
       family fill — that already reads on dark. */
:root {
    --ghost-glass-on-dark: rgba(255, 255, 255, .08);
    --ghost-brand-on-dark: linear-gradient(160deg, rgba(255, 255, 255, .08) 35%, rgba(235, 93, 143, .38) 125%);
    --ghost-success-on-dark: linear-gradient(160deg, rgba(255, 255, 255, .08) 35%, rgba(34, 197, 94, .38) 125%);
    --ghost-primary-on-dark: linear-gradient(160deg, rgba(255, 255, 255, .08) 35%, rgba(44, 117, 222, .38) 125%);
    --ghost-warn-on-dark: linear-gradient(160deg, rgba(255, 255, 255, .08) 35%, rgba(145, 109, 246, .38) 125%);
    --ghost-warning-on-dark: linear-gradient(160deg, rgba(255, 255, 255, .08) 35%, rgba(245, 158, 11, .38) 125%);
    --ghost-accent-on-dark: linear-gradient(160deg, rgba(255, 255, 255, .08) 35%, rgba(105, 226, 225, .38) 125%);
    --ghost-accent-dk-on-dark: linear-gradient(160deg, rgba(255, 255, 255, .08) 35%, rgba(69, 150, 150, .38) 125%);
    --ghost-danger-on-dark: linear-gradient(160deg, rgba(255, 255, 255, .08) 35%, rgba(220, 53, 69, .38) 125%);
    --ghost-dark-on-dark: linear-gradient(160deg, rgba(255, 255, 255, .08) 35%, rgba(15, 42, 71, .38) 125%);
}
/* Bare ghost/outline (no colorway): dark glass, brand text lifted. */
.btn-on-dark.btn-ghost,
.btn-on-dark.mvc-btn--ghost,
.btn-on-dark[class*="btn-outline"] {
    background: var(--ghost-glass-on-dark);
    color: var(--brand-color-lt);
}
.btn-on-dark.btn-ghost-brand,
.mvc-btn--ghost.mvc-btn--ghost-brand.btn-on-dark,
.btn-on-dark.btn-outline-brand {
    background: var(--ghost-brand-on-dark);
    color: var(--brand-color-lt);
}
.btn-on-dark.btn-ghost-success,
.mvc-btn--ghost.mvc-btn--ghost-success.btn-on-dark,
.btn-on-dark.btn-outline-success {
    background: var(--ghost-success-on-dark);
    color: var(--success-color-lt);
}
.btn-on-dark.btn-ghost-primary,
.mvc-btn--ghost.mvc-btn--ghost-primary.btn-on-dark,
.btn-on-dark.btn-outline-primary,
.btn-on-dark.btn-outline.b-primary {
    background: var(--ghost-primary-on-dark);
    color: var(--primary-color-lt);
}
.btn-on-dark.btn-ghost-warn,
.btn-on-dark.btn-outline-warn {
    background: var(--ghost-warn-on-dark);
    color: var(--warn-color-lt);
}
/* warning text steps UP the amber scale on dark (#fbbf24) — the light-mode
   #d97706 was picked for contrast on the light glass, too dim on dark. */
.btn-on-dark.btn-ghost-warning,
.btn-on-dark.btn-outline-warning {
    background: var(--ghost-warning-on-dark);
    color: #fbbf24;
}
.btn-on-dark.btn-ghost-accent,
.btn-on-dark.btn-outline-accent {
    background: var(--ghost-accent-on-dark);
    color: var(--accent-color-lt);
}
.btn-on-dark.btn-ghost-accent-dk,
.btn-on-dark.btn-outline-accent-dk {
    background: var(--ghost-accent-dk-on-dark);
    color: var(--accent-color);
}
.btn-on-dark.btn-ghost-danger,
.btn-on-dark.btn-outline-danger {
    background: var(--ghost-danger-on-dark);
    color: var(--danger-color-lt);
}
/* dark family: the navy tint is near-invisible on dark, so this rests as
   plain dark glass with white text (its engaged navy fill still reads). */
.btn-on-dark.btn-ghost-dark,
.btn-on-dark.btn-outline-dark {
    background: var(--ghost-dark-on-dark);
    color: #fff;
}
.btn-on-dark.btn-ghost-muted,
.mvc-btn--ghost.mvc-btn--report.btn-on-dark {
    color: rgba(255, 255, 255, .55);
}
/* Sticky-tap pins — two classes out-rank §2/§3's single-class pins. */
.btn-on-dark.btn-ghost:hover, .btn-on-dark.btn-ghost:focus {
    background: var(--ghost-glass-on-dark) !important;
    color: var(--brand-color-lt) !important;
}
.btn-on-dark.btn-ghost-brand:hover, .btn-on-dark.btn-ghost-brand:focus,
.btn-on-dark.btn-outline-brand:hover, .btn-on-dark.btn-outline-brand:focus {
    background: var(--ghost-brand-on-dark) !important;
    color: var(--brand-color-lt) !important;
}
.btn-on-dark.btn-ghost-success:hover, .btn-on-dark.btn-ghost-success:focus,
.btn-on-dark.btn-outline-success:hover, .btn-on-dark.btn-outline-success:focus {
    background: var(--ghost-success-on-dark) !important;
    color: var(--success-color-lt) !important;
}
.btn-on-dark.btn-ghost-primary:hover, .btn-on-dark.btn-ghost-primary:focus,
.btn-on-dark.btn-outline-primary:hover, .btn-on-dark.btn-outline-primary:focus,
.btn-on-dark.btn-outline.b-primary:hover, .btn-on-dark.btn-outline.b-primary:focus {
    background: var(--ghost-primary-on-dark) !important;
    color: var(--primary-color-lt) !important;
}
.btn-on-dark.btn-ghost-warn:hover, .btn-on-dark.btn-ghost-warn:focus,
.btn-on-dark.btn-outline-warn:hover, .btn-on-dark.btn-outline-warn:focus {
    background: var(--ghost-warn-on-dark) !important;
    color: var(--warn-color-lt) !important;
}
.btn-on-dark.btn-ghost-warning:hover, .btn-on-dark.btn-ghost-warning:focus,
.btn-on-dark.btn-outline-warning:hover, .btn-on-dark.btn-outline-warning:focus {
    background: var(--ghost-warning-on-dark) !important;
    color: #fbbf24 !important;
}
.btn-on-dark.btn-ghost-accent:hover, .btn-on-dark.btn-ghost-accent:focus,
.btn-on-dark.btn-outline-accent:hover, .btn-on-dark.btn-outline-accent:focus {
    background: var(--ghost-accent-on-dark) !important;
    color: var(--accent-color-lt) !important;
}
.btn-on-dark.btn-ghost-accent-dk:hover, .btn-on-dark.btn-ghost-accent-dk:focus,
.btn-on-dark.btn-outline-accent-dk:hover, .btn-on-dark.btn-outline-accent-dk:focus {
    background: var(--ghost-accent-dk-on-dark) !important;
    color: var(--accent-color) !important;
}
.btn-on-dark.btn-ghost-danger:hover, .btn-on-dark.btn-ghost-danger:focus,
.btn-on-dark.btn-outline-danger:hover, .btn-on-dark.btn-outline-danger:focus {
    background: var(--ghost-danger-on-dark) !important;
    color: var(--danger-color-lt) !important;
}
.btn-on-dark.btn-ghost-dark:hover, .btn-on-dark.btn-ghost-dark:focus,
.btn-on-dark.btn-outline-dark:hover, .btn-on-dark.btn-outline-dark:focus {
    background: var(--ghost-dark-on-dark) !important;
    color: #fff !important;
}
.btn-on-dark.btn-ghost-muted:hover, .btn-on-dark.btn-ghost-muted:focus {
    color: rgba(255, 255, 255, .55) !important;
}

/* ── Fee-incidence pills (D9) — POS cart pills restyled for a light rbox.
       Hoisted from Express-Booking-Create's inline block 2026-07-11; now shared
       with Booking-Details' Send-a-Quote form. */
.ec-pills { display: flex; gap: .4rem; }
.ec-pill {
    flex: 1 1 0; min-width: 0; text-align: center; cursor: pointer; padding: .6rem .35rem;
    border-radius: 12px; border: 2px solid rgba(0, 0, 0, .12); background: transparent;
    color: #555; font-size: .85rem; line-height: 1.15;
}
.ec-pill span { display: block; font-size: .64rem; color: rgba(0, 0, 0, .4); margin-top: .15rem; }
.ec-pill.selected {
    border-color: var(--primary-color);
    background: rgba(44, 117, 222, .08);
    color: var(--primary-color-dk);
}
.ec-pill.selected span { color: var(--primary-color); }

/* ═════════════════════════════════════════════════════════════════════
   RT CALENDAR (rt-calendar.js) — reusable modal date picker, single/range.
   Rebuilt from v1 JCAL's intent (express-booking-create-rework.md D13).
   Booking family = warn (purple). Light card on a dim scrim; the months
   list scrolls inside the card, the page behind never scrolls. Touch-first:
   hover lives only in the (hover:hover) block at the bottom.
   ═════════════════════════════════════════════════════════════════════ */
.rtcal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    /* padding: calc(env(safe-area-inset-top) + 1rem) 1rem calc(env(safe-area-inset-bottom) + 1rem); */
    padding: calc(env(safe-area-inset-top) + 4.125rem) 0 0 0;
    background: rgba(30, 24, 54, .55);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    overscroll-behavior: contain;
    touch-action: none; /* JS guard lets only .rtcal-months pan */
    transition: opacity 300ms cubic-bezier(0.77, 0, 0, 1), visibility 0s linear 300ms;
}
.rtcal-overlay.show {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 300ms cubic-bezier(0.77, 0, 0, 1);
}
.rtcal-card {
    display: flex;
    flex-direction: column;
    width: min(100%, 26rem);
    /* max-height: min(100%, 44rem); */
    max-height: min(100%, 55rem);
    background: #fff;
    border-top-left-radius: 2rem;
    border-top-right-radius: 2rem;
    box-shadow: var(--box-shadow-cont);
    overflow: hidden;
    transform: translateY(1.5rem) scale(.97);
    transition: transform 400ms cubic-bezier(0.77, 0, 0, 1);
}
.rtcal-overlay.show .rtcal-card {
    transform: none;
}

/* ── Top bar: Close (X) + Clear ── */
.rtcal-topbar {
    display: flex;
    align-items: center;
    justify-content: end;
    flex-direction: row-reverse;
    padding: .75rem .75rem 0;
}
.rtcal-close,
.rtcal-clear {
    min-width: 2.75rem;
    min-height: 2.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 100%;
    cursor: pointer;
    transition: all 400ms ease-in-out;
}
.rtcal-close {
    font-size: 1.15rem;
    color: #495057;
    background: rgba(0, 0, 0, .05);
}
.rtcal-close:active {
    background: rgba(0, 0, 0, .12);
}
.rtcal-clear {
    padding: 0 .75rem;
    border-radius: 5rem;
    font-family: soehne-halbfett;
    font-size: .8rem;
    color: var(--warn-color-dk);
    background: transparent;
}
.rtcal-clear:active {
    background: var(--warn-color-slt);
}

/* ── Header readout: Start date → End date ── */
.rtcal-header {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1.25rem;
    padding: .25rem 1.5rem 1rem;
    border-bottom: 1px solid #eee;
}
.rtcal-date {
    text-align: center;
    min-width: 6.5rem;
}
.rtcal-date span {
    display: block;
    font-size: .65rem;
    opacity: .5;
    margin-bottom: .1rem;
}
.rtcal-date div {
    font-family: soehne-halbfett;
    font-size: 1rem;
    color: var(--warn-color-dk);
    transition: all 400ms ease-in-out;
}
.rtcal-arrow {
    color: var(--warn-color-lt);
    font-size: 1rem;
}
/* Single mode: one centered date, no arrow/end */
.rtcal-card.single .rtcal-arrow,
.rtcal-card.single .rtcal-end {
    display: none;
}

/* ── Months: 12 months in one continuous vertical scroll (no paging) ── */
.rtcal-months {
    position: relative; /* offsetParent for the open()-scroll targeting */
    flex: 1;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    touch-action: pan-y;
    padding: 0 1rem .75rem;
}
.rtcal-month-name {
    font-family: soehne-halbfett;
    font-size: .95rem;
    color: #212529;
    padding: 1.25rem .5rem .5rem;
}
.rtcal-weekdays,
.rtcal-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
}
.rtcal-weekdays > div {
    text-align: center;
    font-size: .7rem;
    /* opacity: .4; */
    padding: .25rem 0;
    font-family: soehne-halbfett, sans-serif;
    color: var(--warn-color);
    /* border-bottom: 1px solid var(--warn-color-lt); */
}
.rtcal-day {
    position: relative;
    z-index: 1; /* stacking context so the -z pseudo layers sit behind the number */
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 2.75rem; /* touch-sized */
    font-size: .9rem;
    color: #212529;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.rtcal-day.blank {
    cursor: auto;
}
.rtcal-day.inactive {
    color: #ced4da;
    pointer-events: none;
}
/* Today: subtle ring (selected paint takes over when picked) */
.rtcal-day.today:not(.selected)::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 2.5rem;
    height: 2.5rem;
    transform: translate(-50%, -50%);
    z-index: -1;
    border-radius: 100%;
    border: 2px solid var(--brand-color-lt);
    opacity: .6;
}
/* .rtcal-day.today.inactive::after {
    border-color: #dee2e6;
} */
/* In-range days: light warn tint bar */
.rtcal-day.range::before {
    content: "";
    position: absolute;
    top: .25rem;
    bottom: .25rem;
    left: 0;
    right: 0;
    z-index: -2;
    background: var(--warn-color-slt);
}
/* Range endpoints: half-width bars so the tint meets the selected circle */
.rtcal-day.bar-right::before {
    content: "";
    position: absolute;
    top: .25rem;
    bottom: .25rem;
    left: 50%;
    right: 0;
    z-index: -2;
    background: var(--warn-color-slt);
}
.rtcal-day.bar-left::before {
    content: "";
    position: absolute;
    top: .25rem;
    bottom: .25rem;
    left: 0;
    right: 50%;
    z-index: -2;
    background: var(--warn-color-slt);
}
/* Selected (start or end): solid warn circle, white number */
.rtcal-day.selected {
    color: white;
    font-family: soehne-halbfett;
}
.rtcal-day.selected::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 2.5rem;
    height: 2.5rem;
    transform: translate(-50%, -50%);
    z-index: -1;
    border-radius: 100%;
    background: var(--warn-color);
    border: 2px solid var(--warn-color-lt);
    /* box-shadow: 0 5px 15px -5px rgba(50, 50, 93, .5); */
    box-shadow: 0 .375rem .875rem rgba(145, 109, 246, .35);
}

/* ── Per-day metadata (dayData) — future-booking-mode.md D2: a small price line
      under the day number, shown only on days whose effective rate differs from
      base. .has-meta (set by rt-calendar.js while any label exists) stacks the
      cells and gives the grid rows room. ── */
.rtcal-day-meta {
    display: block;
    font-size: .55rem;
    line-height: 1;
    font-family: soehne-halbfett;
    color: #868e96;
    margin-top: .15rem;
}
.rtcal-day-meta.up {
    color: var(--warn-color-dk);
}
.rtcal-card.has-meta .rtcal-day {
    flex-direction: column;
    min-height: 3.1rem;
}
.rtcal-day.selected .rtcal-day-meta {
    display: none; /* redundant once picked; unreadable on the warn circle */
}

/* ── Continue ── */
.rtcal-continue {
    margin: .75rem 1.25rem calc(env(safe-area-inset-bottom) + 1rem) 1.25rem;
    padding: .7rem;
    text-align: center;
    font-family: soehne-halbfett;
    font-size: 1rem;
    color: white;
    background: var(--warn-color);
    border-radius: 5rem;
    outline: 3px solid var(--warn-color-lt);
    box-shadow: var(--box-shadow-cont-small);
    cursor: pointer;
    transition: all 400ms cubic-bezier(0.77, 0, 0, 1);
}
.rtcal-continue:active {
    background: var(--warn-color-dk);
    transform: scale(.98);
}
.rtcal-continue.locked {
    background: grey;
    outline-color: lightgrey;
    cursor: auto;
    pointer-events: none;
}

/* Desktop-only hover (mobile-first app: never bare :hover) */
@media (hover: hover) and (pointer: fine) {
    .rtcal-close:hover {
        background: rgba(0, 0, 0, .1);
    }
    .rtcal-clear:hover {
        background: var(--warn-color-slt);
    }
    .rtcal-day:not(.inactive):not(.blank):not(.selected):hover {
        color: var(--warn-color-dk);
    }
    .rtcal-continue:hover {
        background: var(--warn-color-dk);
        outline-color: var(--warn-color);
    }
}

/* Laleh destructive controls (WS-J follow-up): the swipe-reveal delete
   button behind list rows + the top-bar delete's two-tap armed state.
   Solid danger fill — a destructive action never reads as glass. */
.lv-row-del,
.mv-ch-btn.lv-del-armed {
    background: var(--brand-color);
    color: #fff;
}


/* KEEPER DASHBOARD TWEAKS */
.floating-lock {
    position: absolute;
    top: .5rem;
    left: .25rem;
    font-size: 1.125rem;
    color: white;
}

/* MESSAGES TWEAKS */
.m-unread {
    background-color: rgba(255, 255, 255, .65);
    border-radius: 1rem;
    margin: .25rem;
    border: none;
}

.m-read {
    background-color: rgba(0, 0, 0, .04);
    border-radius: 1rem;
    margin: .25rem;
    border: none;
}