/* ---- 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. Shared rather than per module: the
   signature templates each had their own copy of these eight lines. */
.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.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 {
        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. */
.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: uppercase;
    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-mono);
    font-size: 0.8rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--ap-faint);
    white-space: nowrap;
}

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

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

.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;
}

.stepper-horizontal .step {
    position: relative;
    flex: 1;
}

.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 {
    margin-top: 1.25rem;
}

/* 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: 767.98px) {
    .stepper-horizontal .step:not(:last-child)::after {
        display: none;
    }

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

.meta-line {
    font-family: var(--ap-font-mono);
    font-size: 0.625rem;
    line-height: 1.9;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    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;
}

/* 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;
}

.site-image {
    width: 100%;
    object-fit: cover;
    border: 1px solid var(--ap-border);
}

/* ---- Audience cards and bullet lists ---- */
.audience-card {
    height: 100%;
    border: 1px solid var(--ap-border);
    border-top: 2px solid var(--ap-accent);
    padding: 1.75rem 1.5rem;
    transition: border-color 0.15s ease;
}

.audience-card:hover {
    border-color: var(--ap-accent);
}

.audience-icon {
    font-size: 1.6rem;
    color: var(--ap-accent);
}

.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 {
    border-top: 1px solid var(--ap-text);
}

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

/* The inverted body pair: no accent token is measured against an ink fill (ADR-0005). */
.offer-ink {
    --bs-border-color: color-mix(in srgb, var(--ap-bg) 22%, transparent);

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

.offer-ink .label,
.offer-ink .offer-note,
.offer-ink__body {
    color: inherit;
    opacity: 0.8;
}

.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,
.hours-card__aside {
    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-ink .eyebrow,
.form-accent .eyebrow,
.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. The accent tokens are measured
   against --ap-bg and carry no guarantee on an ink fill (ADR-0005), so
   everything on it inherits. */
.cta-ink,
.newsletter-band,
.form-ink {
    border-radius: var(--ap-radius);
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

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

.cta-ink .btn-accent,
.newsletter-band .btn-accent,
.form-ink .btn-accent,
.offer-ink .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);
}

/* ---- Blog ---- */
/* Layout goes through the Bootstrap utilities in the template; these classes
   carry nothing but the tokens and the hover. .border already resolves
   --bs-border-color onto --ap-border (theme.css). */
.article-card {
    color: var(--ap-text);
    transition: border-color 0.15s ease;
}

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

.article-card .image-frame,
.article-card .site-image {
    border: 0;
    border-bottom: 1px solid var(--ap-border);
}

/* 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-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;
}

/* ---- 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;
    cursor: pointer;
    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 {
    --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 {
    color: inherit;
}

.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: color-mix(in srgb, var(--ap-accent-contrast) 80%, 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);
}

/* ─────────────────────────────────────────────────────────────
   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;
}

/* Navbar variants: layout re-composition only, same markup tree, same
   tokens, no variant-specific colors. Below lg every variant keeps the stock
   Bootstrap collapse. */
@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;
    }
}

/* 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: a transparent collapse is unreadable. */
@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);
    }

    .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);
    }
}

.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);
}

.article-card,
.audience-card {
    transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

.article-card:hover,
.audience-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
}

.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) {
    .article-card:hover,
    .audience-card:hover {
        transform: none;
    }

    .arrow,
    .btn:hover .arrow,
    a:hover > .arrow {
        transition: none;
        transform: none;
    }

    .reading-progress {
        transition: none;
    }
}

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

.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;
}

/* 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;
}

/* No hairline: an opening photograph reads as a plate, not as a framed
   thumbnail. It takes its crop from its frame like every other slot. */
.site-image.hero-split__image,
.site-image.hero-photo {
    border: 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 overlay case: that is the only one where
   the hero starts 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 .site-nav + .blk .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-vitrine__name {
    font-size: 1rem;
}

.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 {
    padding: 1.75rem;
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
    background: var(--ap-panel);
}

.pricing-tier--on {
    border-color: var(--ap-accent);
    box-shadow: 0 0 0 1px var(--ap-accent);
}

.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);
}

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

@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);
}

.price-matrix-card {
    padding: 1.75rem;
}

.price-matrix-card--on {
    border-color: var(--ap-accent);
    box-shadow: 0 0 0 1px var(--ap-accent);
}

.price-matrix-card__list {
    margin: 0 0 1.25rem;
}

.price-matrix-card__row {
    border-top: 1px solid var(--ap-border);
}

.price-matrix-card__row:first-child {
    border-top: 0;
}

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

/* ---- Key figures ---- */
.stat__value {
    font-family: var(--ap-font-display);
    font-size: clamp(1.9rem, 4vw, 2.75rem);
    line-height: 1;
    color: var(--ap-text);
}

/* 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 .stat__value {
    color: inherit;
    font-size: clamp(2.1rem, 5vw, 3.25rem);
}

.stats-band .label {
    color: color-mix(in srgb, var(--ap-bg) 62%, var(--ap-text));
}

.stats-band__caption {
    max-width: 22ch;
    margin-inline: auto;
    font-size: 0.9rem;
    line-height: 1.5;
    color: color-mix(in srgb, var(--ap-bg) 78%, var(--ap-text));
}

/* ---- 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;
}

.logo-mark:hover {
    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;
}

/* A cell of fixed proportion, so logos of every shape share one height. */
.logo-tile {
    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);
}

/* The inverted body pair: no accent token is measured against an ink fill (ADR-0005). */
.logo-ink {
    border-radius: var(--ap-radius);
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

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

.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;
}

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

    .logo-rail__group {
        animation: none;
    }

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

/* ---- Testimonials ---- */
/* One 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 {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    margin: 0;
    padding: 1.75rem;
    background: var(--ap-panel);
    border: 1px solid var(--ap-border);
    border-radius: var(--ap-radius);
}

.testimonial-card__quote {
    flex: 1;
    margin: 0;
    font-family: var(--ap-font-display);
    font-size: 1.15rem;
    line-height: 1.45;
    text-wrap: pretty;
}

.testimonial-card__who {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--ap-border);
}

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

/* 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;
}

/* ---- 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 {
        position: sticky;
        top: 5rem;
    }
}

/* ---- 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. */
.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);
}

/* One rule per row and one under the last, so the run reads as a sequence rather
   than as three boxes. */
.step-list {
    border-bottom: 1px solid var(--ap-border);
}

.step-list__row {
    display: grid;
    gap: 0.5rem;
    padding: 1.6rem 0;
    border-top: 1px solid var(--ap-border);
}

@media (min-width: 768px) {
    .step-list__row {
        grid-template-columns: 6rem 1fr 1fr;
        gap: 2rem;
        align-items: start;
        padding: 1.9rem 0;
    }
}

/* 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: uppercase;
    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: #fff;
    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: #fff;
    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);
}

/* The inverted body pair: no accent token is measured against an ink fill (ADR-0005). */
.review-figure {
    border-radius: var(--ap-radius);
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.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);
}

/* The inverted body pair: no accent token is measured against an ink fill (ADR-0005). */
.badge-ink {
    border-radius: var(--ap-radius);
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

@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;
}

.badge-card {
    background-color: color-mix(in srgb, var(--ap-text) 5%, var(--ap-bg));
}

.badge-card__icon {
    width: 2.5rem;
    height: 2.5rem;
    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;
}

/* The numbers sit at the foot of the card behind their own rule, so a row of
   cards whose names and organisms wrap differently still lines its proof up. */
.qualification-card__proof {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin-top: 1.25rem;
    padding-top: 1.1rem;
    border-top: 1px solid var(--ap-border);
}

.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,
.qualification-list {
    border-top: 1px solid var(--ap-text);
}

.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;
    }
}

.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;
}

.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__lead {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 600;
}

.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;
}

/* ---- 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);
}

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

.tournees__stop--on .tournees__detail {
    color: inherit;
}

/* 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 {
        justify-content: flex-start;
        align-items: center;
    }

    .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-line__dot {
        margin-top: 0;
    }
}

/* The inverted body pair: no accent token is measured against an ink fill (ADR-0005),
   so the highlighted stop turns back to the page's own pair. */
.tour-board {
    border-radius: var(--ap-radius);
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.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 {
    border-top: 1px solid var(--ap-text);
}

.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__item {
    padding: 1rem 0;
    border-bottom: 1px solid var(--ap-border);
}

.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__media .image-frame,
.price-card__media .site-image {
    border-width: 0 0 1px;
}

.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__flag--pill {
    position: static;
    border-radius: var(--ap-radius-button);
}

.price-card__body {
    padding: 1.1rem 1.25rem 1.25rem;
}

.price-card__price {
    padding-top: 0.75rem;
    border-top: 1px solid var(--ap-border);
    font-family: var(--ap-font-mono);
    color: var(--ap-accent-text);
}

/* ---- Chalkboard ---- */
/* No accent token is guaranteed on an ink fill (ADR-0005): the board keeps the swapped
   body pair and emphasizes by weight alone. */
.ardoise {
    overflow: hidden;
    border-radius: var(--ap-radius);
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.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;
}

.ardoise__photo {
    border: 0;
}

@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,
.ardoise-liste__photo {
    width: 100%;
    height: auto;
    min-height: 0;
    object-fit: cover;
}

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

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

.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: #fff;
}

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

.ardoise-poster__photo {
    border: 0;
    object-fit: cover;
}

.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);
}

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

.ardoise-liste__photo {
    aspect-ratio: 4 / 3;
}

@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;
    }
}

.price-card__price--dashed {
    border-top-style: dashed;
}

/* ---- 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,
.showcase-square,
.showcase-mosaic__photo,
.showcase-fiche__photo {
    width: 100%;
    height: auto;
    min-height: 0;
    object-fit: cover;
}

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

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

.showcase-cover__caption .eyebrow {
    color: inherit;
}

.showcase-cover__title {
    max-width: 16ch;
}

.showcase-cover__link {
    --bs-btn-color: #fff;
    --bs-btn-bg: rgb(255 255 255 / 12%);
    --bs-btn-border-color: rgb(255 255 255 / 70%);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: rgb(255 255 255 / 22%);
    --bs-btn-hover-border-color: #fff;

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

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

/* 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 {
    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__photo {
    aspect-ratio: 3 / 4;
}

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

.showcase-fiche__fact {
    padding-right: 1.5rem;
}

.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 {
    max-width: 52ch;
}

/* 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 ---- */
/* The inverted body pair, like the other ink surfaces: nothing on it keeps a token
   measured against --ap-bg (ADR-0005). */
.content-panel {
    overflow: hidden;
    border-radius: var(--ap-radius);
    background-color: var(--ap-text);
    color: var(--ap-bg);
}

.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: no hairline, and from lg on the height of the
   text beside it rather than a crop of its own. */
.content-panel__media .site-image,
.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);
}

/* ---- 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;
}

/* reservation/split: the picture is flush with the card, cropped 4:3 on a phone and to
   the card's height from lg. */
.resa-split__media .site-image,
.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;
    color: color-mix(in srgb, var(--ap-accent-contrast) 82%, var(--ap-accent));
}


/* ---- 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: in the flow it would sit between the navbar
   and the first block and break the .site-nav + .blk adjacency the full-screen hero
   is promoted by. 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: 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;
}

.products-head__title {
    min-width: 0;
}

/* 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 rayon 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 rayon 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: #fff;
}

.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;
    }
}

/* 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);
    transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1), box-shadow 0.5s ease;
}

/* focus-within, not focus: the card is the link, and a keyboard lands on it. */
.product-card:hover,
.product-card:focus-within {
    color: var(--ap-text);
    transform: translateY(-5px);
    box-shadow: 0 30px 50px -30px rgb(0 0 0 / 35%);
}

.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: 999px;
    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-faint);
}

/* 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);
}

/* None of the frameless styles lifts: a block with no edge rising five pixels off a background
   it shares reads as a mis-set photograph rather than a hover. The two whose photograph is the
   whole object zoom it under its own clip instead, so nothing around it moves. */
.product-card--nue:hover,
.product-card--nue:focus-within,
.product-card--vignette:hover,
.product-card--vignette:focus-within,
.product-card--planche:hover,
.product-card--planche:focus-within {
    transform: none;
    box-shadow: none;
}

.product-card--nue .product-card__media .site-image,
.product-card--planche .product-card__media .site-image {
    transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.product-card--nue:hover .product-card__media .site-image,
.product-card--nue:focus-within .product-card__media .site-image,
.product-card--planche:hover .product-card__media .site-image,
.product-card--planche:focus-within .product-card__media .site-image {
    transform: scale(1.03);
}

.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: #fff;
}

.product-card--bandeau:hover,
.product-card--bandeau:focus-within {
    color: #fff;
}

.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 .site-image {
        transition: none;
    }

    .product-card:hover,
    .product-card:focus-within,
    .product-card:hover .site-image,
    .product-card:focus-within .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 {
        transform: none;
        box-shadow: none;
        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-faint);
    }
}

.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;
    }
}

/* ---------- 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__title {
    margin-top: 0.25rem;
    font-weight: 700;
}

.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,
.price-card {
    color: var(--ap-text);
    transition: opacity 0.15s ease, border-color 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;
}

/* The inverted body pair: no accent token is measured against an ink fill (ADR-0005),
   so the highlighted band stands out by turning back to the page's own pair. */
.price-ink {
    --bs-border-color: color-mix(in srgb, var(--ap-bg) 22%, transparent);

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

.price-ink .label,
.price-ink__body,
.price-ink__count,
.price-ink__note {
    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 {
    background-color: var(--ap-panel);
}

a.price-card:hover {
    border-color: var(--ap-text);
    color: var(--ap-text);
}

.price-card__range {
    aspect-ratio: 4 / 3;
    background-color: color-mix(in srgb, var(--ap-text) 6%, var(--ap-bg));
}

.price-card--on {
    border-color: var(--ap-accent);
}

.price-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-point {
    padding-top: 1rem;
    border-top: 1px solid var(--ap-border);
}

/* ---------- Rayon chips ---------- */

/* Under 992px the chips have the whole width and no column to wrap into: eight
   rayons stack into four rows and push the framed rayon 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: 1px solid var(--ap-border);
    border-radius: var(--ap-radius-button);
    background: var(--ap-panel);
    color: var(--ap-muted);
    font-size: 0.85rem;
    transition: border-color 0.15s ease, color 0.15s ease;
}

.rayon-chip:hover,
.rayon-chip:focus-visible {
    border-color: var(--ap-accent);
    color: var(--ap-accent-text);
}

.rayon-chip.active {
    border-color: var(--ap-accent);
    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 rayon: 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: '⚠ ';
}
