/* app.css - generated by scripts/build-assets.mjs. Do not edit by hand. */
@layer reset, tokens, base, components, utilities, overrides;
/* ==========================================================================
   fonts.css - self-hosted variable fonts (latin subset, WOFF2).
   Served from /assets/fonts/ (copied by scripts/build-assets.mjs).
   Design system faces: Fraunces (display), Inter (sans), JetBrains Mono (data).
   @font-face lives outside @layer on purpose (global, cascade-neutral).
   ========================================================================== */

@font-face {
    font-family: "Fraunces";
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url("/assets/fonts/fraunces-latin-normal.woff2") format("woff2");
}

@font-face {
    font-family: "Fraunces";
    font-style: italic;
    font-weight: 300 700;
    font-display: swap;
    src: url("/assets/fonts/fraunces-latin-italic.woff2") format("woff2");
}

@font-face {
    font-family: "Inter";
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url("/assets/fonts/inter-latin-normal.woff2") format("woff2");
}

@font-face {
    font-family: "JetBrains Mono";
    font-style: normal;
    font-weight: 400 600;
    font-display: swap;
    src: url("/assets/fonts/jetbrains-mono-latin-normal.woff2") format("woff2");
}

@layer reset {
/* core/assets/css/reset.css */
/* ==========================================================================
   reset.css - modern reset (Andy Bell / Josh Comeau lineage), token-neutral.
   No font-family or colour here: those belong to base.css so the reset stays
   fully agnostic and reusable across tenants.
   ========================================================================== */

*,
*::before,
*::after {
    box-sizing: border-box;
}

* {
    margin: 0;
}

html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    -moz-tab-size: 4;
    tab-size: 4;
}

body {
    min-height: 100svh;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

img,
picture,
video,
canvas,
svg {
    display: block;
    max-width: 100%;
    height: auto;
}

input,
button,
textarea,
select {
    font: inherit;
    color: inherit;
}

p,
h1,
h2,
h3,
h4,
h5,
h6 {
    overflow-wrap: break-word;
}

h1,
h2,
h3,
h4,
h5,
h6 {
    text-wrap: balance;
}

p {
    text-wrap: pretty;
}

ul[role="list"],
ol[role="list"] {
    list-style: none;
    padding: 0;
}

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

button {
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
}

:target {
    scroll-margin-block: 5rem;
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

}

@layer tokens {
/* tenant/theme/tokens.css */
/* ==========================================================================
   Sample Tenant — Onyx & Or
   Direction visuelle de production pour Chambre Hammam Oisans (D13).
   Cible : B&B haut de gamme avec différenciateur expérientiel premium.
   Ambiance : hammam intime, luxe discret, montagne nocturne.
   ========================================================================== */

:root {

    /* --- Couleurs ---------------------------------------------------------- */

    --color-bg:               #0E0F12;
    --color-bg-soft:          #16181D;
    --color-surface:          #1E2128;
    --color-surface-hover:    #262A33;

    --color-text:             #F4F1EC;
    --color-text-muted:       #A8A39B;
    --color-text-inverse:     #0E0F12;

    --color-border:           #2A2E37;
    --color-border-strong:    #3A3F4A;

    --color-accent:           #D4A574;   /* or chaud hammam */
    --color-accent-soft:      #2C2419;   /* or 5% pour fond subtle */
    --color-accent-warm:      #C8704A;   /* terracotta */
    --color-accent-cool:      #6B8E9F;   /* glacier */

    --color-success:          #7FA982;
    --color-warning:          #D4A574;   /* aligné sur accent volontairement */
    --color-danger:           #C75A5A;

    --color-focus-ring:       #D4A574;

    /* --- Typographie ------------------------------------------------------- */

    --font-display:           "Fraunces", "Times New Roman", Georgia, serif;
    --font-sans:              "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-mono:              "JetBrains Mono", "SF Mono", Consolas, monospace;

    --font-weight-light:      300;
    --font-weight-regular:    400;
    --font-weight-medium:     500;
    --font-weight-semibold:   600;
    --font-weight-bold:       700;

    /* Échelle modulaire 1.25 — base 16 */
    --text-step--1:           0.8rem;
    --text-step-0:            1rem;
    --text-step-1:            1.25rem;
    --text-step-2:            1.5625rem;
    --text-step-3:            1.953rem;
    --text-step-4:            2.441rem;
    --text-step-5:            3.052rem;

    --leading-tight:          1.15;
    --leading-snug:           1.3;
    --leading-normal:         1.5;
    --leading-loose:          1.75;

    --tracking-tight:         -0.02em;
    --tracking-normal:        0;
    --tracking-wide:          0.05em;

    /* --- Espacement ------------------------------------------------------- */

    --space-1:                0.25rem;
    --space-2:                0.5rem;
    --space-3:                0.75rem;
    --space-4:                1rem;
    --space-6:                1.5rem;
    --space-8:                2rem;
    --space-12:               3rem;
    --space-16:               4rem;
    --space-24:               6rem;
    --space-32:               8rem;

    /* --- Rayons ----------------------------------------------------------- */

    --radius-sm:              4px;
    --radius-md:              8px;
    --radius-lg:              16px;
    --radius-pill:            999px;

    /* --- Ombres ----------------------------------------------------------- */

    --shadow-1:               0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-2:               0 8px 24px rgba(0, 0, 0, 0.35);
    --shadow-3:               0 24px 64px rgba(0, 0, 0, 0.5);
    --shadow-glow:            0 0 80px -20px var(--color-accent);

    /* --- Mouvement -------------------------------------------------------- */

    --dur-1:                  200ms;
    --dur-2:                  400ms;
    --dur-3:                  800ms;

    --ease-out-expo:          cubic-bezier(0.16, 1, 0.3, 1);
    --ease-in-out-quart:      cubic-bezier(0.77, 0, 0.175, 1);
    --ease-bounce:            cubic-bezier(0.68, -0.55, 0.265, 1.55);

    /* --- Breakpoints ------------------------------------------------------ */

    --bp-sm:                  480px;
    --bp-md:                  768px;
    --bp-lg:                  1024px;
    --bp-xl:                  1280px;
    --bp-2xl:                 1536px;

    /* --- Z-index ---------------------------------------------------------- */

    --z-base:                 0;
    --z-sticky:               10;
    --z-dropdown:             100;
    --z-modal:                1000;
    --z-toast:                10000;
}

}

@layer base {
/* core/assets/css/base.css */
/* ==========================================================================
   base.css - element-level styling consuming design tokens.
   Sets the typographic system (Fraunces display / Inter body / mono data),
   colour ground, links, focus ring and selection. Everything is var()-driven.
   ========================================================================== */

html {
    scroll-behavior: smooth;
    font-family: var(--font-sans);
    color-scheme: dark;
    background: var(--color-bg);
}

body {
    background: var(--color-bg);
    color: var(--color-text);
    font-size: var(--text-step-0);
    font-weight: var(--font-weight-regular);
    line-height: var(--leading-normal);
    font-synthesis: none;
    overflow-x: hidden;
}

/* Editorial display faces ------------------------------------------------- */

h1,
h2,
h3 {
    font-family: var(--font-display);
    font-weight: var(--font-weight-light);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-tight);
    color: var(--color-text);
}

h1 { font-size: var(--text-step-5); }
h2 { font-size: var(--text-step-4); }
h3 { font-size: var(--text-step-3); }

h4,
h5,
h6 {
    font-family: var(--font-sans);
    font-weight: var(--font-weight-semibold);
    line-height: var(--leading-snug);
    letter-spacing: var(--tracking-normal);
}

h4 { font-size: var(--text-step-2); }
h5 { font-size: var(--text-step-1); }
h6 { font-size: var(--text-step-0); }

/* Fraunces italic is the "voice" of the brand: emphasise with restraint. */
em,
.u-italic {
    font-family: var(--font-display);
    font-style: italic;
}

strong {
    font-weight: var(--font-weight-semibold);
}

small {
    font-size: var(--text-step--1);
}

code,
kbd,
samp,
.u-mono {
    font-family: var(--font-mono);
    font-size: 0.9em;
}

/* Links ------------------------------------------------------------------- */

a {
    color: inherit;
    transition: color var(--dur-1) var(--ease-out-expo);
}

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

hr {
    border: none;
    border-top: 1px solid var(--color-border);
    margin-block: var(--space-8);
}

/* Accessibility ----------------------------------------------------------- */

:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 3px;
    border-radius: var(--radius-sm);
}

:focus:not(:focus-visible) {
    outline: none;
}

::selection {
    background: var(--color-accent);
    color: var(--color-text-inverse);
}

/* Eyebrow: recurring label above section titles --------------------------- */

.eyebrow {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    max-inline-size: 100%;
    font-family: var(--font-sans);
    font-size: var(--text-step--1);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--color-accent);
}

/* Leading rule stays a fixed length; the label takes the rest and wraps
   instead of forcing horizontal overflow on narrow screens. */
.eyebrow::before {
    content: "";
    flex: none;
    inline-size: 2.25rem;
    block-size: 1px;
    background: currentColor;
}

.eyebrow--bare::before {
    display: none;
}

/* Section rhythm - vertical breathing room between page regions.
   Sections stack as siblings, so adjacent padding-block values ADD at each
   boundary: the effective gap between two regions is ~2x this value. Kept
   moderate so stacked transparent sections read as breathing room, not empty
   voids (L10.7 polish). Boundary gap lands ~96px (narrow) to ~128px (>=1600). */
.section {
    padding-block: clamp(var(--space-12), 4vw, var(--space-16));
}

.section--tight {
    padding-block: clamp(var(--space-8), 3vw, var(--space-12));
}

/* Section head - eyebrow + title pairing used across pages. */
.section-head {
    display: grid;
    gap: var(--space-4);
    margin-block-end: clamp(var(--space-8), 5vw, var(--space-16));
    max-inline-size: 46rem;
}

.section-title {
    font-family: var(--font-display);
    font-weight: var(--font-weight-light);
    font-size: var(--text-step-4);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-tight);
}

.section-title em {
    color: var(--color-accent);
}

/* core/assets/css/animations.css */
/* ==========================================================================
   animations.css - global keyframes. Motion is opt-in and always gated by
   prefers-reduced-motion at the component level (see reset.css safety net).
   ========================================================================== */

/* Vapour: the signature ambient glow breathes like hammam steam. */
@keyframes vapor-breathe {
    0%,
    100% {
        opacity: 0.55;
        transform: translate3d(0, 0, 0) scale(1);
    }
    50% {
        opacity: 0.9;
        transform: translate3d(0, -2%, 0) scale(1.06);
    }
}

/* Skeleton loader shimmer. */
@keyframes skeleton-shimmer {
    0% {
        background-position: -160% 0;
    }
    100% {
        background-position: 160% 0;
    }
}

/* Toast entrance. */
@keyframes toast-in {
    from {
        opacity: 0;
        transform: translateY(0.75rem);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Hero media slow settle (paired with .is-loaded). */
@keyframes hero-settle {
    from {
        transform: scale(1.06);
    }
    to {
        transform: scale(1);
    }
}

}

@layer components {
/* core/assets/css/compositions/center.css */
/* Center - horizontally centred content column with a max measure and inline
   gutters. The page container. Tune with --measure and --gutter.
   border-box so the inline padding is contained within the measure and never
   pushes the box past 100vw (was content-box, which caused a 2x-gutter
   horizontal overflow on mobile). */
.center {
    box-sizing: border-box;
    inline-size: 100%;
    max-inline-size: var(--measure, 72rem);
    margin-inline: auto;
    padding-inline: var(--gutter, clamp(1.5rem, 5vw, 3rem));
}

.center[data-center="narrow"] { --measure: 44rem; }
.center[data-center="prose"] { --measure: 65ch; }
.center[data-center="wide"] { --measure: 84rem; }

/* core/assets/css/compositions/cluster.css */
/* Cluster - horizontal group that wraps gracefully. Nav rows, tag lists,
   button pairs. Tune with --cluster-space, --cluster-align, --cluster-justify. */
.cluster {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cluster-space, var(--space-4));
    align-items: var(--cluster-align, center);
    justify-content: var(--cluster-justify, flex-start);
}

.cluster[data-cluster="between"] { --cluster-justify: space-between; }
.cluster[data-cluster="center"] { --cluster-justify: center; }
.cluster[data-cluster="end"] { --cluster-justify: flex-end; }

/* core/assets/css/compositions/cover.css */
/* Cover - full-height region with a centred principal element and optional
   top (header) and bottom (meta) slots. Used by heroes.
   Tune with --cover-min (min block size) and --cover-space. */
.cover {
    display: flex;
    flex-direction: column;
    min-block-size: var(--cover-min, 100svh);
    padding: var(--cover-space, var(--space-8));
    gap: var(--cover-space, var(--space-8));
}

.cover > * {
    margin-block: 0;
}

.cover > :first-child:not(.cover__principal) {
    margin-block-end: auto;
}

.cover > :last-child:not(.cover__principal) {
    margin-block-start: auto;
}

.cover > .cover__principal {
    margin-block: auto;
}

/* core/assets/css/compositions/frame.css */
/* Frame - crops media to a fixed aspect ratio. Prevents layout shift (CLS=0).
   Tune with --frame-ratio; presets via data-frame. */
.frame {
    display: block;
    overflow: hidden;
    aspect-ratio: var(--frame-ratio, 16 / 9);
    background: var(--color-bg-soft);
}

.frame > img,
.frame > video,
.frame > picture > img {
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
}

.frame[data-frame="square"] { --frame-ratio: 1 / 1; }
.frame[data-frame="portrait"] { --frame-ratio: 4 / 5; }
.frame[data-frame="wide"] { --frame-ratio: 3 / 2; }
.frame[data-frame="cinema"] { --frame-ratio: 21 / 9; }

/* core/assets/css/compositions/grid.css */
/* Grid - responsive auto-fit grid with a sensible minimum column.
   Tune with --grid-min (min column width) and --grid-space. */
.grid {
    display: grid;
    gap: var(--grid-space, var(--space-6));
    grid-template-columns: repeat(
        auto-fit,
        minmax(min(var(--grid-min, 16rem), 100%), 1fr)
    );
}

/* Fixed two-up that collapses to one on narrow viewports. */
.grid[data-grid="halves"] {
    grid-template-columns: 1fr;
}

@media (min-width: 48rem) {
    .grid[data-grid="halves"] {
        grid-template-columns: 1fr 1fr;
    }
}

/* core/assets/css/compositions/sidebar.css */
/* Sidebar - a fixed-width aside next to a fluid main, wrapping when the main
   would get narrower than --sidebar-content-min (Every Layout sidebar).
   Tune with --sidebar-width, --sidebar-space, --sidebar-content-min. */
.sidebar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sidebar-space, var(--space-8));
}

.sidebar > * {
    flex-grow: 1;
}

.sidebar > .sidebar__aside {
    flex-grow: 1;
    flex-basis: var(--sidebar-width, 20rem);
}

.sidebar > .sidebar__main {
    flex-grow: 999;
    flex-basis: 0;
    min-inline-size: var(--sidebar-content-min, 60%);
}

/* core/assets/css/compositions/stack.css */
/* Stack - vertical rhythm. Owns the space between flow siblings.
   Tune with --stack-space (defaults to --space-6). */
.stack {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
}

.stack > * {
    margin-block: 0;
}

.stack > * + * {
    margin-block-start: var(--stack-space, var(--space-6));
}

.stack[data-stack="tight"] { --stack-space: var(--space-3); }
.stack[data-stack="loose"] { --stack-space: var(--space-12); }
.stack[data-stack="section"] { --stack-space: var(--space-24); }

/* core/assets/css/components/availability-calendar.css */
/* Availability calendar - read-only, two months side by side. SSR renders the
   grids all-available with an "indicatif" note; main.js progressively enhances
   by fetching GET /api/v1/availability and marking booked days. No date
   selection in Phase 2 (that lands in L10.4). */
.availability {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-8);
}

@media (min-width: 44rem) {
    .availability { grid-template-columns: repeat(2, 1fr); }
}

.cal-month__name {
    font-family: var(--font-display);
    font-size: var(--text-step-1);
    letter-spacing: var(--tracking-tight);
    margin-block-end: var(--space-4);
    text-transform: capitalize;
}

.cal-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
}

.cal-grid__dow {
    text-align: center;
    font-size: var(--text-step--2);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    color: var(--color-text-muted);
    padding-block: var(--space-2);
}

.cal-day {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-mono);
    font-feature-settings: "tnum" 1;
    font-size: var(--text-step--1);
    color: var(--color-text);
    border-radius: var(--radius-sm);
    background: var(--color-bg-soft);
}

.cal-day--empty { background: transparent; }

.cal-day--past {
    color: var(--color-text-muted);
    opacity: 0.4;
}

.cal-day--booked {
    color: var(--color-text-muted);
    background: transparent;
    text-decoration: line-through;
    text-decoration-color: var(--color-danger);
}

.cal-day--available {
    background: color-mix(in srgb, var(--color-success) 16%, var(--color-bg-soft));
}

.cal-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-6);
    margin-block-start: var(--space-6);
    font-size: var(--text-step--1);
    color: var(--color-text-muted);
}

.cal-legend__item {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.cal-legend__swatch {
    inline-size: 0.85rem;
    block-size: 0.85rem;
    border-radius: var(--radius-sm);
    flex: none;
}

.cal-legend__swatch--available { background: color-mix(in srgb, var(--color-success) 40%, var(--color-bg-soft)); }
.cal-legend__swatch--booked { background: transparent; border: 1px solid var(--color-danger); }

/* core/assets/css/components/breadcrumb.css */
/* Breadcrumb - shallow trail above the property hero. Separators are decorative
   (aria-hidden in markup); the list is a nav landmark. */
.breadcrumb {
    padding-block: var(--space-4);
}

.breadcrumb__list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: var(--text-step--1);
    color: var(--color-text-muted);
}

.breadcrumb__item {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
}

.breadcrumb__sep {
    color: var(--color-border-strong);
}

.breadcrumb a {
    color: var(--color-text-muted);
    text-decoration: none;
    transition: color var(--dur-1) var(--ease-out-expo);
}

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

.breadcrumb__item[aria-current="page"] {
    color: var(--color-text);
}

/* core/assets/css/components/button.css */
/* Button - primary / ghost / link, with an animated arrow affordance. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    padding: 1rem 2rem;
    font-family: var(--font-sans);
    font-size: var(--text-step--1);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    line-height: 1;
    white-space: nowrap;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition:
        transform var(--dur-1) var(--ease-out-expo),
        background-color var(--dur-2) var(--ease-out-expo),
        color var(--dur-2) var(--ease-out-expo),
        border-color var(--dur-2) var(--ease-out-expo),
        box-shadow var(--dur-2) var(--ease-out-expo);
}

.btn:disabled,
.btn[aria-disabled="true"] {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: none;
}

.btn--primary {
    background: var(--color-accent);
    color: var(--color-text-inverse);
}

.btn--primary:hover {
    background: var(--color-text);
    transform: translateY(-2px);
    box-shadow: 0 12px 40px -12px var(--color-accent);
}

.btn--ghost {
    background: transparent;
    color: var(--color-text);
    border-color: var(--color-border-strong);
}

.btn--ghost:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
    transform: translateY(-2px);
}

.btn--link {
    padding: 0;
    border-radius: 0;
    background: none;
    color: var(--color-accent);
    text-transform: none;
    letter-spacing: normal;
    border-bottom: 1px solid transparent;
}

.btn--link:hover {
    border-bottom-color: var(--color-accent);
}

.btn--small {
    padding: 0.6rem 1.25rem;
}

.btn--block {
    inline-size: 100%;
}

.btn__arrow {
    display: inline-block;
    transition: transform var(--dur-1) var(--ease-out-expo);
}

.btn:hover .btn__arrow {
    transform: translateX(4px);
}

/* core/assets/css/components/card.css */
/* Card - surface panel for properties, reviews, villages. Media crops via
   .frame; hover lifts and warms the image. */
.card {
    display: flex;
    flex-direction: column;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    transition:
        transform var(--dur-2) var(--ease-out-expo),
        border-color var(--dur-2) var(--ease-out-expo),
        box-shadow var(--dur-2) var(--ease-out-expo);
}

.card:hover {
    transform: translateY(-4px);
    border-color: var(--color-border-strong);
    box-shadow: var(--shadow-2);
}

.card__media {
    position: relative;
}

.card__media img {
    transition:
        transform var(--dur-3) var(--ease-out-expo),
        filter var(--dur-3) var(--ease-out-expo);
    filter: brightness(0.92) saturate(1.02);
}

.card:hover .card__media img {
    transform: scale(1.04);
    filter: brightness(1) saturate(1.05);
}

.card__badge {
    position: absolute;
    inset-block-start: var(--space-3);
    inset-inline-start: var(--space-3);
    padding: 0.35rem 0.8rem;
    background: color-mix(in srgb, var(--color-bg) 62%, transparent);
    backdrop-filter: blur(8px);
    border-radius: var(--radius-pill);
    font-family: var(--font-mono);
    font-size: var(--text-step--1);
    color: var(--color-text);
}

.card__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-6);
    flex: 1;
}

.card__title {
    font-family: var(--font-display);
    font-weight: var(--font-weight-regular);
    font-size: var(--text-step-2);
    line-height: var(--leading-snug);
    letter-spacing: var(--tracking-tight);
}

.card__meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    font-size: var(--text-step--1);
    color: var(--color-text-muted);
}

.card__price {
    font-family: var(--font-mono);
    color: var(--color-accent);
}

.card__footer {
    margin-block-start: auto;
    padding-block-start: var(--space-2);
}

/* core/assets/css/components/classement-badge.css */
/* Classement badge - official French "Meuble de Tourisme" star rating.
   Belongs to a SINGLE property: rendered only when the tenant content provides
   a `classement` block (data-driven, never generic). Gold stars on the Onyx &
   Or ground; sober, premium, no image asset. */
.classement {
    display: inline-flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: var(--space-3) var(--space-4);
    background: color-mix(in srgb, var(--color-accent) 8%, var(--color-bg));
    border: 1px solid color-mix(in srgb, var(--color-accent) 38%, transparent);
    border-radius: var(--radius-md);
}

.classement__label {
    display: inline-flex;
    align-items: center;
    gap: 0.5ch;
    font-family: var(--font-sans);
    font-size: var(--text-step-0);
    font-weight: var(--font-weight-semibold);
    color: var(--color-text);
    white-space: nowrap;
}

.classement__stars {
    color: var(--color-accent);
    letter-spacing: 0.12em;
    line-height: 1;
}

.classement__sub {
    font-family: var(--font-sans);
    font-size: var(--text-step--1);
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
}

/* Compact one-liner for the home dual-property card (sits inside .card__body,
   anchored to its property - never floating). */
.classement--inline {
    flex-direction: row;
    align-items: center;
    align-self: flex-start;
    gap: 0.6ch;
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--color-accent) 10%, var(--color-bg));
}

.classement--inline .classement__label {
    font-size: var(--text-step--1);
}

/* core/assets/css/components/cta-band.css */
/* CTA band - the recurring closing call to action. Warm gold glow to echo the
   hammam. Reused at the foot of most pages. */
.cta-band {
    position: relative;
    overflow: hidden;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-6);
    text-align: center;
    padding: var(--space-24) var(--space-8);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background:
        radial-gradient(
            ellipse at 50% 120%,
            color-mix(in srgb, var(--color-accent) 22%, transparent) 0%,
            transparent 60%
        ),
        var(--color-bg-soft);
}

.cta-band__title {
    font-family: var(--font-display);
    font-style: italic;
    font-weight: var(--font-weight-light);
    font-size: var(--text-step-4);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-tight);
    max-inline-size: 20ch;
}

.cta-band__text {
    color: var(--color-text-muted);
    max-inline-size: 48ch;
}

/* core/assets/css/components/faq.css */
/* FAQ - native <details> accordion, no JS required (progressive by default).
   Also emits FAQPage schema from the same content (JSON-LD in the template). */
.faq {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    max-inline-size: 52rem;
}

.faq__item {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    overflow: hidden;
}

.faq__item[open] {
    border-color: var(--color-border-strong);
}

.faq__q {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-5) var(--space-6);
    cursor: pointer;
    list-style: none;
    font-family: var(--font-display);
    font-size: var(--text-step-1);
    color: var(--color-text);
    transition: color var(--dur-1) var(--ease-out-expo);
}

.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { color: var(--color-accent); }

.faq__q::after {
    content: "+";
    flex: none;
    font-family: var(--font-sans);
    font-size: var(--text-step-2);
    color: var(--color-accent);
    transition: transform var(--dur-2) var(--ease-out-expo);
}

.faq__item[open] .faq__q::after {
    transform: rotate(45deg);
}

.faq__a {
    padding: 0 var(--space-6) var(--space-6);
    color: var(--color-text-muted);
    max-inline-size: 60ch;
}

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

/* core/assets/css/components/footer.css */
/* Site footer - navigation columns + the Webmarketing Oisans attribution
   (vitrine requirement, CLAUDE.md section 8). */
.site-footer {
    background: var(--color-bg-soft);
    border-block-start: 1px solid var(--color-border);
    padding-block: var(--space-16) var(--space-8);
    margin-block-start: var(--space-24);
}

.site-footer__grid {
    display: grid;
    gap: var(--space-12) var(--space-8);
    grid-template-columns: 1fr;
}

/* Tablet: brand spans the row, the three nav columns sit two-up beneath it. */
@media (min-width: 40rem) {
    .site-footer__grid {
        grid-template-columns: repeat(2, 1fr);
    }

    .footer-brand {
        grid-column: 1 / -1;
    }
}

/* Desktop: brand + Explorer + Découvrir + Informations, all in one row. */
@media (min-width: 64rem) {
    .site-footer__grid {
        grid-template-columns: 1.6fr 1fr 1fr 1fr;
    }

    .footer-brand {
        grid-column: auto;
    }
}

.footer-brand__logo {
    block-size: 5.5rem;
    inline-size: auto;
    margin-block-end: var(--space-4);
}

.footer-brand p {
    color: var(--color-text-muted);
    max-inline-size: 34ch;
}

.footer-col h2 {
    font-family: var(--font-sans);
    font-size: var(--text-step--1);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    color: var(--color-text-muted);
    margin-block-end: var(--space-4);
}

.footer-col a {
    display: block;
    padding-block: var(--space-1);
    color: var(--color-text-muted);
}

.footer-col a:hover {
    color: var(--color-accent);
}

.site-footer__base {
    margin-block-start: var(--space-12);
    padding-block-start: var(--space-6);
    border-block-start: 1px solid var(--color-border);
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    justify-content: space-between;
    align-items: center;
    font-size: var(--text-step--1);
    color: var(--color-text-muted);
}

.wmo-credit {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.wmo-credit a {
    color: var(--color-accent);
    border-bottom: 1px solid transparent;
    transition: border-color var(--dur-1) var(--ease-out-expo);
}

.wmo-credit a:hover {
    border-bottom-color: var(--color-accent);
}

/* core/assets/css/components/gallery.css */
/* Gallery - bento mosaic + full-screen lightbox. Reused by the home
   "Atmosphere" section and the property pages (L10.3). Images crop via
   object-fit; explicit width/height on <img> keep CLS at 0. */
.bento {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-auto-rows: 9rem;
    grid-auto-flow: dense;
    gap: var(--space-3);
}

@media (min-width: 48rem) {
    .bento {
        grid-template-columns: repeat(4, 1fr);
        grid-auto-rows: 11rem;
        gap: var(--space-4);
    }
}

.bento__cell {
    position: relative;
    display: block;
    overflow: hidden;
    border-radius: var(--radius-md);
    background: var(--color-bg-soft);
    cursor: zoom-in;
    border: 1px solid var(--color-border);
}

.bento__cell[data-cell="tall"] { grid-row: span 2; }
.bento__cell[data-cell="wide"] { grid-column: span 2; }
.bento__cell[data-cell="feature"] { grid-column: span 2; grid-row: span 2; }

/* Six-photo tiling that fills the grid with no gaps, in both the 4-column
   (desktop) and 2-column (mobile) layouts, using grid-auto-flow: dense:
     1 feature (2x2) + 2 single (1x1) + 3 wide (2x1)
   => desktop 4x3, mobile 2x6. Change the count/spans together if edited. */

.bento__cell img {
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
    filter: brightness(0.9) saturate(1.03);
    transition:
        transform var(--dur-3) var(--ease-out-expo),
        filter var(--dur-2) var(--ease-out-expo);
}

.bento__cell:hover img {
    transform: scale(1.05);
    filter: brightness(1.02) saturate(1.06);
}

.bento__cell::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(14, 15, 18, 0.35), transparent 40%);
    opacity: 0;
    transition: opacity var(--dur-2) var(--ease-out-expo);
    pointer-events: none;
}

.bento__cell:hover::after { opacity: 1; }

.bento__cell:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 3px;
}

/* --- Lightbox ------------------------------------------------------------- */
.lightbox {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-8);
    background: color-mix(in srgb, var(--color-bg) 94%, transparent);
    backdrop-filter: blur(12px);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur-2) var(--ease-out-expo), visibility var(--dur-2);
}

.lightbox[data-open="true"] {
    opacity: 1;
    visibility: visible;
}

.lightbox__img {
    max-inline-size: min(92vw, 78rem);
    max-block-size: 86vh;
    inline-size: auto;
    block-size: auto;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-3);
}

.lightbox__btn {
    position: absolute;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 3rem;
    block-size: 3rem;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--color-surface) 80%, transparent);
    border: 1px solid var(--color-border);
    color: var(--color-text);
    font-size: var(--text-step-2);
    cursor: pointer;
    transition: background-color var(--dur-1), border-color var(--dur-1), transform var(--dur-1);
}

.lightbox__btn:hover {
    background: var(--color-surface-hover);
    border-color: var(--color-accent);
}

.lightbox__btn--close { inset-block-start: var(--space-6); inset-inline-end: var(--space-6); }
.lightbox__btn--prev { inset-inline-start: var(--space-6); inset-block-start: 50%; transform: translateY(-50%); }
.lightbox__btn--next { inset-inline-end: var(--space-6); inset-block-start: 50%; transform: translateY(-50%); }

.lightbox__btn--prev:hover { transform: translateY(-50%) scale(1.06); }
.lightbox__btn--next:hover { transform: translateY(-50%) scale(1.06); }

@media (prefers-reduced-motion: reduce) {
    .lightbox,
    .lightbox__btn { transition: none; }
}

/* core/assets/css/components/input.css */
/* Form controls - inputs, textarea, select, labels, help and error text. */
.field {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.field__label {
    font-size: var(--text-step--1);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.field__label .req {
    color: var(--color-accent);
}

.input,
.textarea,
.select {
    inline-size: 100%;
    padding: 0.85rem 1rem;
    background: var(--color-bg-soft);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    font-size: var(--text-step-0);
    transition:
        border-color var(--dur-1) var(--ease-out-expo),
        box-shadow var(--dur-1) var(--ease-out-expo),
        background-color var(--dur-1) var(--ease-out-expo);
}

.input::placeholder,
.textarea::placeholder {
    color: var(--color-text-muted);
    opacity: 1;
}

.input:hover,
.textarea:hover,
.select:hover {
    border-color: var(--color-border-strong);
}

.input:focus,
.textarea:focus,
.select:focus {
    outline: none;
    border-color: var(--color-accent);
    background: var(--color-surface);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 22%, transparent);
}

.textarea {
    min-block-size: 7rem;
    resize: vertical;
}

.select {
    appearance: none;
    padding-inline-end: 2.5rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23D4A574' stroke-width='1.5' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 1rem center;
}

.field__help {
    font-size: var(--text-step--1);
    color: var(--color-text-muted);
}

.field[data-invalid="true"] .input,
.field[data-invalid="true"] .textarea,
.field[data-invalid="true"] .select {
    border-color: var(--color-danger);
}

.field__error {
    font-size: var(--text-step--1);
    color: var(--color-danger);
}

/* core/assets/css/components/language-switcher.css */
/* Language switcher - current locale + dropdown of the tenant locales.
   Each item links to the same page under another locale prefix (SSR-built
   hreflang-consistent URLs). Toggled by main.js. */
.lang-switcher {
    position: relative;
}

.lang-switcher__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    font-size: var(--text-step--1);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--color-text-muted);
    transition:
        border-color var(--dur-1) var(--ease-out-expo),
        color var(--dur-1) var(--ease-out-expo);
}

.lang-switcher__toggle:hover {
    border-color: var(--color-accent);
    color: var(--color-text);
}

.lang-switcher__chevron {
    transition: transform var(--dur-1) var(--ease-out-expo);
}

.lang-switcher[data-open="true"] .lang-switcher__chevron {
    transform: rotate(180deg);
}

.lang-switcher__menu {
    position: absolute;
    inset-block-start: calc(100% + var(--space-2));
    inset-inline-end: 0;
    min-inline-size: 10rem;
    padding: var(--space-2);
    display: flex;
    flex-direction: column;
    gap: 2px;
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-2);
    z-index: var(--z-dropdown);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition:
        opacity var(--dur-1) var(--ease-out-expo),
        transform var(--dur-1) var(--ease-out-expo),
        visibility var(--dur-1);
}

.lang-switcher[data-open="true"] .lang-switcher__menu {
    opacity: 1;
    visibility: visible;
    transform: none;
}

.lang-switcher__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    font-size: var(--text-step--1);
    color: var(--color-text-muted);
    transition:
        background-color var(--dur-1) var(--ease-out-expo),
        color var(--dur-1) var(--ease-out-expo);
}

.lang-switcher__item:hover {
    background: var(--color-surface-hover);
    color: var(--color-text);
}

.lang-switcher__item[aria-current="true"] {
    color: var(--color-accent);
}

.lang-switcher__code {
    font-family: var(--font-mono);
    text-transform: uppercase;
}

/* core/assets/css/components/nav.css */
/* Site header - fixed, transparent over the hero, frosts once scrolled.
   Desktop: inline nav. Mobile: slide-in drawer toggled by main.js. */
.site-header {
    position: fixed;
    inset-block-start: 0;
    inset-inline: 0;
    z-index: var(--z-sticky);
    transition:
        background-color var(--dur-2) var(--ease-out-expo),
        backdrop-filter var(--dur-2) var(--ease-out-expo),
        border-color var(--dur-2) var(--ease-out-expo);
    border-block-end: 1px solid transparent;
}

.site-header.is-scrolled {
    background: color-mix(in srgb, var(--color-bg) 82%, transparent);
    backdrop-filter: blur(14px) saturate(1.2);
    border-block-end-color: var(--color-border);
}

.site-header__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-6);
    padding-block: var(--space-4);
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
}

/* Round badge (logo-primary). Self-sufficient mark: no wordmark beside it -
   the badge is identity enough. Sizes up to ~68px for presence. */
.brand__badge {
    block-size: 4.25rem;   /* ~68px, within the 64-72px target */
    inline-size: auto;
    flex-shrink: 0;
}

.nav-primary {
    display: none;
    gap: var(--space-8);
}

@media (min-width: 60rem) {
    .nav-primary {
        display: flex;
    }
}

.nav-primary a {
    position: relative;
    padding-block: var(--space-2);
    font-size: var(--text-step--1);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.nav-primary a:hover,
.nav-primary a[aria-current="page"] {
    color: var(--color-text);
}

.nav-primary a::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    inset-block-end: 0;
    block-size: 1px;
    background: var(--color-accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--dur-2) var(--ease-out-expo);
}

.nav-primary a:hover::after,
.nav-primary a[aria-current="page"]::after {
    transform: scaleX(1);
}

.site-header__actions {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}

.site-header__cta {
    display: none;
}

@media (min-width: 60rem) {
    .site-header__cta {
        display: inline-flex;
    }
}

/* Mobile toggle (burger) --------------------------------------------------- */
.nav-toggle {
    display: inline-flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    inline-size: 2.75rem;
    block-size: 2.75rem;
    align-items: center;
}

@media (min-width: 60rem) {
    .nav-toggle {
        display: none;
    }
}

.nav-toggle__bar {
    inline-size: 1.4rem;
    block-size: 1.5px;
    background: var(--color-text);
    transition: transform var(--dur-1) var(--ease-out-expo), opacity var(--dur-1);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(1) {
    transform: translateY(6.5px) rotate(45deg);
}
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(2) {
    opacity: 0;
}
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(3) {
    transform: translateY(-6.5px) rotate(-45deg);
}

/* Mobile drawer ------------------------------------------------------------ */
.nav-drawer {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
    padding: calc(var(--space-24) + var(--space-4)) var(--gutter, 1.5rem) var(--space-12);
    background: color-mix(in srgb, var(--color-bg) 96%, transparent);
    backdrop-filter: blur(20px);
    transform: translateX(100%);
    visibility: hidden;
    transition: transform var(--dur-2) var(--ease-in-out-quart), visibility var(--dur-2);
}

.nav-drawer[data-open="true"] {
    transform: translateX(0);
    visibility: visible;
}

.nav-drawer a {
    font-family: var(--font-display);
    font-size: var(--text-step-3);
    color: var(--color-text);
}

.nav-drawer a:hover {
    color: var(--color-accent);
}

.nav-drawer__close {
    position: absolute;
    inset-block-start: var(--space-6);
    inset-inline-end: var(--gutter, 1.5rem);
    font-size: var(--text-step-2);
    color: var(--color-text-muted);
}

@media (min-width: 60rem) {
    .nav-drawer {
        display: none;
    }
}

/* core/assets/css/components/price-rail.css */
/* Price rail - the sticky "dès XX €/nuit" booking cue that rides alongside the
   property hero/intro on desktop and pins to the bottom of the viewport on
   mobile. Read-only in Phase 2 (the CTA scrolls to the availability calendar;
   date selection lands in L10.4). */
.price-rail {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-6);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-2);
}

.price-rail__amount {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
}

.price-rail__from {
    font-size: var(--text-step--1);
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
}

.price-rail__value {
    font-family: var(--font-display);
    font-size: var(--text-step-4);
    line-height: 1;
    color: var(--color-text);
}

.price-rail__unit {
    color: var(--color-text-muted);
    font-size: var(--text-step-0);
}

.price-rail__note {
    font-size: var(--text-step--1);
    color: var(--color-text-muted);
}

.price-rail .btn {
    inline-size: 100%;
}

/* Desktop: sticky beside the content column. */
@media (min-width: 60rem) {
    .price-rail {
        position: sticky;
        inset-block-start: calc(var(--space-16) + var(--space-4));
    }
}

/* Mobile: a compact fixed bar at the bottom edge. Toggled visible by JS once
   the hero has scrolled past (data-pinned). Falls back to inline if no JS. */
.price-bar {
    position: fixed;
    inset-inline: 0;
    inset-block-end: 0;
    z-index: var(--z-sticky);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-3) var(--space-4);
    padding-block-end: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
    background: color-mix(in srgb, var(--color-bg) 88%, transparent);
    backdrop-filter: blur(14px) saturate(1.2);
    border-block-start: 1px solid var(--color-border);
    transform: translateY(110%);
    transition: transform var(--dur-2) var(--ease-out-expo);
}

.price-bar[data-pinned="true"] { transform: translateY(0); }

@media (min-width: 60rem) {
    .price-bar { display: none; }
}

.price-bar__value {
    font-family: var(--font-display);
    font-size: var(--text-step-2);
    color: var(--color-text);
}

.price-bar__value small {
    font-family: var(--font-sans);
    font-size: var(--text-step--1);
    color: var(--color-text-muted);
}

/* core/assets/css/components/signature.css */
/* ==========================================================================
   Signature blocks - the two elements that make Onyx & Or specific to this
   subject rather than a generic dark theme (cf. docs/phase-2-brief.md section 4.3).

   1. .vapor-glow      - ambient gold radial glow that breathes like hammam
                         steam. Decorative, aria-hidden, off under reduced motion.
   2. .thermal-threshold - the cold/warm diptych: glacier (montagne) meets gold
                         (hammam). The brand thesis made visible.
   ========================================================================== */

/* --- Vapour glow ---------------------------------------------------------- */
.vapor-glow {
    position: absolute;
    z-index: 0;
    inline-size: var(--vapor-size, 60vmax);
    block-size: var(--vapor-size, 60vmax);
    inset-block-start: var(--vapor-y, 42%);
    inset-inline-start: var(--vapor-x, 68%);
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(
        circle at center,
        color-mix(in srgb, var(--color-accent) 30%, transparent) 0%,
        transparent 62%
    );
    filter: blur(18px);
    opacity: 0.7;
    pointer-events: none;
    animation: vapor-breathe 8s var(--ease-in-out-quart) infinite;
}

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

/* --- Thermal threshold ---------------------------------------------------- */
.thermal-threshold {
    display: grid;
    grid-template-columns: 1fr;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    position: relative;
}

@media (min-width: 48rem) {
    .thermal-threshold {
        grid-template-columns: 1fr 1fr;
    }
}

.thermal-threshold__side {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    justify-content: center;
    padding: var(--space-16) var(--space-8);
    min-block-size: 15rem;
}

.thermal-threshold__side--cold {
    background: linear-gradient(
        155deg,
        color-mix(in srgb, var(--color-accent-cool) 24%, var(--color-bg)) 0%,
        var(--color-bg) 78%
    );
}

.thermal-threshold__side--warm {
    background: linear-gradient(
        155deg,
        color-mix(in srgb, var(--color-accent) 26%, var(--color-bg-soft)) 0%,
        var(--color-bg-soft) 78%
    );
}

.thermal-threshold__temp {
    font-family: var(--font-mono);
    font-size: var(--text-step-4);
    line-height: 1;
}

.thermal-threshold__side--cold .thermal-threshold__temp {
    color: var(--color-accent-cool);
}

.thermal-threshold__side--warm .thermal-threshold__temp {
    color: var(--color-accent);
}

.thermal-threshold__label {
    font-size: var(--text-step--1);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    color: var(--color-text-muted);
}

.thermal-threshold__desc {
    font-family: var(--font-display);
    font-style: italic;
    font-size: var(--text-step-1);
    color: var(--color-text);
    max-inline-size: 26ch;
}

/* The seuil: a hairline where the two temperatures meet. */
.thermal-threshold__seam {
    display: none;
}

@media (min-width: 48rem) {
    .thermal-threshold__seam {
        display: block;
        position: absolute;
        inset-block: 12%;
        inset-inline-start: 50%;
        inline-size: 1px;
        background: linear-gradient(
            to bottom,
            transparent,
            var(--color-accent) 50%,
            transparent
        );
        transform: translateX(-50%);
    }
}

/* core/assets/css/components/skeleton.css */
/* Skeleton - loading placeholder with a token-driven shimmer. Used while the
   availability calendar and API-fed sections stream in. */
.skeleton {
    position: relative;
    overflow: hidden;
    background-color: var(--color-surface);
    background-image: linear-gradient(
        90deg,
        transparent 0%,
        color-mix(in srgb, var(--color-surface-hover) 85%, transparent) 50%,
        transparent 100%
    );
    background-size: 200% 100%;
    background-repeat: no-repeat;
    border-radius: var(--radius-md);
    animation: skeleton-shimmer 1.6s var(--ease-in-out-quart) infinite;
}

.skeleton--text {
    block-size: 0.8em;
    border-radius: var(--radius-pill);
}

.skeleton--title {
    block-size: 1.5em;
    inline-size: 60%;
    border-radius: var(--radius-pill);
}

.skeleton--media {
    inline-size: 100%;
    aspect-ratio: 16 / 9;
}

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

/* core/assets/css/components/spec-grid.css */
/* Spec grid - equipment & services grouped by category, and the paid options
   list. Reused across property pages. Icons are inline SVG in the template;
   here we only lay out the groups and rows. */
.spec-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-8) var(--space-12);
}

@media (min-width: 40rem) {
    .spec-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 64rem) {
    .spec-grid { grid-template-columns: repeat(3, 1fr); }
}

.spec-group__head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding-block-end: var(--space-3);
    margin-block-end: var(--space-4);
    border-block-end: 1px solid var(--color-border);
}

.spec-group__icon {
    flex: none;
    inline-size: 1.5rem;
    block-size: 1.5rem;
    color: var(--color-accent);
}

.spec-group__title {
    font-family: var(--font-display);
    font-size: var(--text-step-1);
    letter-spacing: var(--tracking-tight);
}

.spec-group__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.spec-group__list li {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    color: var(--color-text-muted);
    font-size: var(--text-step-0);
}

.spec-group__list li::before {
    content: "";
    flex: none;
    inline-size: 0.4rem;
    block-size: 0.4rem;
    border-radius: 50%;
    background: var(--color-accent);
    transform: translateY(-0.15em);
}

/* Options - paid add-ons (breakfast, raclette, tourist tax). */
.options-list {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-4);
}

@media (min-width: 48rem) {
    .options-list { grid-template-columns: repeat(3, 1fr); }
}

.option {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-6);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
}

.option__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
}

.option__name {
    font-family: var(--font-display);
    font-size: var(--text-step-1);
}

.option__price {
    color: var(--color-accent);
    white-space: nowrap;
    font-size: var(--text-step-0);
}

.option__desc {
    color: var(--color-text-muted);
    font-size: var(--text-step--1);
}

/* core/assets/css/components/testimonial.css */
/* Testimonial - a single guest review. Reused on the home ("La parole des
   voyageurs") and the property pages. Score in JetBrains Mono (data voice). */
.testimonial {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-8);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
}

.testimonial__quote {
    font-family: var(--font-display);
    font-size: var(--text-step-1);
    line-height: var(--leading-snug);
    color: var(--color-text);
}

.testimonial__quote::before {
    content: "\201C";
    color: var(--color-accent);
    margin-inline-end: 0.15em;
}

.testimonial__foot {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    margin-block-start: auto;
    padding-block-start: var(--space-4);
    border-block-start: 1px solid var(--color-border);
}

.testimonial__author {
    font-weight: var(--font-weight-medium);
    color: var(--color-text);
}

.testimonial__origin {
    color: var(--color-text-muted);
    font-size: var(--text-step--1);
}

.testimonial__score {
    font-family: var(--font-mono);
    color: var(--color-accent);
    font-size: var(--text-step--1);
    white-space: nowrap;
}

/* core/assets/css/components/toast.css */
/* Toast - transient feedback (form sent, error). Region is polite live for AT.
   Managed by main.js (WMO.toast). */
.toast-region {
    position: fixed;
    inset-block-end: var(--space-6);
    inset-inline-end: var(--space-6);
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    max-inline-size: min(24rem, calc(100vw - 2rem));
    pointer-events: none;
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    border-inline-start: 3px solid var(--color-accent);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-2);
    pointer-events: auto;
    animation: toast-in var(--dur-2) var(--ease-out-expo);
}

.toast--success { border-inline-start-color: var(--color-success); }
.toast--danger { border-inline-start-color: var(--color-danger); }

.toast__body {
    flex: 1;
}

.toast__title {
    font-weight: var(--font-weight-semibold);
    font-size: var(--text-step-0);
}

.toast__msg {
    margin-block-start: var(--space-1);
    font-size: var(--text-step--1);
    color: var(--color-text-muted);
}

.toast__close {
    color: var(--color-text-muted);
    font-size: var(--text-step-1);
    line-height: 1;
}

.toast__close:hover {
    color: var(--color-text);
}

}

@layer utilities {
/* core/assets/css/utilities/flow.css */
/* Flow - owns vertical spacing between prose siblings without touching the
   elements themselves. Tune per-context with --flow-space. */
.flow > * + * {
    margin-block-start: var(--flow-space, 1em);
}

.flow[data-flow="loose"] { --flow-space: var(--space-8); }
.flow[data-flow="tight"] { --flow-space: var(--space-3); }

/* core/assets/css/utilities/reveal.css */
/* Reveal - scroll-triggered entrance. JS (main.js) adds .is-visible via an
   IntersectionObserver. Subtle by design. Fully disabled under reduced motion. */
.reveal {
    opacity: 0;
    transform: translateY(1.5rem);
    transition:
        opacity var(--dur-3) var(--ease-out-expo),
        transform var(--dur-3) var(--ease-out-expo);
    transition-delay: var(--reveal-delay, 0ms);
    will-change: opacity, transform;
}

.reveal.is-visible {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .reveal {
        opacity: 1;
        transform: none;
        transition: none;
    }
}

/* Fallback if JS never runs: content must never stay invisible. */
.no-js .reveal {
    opacity: 1;
    transform: none;
}

/* core/assets/css/utilities/text.css */
/* Text utilities - single-purpose helpers. */
.text-center { text-align: center; }
.text-balance { text-wrap: balance; }
.text-pretty { text-wrap: pretty; }
.text-muted { color: var(--color-text-muted); }
.text-accent { color: var(--color-accent); }
.text-measure { max-inline-size: 60ch; }
.text-uppercase {
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
}
.text-display { font-family: var(--font-display); }
.text-mono { font-family: var(--font-mono); }

/* Rating-note voice. JetBrains Mono's 0 carries a centre dot that reads as 8 at
   small sizes ("9,5/10" -> "9,5/18"), and our subset woff2 ships WITHOUT the
   slashed-zero OpenType feature (so font-feature-settings: "zero" is a no-op).
   Rating notes therefore use the sans face (Inter, unambiguous 0) with tabular
   figures. Utilities layer so it overrides component mono (e.g. .card__badge).
   Mono stays the data voice where context disambiguates (prices, dates,
   distances, refs). See docs/decisions-autonomes-phase2-L10-2.md. */
.u-note {
    font-family: var(--font-sans);
    font-feature-settings: "tnum" 1;
    letter-spacing: 0.01em;
}

/* core/assets/css/utilities/visually-hidden.css */
/* Visually hidden - remove from view but keep for assistive tech. */
.visually-hidden:not(:focus):not(:focus-within) {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Skip link becomes visible on focus. */
.skip-link {
    position: absolute;
    inset-block-start: var(--space-2);
    inset-inline-start: var(--space-2);
    z-index: var(--z-toast);
    padding: var(--space-2) var(--space-4);
    background: var(--color-accent);
    color: var(--color-text-inverse);
    border-radius: var(--radius-pill);
    transform: translateY(-150%);
    transition: transform var(--dur-1) var(--ease-out-expo);
}

.skip-link:focus {
    transform: translateY(0);
}

}

@layer overrides {
/* tenant/theme/overrides.css */
/* ==========================================================================
   tenant overrides — Chambre Hammam Oisans
   Use this file ONLY for exceptions that cannot be expressed via tokens.
   If you find yourself adding many overrides, consider adding the missing
   token to the catalog (cf. docs/design-system.md §3) instead.
   ========================================================================== */

}
