/* ---- Site chrome: theme toggle, social links, brand ---- */
.theme-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border: 1px solid var(--ap-border);
    background: transparent;
    color: var(--ap-accent-text);
    border-radius: var(--ap-radius-button);
    transition: border-color 0.15s ease, color 0.15s ease;
}

.theme-toggle:hover {
    border-color: var(--ap-accent);
    color: var(--ap-accent-text-hover);
}

/* The footer's social icons share the theme-toggle recipe on purpose: border,
   accent icon and radius all come from the theme, so the row stays theme-agnostic. */
.social-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border: 1px solid var(--ap-border);
    color: var(--ap-accent-text);
    border-radius: var(--ap-radius-button);
    font-size: 0.85rem;
    transition: border-color 0.15s ease, color 0.15s ease;
}

.social-link:hover {
    border-color: var(--ap-accent);
    color: var(--ap-accent-text-hover);
}

.nav-brand-text {
    font-family: var(--ap-font-display);
    font-weight: 800;
    font-size: 0.8rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.nav-mono {
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

.section {
    padding: var(--ap-space-section) 0;
}

/* ---- Scroll reveal ---- */

/* Armed by app/reveal.js and only when motion is allowed, so a script that never runs
   leaves the section readable rather than invisible. The signature templates rise; a block
   names its own entrance in data-reveal (BlockAnimation). */
.reveal {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 0.8s cubic-bezier(0.2, 0.7, 0.2, 1), transform 0.8s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.reveal[data-reveal="fade"] {
    transform: none;
}

.reveal[data-reveal="zoom"] {
    transform: scale(0.96);
}

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

/* app/reveal.js already refuses to arm under reduced motion; this catches a section the
   class reached before the query flipped. */
@media (prefers-reduced-motion: reduce) {
    .reveal,
    .reveal[data-reveal] {
        opacity: 1;
        transform: none;
        transition: none;
    }
}

/* ---- Per-block settings (the .blk wrapper BlockRenderer emits) ---- */
/* The wrapper owns a block's vertical padding: no block template carries a
   padding utility, so these presets are the only thing that pads a block.
   Normal is one block space per edge, half a section space, so two adjacent
   blocks still add up to the theme's section rhythm. */
/* The ink surface setting: the section itself is the band, so the tenant's spacing pads it. */
.blk--ink .section {
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.blk--top-none .section {
    padding-top: 0;
}

.blk--top-compact .section {
    padding-top: calc(var(--ap-space-block) / 2);
}

.blk--top-normal .section {
    padding-top: var(--ap-space-block);
}

.blk--top-large .section {
    padding-top: calc(var(--ap-space-block) * 2);
}

.blk--bottom-none .section {
    padding-bottom: 0;
}

.blk--bottom-compact .section {
    padding-bottom: calc(var(--ap-space-block) / 2);
}

.blk--bottom-normal .section {
    padding-bottom: var(--ap-space-block);
}

.blk--bottom-large .section {
    padding-bottom: calc(var(--ap-space-block) * 2);
}

/* Split-variant column order. .split-row marks the row whose two columns the options swap; a
   row nested inside one of those columns never carries it, and neither does a block with no
   split variant. A reversal holds at every width, so "comme sur ordinateur" keeps its
   promise, and the mobile option flips it back. */
.blk--reversed .split-row > :first-child {
    order: 2;
}

@media (max-width: 991.98px) {
    .blk--m-inverted .split-row > :first-child {
        order: 2;
    }

    .blk--reversed.blk--m-inverted .split-row > :first-child {
        order: 0;
    }
}

@media (min-width: 992px) {
    .divider-start-lg {
        border-left: 1px solid var(--ap-border);
    }

    /* The rule belongs between the two columns, so it changes side with them.
       The gutter around it is the row's own, never a padding of this rule. */
    .blk--reversed .divider-start-lg {
        border-left: 0;
        border-right: 1px solid var(--ap-border);
    }
}

/* ---- Base typography and surfaces ---- */
.display-title {
    font-family: var(--ap-font-display);
    font-weight: 900;
    letter-spacing: -0.03em;
    text-transform: var(--ap-title-transform);
    line-height: 0.98;
    overflow-wrap: break-word;
    margin-top: 12px;
}

.lead-quote {
    max-width: 520px;
    font-family: var(--ap-font-mono);
    color: var(--ap-faint);
}

.section-head {
    display: flex;
    align-items: baseline;
    gap: 1.25rem;
    margin-bottom: 2.25rem;
}

/* Unscoped: badges/strip and stats/panel place the eyebrow outside a
   .section-head, and it must look the same everywhere. */
.label {
    font-family: var(--ap-font-ui);
    font-size: calc(0.8rem * var(--ap-ui-scale));
    letter-spacing: calc(0.2em * var(--ap-ui-tracking));
    text-transform: var(--ap-ui-transform);
    color: var(--ap-muted);
    white-space: nowrap;
}

.rule-line {
    flex: 1;
    height: 1px;
    background: var(--ap-border);
}

.divider-accent {
    border-top: 2px solid var(--ap-accent);
}

.divider-ink {
    border-top: 1px solid var(--ap-text);
}

.panel {
    position: relative;
    overflow: hidden;
    background-color: var(--ap-panel);
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
}

.panel > *,
.cta-band > .container-fluid {
    position: relative;
    z-index: 1;
}

/* ---- Steps ---- */
.stepper .step-node {
    position: relative;
    z-index: 1;
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3.5rem;
    height: 3.5rem;
    border: 1px solid var(--ap-accent);
    border-radius: 50%;
    background: var(--ap-bg);
    color: var(--ap-accent-text);
    font-size: 1.25rem;
}

.stepper-horizontal {
    row-gap: 2.25rem;
}

/* The node masks the connecting line with the surface it sits on. */
.blk--ink .stepper .step-node {
    background: var(--ap-text);
}

/* min-width 0 lets a step narrow past its longest word, which then breaks rather than
   pushing the row wider than the page. */
.stepper-horizontal .step {
    position: relative;
    flex: 1;
    min-width: 0;
}

.stepper-horizontal .step:not(:last-child)::after {
    content: '';
    position: absolute;
    top: 1.75rem;
    left: 50%;
    width: 100%;
    height: 1px;
    background: var(--ap-border);
    z-index: 0;
}

.stepper-horizontal .step-content {
    max-width: 100%;
    margin-top: 1.25rem;
    overflow-wrap: break-word;
}

/* A code in place of the icon: the round node stretches into a pill around it. */
.stepper .step-node--code {
    width: auto;
    min-width: 3.5rem;
    padding-inline: 0.9rem;
    border-radius: 999px;
    font-family: var(--ap-font-display);
    font-size: 0.9rem;
    font-weight: 700;
}

@media (max-width: 991.98px) {
    .stepper-horizontal .step:not(:last-child)::after {
        display: none;
    }

    .stepper-horizontal .step-content {
        margin-top: 1rem;
    }
}

.meta-line {
    font-family: var(--ap-font-ui);
    font-size: calc(0.625rem * var(--ap-ui-scale));
    line-height: 1.9;
    letter-spacing: calc(0.12em * var(--ap-ui-tracking));
    text-transform: var(--ap-ui-transform);
    color: var(--ap-faint);
}

/* ---- Image frames ---- */
.image-frame {
    position: relative;
    width: 100%;
    border: 1px solid var(--ap-border);
    background-color: var(--ap-panel);
    background-image:
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238a8781' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='18' height='18' rx='2'/%3E%3Ccircle cx='8.5' cy='8.5' r='1.8'/%3E%3Cpath d='m21 15-4.5-4.5a2 2 0 0 0-2.8 0L5 19'/%3E%3C/svg%3E"),
        repeating-linear-gradient(45deg, var(--ap-border) 0, var(--ap-border) 1px, transparent 1px, transparent 14px);
    background-repeat: no-repeat, repeat;
    background-position: center center, 0 0;
    background-size: 52px 52px, auto;
}

/* The editor's empty frame under content (components/_cover_frame.html.twig): its stripes on
   the surface's own color, without the centered mark the content would cover. */
.image-frame--cover {
    background-color: transparent;
    background-size: 0 0, auto;
}

/* Every frame states its crop once, and on the frame rather than the image, so an unfilled
   slot is the same box as a filled one and a row of them keeps one baseline whatever the
   tenant has uploaded. A block wanting the file's own proportions puts aspect-ratio back to
   auto on the image it owns (gallery/masonry, hero/split). */
.frame-hero {
    aspect-ratio: 3 / 2;
}

.frame-square {
    aspect-ratio: 1 / 1;
}

.frame-wide {
    aspect-ratio: 3 / 2;
}

.frame-cinema {
    aspect-ratio: 21 / 9;
}

.frame-portrait {
    aspect-ratio: 4 / 5;
}

.frame-tall {
    aspect-ratio: 3 / 4;
}

.frame-landscape {
    aspect-ratio: 4 / 3;
}

/* The focal point a tenant picks for a médiathèque image: which part a frame's crop keeps. */
.site-image--focus-top {
    object-position: 50% 0;
}

.site-image--focus-bottom {
    object-position: 50% 100%;
}

.site-image--focus-left {
    object-position: 0 50%;
}

.site-image--focus-right {
    object-position: 100% 50%;
}

.site-image--focus-top-left {
    object-position: 0 0;
}

.site-image--focus-top-right {
    object-position: 100% 0;
}

.site-image--focus-bottom-left {
    object-position: 0 100%;
}

.site-image--focus-bottom-right {
    object-position: 100% 100%;
}

/* No border: a photograph ends at its own edge, and around a transparent cutout a frame would
   draw a box about nothing. Only the empty slot (.image-frame) keeps its outline. */
.site-image {
    width: 100%;
    object-fit: cover;
}

/* ---- Bullet lists ---- */
.feature-item {
    display: grid;
    grid-template-columns: 1.5rem 1fr;
    gap: 0.875rem;
    padding: 0.875rem 0;
    border-top: 1px solid var(--ap-border);
    align-items: baseline;
}

.feature-item .check {
    color: var(--ap-accent);
}

/* ---- Offer ---- */
.offer-note {
    color: var(--ap-faint);
}

.offer-steps__rank {
    flex: none;
    min-width: 2rem;
    font-family: var(--ap-font-display);
    font-size: 1.5rem;
    color: var(--ap-accent-text);
}

.offer-centre {
    max-width: 48rem;
}

.offer-pill {
    background-color: var(--ap-panel);
}

/* ---- Call-to-action band ---- */
/* The accent surface: an accent fill is only guaranteed to read against
   --ap-accent-contrast (ADR-0005), so everything on it inherits that pair. */
.cta-band,
.form-accent,
.resa-split {
    position: relative;
    overflow: hidden;
    background-color: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

.form-accent,
.resa-split {
    border-radius: var(--ap-radius);
}

/* The same field panel and split set in the body face, kept to a measure the
   full-width band would otherwise let run edge to edge. */
.cta-band__body {
    max-width: 56ch;
    color: inherit;
}

.cta-band a.btn,
.resa-split a.btn,
.form-accent .btn-accent {
    --bs-btn-bg: var(--ap-accent-contrast);
    --bs-btn-border-color: var(--ap-accent-contrast);
    --bs-btn-color: var(--ap-accent);
    --bs-btn-hover-bg: transparent;
    --bs-btn-hover-border-color: var(--ap-accent-contrast);
    --bs-btn-hover-color: var(--ap-accent-contrast);
    --bs-btn-active-bg: transparent;
    --bs-btn-active-border-color: var(--ap-accent-contrast);
    --bs-btn-active-color: var(--ap-accent-contrast);

    border-radius: var(--ap-radius-button);
}

.cta-band .eyebrow,
.cta-ink .eyebrow,
.form-accent .label,
.resa-split .eyebrow,
.cta-note {
    color: inherit;
}

.cta-note {
    font-family: var(--ap-font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    opacity: 0.8;
}

/* The inverted surface the ink variants share. theme.css re-points the derived
   tokens for its subtree, since no accent token is measured against it (ADR-0005). */
.ink-surface {
    border-radius: var(--ap-radius);
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.cta-ink__body {
    max-width: 52ch;
    opacity: 0.85;
}

.ink-surface .btn-accent,
.card-ink .btn-accent,
.blk--ink .btn-accent,
.price-compare__on .btn-accent {
    --bs-btn-bg: var(--ap-bg);
    --bs-btn-border-color: var(--ap-bg);
    --bs-btn-color: var(--ap-text);
    --bs-btn-hover-bg: transparent;
    --bs-btn-hover-border-color: var(--ap-bg);
    --bs-btn-hover-color: var(--ap-bg);
    --bs-btn-active-bg: transparent;
    --bs-btn-active-border-color: var(--ap-bg);
    --bs-btn-active-color: var(--ap-bg);
}

/* cta/couverture: without a photo, the theme's panel pair. */
.cta-cover {
    min-height: 26rem;
    background-color: var(--ap-panel);
    color: var(--ap-text);
}

@media (min-width: 992px) {
    .cta-cover {
        min-height: 34rem;
    }
}

/* White on a fixed scrim, darkest where the text sits: a scrim over a photograph is
   theme-invariant (ADR-0005). The accent button keeps its fill, which carries its own
   contrast pair whatever the photo behind it. */
.cta-cover--photo {
    color: var(--bs-white);
}

.cta-cover--photo::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(
        15deg,
        rgb(10 10 12 / 85%) 0%,
        rgb(10 10 12 / 45%) 50%,
        rgb(10 10 12 / 10%) 100%
    );
    pointer-events: none;
}

.cta-cover--photo .eyebrow {
    color: inherit;
}

.cta-cover__body {
    z-index: 2;
    max-width: 40rem;
}

.cta-cover__text {
    max-width: 52ch;
    opacity: 0.85;
}

.cta-cover__link,
.cta-cover__link:hover {
    color: inherit;
}

/* ---- Blog ---- */
/* The list variant is an index, so each row carries what a reader decides on:
   when it was written, what it is about, and the first sentence of it. */
.article-list__row {
    border-top: 1px solid var(--ap-border);
}

.article-list__row:last-child {
    border-bottom: 1px solid var(--ap-border);
}

.article-list__link {
    display: grid;
    gap: 0.4rem;
    padding: 1.5rem 0;
    color: var(--ap-text);
    transition: color 0.15s ease;
}

@media (min-width: 768px) {
    .article-list__link {
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: baseline;
        column-gap: 2rem;
    }

    .article-list__meta,
    .article-list__title,
    .article-list__excerpt {
        grid-column: 1;
    }

    .article-list__more {
        grid-column: 2;
        grid-row: 2;
        text-align: right;
        white-space: nowrap;
    }
}

.article-list__link:hover {
    color: var(--ap-accent-text);
}

.article-list__excerpt {
    max-width: 68ch;
    color: var(--ap-muted);
}

.article-list__more {
    color: var(--ap-accent-text);
}

.article-une__all,
.article-une__lead,
.article-une__row,
.article-journal__link {
    color: var(--ap-text);
    transition: color 0.15s ease;
}

.article-une__all:hover,
.article-une__lead:hover,
.article-une__row:hover,
.article-journal__link:hover {
    color: var(--ap-accent-text);
}

.article-une__thumb {
    width: 6rem;
}

/* A newspaper front: a double rule under the masthead, columns parted by rules. */
.article-journal {
    border-top: 3px double var(--ap-text);
}

@media (min-width: 768px) {
    .article-journal__column:not(:nth-child(3n + 1)) {
        padding-left: 1.5rem;
        border-left: 1px solid var(--ap-border);
    }
}

.article-journal__day {
    font-family: var(--ap-font-display);
    font-size: 2.5rem;
    line-height: 1;
}

.article-card-meta {
    padding-top: 0.75rem;
    font-family: var(--ap-font-mono);
    font-size: 0.625rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ap-faint);
}

.measure-article {
    max-width: 760px;
}

.article-body {
    font-size: 1.0625rem;
    line-height: 1.75;
    color: var(--ap-muted);
}

.article-body p {
    margin-bottom: 1.25rem;
}

/* An image placed in the text takes the column's width, at its own proportions. */
.article-body img {
    display: block;
    max-width: 100%;
    height: auto;
    margin-block: 1.5rem;
    border: 1px solid var(--ap-border);
}

/* ---- Public forms ---- */
.public-form .form-label {
    font-weight: 600;
    color: var(--ap-text);
}

/* The one mark every form shares: the asterisk hangs on the `required` class
   bootstrap_5_layout puts on the label, an optional field carries nothing. Its alt text
   is empty: a screen reader acts on the control's required attribute, and the legend
   (components/_required_legend.html.twig) carries the meaning for everyone else. */
.required-legend {
    color: var(--ap-muted);
    font-size: 0.9rem;
    margin-bottom: 1rem;
}

.required-mark,
.form-label.required::after {
    color: var(--ap-accent-text);
    font-weight: 700;
}

.form-label.required::after {
    content: ' *' / '';
}

.public-form .form-control,
.public-form .form-select {
    padding: 0.65rem 0.9rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.public-form .form-control::placeholder {
    color: var(--ap-faint);
}

.public-form .form-control:focus,
.public-form .form-select:focus {
    border-color: var(--ap-accent);
    box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--ap-accent) 25%, transparent);
}

/* Radio choices as selectable cards: bootstrap_5's btn-check markup
   (visually hidden input + adjacent label), pure-CSS states. The
   public site ships no framework JS (ADR-0007). */
.public-form .choice-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: var(--ap-space-tight);
}

.public-form .choice-cards label {
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    min-height: 4.25rem;
    padding: 1rem;
    background-color: var(--ap-panel);
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    font-weight: 600;
    color: var(--ap-text);
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.public-form .choice-cards label:hover {
    border-color: var(--ap-accent-strong);
}

.public-form .choice-cards .btn-check:checked + label {
    background-color: var(--ap-accent);
    border-color: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

.public-form .choice-cards .btn-check:focus-visible + label {
    outline: 2px solid var(--ap-accent);
    outline-offset: 2px;
}

.public-form .choice-cards .btn-check.is-invalid + label {
    border-color: var(--bs-danger);
}

/* Multi-choice select: a <details> disclosure styled as a closed .form-select.
   The panel overlays, so opening it never reflows the form. */
.public-form .choice-dropdown {
    position: relative;
}

/* display:block, not flex: it drops the disclosure triangle and it is what
   makes text-overflow work. The extra right padding keeps a long selection's
   ellipsis clear of the chevron the theme places 12px from the edge. */
.public-form .choice-dropdown-summary {
    display: block;
    list-style: none;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    padding-right: 2.25rem;
}

.public-form .choice-dropdown-summary::-webkit-details-marker {
    display: none;
}

.public-form .choice-dropdown-summary.is-empty {
    color: var(--ap-faint);
}

.public-form .choice-dropdown-summary:focus-visible {
    outline: 2px solid var(--ap-accent);
    outline-offset: 2px;
}

.public-form .choice-dropdown-panel {
    position: absolute;
    z-index: 3;
    inset-inline: 0;
    top: calc(100% + 0.25rem);
    max-height: 15rem;
    overflow-y: auto;
    padding: 0.75rem 1rem;
    background-color: var(--ap-panel);
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
}

.public-form .choice-dropdown-panel .form-check {
    margin-bottom: 0.25rem;
}

.public-form .choice-dropdown-panel .form-check-label {
    cursor: pointer;
}

/* Consent checkbox: accent fill on Bootstrap's own glyph. Its input
   is appearance:none, so accent-color would be a no-op. */
.public-form .form-check-input:checked {
    background-color: var(--ap-accent);
    border-color: var(--ap-accent);
}

.public-form .form-check-input:focus {
    border-color: var(--ap-accent);
    box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--ap-accent) 25%, transparent);
}

.public-form .form-check .form-check-label {
    color: var(--ap-muted);
}

.confirm-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    background: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

/* Multi-step forms: one fieldset per step. The thread and the nav are hidden until
   app/form_steps.js reveals them, so without the script the fieldsets read as sections.
   The rule between two sections; a page the script hid draws none under itself. */
.form-steps__panel:not([hidden]) + .form-steps__panel {
    margin-top: 2rem;
    padding-top: 2rem;
    border-top: 1px solid var(--ap-border);
}

.form-steps__legend {
    gap: 0.75rem;
    font-family: var(--ap-font-display);
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--ap-text);
}

.form-steps__legend:focus {
    outline: none;
}

.form-steps__index {
    font-family: var(--ap-font-mono);
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    color: var(--ap-accent-text);
}

.form-steps__intro {
    margin-bottom: 1.25rem;
}

.form-steps__thread {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-bottom: 1.5rem;
}

.form-steps__rank {
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ap-muted);
}

.form-steps__bars > span {
    flex: 1;
    height: 3px;
    border-radius: 3px;
    background: var(--ap-border);
    transition: background-color 0.2s ease;
}

.form-steps__bars > .is-done {
    background: var(--ap-accent);
}

.form-steps__nav {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-top: 1.5rem;
}

/* The page app/form_steps.js brings on screen slides in; the panel it hides is display:none. */
@media (prefers-reduced-motion: no-preference) {
    .form-steps__panel:not([hidden]) {
        animation: form-steps-in 0.25s ease-out;
    }

    @keyframes form-steps-in {
        from {
            opacity: 0;
            transform: translateY(0.5rem);
        }
    }
}

/* A form on the ink surface. Its fields, choice cards and dropdown keep the page's own
   pair, so only the text set directly on the ink is restated here. */
.form-ink .form-label,
.form-ink .form-label.required::after,
.form-ink .form-steps__legend,
.form-ink .form-steps__index {
    color: inherit;
}

.form-ink .text-muted-ap,
.form-ink .form-text,
.form-ink .form-steps__rank,
.form-ink .form-check-label:not(.choice-cards *, .choice-dropdown-panel *) {
    color: color-mix(in srgb, var(--ap-bg) 78%, var(--ap-text));
}

.form-ink .invalid-feedback {
    color: color-mix(in srgb, var(--bs-danger) 50%, var(--ap-bg));
}

.form-ink .form-steps__bars > span {
    background: color-mix(in srgb, var(--ap-bg) 25%, var(--ap-text));
}

.form-ink .form-steps__bars > .is-done {
    background: var(--ap-bg);
}

.form-ink .btn-outline-accent,
.hero-encre .btn-outline-accent {
    --bs-btn-color: var(--ap-bg);
    --bs-btn-border-color: color-mix(in srgb, var(--ap-bg) 40%, var(--ap-text));
    --bs-btn-hover-color: var(--ap-bg);
    --bs-btn-hover-border-color: var(--ap-bg);
    --bs-btn-active-bg: var(--ap-bg);
    --bs-btn-active-color: var(--ap-text);
    --bs-btn-active-border-color: var(--ap-bg);
}

/* A form on the accent fill. Its fields keep the page's own pair without a frame; every
   mark the page draws in the accent (focus ring, checked choice, steps, badge) would vanish
   on the fill, so it moves to the contrast color. */
.form-accent .form-label,
.form-accent .form-label.required::after,
.form-accent .form-steps__legend,
.form-accent .form-steps__index,
.form-accent .invalid-feedback,
.form-accent .text-muted-ap,
.form-accent .form-text,
.form-accent .form-steps__rank,
.form-accent .form-check-label:not(.choice-cards *, .choice-dropdown-panel *) {
    color: inherit;
}

.form-accent .form-channels {
    --bs-border-color: color-mix(in srgb, var(--ap-accent-contrast) 30%, var(--ap-accent));
}

.form-accent .form-control:not(.is-invalid),
.form-accent .form-select:not(.is-invalid) {
    border-color: transparent;
}

/* Offset from the field, so the ring reads even where the contrast color is the field's own. */
.form-accent .form-control:focus,
.form-accent .form-select:focus,
.form-accent .form-check-input:focus {
    outline: 2px solid var(--ap-accent-contrast);
    outline-offset: 2px;
    box-shadow: none;
}

.form-accent .choice-dropdown-summary:focus-visible,
.form-accent .choice-cards .btn-check:focus-visible + label {
    outline-color: var(--ap-accent-contrast);
}

.form-accent .form-check-input:checked {
    border-color: var(--ap-accent-contrast);
}

.form-accent .choice-cards .btn-check:checked + label,
.form-accent .confirm-badge {
    border-color: var(--ap-accent-contrast);
    background-color: var(--ap-accent-contrast);
    color: var(--ap-accent);
}

.form-accent .form-steps__panel:not([hidden]) + .form-steps__panel {
    border-top-color: color-mix(in srgb, var(--ap-accent-contrast) 25%, var(--ap-accent));
}

.form-accent .form-steps__bars > span {
    background: color-mix(in srgb, var(--ap-accent-contrast) 25%, var(--ap-accent));
}

.form-accent .form-steps__bars > .is-done {
    background: var(--ap-accent-contrast);
}

.form-accent .btn-outline-accent {
    --bs-btn-color: var(--ap-accent-contrast);
    --bs-btn-border-color: var(--ap-accent-contrast);
    --bs-btn-hover-bg: var(--ap-accent-contrast);
    --bs-btn-hover-color: var(--ap-accent);
    --bs-btn-hover-border-color: var(--ap-accent-contrast);
    --bs-btn-active-bg: var(--ap-accent-contrast);
    --bs-btn-active-color: var(--ap-accent);
    --bs-btn-active-border-color: var(--ap-accent-contrast);
}

/* Short answers pair up; a row holding anything else (text area, file, choices, consent,
   the anti-spam widget) keeps the full width. Without :has() every row stays full. */
@media (min-width: 992px) {
    .form-accent :is(.public-form__fields, .form-steps__panel) {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        column-gap: var(--ap-space-tight);
    }

    .form-accent :is(.public-form__fields, .form-steps__panel) > * {
        grid-column: 1 / -1;
    }

    .form-accent :is(.public-form__fields, .form-steps__panel) > :has(> :is(input[type='text'], input[type='email'], input[type='tel'], select)) {
        grid-column: auto;
    }
}

/* ---- FAQ ---- */
.faq details {
    border-top: 1px solid var(--ap-border);
}

.faq details:last-of-type {
    border-bottom: 1px solid var(--ap-border);
}

.faq summary {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    padding: 1.5rem 0;
    cursor: pointer;
    list-style: none;
    font-family: var(--ap-font-display);
    font-weight: 700;
    font-size: 1.15rem;
}

.faq summary:hover {
    color: var(--ap-accent-text);
}

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

.faq summary .sign {
    position: relative;
    flex: none;
    width: 1.75rem;
    height: 1.75rem;
    margin-left: auto;
    color: var(--ap-accent);
    transition: transform 0.15s ease, color 0.15s ease;
}

/* The mark is drawn, not typed: a "+" glyph rides its own font's metrics and
   the mono family is the tenant's choice, so it never lands on the center. */
.faq summary .sign::before,
.faq summary .sign::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 0.875rem;
    height: 2px;
    background-color: currentColor;
    transform: translate(-50%, -50%);
}

.faq summary .sign::after {
    transform: translate(-50%, -50%) rotate(90deg);
}

.faq summary:hover .sign {
    color: var(--ap-accent-text);
}

.faq details[open] summary .sign {
    transform: rotate(45deg);
}

.faq .answer {
    padding: 0 0 1.5rem;
    color: var(--ap-muted);
}

.faq-index__rank {
    flex: none;
    min-width: 2rem;
    padding-top: 0.2rem;
    color: var(--ap-accent-text);
}

.faq-ink .label,
.faq-ink__answer {
    color: inherit;
    opacity: 0.8;
}

.faq-ink__card {
    border-radius: var(--ap-radius);
    background-color: color-mix(in srgb, var(--ap-bg) 8%, var(--ap-text));
}

.faq-ink__mark {
    font-family: var(--ap-font-display);
    font-size: 1.75rem;
    line-height: 1;
    opacity: 0.6;
}

/* ─────────────────────────────────────────────────────────────
   UI polish
   ───────────────────────────────────────────────────────────── */

:focus-visible {
    outline: 2px solid var(--ap-accent);
    outline-offset: 2px;
}

/* Header with a subtle shadow once the page is scrolled; sticky is the
   tenant's choice (.site-nav--sticky). */
.site-nav {
    z-index: 1030;
    background-color: var(--ap-bg);
    transition: box-shadow 0.2s ease;
}

.site-nav.is-scrolled {
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
}

/* The upload keeps its own ratio: only the height is capped, so a wide mark and
   a square one both leave the navbar the same height. */
.navbar-logo {
    max-height: 60px;
}

/* A tenant with a logo for each background gets both in the markup, and the scheme picks one:
   forced server-side or set by the toggle, so the swap needs no script. */
[data-bs-theme="dark"] .brand-logo--on-light,
:root:not([data-bs-theme="dark"]) .brand-logo--on-dark {
    display: none;
}

/* Navbar variants: layout re-composition only, same markup tree, same
   tokens, no variant-specific colors. Below lg every variant hands its links
   to the phone menu. */
@media (min-width: 992px) {
    /* Centered: brand row on top, links row below, toggle in the corner. */
    .site-nav--centered .container-fluid {
        position: relative;
        flex-direction: column;
        gap: 0.25rem;
    }

    .site-nav--centered .navbar-brand {
        justify-content: center;
        margin-right: 0;
    }

    .site-nav--centered .nav-brand-text {
        font-size: 1.35rem;
        letter-spacing: 0.02em;
        text-transform: none;
    }

    .site-nav--centered .navbar-collapse {
        justify-content: center;
    }

    .site-nav--centered .nav-item--toggle {
        position: absolute;
        top: 0.75rem;
        right: 3rem;
    }

    /* The two lists read as one row everywhere but in the split variant, which
       spends the gap on its own side balance instead. */
    .site-nav:not(.site-nav--split) .nav-group--lead:has(.nav-item) {
        margin-inline-end: 1.5rem;
    }

    /* Split: links on both sides of the centered brand. The collapse dissolves
       (display: contents) so both lists flex directly in the container, where equal
       side widths hold the brand centered whatever each side carries. */
    .site-nav--split .navbar-collapse {
        /* stylelint-disable-next-line declaration-no-important -- matches Bootstrap's own on .navbar-expand-lg */
        display: contents !important;
    }

    .site-nav--split .container-fluid {
        column-gap: 1.5rem;
    }

    .site-nav--split .nav-group {
        flex: 1 1 0;
    }

    .site-nav--split .nav-group--lead {
        order: -1;
    }

    .site-nav--split .nav-group--trail {
        order: 1;
        justify-content: flex-end;
    }

    .site-nav--split .navbar-brand {
        order: 0;
        margin-inline: 0;
    }

    .site-nav--split .nav-brand-text {
        font-size: 1.35rem;
        letter-spacing: 0.02em;
        text-transform: none;
    }
}

/* Navbar dropdowns, a card or a full-width panel (NavDropdown), from lg up: below, the phone
   menu opens a dropdown in place whatever the tenant picked. */
.site-nav .dropdown-menu {
    --bs-dropdown-bg: var(--ap-panel);
    --bs-dropdown-color: var(--ap-text);
    --bs-dropdown-border-color: var(--ap-border);
    --bs-dropdown-padding-x: 0.45rem;
    --bs-dropdown-padding-y: 0.45rem;
    --bs-dropdown-item-padding-x: 0.9rem;
    --bs-dropdown-item-padding-y: 0.65rem;
    --bs-dropdown-link-color: var(--ap-text);
    --bs-dropdown-link-hover-color: var(--ap-accent-text);
    --bs-dropdown-link-hover-bg: rgba(var(--ap-accent-rgb), 0.12);
    --bs-dropdown-link-active-color: var(--ap-accent-text);
    --bs-dropdown-link-active-bg: rgba(var(--ap-accent-rgb), 0.12);
    --bs-dropdown-spacer: 0.85rem;
}

.nav-menu__title {
    display: none;
    margin: 0;
}

.nav-menu__link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    border-radius: calc(var(--ap-radius) * 0.6);
    font-size: 0.95rem;
    transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
}

.nav-menu__link::after {
    content: "\2192";
    opacity: 0;
    transform: translateX(-6px);
    transition: opacity 0.15s ease, transform 0.15s ease, color 0.15s ease;
}

.nav-menu__link:is(:hover, :focus-visible)::after {
    opacity: 1;
    transform: none;
}

@media (min-width: 992px) {
    .site-nav--menu-card .dropdown-menu {
        min-width: 16rem;
        box-shadow: 0 28px 50px -24px rgba(0, 0, 0, 0.35), 0 2px 8px rgba(0, 0, 0, 0.04);
    }

    /* Anchored to the bar's container, not the item, so it spans the layout width; the offset
       clears the bar's bottom padding and keeps its border as the panel's top edge. */
    .site-nav--menu-panel .container-fluid {
        position: relative;
    }

    .site-nav--menu-panel .nav-item.dropdown {
        position: static;
    }

    .site-nav--menu-panel .dropdown-menu[data-bs-popper] {
        top: calc(100% + var(--bs-navbar-padding-y) + var(--bs-border-width));
        right: 0;
        left: 0;
        margin-top: 0;
        padding: 0;
        border-width: 0 0 var(--bs-border-width);
        border-radius: 0;
        background: var(--ap-bg);
        box-shadow: 0 50px 70px -50px rgba(0, 0, 0, 0.45);
    }

    .site-nav--menu-panel .nav-menu__inner {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 2.4fr);
        gap: 3rem;
        padding: 2.5rem 3rem 3rem;
    }

    .site-nav--menu-panel .nav-menu__title {
        display: block;
        color: var(--ap-text);
        font-family: var(--ap-font-display);
        font-size: 1.6rem;
        font-weight: 800;
        line-height: 1.1;
        text-transform: var(--ap-title-transform);
        overflow-wrap: anywhere;
    }

    .site-nav--menu-panel .nav-menu__links {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-content: start;
        column-gap: 2.5rem;
    }

    .site-nav--menu-panel .nav-menu__link {
        --bs-dropdown-link-hover-bg: transparent;
        --bs-dropdown-link-active-bg: transparent;

        padding: 1rem 0;
        border-top: 1px solid var(--ap-border);
        border-radius: 0;
        font-family: var(--ap-font-display);
        font-size: 1.2rem;
        font-weight: 600;
        white-space: normal;
    }

    .site-nav--menu-panel .nav-menu__link::after {
        padding-right: 4px;
        color: var(--ap-faint);
        opacity: 1;
        transform: none;
    }

    .site-nav--menu-panel .nav-menu__link:is(:hover, :focus-visible) {
        border-top-color: var(--ap-accent);
    }

    .site-nav--menu-panel .nav-menu__link:is(:hover, :focus-visible)::after {
        color: var(--ap-accent-text);
        transform: translateX(4px);
    }
}

/* Transparent navbar over a media hero. Fixed white and translucent literals,
   never theme tokens: a scrim over a photograph is theme-invariant (the
   .hero-screen precedent). Desktop only. */
@media (min-width: 992px) {
    .site-nav--overlay {
        position: absolute;
        inset-inline: 0;
        top: 0;
    }

    .site-nav--overlay.site-nav--sticky {
        position: fixed;
    }

    .site-nav--overlay:not(.is-past-hero) {
        --bs-border-color: transparent;

        background-color: transparent;
    }

    .site-nav--overlay:not(.is-past-hero).is-scrolled {
        box-shadow: none;
    }

    .site-nav--overlay:not(.is-past-hero)::before {
        content: "";
        position: absolute;
        inset: 0 0 auto;
        height: 34svh;
        background: linear-gradient(180deg, rgb(10 10 12 / 55%), transparent);
        pointer-events: none;
        z-index: -1;
    }

    .site-nav--overlay:not(.is-past-hero) .nav-link {
        color: rgb(255 255 255 / 72%);
    }

    .site-nav--overlay:not(.is-past-hero) .nav-link.is-active {
        color: var(--bs-white);
        border-bottom-color: rgb(255 255 255 / 70%);
    }

    .site-nav--overlay:not(.is-past-hero) .navbar-brand,
    .site-nav--overlay:not(.is-past-hero) .nav-brand-text {
        color: var(--bs-white);
    }

    /* Over the scrim the ground is dark whatever the scheme. */
    .site-nav--overlay:not(.is-past-hero) .brand-logo--on-light {
        display: none;
    }

    .site-nav--overlay:not(.is-past-hero) .brand-logo--on-dark {
        display: block;
    }

    .site-nav--overlay:not(.is-past-hero) .theme-toggle {
        color: var(--bs-white);
        border-color: rgb(255 255 255 / 35%);
    }

    .site-nav--overlay:not(.is-past-hero) .btn-accent {
        background: rgb(255 255 255 / 14%);
        border-color: rgb(255 255 255 / 55%);
        color: var(--bs-white);
        backdrop-filter: blur(6px);
    }
}

/* Phone menus (MobileMenu), below lg. Each is a popover its buttons target, so the browser opens
   and closes it with no script, Escape and a tap beside it included, and draws it in the top
   layer above the bar; app/mobile_menu.js keeps the focus inside and closes it behind a link. */
/* The exit transition holds display: flex past the close, so the direction stays off :popover-open. */
.mobile-menu {
    flex-direction: column;
    max-width: none;
    max-height: none;
    margin: 0;
    padding: 0;
    border: 0;
    overflow: auto;
    overscroll-behavior: contain;
}

.mobile-menu:popover-open {
    display: flex;
}

@media (max-width: 991.98px) {
    :root:has(.mobile-menu:popover-open) {
        overflow: hidden;
    }
}

.mobile-menu__body {
    min-height: 0;
}

.mobile-menu .mobile-menu__foot:not(:has(a, button)) {
    display: none;
}

.mobile-menu__bars {
    display: block;
    width: 14px;
    height: 7px;
    border-block: 1.5px solid currentcolor;
}

.mobile-menu__link,
.mobile-menu__sublink {
    color: inherit;
    text-decoration: none;
}

.mobile-menu__group > .mobile-menu__link {
    list-style: none;
    cursor: pointer;
}

.mobile-menu__group > .mobile-menu__link::-webkit-details-marker {
    display: none;
}

.mobile-menu .mobile-menu__group > .mobile-menu__link::after {
    content: "+";
}

.mobile-menu .mobile-menu__group[open] > .mobile-menu__link::after {
    content: "\2212";
}

.mobile-menu :is(.theme-toggle, .social-link) {
    width: 2.75rem;
    height: 2.75rem;
}

.mobile-menu__cta {
    white-space: nowrap;
}

/* The island reads the body pair swapped, the one pair every palette promises in both schemes;
   an accent text there has no contrast guarantee, so the current entry only underlines. */
.mobile-menu--island {
    --bs-border-color: color-mix(in srgb, var(--ap-bg) 22%, var(--ap-text));

    background: var(--ap-text);
    color: var(--ap-bg);
}

.mobile-menu--island :is(.theme-toggle, .social-link),
.mobile-menu--island :is(.theme-toggle, .social-link):hover {
    border-color: var(--bs-border-color);
    color: inherit;
}

.mobile-menu--island :focus-visible {
    outline-color: currentcolor;
}

:is(.mobile-menu--fullscreen, .mobile-menu--island) [aria-current="page"] > .mobile-menu__label {
    text-decoration: underline;
    text-decoration-color: var(--ap-accent);
    text-decoration-thickness: 0.08em;
    text-underline-offset: 0.18em;
}

.mobile-menu--island .mobile-menu__sublink {
    color: color-mix(in srgb, var(--ap-bg) 72%, var(--ap-text));
}

:is(.mobile-menu--fullscreen, .mobile-menu--island) .mobile-menu__sublink[aria-current="page"] {
    color: inherit;
}

/* The named buttons: the full-screen pill, and the floating bar's own. */
.mobile-menu__pill,
.mobile-island__toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.625rem;
    min-height: 2.75rem;
    padding: 0 1rem;
    background: transparent;
    color: inherit;
    font-family: var(--ap-font-mono);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.mobile-menu__pill {
    border: 1.5px solid currentcolor;
    border-radius: var(--bs-border-radius-pill);
}

.mobile-island__toggle {
    padding: 0 0.75rem;
    border: 0;
}

/* Full screen: the bar's own brand and pill over a panel revealed from the pill, in the page's
   colors, so the logo the bar draws reads there too, whatever the scheme and the logos uploaded. */
.mobile-menu--fullscreen {
    inset: 0;
    width: auto;
    height: auto;
    background: var(--ap-bg);
    color: var(--ap-text);
}

.mobile-menu--fullscreen .mobile-menu__list {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.mobile-menu--fullscreen .mobile-menu__link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    font-family: var(--ap-font-display);
    font-size: clamp(2rem, 10vw, 2.6rem);
    font-weight: 800;
    line-height: 1.08;
    letter-spacing: -0.035em;
    text-transform: var(--ap-title-transform);
}

.mobile-menu--fullscreen .mobile-menu__group > .mobile-menu__link::after {
    color: var(--ap-muted);
    font-family: var(--ap-font-mono);
    font-size: 1.1rem;
    font-weight: 400;
}

.mobile-menu--fullscreen .mobile-menu__sublist {
    padding: 0.25rem 0 0.5rem 0.125rem;
}

.mobile-menu--fullscreen .mobile-menu__sublink {
    display: flex;
    align-items: center;
    min-height: 2.5rem;
    color: var(--ap-muted);
    font-family: var(--ap-font-mono);
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.mobile-menu--fullscreen .mobile-menu__sublink::before {
    content: "\21B3\00A0";
}

/* Drawer: a numbered list sliding in from the right, its call to action pinned at the foot. */
.mobile-menu__opener {
    display: inline-grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius-button);
    background: transparent;
    color: var(--ap-text);
}

.mobile-menu__opener .mobile-menu__bars {
    width: 16px;
    height: 8px;
}

.mobile-menu--drawer {
    inset: 0 0 0 auto;
    width: min(86%, 24rem);
    height: auto;
    border-left: 1px solid var(--ap-border);
    background: var(--ap-bg);
    color: var(--ap-text);
    box-shadow: -12px 0 40px rgb(0 0 0 / 12%);
}

.mobile-menu--drawer::backdrop {
    background: rgb(0 0 0 / 42%);
}

.mobile-menu--drawer .mobile-menu__list {
    counter-reset: entry;
}

.mobile-menu--drawer .mobile-menu__item {
    border-bottom: 1px solid var(--ap-border);
    counter-increment: entry;
}

.mobile-menu--drawer .mobile-menu__link {
    display: grid;
    grid-template-columns: 1.75rem minmax(0, 1fr) 1.25rem;
    align-items: center;
    gap: 0.5rem;
    min-height: 3.625rem;
    padding: 0 1.125rem 0 1.25rem;
    font-family: var(--ap-font-display);
    font-size: 1.3rem;
    font-weight: 700;
    text-transform: var(--ap-title-transform);
}

.mobile-menu--drawer .mobile-menu__link::before {
    content: counter(entry, decimal-leading-zero);
    color: var(--ap-muted);
    font-family: var(--ap-font-mono);
    font-size: 0.6rem;
    font-weight: 400;
}

.mobile-menu--drawer .mobile-menu__link::after {
    content: "\203A";
    color: var(--ap-muted);
    font-family: var(--ap-font-mono);
    font-size: 0.9rem;
    font-weight: 400;
    text-align: right;
}

.mobile-menu--drawer :is([aria-current="page"] > .mobile-menu__label, .mobile-menu__sublink[aria-current="page"]) {
    color: var(--ap-accent-text);
}

.mobile-menu--drawer .mobile-menu__sublist {
    padding: 0 1.125rem 0.75rem 3.5rem;
}

.mobile-menu--drawer .mobile-menu__sublink {
    display: flex;
    align-items: center;
    min-height: 2.5rem;
    font-size: 0.9rem;
}

.mobile-menu--drawer .mobile-menu__foot {
    background: var(--ap-panel);
}

/* Island: a bar floating at the foot of the screen that stretches into a card. */
.mobile-island {
    position: fixed;
    bottom: calc(0.875rem + env(safe-area-inset-bottom));
    left: 50%;
    z-index: 1030;
    transform: translateX(-50%);
    border-radius: var(--ap-radius-button);
    background: var(--ap-text);
    color: var(--ap-bg);
    box-shadow: 0 10px 30px rgb(0 0 0 / 25%);
}

.mobile-menu--island {
    inset: auto 0.75rem calc(0.875rem + env(safe-area-inset-bottom));
    width: auto;
    height: auto;
    max-height: calc(100dvh - 1.75rem);
    border-radius: var(--ap-radius);
    box-shadow: 0 10px 30px rgb(0 0 0 / 25%);
}

.mobile-menu--island::backdrop {
    background: color-mix(in srgb, var(--ap-bg) 55%, transparent);
    backdrop-filter: blur(6px);
}

.mobile-menu--island .mobile-menu__list {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    counter-reset: entry;
}

.mobile-menu--island .mobile-menu__item {
    padding-block: 0.25rem;
    counter-increment: entry;
}

.mobile-menu--island .mobile-menu__link {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    font-family: var(--ap-font-display);
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.15;
    text-transform: var(--ap-title-transform);
}

/* The index trails the label, after the +/- of a dropdown. */
.mobile-menu--island .mobile-menu__link::before {
    content: counter(entry, decimal-leading-zero);
    order: 2;
    color: color-mix(in srgb, var(--ap-bg) 60%, var(--ap-text));
    font-family: var(--ap-font-mono);
    font-size: 0.6rem;
    font-weight: 400;
}

.mobile-menu--island a.mobile-menu__link::before,
.mobile-menu--island .mobile-menu__group > .mobile-menu__link::after {
    margin-left: auto;
}

.mobile-menu--island .mobile-menu__group > .mobile-menu__link::after {
    order: 1;
    color: color-mix(in srgb, var(--ap-bg) 60%, var(--ap-text));
    font-family: var(--ap-font-mono);
    font-size: 0.85rem;
    font-weight: 400;
}

.mobile-menu--island .mobile-menu__sublist {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
    padding: 0.5rem 0 0.25rem;
}

.mobile-menu--island .mobile-menu__sublink {
    display: inline-flex;
    align-items: center;
    min-height: 2.25rem;
    padding: 0 0.75rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius-pill);
    font-family: var(--ap-font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.mobile-island__bar {
    min-height: 3.5rem;
}

@media (max-width: 991.98px) {
    .site-nav--phone-island > .container-fluid {
        justify-content: center;
    }

    /* The floating bar would cover the footer's last line. */
    .site-nav--phone-island ~ .site-footer > .container-fluid {
        padding-bottom: 4.5rem;
    }
}

/* Entrances and exits. The panels leave the top layer only once their exit has played
   (allow-discrete); a browser without @starting-style opens them without one. */
@media (prefers-reduced-motion: no-preference) {
    .mobile-menu--fullscreen {
        clip-path: circle(0% at calc(100% - 4.25rem) 2.75rem);
        transition: clip-path 0.65s cubic-bezier(0.6, 0, 0.2, 1), overlay 0.65s allow-discrete, display 0.65s allow-discrete;
    }

    .mobile-menu--fullscreen:popover-open {
        clip-path: circle(150% at calc(100% - 4.25rem) 2.75rem);
    }

    @starting-style {
        .mobile-menu--fullscreen:popover-open {
            clip-path: circle(0% at calc(100% - 4.25rem) 2.75rem);
        }
    }

    .mobile-menu--fullscreen:popover-open :is(.mobile-menu__item, .mobile-menu__cta, .mobile-menu__foot) {
        animation: mobile-menu-rise 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) 0.16s backwards;
    }

    .mobile-menu--fullscreen:popover-open .mobile-menu__item:nth-child(2) {
        animation-delay: 0.215s;
    }

    .mobile-menu--fullscreen:popover-open .mobile-menu__item:nth-child(3) {
        animation-delay: 0.27s;
    }

    .mobile-menu--fullscreen:popover-open .mobile-menu__item:nth-child(4) {
        animation-delay: 0.325s;
    }

    .mobile-menu--fullscreen:popover-open .mobile-menu__item:nth-child(n + 5),
    .mobile-menu--fullscreen:popover-open :is(.mobile-menu__cta, .mobile-menu__foot) {
        animation-delay: 0.38s;
    }

    .mobile-menu--drawer {
        transform: translateX(104%);
        transition: transform 0.45s cubic-bezier(0.2, 0.8, 0.2, 1), overlay 0.45s allow-discrete, display 0.45s allow-discrete;
    }

    .mobile-menu--drawer:popover-open {
        transform: none;
    }

    @starting-style {
        .mobile-menu--drawer:popover-open {
            transform: translateX(104%);
        }
    }

    .mobile-menu--drawer:popover-open .mobile-menu__item {
        animation: mobile-menu-slide 0.42s cubic-bezier(0.2, 0.8, 0.2, 1) 0.14s backwards;
    }

    .mobile-menu--drawer:popover-open .mobile-menu__item:nth-child(2) {
        animation-delay: 0.18s;
    }

    .mobile-menu--drawer:popover-open .mobile-menu__item:nth-child(3) {
        animation-delay: 0.22s;
    }

    .mobile-menu--drawer:popover-open .mobile-menu__item:nth-child(n + 4) {
        animation-delay: 0.26s;
    }

    .mobile-menu--island {
        clip-path: inset(calc(100% - 3.5rem) 18% 0 round var(--ap-radius-button));
        transition: clip-path 0.55s cubic-bezier(0.65, 0, 0.25, 1), overlay 0.55s allow-discrete, display 0.55s allow-discrete;
    }

    .mobile-menu--island:popover-open {
        clip-path: inset(0 round var(--ap-radius));
    }

    @starting-style {
        .mobile-menu--island:popover-open {
            clip-path: inset(calc(100% - 3.5rem) 18% 0 round var(--ap-radius-button));
        }
    }

    /* The card grows upward, so its entries rise from the bottom one. */
    .mobile-menu--island:popover-open .mobile-menu__item {
        animation: mobile-menu-rise 0.42s cubic-bezier(0.2, 0.8, 0.2, 1) 0.18s backwards;
    }

    .mobile-menu--island:popover-open .mobile-menu__item:nth-last-child(2) {
        animation-delay: 0.225s;
    }

    .mobile-menu--island:popover-open .mobile-menu__item:nth-last-child(3) {
        animation-delay: 0.27s;
    }

    .mobile-menu--island:popover-open .mobile-menu__item:nth-last-child(n + 4) {
        animation-delay: 0.315s;
    }

    .mobile-menu:is(.mobile-menu--drawer, .mobile-menu--island)::backdrop {
        opacity: 0;
        transition: opacity 0.35s ease, overlay 0.35s allow-discrete, display 0.35s allow-discrete;
    }

    .mobile-menu:is(.mobile-menu--drawer, .mobile-menu--island):popover-open::backdrop {
        opacity: 1;
    }

    @starting-style {
        .mobile-menu:is(.mobile-menu--drawer, .mobile-menu--island):popover-open::backdrop {
            opacity: 0;
        }
    }
}

@keyframes mobile-menu-rise {
    from {
        opacity: 0;
        transform: translateY(1.75rem);
    }
}

@keyframes mobile-menu-slide {
    from {
        opacity: 0;
        transform: translateX(1.125rem);
    }
}

.footer-logo {
    max-height: 58px;
}

/* Footer variants. */
.site-footer--statement {
    padding-top: 4rem;
}

.footer-statement {
    font-size: clamp(1.8rem, 4vw, 2.6rem);
    max-width: 20ch;
}

.nav-link.is-active {
    color: var(--ap-accent-text);
    border-bottom: 1px solid var(--ap-accent);
}

.arrow {
    display: inline-block;
    transition: transform 0.15s ease;
}

.btn:hover .arrow,
a:hover > .arrow {
    transform: translateX(3px);
}

.reading-progress {
    position: fixed;
    top: 0;
    left: 0;
    height: 3px;
    width: 0;
    background: var(--ap-accent);
    z-index: 1040;
    transition: width 0.1s linear;
}

@media (prefers-reduced-motion: reduce) {
    .arrow,
    .btn:hover .arrow,
    a:hover > .arrow {
        transition: none;
        transform: none;
    }

    .reading-progress {
        transition: none;
    }
}

/* ---- Hero ---- */
.hero-emphasis {
    color: var(--ap-accent-text);
}

/* hero/encre: the opener on the inverted pair, edge to edge like stats/band. Its accent
   button keeps the fill, whose label is measured against it (ADR-0005). */
.hero-encre {
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.hero-statement-aside {
    border-left: 2px solid var(--ap-accent);
    padding-left: 1.25rem;
    text-wrap: pretty;
}

/* Type-led opener: with no image to carry it, the headline takes the full
   measure and the prose steps back into a reading column under it. */
.hero-typo {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 1.75rem;
}

/* hero: the repères under the buttons; the color is inherited, so they read on a photo too. */
.hero-facts__label {
    font-family: var(--ap-font-mono);
    font-size: 0.75rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    opacity: 0.72;
}

/* The three centered things this opener has: the column, the text inside it and
   the accent rule over the eyebrow. The buttons follow through .hero-ctas. */
.blk--align-start .hero-typo {
    align-items: flex-start;
    text-align: left;
}

.hero-typo .hero-ctas {
    justify-content: center;
}

.blk--align-start .hero-typo .hero-ctas {
    justify-content: flex-start;
}

.hero-typo__eyebrow::before {
    content: "";
    display: block;
    width: 3rem;
    height: 2px;
    margin: 0 auto 1.1rem;
    background: var(--ap-accent);
}

.blk--align-start .hero-typo__eyebrow::before {
    margin-inline: 0;
}

.hero-typo__headline {
    font-size: clamp(2.6rem, 7.5vw, 5.5rem);
    text-wrap: balance;
}

.hero-typo__reading {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    max-width: 60ch;
}

/* hero/encart: the repères on a card over the photo's lower corner from lg, under it below. */
.hero-encart__card {
    margin-top: var(--ap-space-tight);
}

@media (min-width: 992px) {
    .hero-encart__card {
        position: absolute;
        bottom: var(--ap-space-tight);
        left: var(--ap-space-tight);
        max-width: calc(100% - 2 * var(--ap-space-tight));
        margin-top: 0;
    }
}

.hero-photo {
    border-radius: var(--ap-radius);
}

/* Full-screen media hero: the media owns the viewport and the text sits on it
   directly, no panel between the two. Fixed white and translucent literals,
   never theme tokens: a scrim over a photograph is theme-invariant
   (ADR-0005). */
.hero-screen {
    min-height: 100svh;
}

.hero-screen--flat {
    background: var(--ap-panel);
    color: var(--ap-text);
}

/* Darkest at the two edges, lightest across the middle, so the navbar and the
   scroll cue stay readable without a band cutting the photograph in two. It
   stops short of full transparency because the tenant's photo can be bright
   exactly where the headline lands. */
.hero-screen:not(.hero-screen--flat)::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(
        180deg,
        rgb(10 10 12 / 62%) 0%,
        rgb(10 10 12 / 24%) 32%,
        rgb(10 10 12 / 28%) 62%,
        rgb(10 10 12 / 58%) 100%
    );
    pointer-events: none;
}

/* Scroll cue. Decorative, so a pseudo-element rather than markup a screen
   reader announces, and scoped to the hero opening an overlay page: that is the
   only one starting at the top of the viewport, so anywhere else the cue would
   sit below the fold it is pointing at. */
@media (min-width: 992px) {
    body.has-nav-overlay main > .blk:first-child .hero-screen:not(.hero-screen--flat)::after {
        content: "↓";
        position: absolute;
        bottom: 1.75rem;
        left: 50%;
        z-index: 2;
        transform: translateX(-50%);
        color: rgb(255 255 255 / 65%);
        font-size: 1.1rem;
        line-height: 1;
        pointer-events: none;
    }
}

.hero-screen__body {
    position: relative;
    z-index: 2;
    width: 100%;
    padding: 6rem 0 5rem;
}

.hero-screen:not(.hero-screen--flat) .hero-screen__body {
    color: var(--bs-white);
}

/* The theme's text colors are measured against --ap-bg, never against a photograph,
   so over one they give way to the surface's white (ADR-0005). */
.hero-screen:not(.hero-screen--flat) .eyebrow,
.hero-screen:not(.hero-screen--flat) .lead-quote,
.hero-screen:not(.hero-screen--flat) .hero-emphasis,
.hero-screen:not(.hero-screen--flat) .hero-poster__link {
    color: inherit;
}

/* The quote rule reads as a stray mark beside a centered column. */
.hero-screen .lead-quote {
    padding-left: 0;
    border-left: none;
}

/* Glass buttons: the accent fill would be the one colored thing on the photo,
   and its contrast is guaranteed against the theme, not against the media. */
.hero-screen:not(.hero-screen--flat) .btn-accent,
.hero-screen:not(.hero-screen--flat) .btn-outline-accent {
    --bs-btn-bg: rgb(255 255 255 / 14%);
    --bs-btn-border-color: rgb(255 255 255 / 55%);
    --bs-btn-color: var(--bs-white);
    --bs-btn-hover-bg: rgb(255 255 255 / 26%);
    --bs-btn-hover-border-color: var(--bs-white);
    --bs-btn-hover-color: var(--bs-white);
    --bs-btn-active-bg: rgb(255 255 255 / 26%);
    --bs-btn-active-border-color: var(--bs-white);
    --bs-btn-active-color: var(--bs-white);

    backdrop-filter: blur(6px);
}

/* ---- Galleries ---- */
.gallery-masonry {
    columns: 2;
    column-gap: 1rem;
}

@media (min-width: 992px) {
    .gallery-masonry {
        columns: 3;
    }
}

/* The gap under a caption is what separates two images, so it has to beat the
   gap above it by enough that the caption reads as belonging to the one it
   sits under rather than to the one below. */
.gallery-masonry__item {
    break-inside: avoid;
    margin: 0 0 2.25rem;
}

.gallery-masonry__caption {
    display: block;
    margin-top: 0.4rem;
}

.gallery-masonry__item .site-image,
.gallery-masonry__item .image-frame {
    width: 100%;
    height: auto;
    min-height: 0;
    aspect-ratio: auto;
}

.gallery-masonry__item .image-frame {
    min-height: 220px;
}

/* Editor only, and after the rule above so these win: an unfilled slot has no
   image to take its proportions from, so every placeholder would render at one
   height and the mosaic would read as a grid. The template cycles the three
   shapes a mixed gallery produces. */
.gallery-masonry__item--tall .image-frame {
    min-height: 0;
    aspect-ratio: 3 / 4;
}

.gallery-masonry__item--square .image-frame {
    min-height: 0;
    aspect-ratio: 1 / 1;
}

.gallery-masonry__item--wide .image-frame {
    min-height: 0;
    aspect-ratio: 4 / 3;
}

/* An explicit width is load-bearing: the slides size with width: 100%, which
   contributes nothing to a shrink-to-fit box, so an auto-width carousel
   collapses to its indicator dots and never reaches the max-width. */
.gallery-carousel {
    width: 100%;
    max-width: 1080px;
    margin-inline: auto;
}

/* Shared by the gallery block and the boutique gallery: the arrows are
   positioned against the stage, so the thumbnails stay outside it. */
/* Bootstrap's carousel restyled to the theme tokens: its stock controls and
   indicators are white and vanish on the light theme. */
.carousel-control-prev:has(.carousel-chip),
.carousel-control-next:has(.carousel-chip) {
    width: 5rem;
    opacity: 1;
}

.carousel-chip {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    background: var(--ap-panel);
    color: var(--ap-text);
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.carousel-control-prev:hover .carousel-chip,
.carousel-control-next:hover .carousel-chip,
.carousel-control-prev:focus-visible .carousel-chip,
.carousel-control-next:focus-visible .carousel-chip {
    border-color: var(--ap-accent);
    background: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

@media (max-width: 575.98px) {
    .carousel-control-prev:has(.carousel-chip),
    .carousel-control-next:has(.carousel-chip) {
        width: 3.5rem;
    }

    .carousel-chip {
        width: 2.25rem;
        height: 2.25rem;
    }
}

.gallery-carousel .carousel-indicators,
.gallery-stage .carousel-indicators {
    position: static;
    margin: 1rem 0 0;
}

.gallery-carousel .carousel-indicators [data-bs-target],
.gallery-stage .carousel-indicators [data-bs-target] {
    background-color: var(--ap-text);
    opacity: 0.3;
}

.gallery-carousel .carousel-indicators .active,
.gallery-stage .carousel-indicators .active {
    background-color: var(--ap-accent);
    opacity: 1;
}

.gallery-carousel__item .image-frame,
.gallery-carousel__item .site-image {
    aspect-ratio: 16 / 9;
    width: 100%;
    height: auto;
    min-height: 0;
    object-fit: cover;
    border-radius: var(--ap-radius);
}

/* A 16/9 slide on a phone is a 190px letterbox; the taller crop keeps the
   subject readable at the widths a single-image variant is built for. */
@media (max-width: 575.98px) {
    .gallery-carousel__item .image-frame,
    .gallery-carousel__item .site-image {
        aspect-ratio: 4 / 3;
    }
}

/* The stage variant: the current image at the centre, its two neighbours in the
   wings. Bootstrap's carousel is the engine, so the wrap at both ends, the arrows,
   the keyboard and the swipe are its; only the placement is here. */
.gallery-stage {
    --gs-peek: 58%;

    width: 100%;
    max-width: 920px;
    margin-inline: auto;
}

/* One grid cell holds every slide, so the stage keeps the height of the tallest
   and the wings can leave the box. Bootstrap stacks them with floats inside an
   overflow-hidden parent, which would clip exactly what this variant shows. */
.gallery-stage .carousel-inner {
    display: grid;
    overflow: visible;
}

.gallery-stage .carousel-inner > .carousel-item {
    grid-area: 1 / 1;
    display: block;
    float: none;
    margin-right: 0;
    visibility: hidden;
    opacity: 0;
    transition: transform 0.45s ease, opacity 0.45s ease;
}

.gallery-stage .carousel-inner > .carousel-item.active {
    z-index: 2;
    visibility: visible;
    opacity: 1;
    transform: none;
}

/* The wings: the item before .active, and the last one when the first is centred. */
.gallery-stage .carousel-inner > .carousel-item:has(+ .active),
.gallery-stage .carousel-inner:has(> .carousel-item:first-child.active) > .carousel-item:last-child {
    z-index: 1;
    visibility: visible;
    opacity: 0.35;
    transform: translateX(calc(-1 * var(--gs-peek))) scale(0.82);
}

.gallery-stage .carousel-inner > .active + .carousel-item,
.gallery-stage .carousel-inner:has(> .carousel-item:last-child.active) > .carousel-item:first-child {
    z-index: 1;
    visibility: visible;
    opacity: 0.35;
    transform: translateX(var(--gs-peek)) scale(0.82);
}

.gallery-stage__item .image-frame,
.gallery-stage__item .site-image {
    aspect-ratio: 3 / 2;
    width: 100%;
    height: auto;
    min-height: 0;
    object-fit: cover;
    border-radius: var(--ap-radius);
}

/* Only the centred slide names itself: a caption read off a dimmed wing would
   label the wrong photo. */
.gallery-stage__caption {
    margin-top: 1rem;
    text-align: center;
    opacity: 0;
    transition: opacity 0.45s ease;
}

.gallery-stage .carousel-inner > .carousel-item.active .gallery-stage__caption {
    opacity: 1;
}

/* The caption sits inside the carousel box, so centring the chips on the box
   would drop them below the middle of the photo. */
.gallery-stage .carousel-control-prev,
.gallery-stage .carousel-control-next {
    z-index: 3;
    padding-bottom: 2.6rem;
}

/* Wide enough for the chips to clear the centre photo and sit over the wings. */
@media (min-width: 992px) {
    .gallery-stage .carousel-control-prev {
        left: -3.25rem;
    }

    .gallery-stage .carousel-control-next {
        right: -3.25rem;
    }
}

/* A wing on a phone would leave the centre too narrow to read. */
@media (max-width: 767.98px) {
    .gallery-stage {
        --gs-peek: 76%;

        max-width: none;
    }

    .gallery-stage .carousel-inner > .carousel-item:not(.active) {
        opacity: 0.22;
    }
}

.gallery-caption {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin-top: 0.75rem;
}

.gallery-caption--row {
    flex-flow: row wrap;
    justify-content: space-between;
    align-items: baseline;
    column-gap: 1.5rem;
}

.gallery-caption__title,
.gallery-vitrine__name {
    font-family: var(--ap-font-display);
    font-size: 1.1rem;
    line-height: 1.3;
    color: var(--ap-text);
}

.gallery-caption__detail,
.gallery-vitrine__detail {
    font-size: 0.85rem;
    line-height: 1.4;
    color: var(--ap-muted);
}

/* A fixed track, so a portrait reads the same on every width; only the peek of
   the next one is proportional. */
.gallery-rail {
    grid-auto-columns: min(22rem, 75%);
}

.gallery-rail__item {
    min-width: 0;
    scroll-snap-align: start;
}

.gallery-vitrine__stage .site-image,
.gallery-vitrine__stage .image-frame {
    aspect-ratio: 4 / 3;
}

.gallery-vitrine__list {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.gallery-vitrine__thumb {
    display: grid;
    grid-template-columns: 4.5rem minmax(0, 1fr);
    gap: 1rem;
    align-items: center;
    width: 100%;
    padding: 0.5rem;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    background: transparent;
    color: var(--ap-text);
    text-align: start;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.gallery-vitrine__thumb:hover {
    border-color: var(--ap-accent);
}

.gallery-vitrine__thumb.active {
    border-color: var(--ap-accent);
    background: var(--ap-panel);
    box-shadow: inset 0 0 0 1px var(--ap-accent);
}

.gallery-vitrine__text {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

@media (min-width: 992px) {
    .gallery-vitrine__thumb {
        grid-template-columns: 6rem minmax(0, 1fr);
    }
}

/* ---- Pricing ---- */
.pricing-tier__price {
    font-family: var(--ap-font-display);
    font-size: 1.75rem;
    line-height: 1.1;
}

.pricing-tier__list {
    list-style: none;
    margin: 0 0 1.25rem;
    padding: 0;
}

.pricing-tier__list li {
    position: relative;
    padding-left: 1.35rem;
    margin-bottom: 0.4rem;
}

.pricing-tier__list li::before {
    content: "✓";
    position: absolute;
    left: 0;
    color: var(--ap-accent);
}

.pricing-table__row {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    padding: 1.5rem 0;
    border-top: 1px solid var(--ap-border);
}

@media (min-width: 768px) {
    .pricing-table__row {
        grid-template-columns: 1fr 1.4fr auto;
        align-items: start;
        gap: 2rem;
    }
}

.pricing-table__row--on {
    border-top-color: var(--ap-accent);
}

@media (min-width: 768px) {
    .pricing-table__price {
        min-width: 10rem;
    }
}

/* Sticky cells drop their borders under border-collapse, so the matrix keeps
   separate borders and paints them cell by cell. */
/* Positioned so the cells' .visually-hidden spans resolve against this box:
   without it they escape the scroller and widen the page on a phone. */
.price-matrix {
    position: relative;
    overflow-x: auto;
    margin-inline: -0.25rem;
    padding-inline: 0.25rem;
}

.price-matrix__table {
    width: 100%;
    min-width: 34rem;
    border-collapse: separate;
    border-spacing: 0;
}

.price-matrix__corner {
    background: var(--ap-bg);
}

.price-matrix__criterion,
.price-matrix__corner {
    position: sticky;
    left: 0;
    z-index: 1;
    min-width: 11rem;
}

.price-matrix__criterion {
    background: var(--ap-bg);
    font-weight: 500;
    text-align: left;
    padding: 0.85rem 1rem 0.85rem 0;
    border-top: 1px solid var(--ap-border);
}

.price-matrix__head {
    padding: 0 1rem 1rem;
    text-align: center;
    vertical-align: bottom;
}

.price-matrix__price {
    display: block;
    font-family: var(--ap-font-display);
    font-size: 1.5rem;
    line-height: 1.1;
}

.price-matrix__cell {
    padding: 0.85rem 1rem;
    text-align: center;
    vertical-align: middle;
    border-top: 1px solid var(--ap-border);
}

.price-matrix__head--on,
.price-matrix__cell--on {
    background: rgba(var(--ap-accent-rgb), 0.07);
}

.price-matrix__head--on {
    border-top: 2px solid var(--ap-accent);
    border-radius: var(--ap-radius) var(--ap-radius) 0 0;
    padding-top: 1rem;
}

/* Whichever of tbody or tfoot ends the table closes the highlighted column. */
.price-matrix__table > *:last-child > tr:last-child .price-matrix__cell--on {
    border-radius: 0 0 var(--ap-radius) var(--ap-radius);
}

.price-matrix__mark {
    font-size: 1.1rem;
    line-height: 1;
}

.price-matrix__mark--yes {
    color: var(--ap-accent-text);
}

.price-matrix__mark--no {
    color: var(--ap-muted);
}

/* pricing and price_matrix: the stay-length choice. A wrapper takes no box of its own, the
   second price waits for its radio, and a price both lengths share never hides. */
[data-duration] {
    display: contents;
}

[data-duration="alt"],
.blk:has(.duration-switch__alt:checked) [data-duration="main"] {
    display: none;
}

.blk:has(.duration-switch__alt:checked) [data-duration="alt"] {
    display: contents;
}

/* price_matrix/columns: a card per formula, its price set large over a checklist. */
.price-matrix-card__name {
    font-family: var(--ap-font-display);
    font-size: clamp(1.5rem, 1.25rem + 0.8vw, 2rem);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.02em;
}

.price-matrix-card__price {
    column-gap: 0.4rem;
    border-top: 1px solid var(--ap-border);
    font-family: var(--ap-font-display);
    font-size: clamp(2.25rem, 1.85rem + 1.4vw, 3rem);
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    letter-spacing: -0.03em;
}

.price-matrix-card__for {
    font-size: 0.9375rem;
    line-height: 1.5;
    color: var(--ap-muted);
}

.price-matrix-card__period {
    font-family: var(--ap-font-mono);
    font-size: 0.8125rem;
    font-weight: 400;
    letter-spacing: 0;
    color: var(--ap-muted);
}

.price-matrix-card__item {
    font-size: 0.9375rem;
    line-height: 1.4;
}

.price-matrix-card__mark {
    flex: none;
    width: 1rem;
    padding-top: 0.2em;
    color: var(--ap-accent-text);
}

.price-matrix-card__item--off {
    color: var(--ap-muted);
}

.price-matrix-card__item--off .price-matrix-card__mark {
    color: var(--ap-faint);
}

/* price_matrix/comparatif: the cross-tab from md up, the highlighted plan an inverted column
   from its tag to its button. The column takes the ink pair (theme.css), so its button is the
   page's own pair too and no accent is read against ink (ADR-0005). */
.price-compare {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    table-layout: fixed;
}

.price-compare td:first-child,
.price-compare__criterion {
    width: 28%;
}

.price-compare__on {
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.price-compare__head {
    padding: 0 1.25rem 1.5rem;
    text-align: center;
    vertical-align: bottom;
}

.price-compare__head.price-compare__on {
    padding-top: 1.5rem;
    border-radius: var(--ap-radius) var(--ap-radius) 0 0;
}

.price-compare__tag {
    display: inline-block;
    margin-bottom: 1.25rem;
    padding: 0.35rem 0.75rem;
    border-radius: var(--ap-radius-button);
    background-color: var(--ap-bg);
    color: var(--ap-text);
    font-family: var(--ap-font-mono);
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.price-compare__name {
    display: block;
    font-weight: 600;
    line-height: 1.35;
    text-wrap: balance;
}

.price-compare__for {
    display: block;
    margin-top: 0.35rem;
    font-size: 0.875rem;
    color: var(--ap-muted);
}

.price-compare__price {
    display: block;
    margin-top: 1rem;
    font-family: var(--ap-font-display);
    font-size: clamp(2.25rem, 1.6rem + 1.6vw, 3.5rem);
    font-weight: 900;
    font-variant-numeric: tabular-nums;
    line-height: 0.95;
    letter-spacing: -0.03em;
}

.price-compare__period {
    display: block;
    margin-top: 0.4rem;
    font-family: var(--ap-font-mono);
    font-size: 0.8125rem;
    color: var(--ap-muted);
}

.price-compare__criterion {
    padding: 1.1rem 1.5rem 1.1rem 0;
    border-top: 1px solid var(--ap-border);
    font-weight: 500;
    line-height: 1.4;
    text-align: left;
}

.price-compare__cell,
.price-compare__foot {
    padding: 1.1rem 1.25rem;
    border-top: 1px solid var(--ap-border);
    text-align: center;
    vertical-align: middle;
}

.price-compare__foot.price-compare__on {
    padding-bottom: 1.5rem;
    border-radius: 0 0 var(--ap-radius) var(--ap-radius);
}

.price-compare__mark {
    font-size: 1.15rem;
    font-weight: 700;
    line-height: 1;
    color: var(--ap-accent-text);
}

.price-compare__mark--no {
    font-size: 0.875rem;
    color: var(--ap-muted);
}

.price-compare__value {
    font-family: var(--ap-font-display);
    font-size: 1.5rem;
    font-weight: 900;
    line-height: 1;
}

/* One band per plan; the highlighted one inverts the body pair, where no accent
   token is measured (ADR-0005), so its marks and button take the page's own pair. */
.price-tier {
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    background: var(--ap-panel);
}

.price-tier__period,
.price-tier__off {
    color: var(--ap-muted);
}

.price-tier .fa-check {
    color: var(--ap-accent-text);
}

.price-tier--on {
    border-color: var(--ap-text);
    background: var(--ap-text);
    color: var(--ap-bg);
}

.price-tier--on .price-tier__period,
.price-tier--on .price-tier__off,
.price-tier--on .fa-check {
    color: inherit;
    opacity: 0.75;
}

.price-tier--on .btn-accent {
    --bs-btn-bg: var(--ap-bg);
    --bs-btn-border-color: var(--ap-bg);
    --bs-btn-color: var(--ap-text);
    --bs-btn-hover-bg: transparent;
    --bs-btn-hover-border-color: var(--ap-bg);
    --bs-btn-hover-color: var(--ap-bg);
}

.price-tabs {
    --bs-nav-link-color: var(--ap-muted);
    --bs-nav-link-hover-color: var(--ap-text);
    --bs-nav-pills-link-active-bg: var(--ap-text);
    --bs-nav-pills-link-active-color: var(--ap-bg);
    --bs-nav-pills-border-radius: var(--ap-radius-button);

    width: fit-content;
    max-width: 100%;
    border-radius: var(--ap-radius-button);
    background: var(--ap-panel);
}

.price-tab {
    max-width: 36rem;
}

.price-tab__head {
    border-bottom: 1px solid var(--ap-text);
}

/* ---- Key figures ---- */
.stat__value {
    font-family: var(--ap-font-display);
    font-size: clamp(2.25rem, 1.6rem + 2.4vw, 3.5rem);
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
    line-height: 1;
}

/* Every accent token is measured against --ap-bg, never against --ap-text, so
   nothing accent-colored is guaranteed legible here (ADR-0005). Inverting the
   body pair is the only combination the palette promises on an ink fill. */
.stats-band {
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.stats-band__caption {
    max-width: 22ch;
    margin-inline: auto;
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--ap-muted);
}

/* ---- Logo band ---- */
.logo-strip {
    column-gap: 2.5rem;
}

.logo-strip .logo-mark {
    max-height: 88px;
    max-width: 300px;
}

.logo-mark {
    width: auto;
    filter: grayscale(1);
    opacity: 0.7;
    transition: filter 0.15s ease, opacity 0.15s ease;
}

a:hover > .logo-mark {
    filter: none;
    opacity: 1;
}

.logo-name {
    font-family: var(--ap-font-display);
    font-size: 1.15rem;
    font-weight: 700;
    line-height: 1.2;
    color: var(--ap-muted);
    transition: color 0.15s ease;
}

a:hover > .logo-name {
    color: var(--ap-text);
}

/* An unfilled slot keeps its place in the strip: .image-frame has no
   intrinsic height and the flex item shrinks to fit, so the box is sized
   here to match the 88px logo cap. */
.logo-strip__item--empty {
    width: 160px;
}

.logo-strip__item--empty .image-frame {
    height: 88px;
    background-size: 36px 36px, auto;
}

.logo-tile__frame {
    aspect-ratio: 3 / 2;
}

.logo-tile .logo-mark {
    max-width: 100%;
    max-height: 3.5rem;
}

.logo-tile__empty {
    height: 1.4rem;
    background-color: var(--ap-border);
}

.logo-tile--card {
    width: 11.25rem;
    background-color: var(--ap-panel);
    transition: border-color 0.15s ease;
}

.logo-tile--card.logo-tile--linked:hover {
    --bs-border-color: var(--ap-text);
}

.logo-tile--cell {
    transition: background-color 0.15s ease;
}

.logo-tile--cell.logo-tile--linked:hover {
    background-color: color-mix(in srgb, var(--ap-text) 4%, var(--ap-bg));
}

.logo-tile__corner {
    font-size: 0.875rem;
    color: var(--ap-muted);
}

.logo-shelf__mark {
    height: 4rem;
}

.logo-shelf .logo-mark {
    max-width: 100%;
    max-height: 3.5rem;
}

.logo-perk__offer {
    font-size: 0.9rem;
    font-weight: 600;
    line-height: 1.35;
    text-wrap: pretty;
}

.logo-perk__arrow {
    color: var(--ap-accent-text);
}

.promo-code {
    align-items: stretch;
    border: 1px dashed color-mix(in srgb, currentcolor 40%, transparent);
    border-radius: var(--ap-radius-button);
}

.promo-code__value {
    padding: 0.4rem 0.5rem;
    overflow-wrap: anywhere;
    font-family: var(--ap-font-mono);
    font-size: 0.85rem;
    font-weight: 500;
    letter-spacing: 0.05em;
}

.promo-code__copy {
    padding: 0.4rem 0.5rem;
    border: 0;
    border-left: 1px dashed color-mix(in srgb, currentcolor 40%, transparent);
    background-color: transparent;
    color: inherit;
    font-size: 0.75rem;
    font-weight: 600;
}

.promo-code__copy:hover {
    background-color: color-mix(in srgb, currentcolor 8%, transparent);
}

.logo-ticket {
    --logo-ticket-stub: 6rem;

    position: relative;
    display: grid;
    grid-template-columns: var(--logo-ticket-stub) minmax(0, 1fr);
    min-height: 8.5rem;
    overflow: hidden;
    background-color: var(--ap-panel);
}

@media (min-width: 576px) {
    .logo-ticket {
        --logo-ticket-stub: 8.5rem;
    }
}

.logo-ticket::before,
.logo-ticket::after {
    position: absolute;
    left: calc(var(--logo-ticket-stub) - 9px);
    width: 18px;
    height: 18px;
    border: 1px solid var(--ap-border);
    border-radius: 50%;
    background-color: var(--ap-bg);
    content: "";
}

.logo-ticket::before {
    top: -10px;
}

.logo-ticket::after {
    bottom: -10px;
}

.logo-ticket__stub {
    border-right: 1px dashed var(--ap-border);
}

.logo-ticket .logo-mark {
    max-width: 100%;
    max-height: 3rem;
    filter: none;
    opacity: 1;
}

.logo-ticket__offer {
    font-family: var(--ap-font-display);
    font-size: 1.3rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.12;
    text-wrap: balance;
}

.logo-ticket__link {
    font-weight: 650;
    color: var(--ap-accent-text);
    text-decoration: none;
}

.logo-ticket__link:hover {
    color: var(--ap-accent-text-hover);
}

.logo-ink .logo-mark {
    max-height: 3.5rem;
    max-width: 10rem;
}

.logo-ink__item {
    flex: 0 1 11rem;
    min-width: 0;
}

.logo-ink__mark {
    height: 3.5rem;
}

.logo-ink .label,
.logo-ink .logo-name,
.logo-ink a:hover > .logo-name {
    color: inherit;
}

.logo-ink .label {
    opacity: 0.7;
}

.logo-ink__empty {
    width: 6rem;
    height: 1.4rem;
    background-color: color-mix(in srgb, var(--ap-bg) 25%, transparent);
}

/* The two edges fade the logos out rather than cutting them; a mask reads only alpha. */
.logo-rail {
    overflow: hidden;
    mask-image: linear-gradient(90deg, transparent, currentcolor 5rem, currentcolor calc(100% - 5rem), transparent);
}

.logo-rail__group {
    min-width: 100%;
    animation: marquee-slide 40s linear infinite;
}

.logo-rail:hover .logo-rail__group {
    animation-play-state: paused;
}

.logo-rail--still .logo-rail__group {
    animation: none;
}

@media (prefers-reduced-motion: reduce) {
    .logo-rail {
        overflow-x: auto;
    }

    .logo-rail__group {
        animation: none;
    }

    .logo-rail__group[inert] {
        display: none;
    }
}

/* ---- Testimonials ---- */
/* One tinted surface per avis: the quote first because it is the content, the face a
   fixed round crop at the foot of it rather than a column that stretches to
   whatever the quote's height turns out to be. */
.testimonial-card__quote {
    margin: 0;
    font-size: 1rem;
    line-height: 1.6;
    text-wrap: pretty;
}

.testimonial-card--lead {
    --bs-card-spacer-x: clamp(1.75rem, 3.4vw, 2.75rem);
    --bs-card-spacer-y: clamp(1.75rem, 3.4vw, 2.75rem);
}

.testimonial-card--lead .testimonial-card__quote {
    max-width: 30ch;
    font-family: var(--ap-font-display);
    font-size: clamp(1.5rem, 1.1rem + 1.2vw, 2.125rem);
    line-height: 1.15;
    letter-spacing: -0.02em;
}

.testimonial-card__who {
    display: flex;
    align-items: center;
    gap: 0.9rem;
}

.testimonial-card__detail {
    font-family: var(--ap-font-mono);
    font-size: 0.75rem;
    color: var(--ap-accent-text);
}

.testimonial-card__portrait {
    flex: none;
    width: 3.25rem;
}

/* testimonials/affiche: each avis set large over its photograph, on the inverted body
   pair when it has none. */
.testimonial-cover {
    min-height: 26rem;
}

@media (min-width: 992px) {
    .testimonial-cover {
        min-height: 38rem;
    }
}

/* White on a fixed scrim, darkest behind the words: a scrim over a photograph is
   theme-invariant (ADR-0005). */
.testimonial-cover--photo {
    color: var(--bs-white);
}

.testimonial-cover--photo::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background:
        linear-gradient(90deg, rgb(10 10 12 / 82%) 0%, rgb(10 10 12 / 55%) 45%, rgb(10 10 12 / 5%) 80%),
        linear-gradient(0deg, rgb(10 10 12 / 50%), transparent 50%);
    pointer-events: none;
}

.testimonial-cover__body {
    z-index: 2;
}

.testimonial-cover__quote {
    max-width: 26ch;
    font-family: var(--ap-font-display);
    font-size: clamp(1.625rem, 1rem + 2.2vw, 3rem);
    font-weight: 600;
    line-height: 1.1;
    letter-spacing: -0.025em;
    text-wrap: balance;
}

.testimonial-cover__detail {
    font-family: var(--ap-font-mono);
    font-size: 0.8125rem;
    opacity: 0.75;
}

/* The alternating rows carry the testimony at display scale: the photograph is
   support, so the quote is the loudest thing in the row. */
.testimonial-lead {
    margin: 0;
    font-family: var(--ap-font-display);
    font-size: clamp(1.25rem, 2.2vw, 1.75rem);
    line-height: 1.35;
    text-wrap: pretty;
}

/* Centered unless the block's alignment setting says otherwise, like .content-stack: a
   text-center utility is !important and the wrapper class could never beat it. */
.testimonial-quote {
    text-align: center;
}

/* The auto end margin moves the column to the left edge, not just its text. */
.blk--align-start .testimonial-quote {
    margin-inline-end: auto;
    text-align: left;
}

/* testimonials/mosaique: the avis in columns, each card as tall as its photo and quote. */
.testimonial-mosaic {
    column-gap: var(--ap-space-tight);
}

@media (min-width: 768px) {
    .testimonial-mosaic {
        columns: 2;
    }
}

@media (min-width: 992px) {
    .testimonial-mosaic {
        columns: 3;
    }
}

.testimonial-mosaic__item {
    break-inside: avoid;
    margin-bottom: var(--ap-space-tight);
}

.testimonial-mosaic__item .site-image {
    height: auto;
    aspect-ratio: auto;
}

/* ---- Rich text ---- */
.rich-text > :last-child {
    margin-bottom: 0;
}

.rich-text blockquote {
    margin: 1.5rem 0;
    padding-left: 1.25rem;
    border-left: 2px solid var(--ap-accent);
    font-family: var(--ap-font-display);
}

@media (min-width: 992px) {
    .rich-text__title,
    .tournees__head,
    .pricing-split__head,
    .faq-index__head,
    .steps__head {
        position: sticky;
        top: 5rem;
    }
}

/* A book page: a narrow serif column, a drop cap over three lines, the first line in
   small capitals and a closing mark after the last word. */
.rich-text-lettrine {
    max-width: 40rem;
}

.rich-text-lettrine__ornament {
    width: 0.5rem;
    height: 0.5rem;
    background-color: var(--ap-accent);
    transform: rotate(45deg);
}

.rich-text-lettrine__body {
    font-family: var(--ap-font-display);
    font-size: 1.15rem;
    line-height: 1.75;
}

.rich-text-lettrine__body > p:first-child::first-letter {
    float: left;
    margin: 0.4rem 0.7rem 0 0;
    font-size: 5.6em;
    line-height: 0.78;
    color: var(--ap-accent-text);
}

.rich-text-lettrine__body > p:first-child::first-line {
    font-variant: small-caps;
    letter-spacing: 0.04em;
}

.rich-text-lettrine__body > p:last-child::after {
    content: "";
    display: inline-block;
    width: 0.45em;
    height: 0.45em;
    margin-left: 0.5em;
    background-color: var(--ap-accent);
}

.rich-text-columns__title {
    border-bottom: 1px solid var(--ap-text);
}

@media (min-width: 992px) {
    .rich-text-columns {
        columns: 2;
        column-gap: 4rem;
        column-rule: 1px solid var(--ap-border);
    }

    .rich-text-columns :is(ul, ol, blockquote) {
        break-inside: avoid;
    }
}

/* ---- Stats ---- */
/* A rule between the cells, so a short last label reads as the end of a column
   rather than as a hole on the right of the section. The padding is what keeps
   the figures off it: the first cell keeps its edge, aligned with the heading. */
@media (min-width: 768px) {
    .stat-row__cell + .stat-row__cell {
        border-left: 1px solid var(--ap-border);
        padding-left: 2rem;
    }

    .stat-row__cell:not(:last-child) {
        padding-right: 2rem;
    }
}

/* ---- Features: the numbered variants ---- */
/* The rank is the list's own index in both, which is why no variant of this
   block stores a number the order could contradict. */
/* features/photos: the rank sits beside the title under each photo, small and in the accent. */
.feature-photo__rank {
    flex: none;
    padding-top: 0.3em;
    font-family: var(--ap-font-mono);
    font-size: 0.8125rem;
    color: var(--ap-accent-text);
}

.step-rank {
    font-family: var(--ap-font-display);
    font-size: 2.4rem;
    font-weight: 600;
    line-height: 0.9;
    color: var(--ap-muted);
}

.step-title {
    margin: 0;
    font-family: var(--ap-font-display);
    font-size: 1.1rem;
    font-weight: 600;
}

.step-body {
    margin: 0;
    color: var(--ap-muted);
}

/* features/numbered: the heading holds still beside the steps from lg, and each step rises
   as it arrives (app/reveal.js arms its data-reveal). The rank is drawn in outline and fills
   when the step is pointed at. */
.steps__row {
    display: grid;
    grid-template-columns: 3.5rem minmax(0, 1fr);
    gap: 1rem;
    padding: 1.75rem 0 2rem;
    border-top: 1px dashed color-mix(in srgb, var(--ap-text) 28%, transparent);
}

.steps__row:last-child {
    border-bottom: 1px dashed color-mix(in srgb, var(--ap-text) 28%, transparent);
}

.steps__rank {
    font-family: var(--ap-font-display);
    font-size: clamp(2.4rem, 4vw, 3.25rem);
    font-weight: 900;
    line-height: 0.9;
    letter-spacing: -0.05em;
    color: transparent;
    -webkit-text-stroke: 1.5px var(--ap-accent);
    transition: color 0.35s ease;
}

.steps__row:hover .steps__rank {
    color: var(--ap-accent);
}

.steps__tag {
    padding: 0.2rem 0.5rem;
    border-radius: var(--ap-radius);
    background: var(--ap-text);
    color: var(--ap-bg);
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    font-weight: 500;
    letter-spacing: 0.04em;
}

@media (min-width: 768px) {
    .steps__row {
        grid-template-columns: 5.5rem minmax(0, 1fr);
        gap: 1.5rem;
    }
}


/* The code a tenant types in place of an item's icon or rank, and the rank of the split.
   It sits in flex rows, flex columns and grid cells, so it opts out of stretching in all three. */
.feature-chip {
    display: inline-grid;
    flex: none;
    place-items: center;
    align-self: flex-start;
    justify-self: start;
    min-width: 2.75rem;
    height: 2.75rem;
    padding-inline: 0.6rem;
    border-radius: var(--ap-radius);
    font-family: var(--ap-font-display);
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.02em;
}

/* A faint accent wash over the surface keeps the accent text close to the pair it is
   measured against. Shared by the content tags, the glossary sigles and the feature chip. */
.feature-chip,
.tint-pill {
    background: rgba(var(--ap-accent-rgb), 0.1);
    color: var(--ap-accent-text);
}

.tint-pill {
    display: inline-block;
    padding: 0.3rem 0.8rem;
    border-radius: var(--ap-radius-button);
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1.4;
}

/* Cells joined in one frame: the 1px gap on a border-colored ground draws the shared rules,
   as in .price-bands, and flex-grow fills every row whatever the count. */
.feature-cells {
    display: flex;
    flex-wrap: wrap;
    gap: 1px;
    overflow: hidden;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    background: var(--ap-border);
}

.feature-cells__cell {
    flex: 1 1 100%;
    padding: 1.75rem 1.5rem;
    background: var(--ap-panel);
}

/* Everything on the accent fill takes the contrast ink. The chip keeps the page's own
   background, the one surface its accent text is measured against. */
.feature-cells__cell:first-child {
    background: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

.feature-cells__cell:first-child :is(.step-rank, .step-body, .meta-line) {
    color: inherit;
}

.feature-cells__cell:first-child .feature-chip {
    background: var(--ap-bg);
}

/* Two to a row from md, an odd last cell taking the row; up to four on one row from lg,
   and five or six break three by three. */
@media (min-width: 768px) {
    .feature-cells__cell {
        flex-basis: 40%;
    }
}

@media (min-width: 992px) {
    .feature-cells__cell {
        flex-basis: 20%;
    }

    .feature-cells--5 .feature-cells__cell,
    .feature-cells--6 .feature-cells__cell {
        flex-basis: 30%;
    }
}

/* ---- Divider ---- */
/* A block that says nothing, on purpose. The rule takes the theme's border tone
   and never the accent, which is kept for the things that do mean something, and
   the size is the air the rule sits in rather than the block's own margins. */
.divider-rule--small {
    padding: 2.5rem 0;
}

.divider-rule--medium {
    padding: 4rem 0;
}

.divider-rule--large {
    padding: 6rem 0;
}

/* The accent variant stacks its mark over its word, so the two centre on each
   other instead of sitting on one baseline. */
.divider-rule--centered {
    gap: 0.9rem;
}

.divider-rule__mark,
.price-list__rule {
    width: 3.5rem;
}

.divider-rule--editing {
    outline: 1px dashed var(--ap-border);
}

/* ---- Bandeau défilant ---- */
/* The track is the band's own width and each group is never narrower than it, so a
   list that does not fill the band spreads its words instead of running out mid-cycle
   and leaving it empty until the loop restarts. */
.marquee__group {
    min-width: 100%;
    animation: marquee-slide 34s linear infinite;
}

.marquee__word {
    font-family: var(--ap-font-display);
}

.marquee__dot {
    background: var(--ap-accent);
}

/* Exactly one group, so the second one lands where the first started. */
@keyframes marquee-slide {
    from { transform: translate3d(0, 0, 0); }
    to { transform: translate3d(-100%, 0, 0); }
}

.marquee--loop {
    padding-block: clamp(1.125rem, 2.6vw, 1.75rem);
    border-block: 1px solid var(--ap-border);
    background: var(--ap-panel);
}

.marquee--loop .marquee__word {
    gap: clamp(1.5rem, 4vw, 3.25rem);
    padding-right: clamp(1.5rem, 4vw, 3.25rem);
    font-weight: 800;
    font-size: clamp(1.3rem, 3vw, 2.4rem);
    letter-spacing: -0.02em;
    text-transform: var(--ap-title-transform);
    color: var(--ap-muted);
}

.marquee--loop .marquee__dot {
    width: 0.5rem;
    height: 0.5rem;
}

/* Pulled up under the opener and tipped, so the band cuts the page rather than stacking
   under it. Inset by the bleed the tilt costs, half a band height times the sine either
   side: any wider and a phone scrolls sideways, and nothing here clips it, since the
   band's own overflow governs its contents and not its rotated box. The triangles of
   page its corners leave are the tilt rather than a gap to close. */
.marquee--defile {
    position: relative;
    z-index: 1;
    margin-block: -1.25rem 0;
    margin-inline: 2px;
    padding-block: 14px;
    transform: rotate(-1.5deg);
    background: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

.marquee--defile .marquee__group {
    animation-duration: 40s;
}

.marquee--defile .marquee__word {
    gap: 1.5rem;
    padding-right: 1.5rem;
    font-weight: 600;
    font-size: clamp(1rem, 1.6vw, 1.25rem);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.marquee--defile .marquee__dot {
    width: 0.35rem;
    height: 0.35rem;
    background: currentcolor;
    opacity: 0.7;
}

/* Every accent token is measured against --ap-bg and carries no guarantee on an ink
   fill (ADR-0005), so the words take the inverted body pair and only the dot, which
   is decoration inside an aria-hidden track, keeps the accent. */
.marquee--bandeau {
    padding-block: 0.7rem;
    background: var(--ap-text);
    color: var(--ap-bg);
}

.marquee--bandeau .marquee__group {
    animation-duration: 46s;
}

.marquee--bandeau .marquee__word {
    gap: 1.15rem;
    padding-right: 1.15rem;
    font-weight: 400;
    font-size: clamp(0.95rem, 1.4vw, 1.15rem);
}

.marquee--bandeau .marquee__dot {
    width: 0.32rem;
    height: 0.32rem;
}

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

/* ---- Video ---- */
/* Click-to-load, the same contract as the map: the poster and the button are all
   a page view loads. The 16:9 lives on the frame rather than on a shared crop,
   because what fills it is an iframe as often as an image. */
.video-stacked__body {
    max-width: 60ch;
}

.video-canvas {
    position: relative;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    background: var(--ap-panel);
}

.video-canvas .site-image,
.video-canvas .image-frame,
.video-canvas iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    object-fit: cover;
}

.video-canvas__play {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    color: var(--ap-text);
}

/* White on a scrim rather than a token: an accent has no contrast guarantee over
   a photograph, and this one is over whichever photograph the tenant picked. */
.video-canvas--poster .video-canvas__play {
    color: var(--bs-white);
    background: linear-gradient(180deg, rgba(10, 10, 12, 0.2), rgba(10, 10, 12, 0.55));
}

.video-canvas__button {
    display: grid;
    place-items: center;
    width: 4.5rem;
    height: 4.5rem;
    border: 1px solid currentcolor;
    border-radius: 50%;
}

.video-canvas__play:hover .video-canvas__button {
    background: rgba(var(--ap-accent-rgb), 0.16);
}

.video-canvas__cue,
.map-canvas__cue {
    font-family: var(--ap-font-mono);
    font-size: 0.75rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* The bleed variant is one frame per page, so it takes a cinema crop and carries
   its title inside. The text takes no pointer events: the whole frame is the
   play control, and a title on top of it must not swallow the click. */
.video-canvas--bleed {
    aspect-ratio: 21 / 9;
}

.video-bleed__text {
    position: absolute;
    right: 1.5rem;
    bottom: 1.5rem;
    left: 1.5rem;
    color: var(--bs-white);
    pointer-events: none;
}

@media (max-width: 767.98px) {
    .video-canvas--bleed {
        aspect-ratio: 4 / 3;
    }
}

/* ---- Review band ---- */
/* The rating on one line between two rules, in the register of the badge band: the
   figure, its source, then the way to add one. It wraps on a phone, the link last. */
.review-band {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1.75rem;
    padding: 1.5rem 0;
    border-top: 1px solid var(--ap-border);
    border-bottom: 1px solid var(--ap-border);
}

.review-band__stars {
    display: inline-flex;
    gap: 0.2rem;
    color: var(--ap-accent);
}

.review-band__figure {
    font-family: var(--ap-font-display);
    font-size: 1.5rem;
    line-height: 1;
    color: var(--ap-text);
}

.review-band__source {
    color: var(--ap-muted);
}

.review-band__link {
    margin-inline-start: auto;
    color: var(--ap-accent-text);
    font-weight: 600;
}

.review-band__link:hover {
    color: var(--ap-accent-text-hover);
}

.review-figure__figure {
    font-family: var(--ap-font-display);
    font-size: clamp(5rem, 14vw, 11rem);
    line-height: 0.8;
    letter-spacing: -0.04em;
}

@media (min-width: 768px) {
    .review-figure__figure + .review-figure__body {
        padding-left: 3rem;
        border-left: 1px solid color-mix(in srgb, var(--ap-bg) 25%, transparent);
    }
}

.review-figure__link,
.review-figure__link:hover {
    color: inherit;
    border-bottom: 1px solid currentcolor;
}

.review-pill {
    background-color: var(--ap-panel);
    color: var(--ap-text);
    transition: border-color 0.15s ease;
}

.review-pill:hover {
    border-color: var(--ap-text);
    color: var(--ap-text);
}

.review-pill__icon {
    width: 2.5rem;
    height: 2.5rem;
    background-color: color-mix(in srgb, var(--ap-text) 8%, var(--ap-bg));
    color: var(--ap-muted);
}

.review-pill__figure,
.review-gauge__figure {
    font-family: var(--ap-font-display);
    line-height: 1;
}

.review-pill__figure {
    font-size: 1.6rem;
}

.review-pill__stars {
    color: var(--ap-accent);
}

.review-pill__cta {
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.review-gauge__figure {
    font-size: 3.5rem;
}

.review-gauge__scale {
    font-size: 1.3rem;
    color: var(--ap-faint);
}

.review-gauge {
    grid-template-columns: repeat(5, minmax(0, 1fr));
}

.review-gauge .progress {
    --bs-progress-height: 0.875rem;
    --bs-progress-bg: color-mix(in srgb, var(--ap-text) 10%, var(--ap-bg));
    --bs-progress-bar-bg: var(--ap-accent);
}

/* ---- Badge band ---- */
/* A band of guarantees, not a row of fields: no box around each item, one rule
   over and under the band, and a hairline between the cells, so the items read
   as one line rather than as a row of labelled inputs. */
.badge-strip {
    list-style: none;
    display: grid;
    gap: 1.25rem;
    margin: 0;
    padding: 1.75rem 0;
    border-top: 1px solid var(--ap-border);
    border-bottom: 1px solid var(--ap-border);
}

@media (min-width: 768px) {
    .badge-strip {
        grid-auto-flow: column;
        grid-auto-columns: 1fr;
        gap: 0;
    }

    .badge-strip__item {
        padding-inline: 2rem;
    }

    .badge-strip__item:first-child {
        padding-inline-start: 0;
    }

    .badge-strip__item:last-child {
        padding-inline-end: 0;
    }

    .badge-strip__item + .badge-strip__item {
        border-left: 1px solid var(--ap-border);
    }
}

.badge-item__icon {
    flex: none;
    font-size: 1.05rem;
    color: var(--ap-accent);
}

.badge-item__title {
    font-family: var(--ap-font-display);
    font-size: 1rem;
    font-weight: 700;
}

.badge-item__detail {
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--ap-muted);
}

@media (min-width: 992px) {
    .badge-ink__item + .badge-ink__item {
        border-left: 1px solid color-mix(in srgb, var(--ap-bg) 22%, transparent);
    }
}

.badge-ink .label,
.badge-ink .badge-item__detail {
    color: inherit;
    opacity: 0.8;
}

/* The accent tile behind an icon or a rank, shared by features/grid and badges/cartes. */
.icon-tile {
    width: 2.75rem;
    height: 2.75rem;
    font-family: var(--ap-font-display);
    font-size: 1.1rem;
    font-weight: 800;
    background-color: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

/* ---- Qualification cards ---- */
/* Wins over .site-image and .image-frame, which both set width: 100%. */
.qualification-card__logo {
    width: 3.5rem;
    aspect-ratio: 1;
    object-fit: contain;
}

.qualification-card__number {
    font-family: var(--ap-font-mono);
    font-size: 0.8rem;
    color: var(--ap-text);
}

.qualification-card__link {
    color: var(--ap-accent-text);
}

.qualification-card__link:hover {
    color: var(--ap-accent-text-hover);
}

.qualification-columns__item + .qualification-columns__item {
    border-top: 1px solid var(--ap-border);
}

.qualification-list__logo {
    width: 3rem;
}

/* A certificate's double rule: the outline is drawn inside the border. */
.qualification-frame {
    border: 1px solid var(--ap-text);
    outline: 1px solid var(--ap-text);
    outline-offset: -0.4rem;
}

@media (min-width: 992px) {
    .qualification-columns__item {
        padding-inline: 1.5rem;
    }

    .qualification-columns__item:first-child {
        padding-inline-start: 0;
    }

    .qualification-columns__item + .qualification-columns__item,
    .qualification-frame__item + .qualification-frame__item {
        border-top: 0;
        border-left: 1px solid var(--ap-border);
    }
}

/* ---- Hours, map and contact ---- */
/* The hours status is a state, so it keeps the pill. The contact note is a
   sentence about the channels under it, and a bordered pill above a list of
   links reads as the first button in the section. */
.contact-note {
    display: block;
    margin-bottom: 1.5rem;
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ap-muted);
}

.hours-status {
    display: inline-block;
    margin-bottom: 1.5rem;
    padding: 0.45rem 0.9rem;
    border: 1px solid var(--ap-accent);
    border-radius: var(--ap-radius);
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ap-accent-text);
}

.hours-row {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    padding: 0.8rem 0;
    border-bottom: 1px solid var(--ap-border);
}

.hours-row__day {
    font-family: var(--ap-font-mono);
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

.hours-row--closed .hours-row__day,
.hours-row--closed .hours-row__value {
    color: var(--ap-faint);
}

/* The dotted run that ties a label to its value. Baseline alignment keeps it on
   the first line, so a value carrying a unit under it does not drag it down.
   Margins stay at zero: the synthesized baseline is then the dotted edge. */
.dot-leader {
    flex: 1;
    align-self: baseline;
    border-bottom: 1px dotted var(--ap-border);
}

/* Shared click-to-load map surface: hours/panel and every map variant draw the
   same frame, so the RGPD-sensitive placeholder has one implementation. */
.map-canvas {
    display: block;
    height: 16rem;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    background-image:
        repeating-linear-gradient(0deg, var(--ap-border) 0, var(--ap-border) 1px, transparent 1px, transparent 2.75rem),
        repeating-linear-gradient(90deg, var(--ap-border) 0, var(--ap-border) 1px, transparent 1px, transparent 2.75rem);
}

.map-canvas--flat {
    height: 7rem;
}

.map-canvas--tall {
    height: 20rem;
}

.map-canvas--bleed {
    height: 20rem;
}

.map-canvas--embed.is-loaded {
    background-image: none;
}

.map-canvas__load {
    background: transparent;
    color: var(--ap-text);
}

.map-canvas__load:hover {
    background: rgba(var(--ap-accent-rgb), 0.08);
}

.map-canvas__notice {
    max-width: 24rem;
    color: var(--ap-muted);
    font-size: 0.75rem;
}

.map-access {
    display: grid;
    gap: 1.25rem;
}

.map-access__item dt {
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ap-faint);
}

.map-access__item dd {
    margin: 0.35rem 0 0;
    color: var(--ap-muted);
}

.map-bleed__panel {
    margin-top: -2rem;
    margin-right: 1rem;
    margin-left: 1rem;
}

@media (min-width: 768px) {
    .map-access {
        grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    }
}

@media (min-width: 992px) {
    .map-canvas--tall {
        height: 100%;
        min-height: 24rem;
    }

    .map-canvas--bleed {
        height: 32rem;
    }

    .map-bleed__panel {
        position: absolute;
        bottom: 2rem;
        left: 2rem;
        max-width: 26rem;
        margin: 0;
    }
}

.map-fiche {
    --bs-border-color: color-mix(in srgb, var(--ap-bg) 22%, transparent);
}

.map-fiche .label,
.map-fiche__muted {
    color: inherit;
    opacity: 0.75;
}

.map-fiche__address {
    max-width: 16ch;
    font-family: var(--ap-font-display);
    font-size: clamp(2rem, 5vw, 3.5rem);
    line-height: 1.1;
    text-wrap: balance;
}

.hours-contact {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    margin-top: 1rem;
    font-family: var(--ap-font-mono);
    font-size: 0.75rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.hours-card__aside a,
.hours-card__aside a:hover {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 0.2em;
}

.hours-card__note {
    font-family: var(--ap-font-display);
    font-size: clamp(1.25rem, 2.5vw, 1.75rem);
    line-height: 1.25;
}

/* A door plate on a tinted band: a double rule drawn by the frame and its inner panel. */
.hours-plaque {
    border-radius: var(--ap-radius);
    background-color: rgba(var(--ap-accent-rgb), 0.08);
}

.hours-plaque__frame {
    max-width: 46rem;
    border: 1px solid var(--ap-text);
    border-radius: var(--ap-radius);
    background-color: var(--ap-bg);
}

.hours-plaque__inner {
    border: 1px solid var(--ap-text);
    border-radius: var(--ap-radius);
}

.hours-ink {
    --bs-border-color: color-mix(in srgb, var(--ap-bg) 22%, transparent);
}

.hours-ink .label,
.hours-ink .eyebrow,
.hours-ink .hours-status,
.hours-ink .hours-row--closed .hours-row__day,
.hours-ink .hours-row--closed .hours-row__value,
.hours-ink__muted {
    border-color: currentColor;
    color: inherit;
    opacity: 0.75;
}

.hours-ink .hours-row,
.hours-ink .dot-leader {
    border-color: var(--bs-border-color);
}

.hours-ink a,
.hours-ink a:hover {
    color: inherit;
}

.hours-ink__lead {
    font-family: var(--ap-font-display);
    font-size: 1.5rem;
    line-height: 1.25;
}

.contact-channel {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    height: 100%;
    padding: 1.25rem;
    background: var(--ap-panel);
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    color: var(--ap-text);
    transition: border-color 0.2s ease, transform 0.2s ease;
}

.contact-channel:hover,
.contact-channel:focus-visible {
    border-color: var(--ap-accent);
    color: var(--ap-text);
    transform: translateY(-2px);
}

.contact-channel__kind {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ap-faint);
}

.contact-channel__label {
    font-family: var(--ap-font-display);
    font-size: 1.15rem;
    line-height: 1.3;
}

.contact-channel__detail {
    font-size: 0.9rem;
    color: var(--ap-muted);
}

/* The bar leaves the flow only on phones, where it is the block's whole point. */
.contact-bar {
    list-style: none;
    display: flex;
    gap: 0.75rem;
    margin: 0;
    padding: 0;
}

/* min-width:0 beats the flex-item default that would stop the label ellipsizing. */
.contact-bar__item {
    flex: 1 1 0;
    min-width: 0;
}

.contact-bar__link {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.85rem 1rem;
    background: var(--ap-accent);
    border: 1px solid var(--ap-accent);
    border-radius: var(--ap-radius);
    color: var(--ap-accent-contrast);
    font-family: var(--ap-font-mono);
    font-size: 0.75rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    min-width: 0;
}

.contact-bar__link:hover,
.contact-bar__link:focus-visible {
    background: var(--ap-accent-strong);
    border-color: var(--ap-accent-strong);
    color: var(--ap-accent-contrast);
}

.contact-bar__item:nth-child(n + 2) .contact-bar__link {
    background: var(--ap-panel);
    border-color: var(--ap-border);
    color: var(--ap-text);
}

.contact-bar__item:nth-child(n + 2) .contact-bar__link:hover,
.contact-bar__item:nth-child(n + 2) .contact-bar__link:focus-visible {
    border-color: var(--ap-accent);
    color: var(--ap-text);
}

@media (max-width: 767.98px) {
    .contact-bar {
        position: fixed;
        z-index: 1030;
        right: 0;
        bottom: 0;
        left: 0;
        gap: 0.5rem;
        padding: 0.6rem 0.75rem calc(0.6rem + env(safe-area-inset-bottom));
        background: var(--ap-bg);
        border-top: 1px solid var(--ap-border);
    }

    .contact-bar__link {
        gap: 0.4rem;
        padding: 0.85rem 0.5rem;
        font-size: 0.7rem;
        letter-spacing: 0.06em;
    }
}

/* Pinned at every width, at a fixed height the body reserves at its end, so the last
   content of the page always scrolls clear of the bar. */
body:has(.contact-dock) {
    --contact-dock-height: 4rem;

    padding-bottom: calc(var(--contact-dock-height) + env(safe-area-inset-bottom));
}

.contact-dock {
    height: calc(var(--contact-dock-height) + env(safe-area-inset-bottom));
    padding-bottom: env(safe-area-inset-bottom);
    background: var(--ap-text);
    color: var(--ap-bg);
}

.contact-dock__note {
    margin-left: 0.75rem;
    font-weight: 400;
    color: color-mix(in srgb, var(--ap-bg) 72%, var(--ap-text));
}

.contact-dock__channels {
    min-width: 0;
}

.contact-dock__item {
    flex: 1 1 0;
    min-width: 0;
}

@media (min-width: 768px) {
    .contact-dock__item {
        flex: 0 1 auto;
    }
}

.contact-dock__link {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: 2.75rem;
    padding: 0 1rem;
    border: 1px solid color-mix(in srgb, var(--ap-bg) 35%, var(--ap-text));
    border-radius: var(--ap-radius-button);
    color: var(--ap-bg);
    font-size: 0.875rem;
    font-weight: 600;
    white-space: nowrap;
}

.contact-dock__link:hover,
.contact-dock__link:focus-visible {
    border-color: var(--ap-bg);
    color: var(--ap-bg);
}

.contact-dock__item:first-child .contact-dock__link {
    border-color: var(--ap-accent);
    background: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

.contact-dock__item:first-child .contact-dock__link:hover,
.contact-dock__item:first-child .contact-dock__link:focus-visible {
    border-color: var(--ap-accent-strong);
    background: var(--ap-accent-strong);
    color: var(--ap-accent-contrast);
}

/* A template's Aperçu pins its own banner there, which the dock would cover. */
.preview-banner ~ main .contact-dock {
    position: static;
}

/* ---- Opening status ---- */
.opening-now__eyebrow {
    font-family: var(--ap-font-mono);
    font-size: 0.65rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ap-muted);
}

/* The visitor came for this line, so it outweighs the sentence naming the next
   change rather than sitting under it as a caption. */
.opening-state {
    gap: 0.6rem;
    padding: 0.6rem 1.1rem;
    border: 1px solid var(--ap-border);
    font-family: var(--ap-font-mono);
    font-size: 0.85rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ap-text);
}

.opening-state__dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: currentColor;
}

/* An accent fill is only guaranteed to read against --ap-accent-contrast (ADR-0005). */
.opening-state--open {
    border-color: var(--ap-accent);
    background: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

.opening-state--closing-soon {
    border-color: var(--ap-accent);
    color: var(--ap-accent-text);
}

.opening-now__headline {
    margin: 0;
    font-family: var(--ap-font-display);
    font-size: clamp(1.35rem, 4vw, 1.75rem);
    line-height: 1.15;
}

.opening-closure {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin: 0;
    padding: 0.9rem 1rem;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    background: var(--ap-panel);
    font-size: 0.9rem;
}

.opening-closure__eyebrow {
    font-family: var(--ap-font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ap-muted);
}

.opening-week__row {
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--ap-border);
}

.opening-week__row:last-child {
    border-bottom: 0;
}

.opening-week__hours {
    font-family: var(--ap-font-mono);
    font-size: 0.75rem;
    letter-spacing: 0.04em;
    text-align: right;
}

.opening-week__row--closed .opening-week__day,
.opening-week__row--closed .opening-week__hours {
    color: var(--ap-faint);
}

/* The swapped body pair is the only combination an inverted surface promises. */
.opening-week__row--today {
    margin: 0 -0.9rem;
    padding: 0.75rem 0.9rem;
    border-bottom-color: transparent;
    border-radius: var(--ap-radius);
    background: var(--ap-text);
    color: var(--ap-bg);
    font-weight: 600;
}

.opening-week__row--today.opening-week__row--closed .opening-week__day,
.opening-week__row--today.opening-week__row--closed .opening-week__hours {
    color: color-mix(in srgb, var(--ap-bg) 78%, var(--ap-text));
}

.opening-week__marker {
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    background: currentColor;
}

/* The week folded under its state line: the summary is the whole control. */
.opening-pill {
    max-width: 34rem;
}

.opening-pill__summary::-webkit-details-marker {
    display: none;
}

.opening-pill__summary .opening-state {
    padding: 0.35rem 0.8rem;
    font-size: 0.75rem;
}

.opening-pill__chevron {
    transition: transform 0.15s ease;
}

details[open] .opening-pill__chevron {
    transform: rotate(180deg);
}

.opening-pill .opening-week__row {
    padding-block: 0.45rem;
}

/* ---- Tour stops ---- */
.tournees__day {
    font-family: var(--ap-font-display);
    font-size: 1.4rem;
    line-height: 1.1;
}

.tournees__detail {
    font-size: 0.85rem;
    color: var(--ap-muted);
}

/* One line through the week: down the left on a phone, across from lg with the stops
   as equal columns. Each stop draws its own length of line, so a lone one still has one. */
.tour-line__stop {
    display: grid;
    grid-template: "track when" auto "track where" auto / 1.25rem minmax(0, 1fr);
    align-content: start;
    column-gap: 1rem;
    row-gap: 0.4rem;
    padding-bottom: 1.75rem;
}

.tour-line__when {
    grid-area: when;
}

.tour-line__where {
    grid-area: where;
}

.tour-line__track {
    grid-area: track;
    position: relative;
    display: flex;
    justify-content: center;
}

.tour-line__track::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: -1.75rem;
    left: calc(50% - 1px);
    width: 2px;
    background-color: var(--ap-text);
}

.tour-line__stop:last-child .tour-line__track::before {
    display: none;
}

.tour-line__dot {
    position: relative;
    width: 1rem;
    height: 1rem;
    margin-top: 0.4rem;
    border: 2px solid var(--ap-text);
    background-color: var(--ap-bg);
}

.tour-line__stop--on .tournees__day,
.tour-line__stop--on .meta-line {
    color: var(--ap-accent-text);
}

.tour-line__stop--on .tour-line__dot {
    border-color: var(--ap-accent);
    background-color: var(--ap-accent);
    box-shadow: 0 0 0 4px var(--ap-bg), 0 0 0 6px var(--ap-accent);
}

@media (min-width: 992px) {
    .tour-line {
        display: grid;
        grid-auto-columns: minmax(0, 1fr);
        grid-auto-flow: column;
    }

    .tour-line__stop {
        grid-template: "when" auto "track" 1.75rem "where" auto / minmax(0, 1fr);
        row-gap: 0.9rem;
        padding: 0 1rem 0 0;
    }

    .tour-line__track::before,
    .tour-line__stop:last-child .tour-line__track::before {
        display: block;
        top: calc(50% - 1px);
        bottom: auto;
        left: 0;
        right: -1rem;
        width: auto;
        height: 2px;
    }

    .tour-line__stop:last-child .tour-line__track::before {
        right: 0;
    }
}

.tour-board .label,
.tour-board__intro {
    color: inherit;
    opacity: 0.75;
}

.tour-board__table {
    --bs-table-bg: transparent;
    --bs-table-color: inherit;
    --bs-border-color: color-mix(in srgb, var(--ap-bg) 18%, transparent);
}

.tour-board__table thead th {
    font-size: 0.6rem;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    opacity: 0.75;
}

.tour-board__table .tour-board__on {
    --bs-table-bg: var(--ap-bg);
    --bs-table-color: var(--ap-text);
}

/* An accent fill is only guaranteed to read against --ap-accent-contrast (ADR-0005). */
.tour-next {
    background-color: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

.tour-next .label,
.tour-next .meta-line {
    color: inherit;
}

.tour-next__day {
    font-family: var(--ap-font-display);
    font-size: clamp(2.4rem, 5vw, 3.4rem);
    line-height: 0.95;
}

.tour-next__where {
    border-top: 1px solid color-mix(in srgb, var(--ap-accent-contrast) 45%, transparent);
}

.tour-rest__day {
    flex: none;
    width: 6rem;
}

/* The week as one row of equal cells from md, four then three on a phone. */
.opening-strip__now {
    padding: 1.25rem 0;
    border-top: 2px solid var(--ap-text);
}

.opening-strip__state {
    font-family: var(--ap-font-display);
    font-size: clamp(1.5rem, 4vw, 2.25rem);
    line-height: 1.15;
}

.opening-strip__dot {
    display: inline-block;
    width: 0.6rem;
    height: 0.6rem;
    margin-right: 0.6rem;
    border-radius: 50%;
    vertical-align: 0.2em;
    background: currentColor;
    color: var(--ap-faint);
}

.opening-strip__state--open .opening-strip__dot {
    color: var(--ap-accent);
}

.opening-strip__state--closing-soon .opening-strip__dot {
    color: var(--ap-accent-text);
}

.opening-strip__headline {
    color: var(--ap-muted);
}

.opening-strip__closure {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem 0.75rem;
    padding-bottom: 1.25rem;
    font-size: 0.9rem;
}

.opening-strip__week {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    border-top: 1px solid var(--ap-border);
    border-bottom: 1px solid var(--ap-border);
}

@media (min-width: 768px) {
    .opening-strip__week {
        grid-template-columns: none;
        grid-auto-columns: minmax(0, 1fr);
        grid-auto-flow: column;
    }
}

.opening-strip__weekday {
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ap-muted);
}

.opening-strip__hours {
    font-size: 0.8rem;
    overflow-wrap: anywhere;
}

@media (min-width: 992px) {
    .opening-strip__hours {
        font-size: 0.9rem;
    }
}

.opening-strip__day--closed .opening-strip__weekday,
.opening-strip__day--closed .opening-strip__hours {
    color: var(--ap-faint);
}

.opening-strip__day--today {
    background: color-mix(in srgb, var(--ap-accent) 14%, var(--ap-bg));
}

.opening-strip__day--today .opening-strip__weekday {
    font-weight: 600;
    color: var(--ap-accent-text);
}

/* ---- Services and price cards ---- */
.price-list__head {
    align-items: baseline;
    justify-content: space-between;
    padding-bottom: 0.75rem;
    border-bottom: 1px solid var(--ap-border);
    margin-bottom: 0.5rem;
}

.price-list__head--ruled {
    border-bottom: 2px solid var(--ap-text);
}

.price-list__line {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
}

.price-list__badge {
    padding: 0.15rem 0.5rem;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    font-family: var(--ap-font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ap-accent-text);
}

.price-list__price {
    font-family: var(--ap-font-mono);
    color: var(--ap-accent-text);
}

.price-list__price--display {
    font-family: var(--ap-font-display);
    font-size: 1.4rem;
    line-height: 1.1;
}

.price-list__description {
    max-width: 48ch;
    margin-top: 0.25rem;
    font-size: 0.925rem;
}

.price-list--carte .price-list__name,
.price-list--carte .price-list__price {
    font-family: var(--ap-font-display);
}

.price-list__facts {
    margin: 1.5rem 0;
    padding-top: 1.25rem;
    border-top: 1px solid var(--ap-border);
}

.price-card__flag {
    position: absolute;
    top: 0.6rem;
    left: 0.6rem;
    padding: 0.2rem 0.55rem;
    background: var(--ap-accent);
    color: var(--ap-accent-contrast);
    font-family: var(--ap-font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.price-card__price {
    font-family: var(--ap-font-display);
    font-size: 1.6rem;
    font-weight: 800;
    letter-spacing: -0.02em;
}

/* ---- Chalkboard ---- */
.ardoise__board {
    border: 0.625rem solid color-mix(in srgb, var(--ap-bg) 14%, var(--ap-text));
}

.ardoise__note {
    font-size: 0.9rem;
    color: color-mix(in srgb, var(--ap-bg) 78%, var(--ap-text));
}

.ardoise__line {
    padding-bottom: 1rem;
    border-bottom: 1px dashed color-mix(in srgb, var(--ap-bg) 30%, var(--ap-text));
}

.ardoise__course {
    margin-bottom: 0.25rem;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.18em;
    text-transform: uppercase;
}

.ardoise__dish,
.ardoise__formulas {
    font-family: var(--ap-font-display);
    line-height: 1.35;
}

.ardoise__dish {
    font-size: 1.2rem;
}

.ardoise__formulas {
    font-size: 1.1rem;
}

@media (min-width: 576px) {
    .ardoise__course {
        flex: 0 0 7rem;
        margin-bottom: 0;
    }
}

.ardoise-note {
    font-family: var(--ap-font-display);
    font-style: italic;
}

.ardoise-banner {
    aspect-ratio: 16 / 9;
}

.ardoise-columns__item + .ardoise-columns__item {
    border-top: 1px solid var(--ap-border);
}

.ardoise-pill {
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

/* White on a fixed scrim over the photograph (ADR-0005); without one the menu sits on
   the swapped body pair instead. */
.ardoise-poster {
    min-height: 32rem;
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.ardoise-poster--photo {
    color: var(--bs-white);
}

.ardoise-poster--photo::after {
    content: "";
    position: absolute;
    inset: 0;
    background: rgb(10 10 12 / 55%);
}

.ardoise-poster__menu {
    max-width: 36rem;
}

.ardoise-poster .label,
.ardoise-poster .eyebrow {
    color: inherit;
}

.ardoise-poster__formulas {
    border-top: 1px solid color-mix(in srgb, currentcolor 50%, transparent);
}

@media (min-width: 576px) {
    .ardoise-liste__course {
        flex: 0 0 7rem;
    }
}

@media (min-width: 768px) {
    .ardoise-banner {
        aspect-ratio: 32 / 9;
    }

    /* Every column draws its left rule and the list sits that far out, so the frame
       clips the rule opening each row whatever the column count. */
    .ardoise-columns__list {
        margin-left: calc(-1.75rem - 1px);
    }

    .ardoise-columns__item,
    .ardoise-columns__item + .ardoise-columns__item {
        padding-inline: 1.75rem;
        border-top: 0;
        border-left: 1px solid var(--ap-border);
    }
}

/* Beside the board the photo takes the board's height instead of imposing its own. */
@media (min-width: 992px) {
    .ardoise__photo {
        position: absolute;
        inset: 0;
        height: 100%;
        aspect-ratio: auto;
    }
}

/* ---- Team ---- */
.team-member__bio {
    max-width: 28ch;
    margin-inline: auto;
}

/* The name sits under a rectangular crop rather than beside a round one, so it
   reads as a caption for the picture above it. */
.team-card__name {
    font-family: var(--ap-font-display);
    font-size: 1.05rem;
    font-weight: 600;
}

/* One line per person: the portrait is a thumbnail beside the text rather than
   the head of a card, so a page « à propos » holds a whole team without a grid. */
.team-list__row {
    display: grid;
    grid-template-columns: 5rem 1fr;
    gap: 1.5rem;
    padding: 1.6rem 0;
    border-top: 1px solid var(--ap-border);
}

.team-list__row:last-child {
    border-bottom: 1px solid var(--ap-border);
}

.team-inline__portrait {
    flex: none;
    width: 6rem;
}

@media (min-width: 768px) {
    .team-list__row {
        grid-template-columns: 7rem 1fr;
        gap: 2rem;
    }
}

.team-member__portrait {
    width: min(11rem, 60%);
    margin: 0 auto 1.25rem;
}

.frame-round,
.team-member__portrait .site-image {
    aspect-ratio: 1 / 1;
    min-height: 0;
    border-radius: 50%;
}

/* ---- Showcase ---- */
/* Stacked réalisations read as one list, so each carries its own bottom rule. */
.showcase-item {
    padding-bottom: 2.5rem;
    border-bottom: 1px solid var(--ap-border);
}

/* The crop follows the column the frame lands in, so the plank keeps its weight
   whatever the count: the span is what changes, in the template. */
.showcase-frames .image-frame,
.showcase-frames .site-image {
    aspect-ratio: 4 / 3;
    height: auto;
    min-height: 0;
    object-fit: cover;
}

/* A third of the plank is a narrow column, so the triptych stands its frames up:
   square on its own line, and at lg, where the story sits beside them, as tall as
   the story itself. The floor and the cap are in cqw so they follow the frame's
   own width: never flatter than square, never more upright than 4:5. */
@media (min-width: 768px) {
    .showcase-frames--alone {
        justify-content: flex-end;
    }

    .showcase-frames--triptych .image-frame,
    .showcase-frames--triptych .site-image {
        aspect-ratio: 1 / 1;
    }
}

@media (min-width: 992px) {
    .blk--reversed .showcase-frames--alone {
        justify-content: flex-start;
    }

    .showcase-frames--triptych {
        height: 100%;
    }

    .showcase-frames--triptych > * {
        container-type: inline-size;
    }

    .showcase-frames--triptych .image-frame,
    .showcase-frames--triptych .site-image {
        aspect-ratio: auto;
        height: 100%;
        min-height: 100cqw;
        max-height: 125cqw;
    }
}

.showcase-cover__photo {
    aspect-ratio: 4 / 5;
}

/* White on a fixed scrim: a scrim over a photograph is theme-invariant (ADR-0005). */
.cover-caption {
    padding-top: 6rem;
    background: linear-gradient(0deg, rgb(10 10 12 / 72%), rgb(10 10 12 / 0%));
    color: var(--bs-white);
}

.cover-caption .eyebrow {
    color: inherit;
}

.showcase-cover__title {
    max-width: 16ch;
}

.showcase-cover__link {
    --bs-btn-color: var(--bs-white);
    --bs-btn-bg: rgb(255 255 255 / 12%);
    --bs-btn-border-color: rgb(255 255 255 / 70%);
    --bs-btn-hover-color: var(--bs-white);
    --bs-btn-hover-bg: rgb(255 255 255 / 22%);
    --bs-btn-hover-border-color: var(--bs-white);

    border-radius: var(--ap-radius-button);
}

/* The first photograph leads: full width on a phone, two rows tall beside the text from md. */
.showcase-mosaic {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem;
}

.showcase-mosaic__photo {
    min-height: 0;
    aspect-ratio: 1 / 1;
}

.showcase-mosaic > :nth-child(-n + 2) {
    grid-column: 1 / -1;
}

.showcase-mosaic > .showcase-mosaic__photo:first-child {
    aspect-ratio: 4 / 3;
}

.showcase-mosaic__text {
    background-color: color-mix(in srgb, var(--ap-text) 5%, var(--ap-bg));
}

.showcase-mosaic__link,
.showcase-mosaic__link:hover {
    color: var(--ap-text);
    border-bottom: 1px solid currentcolor;
}

@media (min-width: 768px) {
    .showcase-mosaic {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .showcase-mosaic--1,
    .showcase-mosaic--2 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .showcase-mosaic > :nth-child(-n + 2) {
        grid-column: auto;
    }

    .showcase-mosaic > .showcase-mosaic__photo:first-child {
        grid-row: span 2;
        height: 100%;
        aspect-ratio: auto;
    }

    .showcase-mosaic--1 > .showcase-mosaic__photo:first-child {
        grid-row: auto;
        aspect-ratio: 4 / 3;
    }

    .showcase-mosaic--3 > :last-child {
        grid-column: span 2;
        aspect-ratio: 2 / 1;
    }

    .showcase-cover__photo {
        aspect-ratio: 3 / 2;
    }
}

@media (min-width: 992px) {
    .showcase-cover__photo {
        aspect-ratio: 2 / 1;
    }
}

.showcase-fiche__head {
    max-width: 40rem;
}

.showcase-fiche__fact + .showcase-fiche__fact {
    padding-left: 1.5rem;
    border-left: 1px solid var(--ap-border);
}

/* The paragraph sits beside a portrait, so it keeps a reading measure of its
   own: half a wide band is far past the line length prose is read at. */
.content-split__body,
.portrait-plate__body,
.portrait-palmares__body {
    max-width: 52ch;
}

.portrait-palmares__rank {
    flex: none;
    min-width: 6ch;
}

/* The splits read left by default, so centering is the side they override. The
   body keeps its 52ch measure, which has to be centered as a box of its own. */
.blk--align-center .content-split__text {
    text-align: center;
}

.blk--align-center .content-split__body {
    margin-inline: auto;
}

.blk--align-center .content-split__tags {
    justify-content: center;
}

/* ---- Content panel ---- */
.content-panel__body {
    max-width: 52ch;
}

.content-panel__body,
.content-panel__label {
    color: color-mix(in srgb, var(--ap-bg) 78%, var(--ap-text));
}

.content-panel__facts {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1rem;
    padding-top: 1.5rem;
    border-top: 1px solid color-mix(in srgb, var(--ap-bg) 22%, var(--ap-text));
}

.content-panel__value {
    font-family: var(--ap-font-display);
    font-size: clamp(1.5rem, 3vw, 1.9rem);
    font-weight: 400;
    line-height: 1.1;
}

.content-panel__label {
    font-size: 0.85rem;
}

/* The picture covers its half edge to edge, empty slot included, and from lg on the height of
   the text beside it rather than a crop of its own. */
.content-panel__media .image-frame {
    border: 0;
}

@media (min-width: 992px) {
    .content-panel__media .site-image,
    .content-panel__media .image-frame {
        height: 100%;
        min-height: 26rem;
        aspect-ratio: auto;
    }
}

/* ---- Content stack ---- */
/* Centered unless the block's alignment setting says otherwise. The default
   lives here rather than on a text-center utility in the template: a utility is
   !important and the wrapper class below could never beat it. */
.content-stack {
    text-align: center;
}

/* The auto end margin moves the column, not just its text: .row centers it with
   justify-content-center, so aligning the text alone would leave the block
   indented from both gutters. The reading measure is kept on purpose. */
.blk--align-start .content-stack {
    margin-inline-end: auto;
    text-align: left;
}

/* The image is contained, never cropped: capping it at the reading column is
   what lets this block promise three fields and no layout choice. */
.content-stack__media {
    margin-inline: auto;
    max-width: 560px;
}

/* The same cap pulled to the left edge: aligning the block changes where the
   picture sits, never its size. */
.blk--align-start .content-stack__media {
    margin-inline: 0;
}

.content-stack--medallion .content-stack__media {
    max-width: 11rem;
}

/* An empty slot has no image to give it a height; the medallion takes its own
   from .frame-round, so it must not inherit this one. */
.content-stack__media .image-frame {
    min-height: 220px;
}

.content-stack--medallion .content-stack__media .image-frame {
    min-height: 0;
}

/* The holding page shown for a tenant whose site is not public yet. */
.holding-page {
    max-width: 640px;
    margin: 0 auto;
    padding-top: 10vh;
}

/* ---- Announcement ---- */
/* The band bleeds full width and the note stays in the measure, so only the
   framing differs: the row inside is one shared partial. */
.announce__row {
    column-gap: 1.25rem;
}

.announce__tag {
    flex: none;
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.announce__message {
    text-wrap: pretty;
}

.announce__cta {
    flex: none;
    font-family: var(--ap-font-mono);
    font-size: 0.8rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-decoration: underline;
    text-underline-offset: 0.25em;
}

/* Editor-only: an expired annonce keeps its place in the builder so the tenant
   can see why it left the site. */
.announce__expired {
    flex: none;
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    opacity: 0.75;
}

.announce--band.announce--accent {
    background-color: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

.announce--band.announce--neutral {
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.announce--band .announce__cta {
    color: inherit;
}

.announce--note {
    padding: 1.25rem;
    background-color: var(--ap-panel);
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
}

.announce--note.announce--accent {
    border-left: 3px solid var(--ap-accent);
}

.announce--note .announce__tag {
    color: var(--ap-faint);
}

.announce--note .announce__cta {
    color: var(--ap-accent-text);
}

/* The notice: a card with the end date set large beside the message. The accent tone
   fills it (.card-accent), so its tag inverts onto the contrast ink (ADR-0005). */
.announce-notice__tag {
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.card-accent .announce-notice__tag {
    background-color: var(--ap-accent-contrast);
    color: var(--ap-accent);
}

.announce-notice--neutral .announce-notice__tag {
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.announce-notice__message {
    max-width: 34ch;
    font-family: var(--ap-font-display);
    font-size: clamp(1.4rem, 3vw, 2rem);
    line-height: 1.25;
    text-wrap: pretty;
}

.announce-notice__day {
    font-family: var(--ap-font-display);
    font-size: clamp(4rem, 9vw, 6.5rem);
    line-height: 1;
    color: var(--ap-accent-text);
}

/* The floating pill: Bootstrap's alert brings the dismiss, the tones repaint it. It
   rides above contact/dock when the page has one. */
.announce-float {
    --bs-alert-border-color: transparent;

    bottom: calc(var(--contact-dock-height, 0px) + env(safe-area-inset-bottom, 0px));
    z-index: 1020;
    width: max-content;
    max-width: min(46rem, calc(100% - 2rem));
    border-radius: var(--ap-radius-button);
}

.announce-float--neutral {
    --bs-alert-bg: var(--ap-text);
    --bs-alert-color: var(--ap-bg);
}

.announce-float--accent {
    --bs-alert-bg: var(--ap-accent);
    --bs-alert-color: var(--ap-accent-contrast);
}

/* Below about 14rem of room the message takes the whole first line, the button and
   the close under it, rather than a column one word wide. */
.announce-float .announce__message {
    flex: 1 1 14rem;
}

.announce-float__cta {
    --bs-btn-bg: var(--bs-alert-color);
    --bs-btn-color: var(--bs-alert-bg);
    --bs-btn-hover-bg: var(--bs-alert-color);
    --bs-btn-hover-color: var(--bs-alert-bg);

    border-radius: var(--ap-radius-button);
}

.announce-float__close {
    --bs-btn-color: inherit;
    --bs-btn-hover-color: inherit;
}

/* ---- Before / after ---- */
/* Both frames stay on one row at every width: a comparison read one image at a
   time is not a comparison. A fixed ratio on both sides is what makes the two
   photos comparable when their crops differ. */
/* minmax(0, …) rather than 1fr: a grid track sized to a replaced element takes
   its minimum from the file's intrinsic width, so a 2000px photo would push the
   row past the viewport. */
.before-after__pair {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0.5rem;
}

.before-after__side .image-frame,
.before-after__side .site-image {
    aspect-ratio: 4 / 3;
    min-height: 0;
}

.before-after__tag {
    position: absolute;
    top: 0.6rem;
    left: 0.6rem;
    padding: 0.2rem 0.55rem;
    background: var(--ap-panel);
    color: var(--ap-text);
    border: 1px solid var(--ap-border);
    font-family: var(--ap-font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.before-after__tag--after {
    background: var(--ap-accent);
    border-color: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

.before-after__note {
    display: block;
    margin-top: 0.6rem;
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--ap-muted);
}

.before-after__note strong {
    color: var(--ap-text);
}

.before-after__notes {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0.5rem 1.5rem;
}

.before-after__case + .before-after__case {
    margin-top: 2.5rem;
    padding-top: 2.5rem;
    border-top: 1px solid var(--ap-border);
}

.before-after__facts {
    display: grid;
    gap: 1rem;
    margin-top: 2rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--ap-border);
}

.before-after__slider {
    --ba-split: 50%;
    position: relative;
    aspect-ratio: 16 / 9;
    overflow: hidden;
}

@media (max-width: 575.98px) {
    .before-after__slider {
        aspect-ratio: 4 / 3;
    }
}

.before-after__slider .image-frame,
.before-after__slider .site-image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.before-after__reveal {
    position: absolute;
    inset: 0;
    clip-path: inset(0 calc(100% - var(--ba-split)) 0 0);
}

.before-after__slider .before-after__tag--after {
    left: auto;
    right: 0.6rem;
}

.before-after__handle,
.before-after__range {
    display: none;
}

.before-after__slider.is-interactive .before-after__handle {
    display: block;
    position: absolute;
    top: 0;
    bottom: 0;
    left: var(--ba-split);
    width: 2px;
    margin-left: -1px;
    background: var(--ap-bg);
}

.before-after__knob {
    position: absolute;
    top: 50%;
    left: 50%;
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    transform: translate(-50%, -50%);
    border: 2px solid var(--ap-bg);
    border-radius: 50%;
    background: var(--ap-accent);
    color: var(--ap-accent-contrast);
    font-family: var(--ap-font-mono);
}

.before-after__slider.is-interactive .before-after__range {
    display: block;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    appearance: none;
    background: transparent;
    opacity: 0;
    cursor: ew-resize;
}

/* The range itself is transparent, so the ring goes on the knob. Light border
   then dark outline, because neither token is guaranteed against a photograph. */
.before-after__slider:has(.before-after__range:focus-visible) .before-after__knob {
    outline: 2px solid var(--ap-text);
    outline-offset: 2px;
}

/* Arrows would need framework JS on a public page (ADR-0007), so the scroll is
   the control and a peeking next card is the affordance. */
.before-after__series {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 88%;
    gap: 1.5rem;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    padding-bottom: 1rem;

    /* A drawn track, because the platform hides the overlay one until a scroll
       starts: the peeking card says there is more, this says how much more. */
    scrollbar-width: thin;
    scrollbar-color: var(--ap-border) transparent;
}

.before-after__card {
    min-width: 0;
    scroll-snap-align: start;
}

@media (min-width: 576px) {
    .before-after__facts {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (min-width: 768px) {
    .before-after__series {
        grid-auto-columns: 58%;
    }
}

@media (min-width: 992px) {
    .before-after__series {
        grid-auto-columns: 42%;
    }
}

/* The prestations are what the block is selling, so they read as a list on a
   surface rather than as loose rows under a centered title. */
.resa-services {
    list-style: none;
    margin: 0;
    padding: 0.5rem 1.25rem;
    background: var(--ap-panel);
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
}

.resa-services__row {
    display: flex;
    justify-content: space-between;
    gap: 1.5rem;
    padding: 0.85rem 0;
}

.resa-services__row + .resa-services__row {
    border-top: 1px solid var(--ap-border);
}

.resa-services__meta {
    color: var(--ap-muted);
    white-space: nowrap;
}

.resa-card__name {
    font-family: var(--ap-font-display);
}

/* reservation/couverture: the photograph fills the section and the panel sits on its
   bottom-left corner from lg; on a phone the panel follows the photo. */
.resa-cover__panel {
    position: relative;
    margin: -2rem 1rem 0;
}

@media (min-width: 992px) {
    .resa-cover__photo {
        aspect-ratio: auto;
        height: 38rem;
    }

    .resa-cover__panel {
        position: absolute;
        bottom: 2.5rem;
        left: 2.5rem;
        max-width: 28rem;
        margin: 0;
    }
}

/* reservation/split: the picture is flush with the card, empty slot included, cropped 4:3 on
   a phone and to the card's height from lg. */
.resa-split__media .image-frame {
    border: 0;
}

@media (min-width: 992px) {
    .resa-split__media {
        min-height: 28rem;
    }

    .resa-split__media .site-image,
    .resa-split__media .image-frame {
        position: absolute;
        inset: 0;
        height: 100%;
        aspect-ratio: auto;
    }
}

.resa-split__text {
    max-width: 42ch;
}


/* ---- Module pages: the shared header ---- */
/* Back link, title, standfirst and meta, then a neutral rule: one skeleton for a form's
   page and for the booking ones, painted with the existing tokens. */
.module-head {
    gap: 0.75rem;
    padding-bottom: 1.75rem;
    margin-bottom: 2.5rem;
    border-bottom: 1px solid var(--ap-border);
}

.module-head__back {
    font-family: var(--ap-font-mono);
    font-size: 0.72rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ap-muted);
}

.module-head__intro {
    max-width: 58ch;
    color: var(--ap-muted);
    text-wrap: pretty;
}

.module-head__tag {
    padding: 0.4rem 0.75rem;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius-button);
    font-family: var(--ap-font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ap-faint);
}

.module-body {
    max-width: 45rem;
}


/* ---- Public flash banner ---- */
/* Pinned to the bottom of the viewport: it is emitted after the footer, where the
   flow would put it out of sight. The container takes no pointer events, only the
   alert does. */
.site-flash {
    z-index: 1040;
    padding-bottom: 1.25rem;
}

.site-flash .alert {
    width: min(100%, 34rem);
    box-shadow: 0 6px 24px rgb(0 0 0 / 18%);
}

/* ---- Newsletter: the page section ---- */
/* No Bootstrap .input-group: its radii come from a selector too specific to
   override without !important, and the field welded to the button falls to a
   few characters on a phone. The flex-basis wraps the button to the next line
   before the address becomes unreadable. */
.newsletter-blk__group {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}

.newsletter-blk__group > .form-control {
    flex: 1 1 14rem;
}

.newsletter-blk__text {
    max-width: 46ch;
    color: var(--ap-muted);
}

/* Tied to the field above it rather than hung off the bottom of the section. */
.newsletter-blk__note {
    margin-top: 0.6rem;
    font-size: 0.8rem;
    color: var(--ap-faint);
}

/* Nothing on the ink surface keeps a token measured against --ap-bg. */
.newsletter-band .label,
.newsletter-band .newsletter-blk__text,
.newsletter-band .newsletter-blk__note {
    color: inherit;
    opacity: 0.78;
}

/* The quiet variant: an accent edge so the panel reads as a section rather
   than a grey box, and a measure so the field does not span it end to end. */
.newsletter-blk--stacked {
    border-top: 2px solid var(--ap-accent);
}

.newsletter-blk__stack {
    width: min(30rem, 100%);
    margin-inline: auto;
}

.newsletter-blk--stacked .newsletter-blk__group {
    justify-content: center;
}

.newsletter-ink .label,
.newsletter-ink .newsletter-blk__text,
.newsletter-ink .newsletter-blk__note {
    color: inherit;
    opacity: 0.78;
}

.newsletter-ink .newsletter-blk__group {
    flex-wrap: nowrap;
    align-items: center;
    border-bottom: 1px solid currentColor;
}

.newsletter-ink .form-control {
    min-width: 0;
    padding-inline: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: inherit;
    font-family: var(--ap-font-display);
    font-size: 1.25rem;
}

.newsletter-ink .form-control::placeholder {
    color: inherit;
    opacity: 0.55;
}

.newsletter-ink .form-control:focus {
    box-shadow: none;
}

.newsletter-ink .form-control:focus-visible,
.newsletter-ink .btn-accent:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 4px;
}

.newsletter-ink .btn-accent {
    --bs-btn-bg: transparent;
    --bs-btn-border-color: transparent;
    --bs-btn-color: var(--ap-bg);
    --bs-btn-hover-bg: transparent;
    --bs-btn-hover-border-color: transparent;
    --bs-btn-hover-color: var(--ap-bg);
    --bs-btn-active-bg: transparent;
    --bs-btn-active-border-color: transparent;
    --bs-btn-active-color: var(--ap-bg);

    flex: none;
    padding-inline: 0;
}

.newsletter-ink .btn-accent:hover {
    text-decoration: underline;
    text-underline-offset: 0.25em;
}

/* ---- Newsletter: the footer column ---- */
/* The field is welded to the button, so only the outer corners are shaped: Bootstrap
   already squares the joint, from a selector specific enough to hold against this one.
   They follow the button token, not --ap-radius, because the pair reads as one control. */
.newsletter .input-group > .form-control {
    border-start-start-radius: var(--ap-radius-button);
    border-end-start-radius: var(--ap-radius-button);
}

/* ---------- Products ---------- */

.products-head {
    margin-bottom: 2rem;
}

/* Arrows would need framework JS on a public page (ADR-0007), so the scroll is
   the control and the card cut by the right edge is the affordance: the rail
   bleeds past the container padding it sits in. */
.products-rail,
.rayons-rail,
.gallery-rail {
    display: grid;
    grid-auto-flow: column;
    gap: 1.5rem;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    margin-right: -1.5rem;
    padding-right: 1.5rem;
    padding-bottom: 1rem;
}

.products-rail {
    grid-auto-columns: 62%;
}


/* A category card is the same object at every width, a square photo over one line
   of text, so its track is a constant: sized as a share of the viewport it
   shrank to a thumbnail on a phone. Only the peek stays proportional. */
.rayons-rail {
    grid-auto-columns: min(20rem, 85%);
}

.products-rail__card,
.rayons-rail__card {
    min-width: 0;
    scroll-snap-align: start;
}

/* Two tracks on a phone and four from lg, with the first category on a quarter of the
   mosaic. Explicit tracks rather than auto-fit: a spanning child and an auto-fit
   grid disagree about how many columns there are, and the tile lands on its own row. */
.rayons-bento {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(0.875rem, 1.6vw, 1.25rem);
}

.rayons-bento__tile {
    position: relative;
    display: flex;
    align-items: flex-end;
    min-height: 14rem;
    overflow: hidden;
    border-radius: var(--ap-radius);
    background: var(--ap-panel);
    color: var(--bs-white);
}

.rayons-bento__photo,
.rayons-bento .image-frame {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.8s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.rayons-bento__tile:hover .rayons-bento__photo,
.rayons-bento__tile:focus-visible .rayons-bento__photo {
    transform: scale(1.04);
}

/* Fixed rather than themed: a scrim over a photograph is theme-invariant, which is
   also why the tile's own text is white in both schemes. */
.rayons-bento__scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, transparent 45%, rgb(0 0 0 / 72%) 100%);
}

.rayons-bento__body {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: clamp(1rem, 2vw, 1.5rem);
}

.rayons-bento__name {
    font-family: var(--ap-font-display);
    font-size: clamp(1.2rem, 1.8vw, 1.6rem);
    line-height: 1.1;
}

.rayons-bento__detail {
    font-size: 0.84rem;
    color: rgb(255 255 255 / 85%);
}

@media (min-width: 992px) {
    .rayons-bento {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }

    .rayons-bento__tile:first-child {
        grid-column: span 2;
        grid-row: span 2;
    }
}

/* Choix du public. In `affiche` the photograph fills the card and the words sit on a scrim
   at its foot: white on a fixed scrim rather than a token, since a scrim over a photograph
   is theme-invariant (ADR-0005). A pair staggers, the narrow card standing taller. */
.publics-photo {
    isolation: isolate;
    min-height: 26rem;
    background: var(--ap-panel);
    color: var(--bs-white);
}

.publics-photo:hover,
.publics-photo:focus-visible {
    color: var(--bs-white);
}

@media (min-width: 768px) {
    .publics-photo {
        min-height: clamp(26rem, 40vw, 34rem);
    }

    .publics-photo--tall {
        min-height: clamp(30rem, 50vw, 42rem);
    }
}

.publics-photo__media {
    transition: transform 0.8s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.publics-photo:hover .publics-photo__media,
.publics-photo:focus-visible .publics-photo__media {
    transform: scale(1.04);
}

.publics-photo__scrim {
    background: linear-gradient(180deg, transparent 30%, rgb(10 10 12 / 50%) 58%, rgb(10 10 12 / 85%) 100%);
}

.publics-photo__kicker {
    font-family: var(--ap-font-mono);
    font-size: 0.8rem;
    letter-spacing: 0.06em;
    color: rgb(255 255 255 / 80%);
}

.publics-photo__title {
    max-width: 20ch;
    font-family: var(--ap-font-display);
    line-height: 1.05;
}

.publics-photo__cta {
    padding-bottom: 2px;
    border-bottom: 1px solid currentcolor;
}

.publics-card,
.publics-card:hover {
    color: var(--ap-text);
}

.publics-card__cta {
    color: var(--ap-accent-text);
}

.publics-card:hover .publics-card__cta {
    text-decoration: underline;
}

.publics-photo:hover .arrow,
.publics-card:hover .arrow {
    transform: translateX(3px);
}

@media (prefers-reduced-motion: reduce) {
    .publics-photo__media,
    .publics-photo:hover .publics-photo__media,
    .publics-photo:focus-visible .publics-photo__media,
    .publics-photo:hover .arrow,
    .publics-card:hover .arrow {
        transition: none;
        transform: none;
    }
}

/* The one product card, rendered by the produits block and by the boutique listings
   alike, so it lives here rather than in shop.css: that sheet is a shop-only entrypoint
   and a builder page never loads it.

   A panel fill with a themed edge, and the photograph bleeding to the corners. Bootstrap's
   own .card gives the box the page's background and a translucent black border that belongs
   to no palette, which left the outline doing all the separating.

   Three rows: the photo, the text, then the price. The middle row takes the slack, so a
   rail of cards and a grid of them both line their prices up whatever the names are long. */
.product-card {
    display: grid;
    /* Explicit, so a style can span its photograph across every row (the band does) and a
       long word cannot widen the column past its share. */
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto 1fr auto;
    height: 100%;
    overflow: hidden;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    background-color: var(--ap-panel);
    color: var(--ap-text);
}

/* focus-within, not focus: the card is the link, and a keyboard lands on it. Nothing lifts:
   the photograph zooms under the card's clip, so nothing around it moves. */
.product-card:hover,
.product-card:focus-within {
    color: var(--ap-text);
}

.product-card .product-card__media .site-image {
    transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.product-card:hover .product-card__media .site-image,
.product-card:focus-within .product-card__media .site-image {
    transform: scale(1.03);
}

.product-card__media {
    position: relative;
    display: block;
    overflow: hidden;
    aspect-ratio: 1 / 1;
    background-color: rgba(var(--ap-accent-rgb), 0.08);
}

/* No edge and no corner of its own: the card draws the one and clips the other. Without the
   radius reset, shop.css rounds every .site-image on the shopfront, which inside a framed card
   cuts two notches of panel out from under the photograph while the card's own corners are
   somewhere else entirely. Three classes because that sheet loads after this one. */
.product-card .product-card__media .site-image,
.product-card .product-card__media .image-frame {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
    border-radius: 0;
    object-fit: cover;
}

.product-card--sold-out .product-card__media {
    filter: grayscale(1);
    opacity: 0.65;
}

/* The marks sit in a row rather than one over the other, because a card can now carry two. */
.product-card__badges {
    position: absolute;
    top: 0.75rem;
    left: 0.75rem;
    display: flex;
    gap: 0.35rem;
}

/* Accent ink on the page's own background, which is the one pair the accent tokens
   are measured against (ADR-0005); over a photograph it needs that plate anyway. */
.product-card__badge {
    padding: 0.375rem 0.7rem;
    border-radius: var(--ap-radius-button);
    background-color: rgba(var(--ap-bg-rgb), 0.9);
    color: var(--ap-accent-text);
    font-family: var(--ap-font-mono);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.product-card__badge--quiet {
    color: var(--ap-muted);
}

.product-card__text {
    display: flex;
    flex-direction: column;
    gap: 0.1875rem;
    min-width: 0;
    padding: 1rem 1.125rem 0;
}

.product-card__name {
    margin: 0;
    font-family: var(--ap-font-display);
    font-size: 1.1rem;
    line-height: 1.18;
}

/* The one line a card can carry besides its name and its price: « épuisé », « plus que 3 »,
   « téléchargement ». One phrase of its own making, so it needs no clamp. */
.product-card__detail {
    font-size: 0.84rem;
    color: var(--ap-muted);
}

/* Its own row under a hairline rather than a column beside the name: at five cards across
   the figure took a third of the width, broke the name a word to the line, and left the
   space under itself empty. */
.product-card__foot {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    margin: 0.7rem 1.125rem 0;
    padding: 0.6rem 0 1.125rem;
    border-top: 1px solid var(--ap-border);
}

.product-card__price {
    font-weight: 800;
    font-size: 0.95rem;
}

.product-card__from {
    font-weight: 400;
    color: var(--ap-muted);
}

/* The frameless styles, which are every style but the two framed ones and the band. */
.product-card--nue,
.product-card--vignette,
.product-card--planche {
    border-color: transparent;
    border-radius: 0;
    background-color: transparent;
}

/* The card is invisible there, so the photograph is the object and carries the theme's
   radius on all four of its own corners. Left on the card, the clip rounded the top two
   and left the bottom two square. */
.product-card--nue .product-card__media,
.product-card--vignette .product-card__media,
.product-card--planche .product-card__media {
    border-radius: var(--ap-radius);
}

.product-card--nue .product-card__text {
    padding-right: 0;
    padding-left: 0;
}

.product-card--nue .product-card__foot {
    justify-content: flex-start;
    margin-right: 0;
    margin-left: 0;
    padding-bottom: 0;
    border-top: 0;
}

/* The vignette: the price rides on the photograph and the name takes one line under it, so
   the card is short enough that a long shelf is scanned rather than read. Media and price share
   the first grid row, which is what puts one over the other without a second template. */
.product-card--vignette {
    grid-template-rows: auto auto;
}

.product-card--vignette .product-card__media {
    grid-area: 1 / 1;
    border: 1px solid var(--ap-border);
}

/* Positioned, because the media it shares the cell with is: a static sibling paints under a
   positioned one whatever the source order says. */
.product-card--vignette .product-card__foot {
    position: relative;
    grid-area: 1 / 1;
    align-self: end;
    justify-self: start;
    margin: 0 0 0.5rem 0.5rem;
    padding: 0.3rem 0.55rem;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius-button);
    background-color: rgba(var(--ap-bg-rgb), 0.94);
}

.product-card--vignette .product-card__price {
    font-family: var(--ap-font-mono);
    font-weight: 600;
    font-size: 0.72rem;
}

.product-card--vignette .product-card__text {
    grid-area: 2 / 1;
    padding: 0.55rem 0 0;
}

.product-card--vignette .product-card__name {
    overflow: hidden;
    font-size: 0.92rem;
    line-height: 1.25;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The plate: the photograph standing tall and the words centered under it, no card at all.
   It is the produits block's editorial variant, which now draws this card rather than a second
   copy of it, and a style any listing can choose. */
.product-card--planche {
    text-align: center;
}

.product-card--planche .product-card__text {
    padding: 0.85rem 0 0;
    gap: 0.35rem;
}

.product-card--planche .product-card__name {
    font-size: 1.28rem;
}

.product-card--planche .product-card__foot {
    justify-content: center;
    margin: 0.35rem 0 0;
    padding: 0;
    border-top: 0;
}

.product-card--planche .product-card__price {
    font-family: var(--ap-font-mono);
    font-weight: 600;
    font-size: 0.78rem;
    letter-spacing: 0.06em;
}

/* The band: the words move into the photograph, on a veil at its foot. Nothing outside the
   picture, so no text height has to be equalized and the grid is regular whatever the names
   are long. The photograph spans every row and the words take the last two, which is what
   stacks them over it without a second template.

   White on a scrim rather than a token, as elsewhere in this sheet: an accent has no contrast
   guarantee over a photograph, and this one is over whichever photograph the tenant picked. */
.product-card--bandeau {
    grid-template-rows: 1fr auto auto;
    border: 0;
    color: var(--bs-white);
}

.product-card--bandeau:hover,
.product-card--bandeau:focus-within {
    color: var(--bs-white);
}

.product-card--bandeau .product-card__media {
    grid-row: 1 / -1;
    grid-column: 1;
}

.product-card--bandeau .product-card__media::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgb(14 12 9 / 88%) 0%, rgb(14 12 9 / 55%) 34%, transparent 62%);
}

/* Positioned, because the photograph they sit on is: a static sibling paints under a
   positioned one whatever the source order says. */
.product-card--bandeau .product-card__text {
    position: relative;
    grid-row: 2;
    grid-column: 1;
    padding: 0 1.1rem;
}

/* Two lines here rather than as many as the name is long: the veil has a top, and a name
   that runs past it is a name on the photograph itself. */
.product-card--bandeau .product-card__name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: 1.06rem;
}

.product-card--bandeau .product-card__foot {
    position: relative;
    grid-row: 3;
    grid-column: 1;
    align-items: baseline;
    margin: 0;
    padding: 0.35rem 1.1rem 1rem;
    border-top: 0;
}

.product-card--bandeau .product-card__from,
.product-card--bandeau .product-card__detail {
    color: rgb(255 255 255 / 78%);
}

/* The portrait crop, wherever a style stands its photograph up. */
.product-card--portrait .product-card__media,
.product-card--planche .product-card__media,
.product-card--bandeau .product-card__media {
    aspect-ratio: 4 / 5;
}

@media (prefers-reduced-motion: reduce) {
    .product-card .product-card__media .site-image {
        transition: none;
    }

    .product-card:hover .product-card__media .site-image,
    .product-card:focus-within .product-card__media .site-image {
        transform: none;
    }
}


/* The phone list. Below the sm breakpoint a catalogue set to « liste » draws its cards as rows,
   a thumbnail then the name then the figure, eight to a screen where the square card gives one.
   It is a phone layout and nothing else: above sm the tenant's own card style draws, untouched.

   Every selector below names .product-card twice on purpose. The card styles reach their own
   parts with two classes, so a rule of two classes here would only tie with them and win on
   source order; three settles it, whatever order the sheet ends up in. */
@media (max-width: 575.98px) {
    /* A list has no gutter between its rows: the rule under each one is the separation. */
    .product-grid--liste {
        --bs-gutter-y: 0;
    }

    .product-grid--liste .product-card {
        grid-template-rows: auto;
        grid-template-columns: auto minmax(0, 1fr) auto;
        align-items: center;
        gap: 0.875rem;
        padding: 0.75rem 0;
        border: 0;
        border-bottom: 1px solid var(--ap-border);
        border-radius: 0;
        background-color: transparent;
        color: var(--ap-text);
    }

    .product-grid--liste .product-card:hover,
    .product-grid--liste .product-card:focus-within {
        border-bottom-color: var(--ap-accent);
        color: var(--ap-text);
    }

    .product-grid--liste .product-card .product-card__media {
        grid-area: auto;
        width: 76px;
        aspect-ratio: 1 / 1;
        border: 1px solid var(--ap-border);
        border-radius: var(--ap-radius);
    }

    /* The band paints a veil over its photograph for text that is no longer on it. */
    .product-grid--liste .product-card .product-card__media::after {
        content: none;
    }

    /* A pill in the corner of a 76px thumbnail is unreadable, so the mark becomes a strip
       across its foot. It has to stay legible: a listing that hides it sends the visitor to a
       product they cannot buy. */
    .product-grid--liste .product-card .product-card__badges {
        top: auto;
        right: 0;
        bottom: 0;
        left: 0;
    }

    /* One mark only, the first, which the card orders épuisé before the discount before the
       novelty: two across 76 px are two unreadable marks rather than one readable one. */
    .product-grid--liste .product-card .product-card__badge:not(:first-child) {
        display: none;
    }

    .product-grid--liste .product-card .product-card__badge {
        flex: 1;
        padding: 0.1rem 0.2rem;
        border-radius: 0;
        font-size: 0.48rem;
        letter-spacing: 0.02em;
        text-align: center;
    }

    .product-grid--liste .product-card .product-card__text {
        grid-area: auto;
        padding: 0;
        text-align: left;
    }

    .product-grid--liste .product-card .product-card__name {
        display: block;
        overflow: hidden;
        font-size: 1rem;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .product-grid--liste .product-card .product-card__foot {
        flex-direction: column;
        grid-area: auto;
        align-items: flex-end;
        gap: 0.15rem;
        margin: 0;
        padding: 0;
        border: 0;
        border-radius: 0;
        background-color: transparent;
    }

    .product-grid--liste .product-card .product-card__price {
        font-family: var(--ap-font-body);
        font-weight: 800;
        font-size: 0.95rem;
        letter-spacing: normal;
    }

    .product-grid--liste .product-card .product-card__from,
    .product-grid--liste .product-card .product-card__detail {
        color: var(--ap-muted);
    }
}

.shop-compare-at {
    color: var(--ap-faint);
    font-size: 0.75em;
    font-weight: 400;
}

.products-selection__cta {
    display: flex;
    justify-content: center;
    margin-top: 2.5rem;
    padding-top: 2rem;
    border-top: 1px solid var(--ap-border);
}

@media (min-width: 576px) {
    .products-rail {
        grid-auto-columns: 38%;
    }
}

@media (min-width: 768px) {
    .products-rail {
        grid-auto-columns: 28%;
    }
}

@media (min-width: 992px) {
    .products-rail,
    .rayons-rail,
    .gallery-rail {
        margin-right: -3rem;
        padding-right: 3rem;
    }

    .products-rail {
        grid-auto-columns: 22%;
    }
}

/* Legal page: anchors styled as tabs. :target picks the visible
   section, so the links deep-link, survive JavaScript being off and stay
   indexable. Without :has() support every section shows, which is the safe
   way to fail on a page whose purpose is being readable. */
.legal-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    border-bottom: 1px solid var(--ap-border);
    margin-bottom: var(--ap-space-block);
}

.legal-tabs a {
    padding: 0.75rem 0;
    margin-bottom: -1px;
    border-bottom: 2px solid transparent;
    color: var(--ap-muted);
}

.legal-tabs a:hover,
.legal-tabs a:focus-visible {
    color: var(--ap-text);
}

.legal-section + .legal-section {
    margin-top: var(--ap-space-block);
}

.legal-block {
    color: var(--ap-muted);
}

@supports selector(:has(*)) {
    /* The fragment swaps the section in place, so it must not also scroll: a
       margin taller than the viewport puts the scroll target above the document
       top, where it clamps to zero. Only inside @supports, because the fallback
       stacks every section and there jumping to one is the right behavior. */
    .legal-section {
        scroll-margin-top: 100vh;
    }

    body:has(.legal-section:target) .legal-section {
        display: none;
    }

    body:has(.legal-section:target) .legal-section:target {
        display: block;
        margin-top: 0;
    }

    body:has(#mentions:target) .legal-tabs a[href="#mentions"],
    body:has(#donnees:target) .legal-tabs a[href="#donnees"],
    body:has(#cgv:target) .legal-tabs a[href="#cgv"],
    body:not(:has(.legal-section:target)) .legal-tabs a:first-child {
        color: var(--ap-text);
        border-bottom-color: var(--ap-accent);
    }

    body:not(:has(.legal-section:target)) .legal-section:not(:first-of-type) {
        display: none;
    }
}

/* ---------- Portrait ---------- */
.portrait-medaillon {
    max-width: 40rem;
}

.portrait-medaillon__photo {
    width: min(11rem, 50vw);
}

.portrait-cover__photo {
    aspect-ratio: 4 / 5;
}

@media (min-width: 768px) {
    .portrait-cover__photo {
        aspect-ratio: 21 / 9;
    }
}

/* White on a fixed scrim: a scrim over a photograph is theme-invariant (ADR-0005). */
.portrait-cover__body {
    max-width: 52ch;
    font-family: var(--ap-font-display);
    font-size: 1.25rem;
    line-height: 1.5;
}

/* ---------- Medallion hero ---------- */

/* The rule crosses the column and the medallion sits on it, so the image is
   held by the section instead of floating above the text. */
.hero-medallion::before {
    content: "";
    position: absolute;
    top: 50%;
    right: 0;
    left: 0;
    border-top: 1px solid var(--ap-border);
}

.hero-medallion__media {
    position: relative;
    width: min(420px, 78vw);
}

.hero-medallion__image,
.hero-collage .hero-photo {
    box-shadow: 0 1.25rem 2rem -0.75rem color-mix(in srgb, var(--ap-text) 30%, transparent);
}

/* ---------- Editorial hero ---------- */
.hero-editorial__text {
    align-items: flex-start;
}

.hero-editorial__headline {
    --hero-overlap: -40%;

    font-size: clamp(2.75rem, 9vw, 8rem);
    text-wrap: balance;
}

/* Side by side, the headline may run past its column onto the photo. Reversed, the column
   ranges to its far edge, so the headline still runs toward the photo. */
@media (min-width: 992px) {
    .hero-editorial__text {
        position: relative;
        z-index: 1;
    }

    .hero-editorial__headline {
        margin-right: var(--hero-overlap);
    }

    .blk--reversed .hero-editorial__text {
        align-items: flex-end;
        text-align: end;
    }

    .blk--reversed .hero-editorial__headline {
        margin-right: 0;
        margin-left: var(--hero-overlap);
    }
}

/* ---------- Triptych and band heroes ---------- */
.hero-row__headline {
    text-wrap: balance;
}

.hero-triptych__center {
    flex-grow: 1.25;
}

/* A tag takes the button's shape, not the surface radius. */
.hero-band__text .eyebrow,
.hero-collage__caption {
    padding: 0.5rem 0.9rem;
    border-radius: var(--ap-radius-button);
    background: var(--ap-panel);
}

.hero-band__text .eyebrow {
    border: 1px solid var(--ap-border);
}

/* ---------- Collage hero ---------- */
.hero-collage {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.hero-collage__caption {
    grid-column: 1 / -1;
    justify-self: start;
    font-family: var(--ap-font-display);
}

@media (min-width: 992px) {
    .hero-collage {
        position: relative;
        aspect-ratio: 8 / 7;
    }

    .hero-collage__back,
    .hero-collage__front,
    .hero-collage__caption {
        position: absolute;
    }

    .hero-collage__back {
        top: 0;
        right: 0;
        width: 62%;
        transform: rotate(2deg);
    }

    .hero-collage__front {
        bottom: 0;
        left: 0;
        z-index: 1;
        width: 52%;
        transform: rotate(-3deg);
    }

    .hero-collage__front .hero-photo {
        border: 0.5rem solid var(--ap-bg);
    }

    .hero-collage__caption {
        top: 12%;
        left: 8%;
        z-index: 2;
        transform: rotate(-4deg);
    }
}

/* ---------- Price bands ---------- */

/* The 1px gap on a border-colored grid draws the cell rules at any column count. */
.price-bands {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1px;
    overflow: hidden;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    background: var(--ap-border);
}

.price-bands__cell {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 1.75rem 1.5rem;
    background: var(--ap-panel);
    color: var(--ap-text);
    transition: background-color 0.15s ease;
}

a.price-bands__cell:hover,
a.price-bands__cell:focus-visible {
    background: var(--ap-bg);
    color: var(--ap-text);
}

.price-bands__cell--on {
    box-shadow: inset 0 3px 0 var(--ap-accent);
}

.price-bands__range {
    font-family: var(--ap-font-display);
    font-size: 1.9rem;
    line-height: 1;
    color: var(--ap-accent-text);
}

.price-bands__body {
    font-size: 0.9rem;
}

.price-bands__count {
    margin-top: auto;
    padding-top: 0.75rem;
}

@media (min-width: 768px) {
    .price-bands {
        grid-template-columns: repeat(2, 1fr);
    }

    /* An odd last cell spans the row, so the grid never shows an empty slot. */
    .price-bands__cell:last-child:nth-child(odd) {
        grid-column: 1 / -1;
    }
}

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

    .price-bands--4 {
        grid-template-columns: repeat(4, 1fr);
    }

    .price-bands--3 .price-bands__cell:last-child:nth-child(odd),
    .price-bands--4 .price-bands__cell:last-child:nth-child(odd) {
        grid-column: auto;
    }
}

.price-index__row {
    color: var(--ap-text);
    transition: opacity 0.15s ease;
}

a.price-index__row:hover {
    color: var(--ap-text);
    opacity: 0.75;
}

.price-index__range {
    font-family: var(--ap-font-display);
    font-size: 1.6rem;
    line-height: 1;
}

.price-index__row--on .price-index__range {
    color: var(--ap-accent-text);
}

.price-index__row--on .price-index__title::before {
    content: '';
    display: inline-block;
    width: 0.5rem;
    height: 0.5rem;
    margin-right: 0.6rem;
    border-radius: 50%;
    background: var(--ap-accent);
    vertical-align: middle;
}

/* A cell's own pair, not the ink scope's tokens: the highlighted cell inverts back. */
.price-ink__body,
.price-ink__count {
    color: inherit;
    opacity: 0.8;
}

.price-ink__cell,
a.price-ink__cell:hover {
    color: inherit;
}

.price-ink__cell--on,
a.price-ink__cell--on:hover {
    background-color: var(--ap-bg);
    color: var(--ap-text);
}

.price-ink__range,
.price-card__range {
    font-family: var(--ap-font-display);
    font-size: clamp(1.8rem, 3.4vw, 2.8rem);
    line-height: 1;
}

@media (min-width: 992px) {
    .price-ink__item + .price-ink__item {
        border-left: 1px solid var(--bs-border-color);
    }
}

.price-card__range {
    aspect-ratio: 4 / 3;
    background-color: color-mix(in srgb, var(--ap-text) 12%, var(--ap-bg));
}

.card-on .price-card__range {
    background-color: var(--ap-accent);
    color: var(--ap-accent-contrast);
}

/* ---------- Glossary ---------- */

/* The sigle column grows past its 6rem floor for a long sigle, up to the twelve characters
   the form allows, rather than breaking the word; wrapping is the last resort. */
.glossary-term {
    display: grid;
    grid-template-columns: minmax(6rem, max-content) minmax(0, 1fr);
    gap: 1.25rem;
    align-items: start;
    padding: 1.35rem 1.5rem;
    background: var(--ap-panel);
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
}

.glossary-term__code {
    font-family: var(--ap-font-display);
    font-size: 1.35rem;
    line-height: 1.1;
    color: var(--ap-accent-text);
    overflow-wrap: anywhere;
}

.glossary-entries__code {
    font-family: var(--ap-font-display);
    font-size: 1.75rem;
    line-height: 1.1;
    overflow-wrap: anywhere;
}

.glossary-ink .label,
.glossary-ink__body {
    color: inherit;
    opacity: 0.8;
}

.glossary-ink__code {
    background-color: var(--ap-bg);
    color: var(--ap-text);
}

@media (min-width: 768px) {
    .glossary-ink__term + .glossary-ink__term {
        border-left: 1px solid color-mix(in srgb, var(--ap-bg) 22%, transparent);
    }
}

.glossary-ink__points {
    background-color: var(--ap-panel);
    color: var(--ap-text);
}

/* ---------- Category chips ---------- */

/* Under 992px the chips have the whole width and no column to wrap into: eight
   categories stack into four rows and push the framed category off the screen. One
   scrolling line instead, bleeding past the container padding like the rails. */
.rayon-chips {
    gap: 0.6rem;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    margin-right: -1.5rem;
    padding-right: 1.5rem;
    padding-bottom: 0.75rem;
}

.rayon-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 1.1rem;
    border-radius: var(--ap-radius-button);
    background: color-mix(in srgb, var(--ap-text) 5%, var(--ap-bg));
    color: var(--ap-muted);
    font-size: 0.85rem;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.rayon-chip:hover,
.rayon-chip:focus-visible {
    background: color-mix(in srgb, var(--ap-text) 9%, var(--ap-bg));
    color: var(--ap-text);
}

.rayon-chip.active {
    background: var(--ap-accent);
    color: var(--ap-accent-contrast);
    font-weight: 600;
}

.rayon-chip__count {
    font-family: var(--ap-font-mono);
    font-size: 0.65rem;
    letter-spacing: 0.08em;
    opacity: 0.8;
}

.rayon-featured__facts {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem 1.75rem;
    margin: 1.4rem 0 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--ap-border);
}

.rayon-featured__value {
    display: block;
    margin-bottom: 0.35rem;
    font-family: var(--ap-font-display);
    font-size: 1.5rem;
    line-height: 1;
}

/* The chips have their own column beside the framed category: they wrap into it
   rather than hide a tab behind a scroll gesture. */
@media (min-width: 992px) {
    .rayon-chips {
        flex-wrap: wrap;
        overflow-x: visible;
        margin-right: 0;
        padding-right: 0;
        padding-bottom: 0;
    }
}

/* Address suggestions. The list is built by address_autocomplete.js
   and never exists without it, so no rule here has a no-JavaScript counterpart. */
.address-autocomplete {
    position: relative;
}

.address-suggestions {
    position: absolute;
    z-index: 3;
    inset-inline: 0;
    top: calc(100% + 0.25rem);
    max-height: 15rem;
    overflow-y: auto;
    margin: 0;
    padding: 0.25rem;
    list-style: none;
    background-color: var(--ap-panel);
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
}

.address-suggestion {
    padding: 0.5rem 0.75rem;
    border-radius: var(--ap-radius);
    cursor: pointer;
    color: var(--ap-text);
}

.address-suggestion:hover,
.address-suggestion.is-active {
    background-color: color-mix(in srgb, var(--ap-accent) 14%, transparent);
}

/* The address register's advisory notice: it warns, it never refuses,
   so it reads as a remark rather than as a field error. */
.address-notice {
    margin-top: 0.25rem;
    color: var(--ap-muted);
}

.address-notice::before {
    content: '⚠ ';
}
