/* =========================================================================
   Component layer
   =========================================================================

   Depends on tokens.css. Nothing here hardcodes a colour: every value comes
   from a --fb-* token, so Nightfall costs nothing extra.

   Load order (see annuaire/templates/annuaire/base.html):
       bootstrap.css → tokens.css → components.css → {vendor_css} → main.css

   Vendor re-skins (leaflet, family-chart) deliberately live in main.css
   instead of here: they have to win against the vendor stylesheets, which
   load after this file.

   Sections
     1  Base & typography
     2  The eaves rule — the one device that unifies every view
     3  The ridge
     4  App shell: sidebar, brand block, content column
     5  Cards: person, place, post, document
     6  Page furniture: toolbars, chips, empty states, alerts, pagination
     7  Archetype D — record detail
     8  Archetype E — long-form
     9  Archetype F — canvas tools
    10  Archetype G — forms
    11  Archetype H — threshold (auth)
    12  Archetype I — tabular
    13  Archetype J — destructive confirm
    14  Motion & focus
    15  Archetype A — hub
    16  Category tree (documents)
    17  Stock-Bootstrap seams

   ========================================================================= */

/* =========================================================================
   1. Base & typography
   ========================================================================= */

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

body {
    margin: 0;
    background: var(--fb-parchment);
    color: var(--fb-ink);
    font-family: var(--fb-font-body);
    font-size: var(--fb-text-base);
    line-height: var(--fb-leading-body);
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, .fb-display {
    font-family: var(--fb-font-display);
    color: var(--fb-ink);
    margin: 0 0 var(--fb-sp-3);
    text-wrap: balance;
}

h1 { font-size: var(--fb-text-h1); font-weight: 700; line-height: var(--fb-leading-tight); letter-spacing: -0.012em; }
h2 { font-size: var(--fb-text-h2); font-weight: 600; line-height: var(--fb-leading-snug); }
h3 { font-size: var(--fb-text-h3); font-weight: 600; line-height: 1.4; letter-spacing: 0.005em; }

.fb-display {
    font-size: var(--fb-text-display);
    font-weight: 700;
    line-height: 1.08;
    letter-spacing: -0.02em;
}

p { margin: 0 0 var(--fb-sp-3); }
p:last-child { margin-bottom: 0; }

a { color: var(--fb-ember); text-decoration-color: color-mix(in srgb, var(--fb-ember) 35%, transparent); text-underline-offset: 0.16em; }
a:hover { color: var(--fb-alpenglow); text-decoration-color: currentColor; }

/* Inline SVG glyphs from the {% icon %} tag. They inherit colour via
   currentColor and sit on the text baseline rather than above it. */
.fb-icon {
    display: inline-block;
    vertical-align: -0.18em;
    flex: none;
}

/* Meta lines: authorship, dates, counts, captions. */
.fb-meta {
    font-size: var(--fb-text-sm);
    color: var(--fb-bark);
    line-height: 1.5;
    margin: 0;
}

/* Small-caps section label. Replaces the ad-hoc bold-uppercase used today in
   the sidebar, table heads, and form legends — one class for all three. */
.fb-eyebrow {
    font-size: var(--fb-text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--fb-tracking-eyebrow);
    color: var(--fb-bark);
    margin: 0;
}

/* =========================================================================
   2. The eaves rule
   -------------------------------------------------------------------------
   The emails put a 2px ink rule over a 1px hairline directly beneath the
   ridge, echoing the fascia trim under a chalet roof. On the web that same
   double rule becomes the page-header device: a short alpenglow stub above a
   full-width hairline. One class, applied to every view's <h1> block, is what
   makes forty templates read as one product.
   ========================================================================= */

.fb-page-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--fb-sp-4);
    padding-bottom: var(--fb-sp-4);
    margin-bottom: var(--fb-sp-5);
    position: relative;
}

.fb-page-head::before,
.fb-page-head::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
}

.fb-page-head::before {   /* the stub */
    width: 56px;
    height: 3px;
    background: var(--fb-alpenglow);
    border-radius: 2px;
    z-index: 1;
}

.fb-page-head::after {    /* the hairline */
    width: 100%;
    height: 1px;
    background: var(--fb-border);
}

.fb-page-head__titles { min-width: 0; }
.fb-page-head__titles h1 { margin-bottom: var(--fb-sp-1); }
.fb-page-head__lede {
    font-size: var(--fb-text-sm);
    color: var(--fb-bark);
    max-width: var(--fb-measure-max);
    margin: 0;
}
.fb-page-head__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--fb-sp-2);
    align-items: center;
}

/* The eaves device on its own, with no title attached — used where a heading
   block has already been written out by hand (long-form articles put the
   byline between the title and the rule). */
.fb-rule {
    position: relative;
    height: 3px;
    margin-bottom: var(--fb-sp-5);
}
.fb-rule::before,
.fb-rule::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
}
.fb-rule::before {
    width: 56px;
    height: 3px;
    background: var(--fb-alpenglow);
    border-radius: 2px;
    z-index: 1;
}
.fb-rule::after {
    width: 100%;
    height: 1px;
    background: var(--fb-border);
}

/* Section rule inside a page — same grammar, quieter. */
.fb-section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--fb-sp-4);
    margin: var(--fb-sp-6) 0 var(--fb-sp-4);
    padding-bottom: var(--fb-sp-2);
    border-bottom: 1px solid var(--fb-border);
}
.fb-section-head h2 { margin: 0; }

/* =========================================================================
   3. The ridge
   -------------------------------------------------------------------------
   Drawn as SVG here rather than the emails' bordered-triangle hack, because
   nothing on the web needs to survive Outlook. Same three-uneven-peaks
   silhouette, same layering (slate-mist behind, spruce in front, snowcap on
   the tallest), and the two pines from favicon.svg — the only pre-existing
   brand asset — planted at the base so the icon and the app share a mark.

   It appears in exactly three places: the sidebar brand block, the threshold
   card header, and as a watermark in empty states. A ridge on every page
   would be wallpaper; three placements make it a signature.
   ========================================================================= */

.fb-ridge {
    display: block;
    width: 100%;
    height: auto;
    background: var(--fb-sky);
}
.fb-ridge__far   { fill: var(--fb-slate-mist); }
.fb-ridge__near  { fill: var(--fb-spruce); }
.fb-ridge__snow  { fill: var(--fb-snowcap); }
.fb-ridge__pine  { fill: color-mix(in srgb, var(--fb-spruce) 72%, #000); }

/* The eaves line as it appears under a ridge (not under a page title). */
.fb-eaves {
    height: 3px;
    background: var(--fb-ink);
    border-bottom: 1px solid var(--fb-border);
}

.fb-wordmark {
    font-family: var(--fb-font-display);
    font-size: var(--fb-text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--fb-tracking-wordmark);
    color: var(--fb-ink);
    text-decoration: none;
    white-space: nowrap;
}

/* =========================================================================
   4. App shell
   ========================================================================= */

.fb-layout {
    display: flex;
    min-height: 100vh;
    align-items: stretch;
}

/* The offcanvas host. Bootstrap's .offcanvas-lg is display:flex below the lg
   breakpoint and a plain block above it; the sidebar panel inside does the
   actual styling, so this element only has to get out of the way and not
   introduce padding of its own. */
.fb-sidebar-host { flex-shrink: 0; }

@media (min-width: 992px) {
    .fb-sidebar-host .offcanvas-body { padding: 0; overflow: visible; }
    .fb-sidebar-host .offcanvas-header { display: none; }
}

.fb-sidebar {
    width: var(--fb-sidebar-w);
    flex-shrink: 0;
    background: var(--fb-panel);
    border-right: 1px solid var(--fb-border);
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

@media (min-width: 992px) {
    .fb-sidebar {
        position: sticky;
        top: 0;
        height: 100vh;
        overflow-y: auto;
    }
}

/* Inside the mobile offcanvas the panel is already the drawer — it must not
   carry its own border or a fixed width. */
@media (max-width: 991.98px) {
    .fb-sidebar { width: 100%; border-right: 0; min-height: 0; }
}

/* --- Brand block: the email header, transplanted ------------------------ */
.fb-brand {
    display: block;
    background: var(--fb-sky);
    position: relative;
    text-decoration: none;
}
/* Reserves the vertical room the absolutely-positioned wordmark occupies, so
   the ridge starts below it rather than behind it. */
.fb-brand__spacer { display: block; height: 34px; }
.fb-brand__name {
    position: absolute;
    top: var(--fb-sp-4);
    left: 0;
    right: 0;
    text-align: center;
}
.fb-brand .fb-ridge { height: 62px; }

/* --- Identity pill ------------------------------------------------------ */
.fb-identity {
    display: flex;
    align-items: center;
    gap: var(--fb-sp-3);
    margin: var(--fb-sp-3);
    padding: var(--fb-sp-1) var(--fb-sp-3);
    background: var(--fb-card);
    border: 1px solid var(--fb-border);
    border-radius: var(--fb-radius);
    text-decoration: none;
    color: var(--fb-ink);
    transition: border-color var(--fb-dur-fast) var(--fb-ease);
}
.fb-identity:hover { border-color: var(--fb-alpenglow); color: var(--fb-ink); }
.fb-identity img {
    width: 28px; height: 28px;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--fb-border);
}
.fb-identity__text { min-width: 0; display: grid; }
.fb-identity__name {
    font-weight: 600;
    font-size: var(--fb-text-sm);
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.fb-identity__role { font-size: var(--fb-text-xs); color: var(--fb-bark); }

/* --- Nav ---------------------------------------------------------------- */
/* The nav scrolls, the foot below it does not: a nav taller than the viewport
   used to push the theme toggle and logout below the fold on a 768p laptop,
   reachable only through an unaffordanced nested scroll. */
.fb-nav {
    padding: 0 var(--fb-sp-3) var(--fb-sp-4);
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}
.fb-nav__group { margin-top: var(--fb-sp-4); }
.fb-nav__label { padding: 0 var(--fb-sp-3) var(--fb-sp-1); }
.fb-nav ul { list-style: none; margin: 0; padding: 0; }

.fb-nav a,
.fb-nav__logout {
    display: block;
    width: 100%;
    text-align: left;
    padding: var(--fb-sp-1) var(--fb-sp-3);
    margin-bottom: 1px;
    border: 0;
    border-left: 3px solid transparent;
    border-radius: 0 var(--fb-radius-sm) var(--fb-radius-sm) 0;
    background: none;
    font: inherit;
    font-size: 0.875rem;
    color: var(--fb-ink);
    text-decoration: none;
    cursor: pointer;
    transition: background var(--fb-dur-fast) var(--fb-ease),
                border-color var(--fb-dur-fast) var(--fb-ease);
}
.fb-nav a:hover,
.fb-nav__logout:hover {
    background: color-mix(in srgb, var(--fb-card) 70%, transparent);
    border-left-color: var(--fb-border-strong);
    color: var(--fb-ink);
}

/* Today the sidebar has no active state at all — you cannot tell which page
   you are on. This is that state. */
.fb-nav a[aria-current="page"] {
    background: var(--fb-card);
    border-left-color: var(--fb-alpenglow);
    font-weight: 600;
    color: var(--fb-ink);
}

.fb-nav__foot {
    flex: 0 0 auto;
    margin: 0 var(--fb-sp-3);
    padding: var(--fb-sp-3) 0;
    border-top: 1px solid var(--fb-border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--fb-sp-2);
}
.fb-nav__logout-form { margin: 0; }

/* The nav's own <a> rule above turns every descendant link into a full-width
   nav row; the foot's buttons and links are ordinary inline controls. */
.fb-nav__foot a {
    display: inline-flex;
    width: auto;
    border-left: 0;
    padding: 0.25rem 0.625rem;
}

/* On mobile the sidebar is the offcanvas drawer, which scrolls as one piece --
   a nested scrolling nav there would trap the foot instead of revealing it. */
@media (max-width: 991.98px) {
    .fb-nav { overflow-y: visible; }
}

/* --- Content column ----------------------------------------------------- */
.fb-content {
    flex: 1;
    min-width: 0;   /* keep: wide canvases must scroll inside, not push the shell */
    padding: var(--fb-sp-6) var(--fb-sp-6) var(--fb-sp-8);
}
.fb-content__inner { max-width: var(--fb-content-max); margin: 0 auto; }
.fb-footer {
    margin-top: var(--fb-sp-8);
    text-align: center;
    font-size: var(--fb-text-xs);
    color: var(--fb-bark);
}

/* --- Search form ---------------------------------------------------------
   Mounted twice (annuaire/_search_form.html): in .fb-topbar (mobile) and at
   the top of .fb-sidebar's <nav> (desktop) -- .fb-topbar is mobile-only,
   see below. */
.fb-search-form { margin: 0; }
.fb-topbar .fb-search-form { flex: 1 1 auto; min-width: 0; }
.fb-topbar .fb-search-form .fb-input { width: 100%; }
.fb-nav .fb-search-form { margin-bottom: var(--fb-sp-4); }
.fb-nav .fb-search-form .fb-input { width: 100%; }

/* --- Mobile ------------------------------------------------------------- */
.fb-topbar {
    display: none;
    align-items: center;
    gap: var(--fb-sp-3);
    padding: var(--fb-sp-3) var(--fb-sp-4);
    background: var(--fb-sky);
    border-bottom: 3px solid var(--fb-ink);
    box-shadow: inset 0 -4px 0 -1px var(--fb-border);
    position: sticky;
    top: 0;
    z-index: 1040;
}

@media (max-width: 991.98px) {
    .fb-topbar { display: flex; }
    .fb-sidebar { position: static; height: auto; }
    .fb-content { padding: var(--fb-sp-5) var(--fb-sp-4) var(--fb-sp-7); }
}

/* =========================================================================
   5. Cards
   ========================================================================= */

.fb-card {
    background: var(--fb-card);
    border: 1px solid var(--fb-border);
    border-radius: var(--fb-radius);
    box-shadow: var(--fb-shadow-1);
    padding: var(--fb-sp-5);
}

a.fb-card,
.fb-card--link {
    display: block;
    text-decoration: none;
    color: inherit;
    transition: transform var(--fb-dur-fast) var(--fb-ease),
                box-shadow var(--fb-dur-fast) var(--fb-ease),
                border-color var(--fb-dur-fast) var(--fb-ease);
}
a.fb-card:hover,
.fb-card--link:hover {
    transform: translateY(-2px);
    box-shadow: var(--fb-shadow-2);
    border-color: color-mix(in srgb, var(--fb-alpenglow) 45%, var(--fb-border));
    color: inherit;
}

.fb-grid {
    display: grid;
    gap: var(--fb-sp-4);
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}
.fb-grid--wide { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.fb-grid--tiles {
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: var(--fb-sp-5);
    align-items: start;   /* tiles size to content — no more stretched voids */
}

/* --- Person card: people are round -------------------------------------- */
/* Two-class selector on purpose: the `a.fb-card` rule above is (0,1,1) and would
   otherwise win regardless of order, collapsing the card back to inline flow. */
.fb-card.fb-person-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--fb-sp-2);
    padding: var(--fb-sp-4) var(--fb-sp-3);
}
.fb-avatar {
    width: 76px; height: 76px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--fb-panel);
    border: 2px solid var(--fb-border);
    transition: border-color var(--fb-dur-fast) var(--fb-ease);
}
.fb-card--link:hover .fb-avatar { border-color: var(--fb-gold); }
.fb-avatar--sm { width: 40px; height: 40px; border-width: 1px; }
.fb-avatar--lg { width: 150px; height: 150px; border-width: 3px; }

/* Existing behaviour, recoloured: deceased profiles are greyed and ringed. */
.fb-avatar--deceased { filter: grayscale(1); border-color: var(--fb-ink); }

.fb-person-card__name { font-weight: 600; font-size: 0.9375rem; line-height: 1.25; }

/* --- Place card: places are rectangular --------------------------------- */
.fb-place-card { padding: 0; overflow: hidden; }
.fb-place-card__media {
    aspect-ratio: 3 / 2;
    width: 100%;
    object-fit: cover;
    display: block;
    background: var(--fb-sky);
}
/* display:block spelled out because these are <span>s inside an <a> card —
   an <a> may not contain block elements, so the card body is built from
   inline elements and made block by CSS. */
.fb-place-card__body { display: block; padding: var(--fb-sp-3) var(--fb-sp-4) var(--fb-sp-4); }
.fb-place-card__name { display: block; font-family: var(--fb-font-display); font-weight: 600; margin-bottom: 2px; }
.fb-place-card__body .fb-meta { display: block; }

/* Placeholder for a place with no photo. Replaces the 🏔️ emoji currently
   used, which renders as a tofu box wherever the system has no emoji font —
   already visible in the project's own screenshots. */
.fb-place-placeholder {
    aspect-ratio: 3 / 2;
    background: var(--fb-sky);
    display: flex;
    align-items: flex-end;
    overflow: hidden;
}

/* --- Post card ---------------------------------------------------------- */
.fb-post-card {
    position: relative;
    padding-left: calc(var(--fb-sp-5) + 3px);
    margin-bottom: var(--fb-sp-4);
    transition: transform var(--fb-dur-fast) var(--fb-ease),
                box-shadow var(--fb-dur-fast) var(--fb-ease),
                border-color var(--fb-dur-fast) var(--fb-ease);
}
.fb-post-card::before {
    content: "";
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 3px;
    border-radius: var(--fb-radius) 0 0 var(--fb-radius);
    background: var(--fb-alpenglow);
}
.fb-post-card--gold::before { background: var(--fb-gold); }
.fb-post-card__title { font-size: 1.25rem; margin-bottom: var(--fb-sp-1); }
.fb-post-card__title a { text-decoration: none; color: var(--fb-ink); }
.fb-post-card__title a:hover { color: var(--fb-ember); }
.fb-post-card__excerpt {
    margin: var(--fb-sp-3) 0 0;
    color: var(--fb-bark);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* --- Whole-card click target (#128) ------------------------------------- */
/* The title link carries Bootstrap's .stretched-link, whose ::after overlay
   fills the nearest positioned ancestor -- the card itself. Side effect of
   the technique: text over the card body is no longer selectable. Written as
   its own rule rather than folded into the a.fb-card group above, which the
   source-text test helpers read by exact selector. */
.fb-post-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--fb-shadow-2);
    border-color: color-mix(in srgb, var(--fb-alpenglow) 45%, var(--fb-border));
}

/* Links in the meta line sit above the overlay so they stay independently
   clickable -- losing the author and category links would be a regression. */
.fb-post-card .fb-meta a {
    position: relative;
    z-index: 2;
}

/* Keyboard focus has to read on the whole card, not just the title words. */
.fb-post-card:has(.stretched-link:focus-visible) {
    outline: 2px solid var(--fb-alpenglow);
    outline-offset: 2px;
}

/* --- Documents list row (#123) ------------------------------------------ */
/* Scoped to a documents modifier: the same post card renders the publications
   feed, which keeps its roomier spacing. Two-class selector because the base
   rule has identical specificity and would otherwise win on source order. */
.fb-post-card.fb-post-card--doc {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--fb-sp-1) var(--fb-sp-4);
    padding: var(--fb-sp-4);
    padding-left: calc(var(--fb-sp-4) + 3px);
    margin-bottom: var(--fb-sp-3);
}

/* Basis rather than a fixed width: the meta drops onto its own line on a phone
   instead of overflowing next to a long title. */
.fb-post-card--doc .fb-post-card__body {
    flex: 1 1 16rem;
    min-width: 0;
}

.fb-post-card--doc .fb-post-card__meta {
    margin-left: auto;
    text-align: right;
    white-space: nowrap;
}

.fb-post-card--doc .fb-post-card__excerpt {
    font-size: var(--fb-text-sm);
    line-height: 1.4;
    margin-top: var(--fb-sp-1);
}

/* =========================================================================
   6. Page furniture
   ========================================================================= */

/* --- Chip / badge ------------------------------------------------------- */
.fb-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--fb-sp-1);
    padding: 2px var(--fb-sp-2);
    border-radius: var(--fb-radius-pill);
    border: 1px solid var(--fb-border);
    background: var(--fb-parchment);
    font-size: var(--fb-text-xs);
    font-weight: 600;
    color: var(--fb-bark);
    white-space: nowrap;
}
.fb-chip--accent  { background: var(--fb-ember-soft);   border-color: color-mix(in srgb, var(--fb-alpenglow) 40%, transparent); color: var(--fb-ember); }
.fb-chip--gold    { background: var(--fb-warning-soft); border-color: color-mix(in srgb, var(--fb-gold) 55%, transparent); color: var(--fb-ink); }
.fb-chip--success { background: var(--fb-success-soft); border-color: color-mix(in srgb, var(--fb-success) 40%, transparent); color: var(--fb-ink); }
.fb-chip--danger  { background: var(--fb-danger-soft);  border-color: color-mix(in srgb, var(--fb-danger) 40%, transparent); color: var(--fb-ink); }
.fb-chip--lock    { border-style: dashed; }

/* --- Toolbar ------------------------------------------------------------ */
/* A filter/search/action strip. Reads as one object with whatever it sits on
   (a results grid, a canvas), not as a floating row of loose controls. */
.fb-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--fb-sp-2);
    padding: var(--fb-sp-3);
    background: var(--fb-card);
    border: 1px solid var(--fb-border);
    border-radius: var(--fb-radius);
    margin-bottom: var(--fb-sp-4);
}
.fb-toolbar--attached {
    border-radius: var(--fb-radius) var(--fb-radius) 0 0;
    border-bottom: 0;
    margin-bottom: 0;
}
.fb-toolbar__spacer { margin-left: auto; }

.fb-search {
    flex: 1 1 240px;
    min-width: 180px;
    max-width: 340px;
}

/* --- Empty state -------------------------------------------------------- */
/* Today every "nothing here" is a bare <p>. Fifteen of them across the app.
   One component, with the ridge as a faint watermark. */
.fb-empty {
    /* Span the whole track when dropped straight into a .fb-grid — otherwise the
       empty state renders inside a single ~180px column. */
    grid-column: 1 / -1;
    text-align: center;
    padding: var(--fb-sp-7) var(--fb-sp-4);
    color: var(--fb-bark);
    border: 1px dashed var(--fb-border-strong);
    border-radius: var(--fb-radius);
    background: color-mix(in srgb, var(--fb-card) 55%, transparent);
}
.fb-empty__ridge {
    width: 120px;
    margin: 0 auto var(--fb-sp-3);
    opacity: 0.5;
    background: none;
}
/* The watermark reads off the card, not off the sky — so it uses the far-ridge
   haze rather than spruce, which in Nightfall is nearly the card colour itself. */
.fb-empty__ridge .fb-ridge__near { fill: var(--fb-slate-mist); }
.fb-empty__ridge .fb-ridge__snow { fill: var(--fb-bark); }
.fb-empty__title { font-family: var(--fb-font-display); font-weight: 600; color: var(--fb-ink); margin-bottom: var(--fb-sp-1); }
.fb-empty p { font-size: var(--fb-text-sm); }
.fb-empty__hint { color: var(--fb-bark); margin-top: var(--fb-sp-2); }
.fb-empty__action { margin-top: var(--fb-sp-4); }
.fb-empty__secondary { margin-top: var(--fb-sp-2); }

/* --- Alerts ------------------------------------------------------------- */
/* Ground is tinted, text is always --fb-ink: AA by construction, whatever the
   status. Fixes today's split personality where .alert-error is hand-rolled
   and success/info/warning fall through to stock Bootstrap. */
.fb-alert {
    display: block;   /* block, not flex: the body is inline prose with <strong>
                         and links in it, which a flex container would blow apart
                         into one column item per text run. */
    padding: var(--fb-sp-3) var(--fb-sp-4);
    border: 1px solid var(--fb-border);
    border-left: 3px solid var(--fb-bark);
    border-radius: var(--fb-radius-sm);
    background: var(--fb-card);
    color: var(--fb-ink);
    margin-bottom: var(--fb-sp-3);
    font-size: 0.9375rem;
}
.fb-alert--success { background: var(--fb-success-soft); border-left-color: var(--fb-success); }
.fb-alert--info    { background: var(--fb-info-soft);    border-left-color: var(--fb-info); }
.fb-alert--warning { background: var(--fb-warning-soft); border-left-color: var(--fb-warning); }
.fb-alert--error,
.fb-alert--danger  { background: var(--fb-danger-soft);  border-left-color: var(--fb-danger); }

/* --- Buttons ------------------------------------------------------------ */
/* Stock .btn inherits the palette through the Bootstrap bridge; these are the
   two shapes the bridge cannot express. */
.fb-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--fb-sp-2);
    padding: 0.5rem 1rem;
    border-radius: var(--fb-radius-sm);
    border: 1px solid transparent;
    font: inherit;
    font-size: 0.9375rem;
    font-weight: 600;
    line-height: 1.4;
    text-decoration: none;
    cursor: pointer;
    transition: background var(--fb-dur-fast) var(--fb-ease),
                border-color var(--fb-dur-fast) var(--fb-ease),
                color var(--fb-dur-fast) var(--fb-ease);
}
/* Filled with ember, not alpenglow: card-tinted text on alpenglow is 2.98:1
   and fails AA. On ember it is 5.90:1. Alpenglow stays the hover. */
.fb-btn--primary { background: var(--fb-ember); color: var(--fb-card); }
.fb-btn--primary:hover { background: var(--fb-alpenglow); color: var(--fb-card); }

.fb-btn--quiet { background: var(--fb-card); border-color: var(--fb-border-strong); color: var(--fb-ink); }
.fb-btn--quiet:hover { border-color: var(--fb-alpenglow); color: var(--fb-ember); }

.fb-btn--danger { background: var(--fb-danger); color: var(--fb-card); }
.fb-btn--ghost { background: none; color: var(--fb-bark); padding-inline: var(--fb-sp-2); }
.fb-btn--ghost:hover { color: var(--fb-ember); }
.fb-btn--sm { padding: 0.25rem 0.625rem; font-size: var(--fb-text-sm); }

/* A destructive action that is not the page's primary one — a per-row delete
   in a table, say. Filled red would shout on every line. */
.fb-btn--danger-text { color: var(--fb-danger); }
.fb-btn--danger-text:hover { color: var(--fb-danger); background: var(--fb-danger-soft); }

/* Eyebrow directly under a card's top padding needs a little air below it. */
.fb-card__label { margin-bottom: var(--fb-sp-3); }

/* --- Pagination --------------------------------------------------------- */
.fb-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--fb-sp-4);
    margin-top: var(--fb-sp-6);
    padding-top: var(--fb-sp-4);
    border-top: 1px solid var(--fb-border);
    font-size: var(--fb-text-sm);
    color: var(--fb-bark);
}

/* =========================================================================
   7. Archetype D — record detail (personne, résidence)
   -------------------------------------------------------------------------
   Today a person page is one full-width column: an image that grows on hover,
   an <h1> with four buttons crammed inline after it, emoji-bulleted contact
   lines, then relations as 1.5em avatars with no names — and two thirds of
   the screen empty. This splits it into a sticky identity rail and a content
   column, and gives relations back their names.
   ========================================================================= */

.fb-record {
    display: grid;
    grid-template-columns: 280px minmax(0, 1fr);
    gap: var(--fb-sp-6);
    align-items: start;
}
@media (max-width: 767.98px) {
    .fb-record { grid-template-columns: minmax(0, 1fr); gap: var(--fb-sp-5); }
}

.fb-record__rail { position: sticky; top: var(--fb-sp-5); display: grid; gap: var(--fb-sp-4); }
.fb-record__portrait { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--fb-sp-3); }
.fb-record__name { font-size: 1.5rem; margin: 0; }
.fb-record__actions { display: grid; gap: var(--fb-sp-2); }

/* Contact rows: the emoji stays (it is warm, and it is free), but it moves
   into a fixed-width muted column so the values align into a real list. */
.fb-contact { display: grid; gap: 0; }
.fb-contact__row {
    display: grid;
    grid-template-columns: 1.75rem minmax(0, 1fr);
    align-items: baseline;
    gap: var(--fb-sp-2);
    padding: var(--fb-sp-2) 0;
    border-bottom: 1px solid var(--fb-border);
    font-size: 0.9375rem;
}
.fb-contact__row:last-child { border-bottom: 0; }
.fb-contact__icon { opacity: 0.75; font-size: 0.9rem; }
.fb-contact__value { min-width: 0; overflow-wrap: anywhere; }

/* Relations, with names. */
.fb-relations { display: grid; gap: var(--fb-sp-5); }
.fb-relation-group__label { margin-bottom: var(--fb-sp-3); }
.fb-relation-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
    gap: var(--fb-sp-3);
}
.fb-relation {
    display: flex;
    align-items: center;
    gap: var(--fb-sp-3);
    padding: var(--fb-sp-2);
    border: 1px solid var(--fb-border);
    border-radius: var(--fb-radius-pill);
    background: var(--fb-card);
    text-decoration: none;
    color: var(--fb-ink);
    font-size: var(--fb-text-sm);
    font-weight: 600;
    transition: border-color var(--fb-dur-fast) var(--fb-ease);
}
.fb-relation:hover { border-color: var(--fb-alpenglow); color: var(--fb-ink); }

/* =========================================================================
   8. Archetype E — long-form (publication, document, help)
   ========================================================================= */

.fb-longform { max-width: var(--fb-measure-max); }
.fb-longform__title { font-size: 2.25rem; line-height: 1.1; margin-bottom: var(--fb-sp-3); }
.fb-longform__body {
    font-size: var(--fb-text-lg);
    line-height: var(--fb-leading-long);
}
.fb-longform__body > h2 { margin-top: var(--fb-sp-6); }
.fb-longform__body > h3 { margin-top: var(--fb-sp-5); }
.fb-longform__body blockquote {
    margin: var(--fb-sp-5) 0;
    padding-left: var(--fb-sp-4);
    border-left: 3px solid var(--fb-alpenglow);
    color: var(--fb-bark);
    font-style: italic;
}
.fb-longform__body pre,
.fb-longform__body code { background: var(--fb-parchment); border-radius: var(--fb-radius-sm); }
.fb-longform__body pre { padding: var(--fb-sp-3) var(--fb-sp-4); border: 1px solid var(--fb-border); overflow-x: auto; }
.fb-longform__body img { max-width: 100%; height: auto; border-radius: var(--fb-radius-sm); }
.fb-longform.fb-longform--full { max-width: none; margin-bottom: var(--fb-sp-6); }

/* Attachments gallery */
.fb-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: var(--fb-sp-3);
}
.fb-gallery figure { margin: 0; }
.fb-gallery img {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: var(--fb-radius-sm);
    border: 1px solid var(--fb-border);
    display: block;
}
.fb-gallery figcaption { font-size: var(--fb-text-xs); color: var(--fb-bark); margin-top: var(--fb-sp-1); }

/* Comments */
.fb-comment {
    display: grid;
    grid-template-columns: 32px minmax(0, 1fr);
    gap: var(--fb-sp-3);
    padding: var(--fb-sp-4) 0;
    border-bottom: 1px solid var(--fb-border);
}
.fb-comment__disc {
    width: 32px; height: 32px;
    border-radius: 50%;
    background: var(--fb-ember-soft);
    color: var(--fb-ember);
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--fb-font-display);
    font-weight: 700;
    font-size: var(--fb-text-sm);
}
.fb-comment__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--fb-sp-2); margin-bottom: var(--fb-sp-1); }
.fb-comment__author { font-weight: 700; }

/* "Your turn": recessed ground inside the card so the composer reads as a
   different surface from the comments above it. */
.fb-composer {
    margin-top: var(--fb-sp-5);
    padding: var(--fb-sp-4);
    background: var(--fb-parchment);
    border: 1px solid var(--fb-border);
    border-radius: var(--fb-radius);
}

/* =========================================================================
   9. Archetype F — canvas tools (carte, généalogie, visionneuse)
   -------------------------------------------------------------------------
   Rule for this archetype: the chrome recedes. Page head, then a toolbar
   welded to the canvas so the two form one instrument.
   ========================================================================= */

.fb-canvas {
    height: 70vh;
    min-height: 340px;
    border: 1px solid var(--fb-border);
    border-radius: 0 0 var(--fb-radius) var(--fb-radius);
    background: var(--fb-card);
    overflow: hidden;
    position: relative;
    z-index: 0;   /* keep: traps leaflet's panes below positioned siblings */
}
.fb-canvas--stage {
    background: var(--fb-parchment);
    display: flex;
    align-items: safe center;
    justify-content: safe center;
    overflow: auto;
    padding: var(--fb-sp-3);
}

.fb-canvas__aside {
    width: 320px;
    padding: var(--fb-sp-4);
    background: var(--fb-card);
    border: 1px solid var(--fb-border);
    border-radius: var(--fb-radius);
    box-shadow: var(--fb-shadow-2);
}

/* =========================================================================
   10. Archetype G — forms
   ========================================================================= */

.fb-form { max-width: 46rem; }
.fb-fieldset {
    border: 1px solid var(--fb-border);
    border-radius: var(--fb-radius);
    background: var(--fb-card);
    box-shadow: var(--fb-shadow-1);
    padding: var(--fb-sp-5);
    margin: 0 0 var(--fb-sp-5);
}
.fb-fieldset > legend {
    float: left;          /* keep the legend in flow so padding applies */
    width: 100%;
    padding: 0 0 var(--fb-sp-4);
    margin: 0;
    font-family: var(--fb-font-display);
    font-size: var(--fb-text-h2);
    font-weight: 600;
    color: var(--fb-ink);
    border-bottom: 1px solid var(--fb-border);
    margin-bottom: var(--fb-sp-4);
}
/* The floated legend above is what lets padding apply to it; every sibling
   after it has to clear that float, or a flex/grid child shrink-wraps beside
   it instead of starting on its own line. */
.fb-fieldset > legend ~ * { clear: both; }

.fb-field { margin-bottom: var(--fb-sp-4); }
.fb-field > label,
.fb-label {
    display: block;
    font-size: var(--fb-text-sm);
    font-weight: 700;
    color: var(--fb-ink);
    margin-bottom: var(--fb-sp-1);
}
.fb-input,
.fb-select,
.fb-textarea {
    width: 100%;
    padding: 0.5rem 0.75rem;
    font: inherit;
    font-size: 0.9375rem;
    color: var(--fb-ink);
    background: var(--fb-card);
    border: 1px solid var(--fb-border-strong);
    border-radius: var(--fb-radius-sm);
    transition: border-color var(--fb-dur-fast) var(--fb-ease),
                box-shadow var(--fb-dur-fast) var(--fb-ease);
}
.fb-input:focus,
.fb-select:focus,
.fb-textarea:focus {
    outline: none;
    border-color: var(--fb-alpenglow);
    box-shadow: 0 0 0 3px rgba(217, 121, 63, 0.18);
}
.fb-input::placeholder { color: var(--fb-bark); opacity: 0.7; }
.fb-help { font-size: var(--fb-text-sm); color: var(--fb-bark); margin-top: var(--fb-sp-1); }
.fb-error { font-size: var(--fb-text-sm); color: var(--fb-danger); font-weight: 600; margin-top: var(--fb-sp-1); }
.fb-field--invalid .fb-input { border-color: var(--fb-danger); }

.fb-form__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--fb-sp-3);
    padding-top: var(--fb-sp-4);
    border-top: 1px solid var(--fb-border);
}

/* =========================================================================
   11. Archetype H — threshold (login, signup, magic link, password reset)
   -------------------------------------------------------------------------
   These pages get NO sidebar. A logged-out visitor should not be looking at a
   nav full of links that will all bounce back to the login form.

   The card is deliberately the transactional email's card, 1:1 — same ridge,
   same wordmark, same eaves line, same 600px column feel. Clicking a magic
   link and landing here should feel like walking through the email's front
   door rather than arriving somewhere else.
   ========================================================================= */

.fb-threshold {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--fb-sp-6) var(--fb-sp-4);
    background: var(--fb-parchment);
}
.fb-threshold__card {
    width: 100%;
    max-width: 460px;
    background: var(--fb-card);
    border: 1px solid var(--fb-border);
    border-radius: var(--fb-radius-lg);
    box-shadow: var(--fb-shadow-3);
    overflow: hidden;
}
.fb-threshold__head { background: var(--fb-sky); padding-top: var(--fb-sp-5); text-align: center; }
.fb-threshold__body { padding: var(--fb-sp-6); }
.fb-threshold__body h1 { font-size: 1.5rem; text-align: left; }
.fb-threshold__links {
    margin-top: var(--fb-sp-5);
    padding-top: var(--fb-sp-4);
    border-top: 1px solid var(--fb-border);
    font-size: var(--fb-text-sm);
    color: var(--fb-bark);
    display: grid;
    gap: var(--fb-sp-2);
}
.fb-threshold__foot {
    text-align: center;
    margin-top: var(--fb-sp-4);
    font-size: var(--fb-text-xs);
    color: var(--fb-bark);
}

/* The restrained variant, matching the transactional emails: no peaks, one
   flat horizon line. Used for password reset / magic link / account setup. */
.fb-horizon { height: 14px; background: var(--fb-spruce); }

/* =========================================================================
   12. Archetype I — tabular (groupes, création de comptes en masse)
   ========================================================================= */

.fb-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--fb-card);
    border: 1px solid var(--fb-border);
    border-radius: var(--fb-radius);
    overflow: hidden;
    font-size: 0.9375rem;
}
.fb-table thead th {
    background: var(--fb-panel);
    text-align: left;
    padding: var(--fb-sp-3) var(--fb-sp-4);
    font-size: var(--fb-text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--fb-tracking-eyebrow);
    color: var(--fb-bark);
    border-bottom: 1px solid var(--fb-border);
    white-space: nowrap;
}
.fb-table td {
    padding: var(--fb-sp-3) var(--fb-sp-4);
    border-bottom: 1px solid var(--fb-border);
    vertical-align: middle;
}
.fb-table tbody tr:last-child td { border-bottom: 0; }
.fb-table tbody tr:hover { background: color-mix(in srgb, var(--fb-parchment) 60%, transparent); }
.fb-table__actions { display: flex; gap: var(--fb-sp-2); justify-content: flex-end; }

/* Presence rows keep their semantic tint, retuned to the palette. */
.fb-row--current { background: var(--fb-success-soft); }
.fb-row--past td { color: var(--fb-bark); }

/* =========================================================================
   13. Archetype J — destructive confirm
   ========================================================================= */

.fb-confirm {
    max-width: 34rem;
    border-left: 3px solid var(--fb-danger);
}
.fb-confirm h1 { font-size: 1.5rem; }
.fb-confirm__consequences {
    margin: var(--fb-sp-4) 0;
    padding: var(--fb-sp-3) var(--fb-sp-4);
    background: var(--fb-danger-soft);
    border-radius: var(--fb-radius-sm);
    font-size: 0.9375rem;
}
.fb-confirm__consequences ul { margin: 0; padding-left: 1.1rem; }

/* =========================================================================
   14. Motion & focus
   ========================================================================= */

/* Today only the genealogy search has a focus ring. This gives every
   interactive element one, in the accent colour. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--fb-alpenglow);
    outline-offset: 2px;
    border-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* =========================================================================
   15. Archetype A — hub
   -------------------------------------------------------------------------
   The home page only. The ridge banner is the one place the full motif
   appears in the content column.
   ========================================================================= */

.fb-hub-banner {
    position: relative;
    border: 1px solid var(--fb-border);
    border-radius: var(--fb-radius);
    overflow: hidden;
    margin-bottom: var(--fb-sp-6);
    background: var(--fb-sky);
}

.fb-hub-banner__ridge { height: 112px; }
.fb-hub-banner__ridge .fb-ridge { height: 112px; }

.fb-hub-banner__body {
    background: var(--fb-card);
    padding: var(--fb-sp-5) var(--fb-sp-6);
    border-top: 3px solid var(--fb-ink);
    box-shadow: inset 0 1px 0 0 var(--fb-border);
}

.fb-hub-banner__body h1 { margin-bottom: var(--fb-sp-2); }

/* --- Tiles -------------------------------------------------------------- */

.fb-tile__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--fb-sp-3);
    padding-bottom: var(--fb-sp-3);
    margin-bottom: var(--fb-sp-4);
    border-bottom: 1px solid var(--fb-border);
}

.fb-tile__head h2 { margin: 0; font-size: 1.125rem; }

.fb-tile__link {
    font-size: var(--fb-text-sm);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.fb-tile__subhead { margin: var(--fb-sp-5) 0 var(--fb-sp-2); }

/* --- Mini grid (thumbnails inside a tile) -------------------------------- */

.fb-mini-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--fb-sp-3);
}

.fb-mini {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--fb-sp-1);
    text-decoration: none;
    color: inherit;
    text-align: center;
}

.fb-mini__photo {
    width: 54px;
    height: 54px;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid var(--fb-border);
    background: var(--fb-panel);
    transition: border-color var(--fb-dur-fast) var(--fb-ease);
}

/* People are round, places are rectangular — the same distinction the
   collection grids make. */
.fb-mini__photo--place {
    width: 54px;
    height: 36px;
    border-radius: var(--fb-radius-sm);
}

.fb-mini:hover .fb-mini__photo { border-color: var(--fb-gold); }

.fb-mini__name {
    font-size: var(--fb-text-xs);
    line-height: 1.2;
    color: var(--fb-ink);
}

/* --- Feed (a list of recent things inside a tile) ------------------------ */

.fb-feed { display: grid; }

.fb-feed__item {
    padding: var(--fb-sp-3) 0;
    border-bottom: 1px solid var(--fb-border);
}

.fb-feed__item:first-child { padding-top: 0; }
.fb-feed__item:last-child { border-bottom: 0; padding-bottom: 0; }

.fb-feed__item > a {
    text-decoration: none;
    font-weight: 600;
    color: var(--fb-ink);
}

.fb-feed__item > a:hover { color: var(--fb-ember); }

.fb-feed .fb-comment:first-child { padding-top: 0; }
.fb-feed .fb-comment:last-child { border-bottom: 0; padding-bottom: 0; }

.fb-stay {
    display: flex;
    align-items: baseline;
    gap: var(--fb-sp-3);
}

.fb-stay__who { display: grid; }
.fb-stay__dates { margin-left: auto; white-space: nowrap; }

/* =========================================================================
   16. Category tree (documents)
   -------------------------------------------------------------------------
   Previously an unstyled <ul>. Indent guides make the hierarchy readable at
   a glance; the locked state is spelled out in words as well as in styling.
   ========================================================================= */

.category-tree { list-style: none; margin: 0; padding: 0; }

.category-tree ul {
    list-style: none;
    margin: var(--fb-sp-2) 0 0;
    padding-left: var(--fb-sp-4);
    border-left: 1px solid var(--fb-border);
}

.category-tree li { margin-bottom: var(--fb-sp-3); }
.category-tree li:last-child { margin-bottom: 0; }

.category-tree__name {
    font-family: var(--fb-font-display);
    font-weight: 600;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: var(--fb-sp-2);
}

.category-tree__name--locked { color: var(--fb-bark); }

/* Positioned ancestor for the row's whole-row click overlay (#128). It wraps
   only the row itself, never the nested child list -- covering that would
   make every child row unclickable. A locked row carries no link, so it
   stays inert. */
.category-tree__row { position: relative; }

.category-tree-excerpt { margin: 2px 0 0; }

.category-breadcrumb {
    font-size: var(--fb-text-sm);
    color: var(--fb-bark);
    margin-bottom: var(--fb-sp-2);
}

.category-breadcrumb a { text-decoration: none; }

/* Bulleted lists inside an alert need to keep the alert's left inset. */
.fb-alert__list { margin: var(--fb-sp-2) 0 0; padding-left: 1.2rem; }
.fb-alert > summary { cursor: pointer; font-weight: 600; }

/* Confirm-page details. */
.fb-confirm__lead { font-weight: 600; margin-bottom: var(--fb-sp-2); }

.fb-confirm__quote {
    margin: var(--fb-sp-4) 0;
    padding: var(--fb-sp-3) var(--fb-sp-4);
    background: var(--fb-parchment);
    border-left: 3px solid var(--fb-border-strong);
    border-radius: 0 var(--fb-radius-sm) var(--fb-radius-sm) 0;
}

.fb-table__strong { font-weight: 600; }

/* Threshold extras. */
.fb-threshold__wrap { width: 100%; max-width: 460px; }
.fb-threshold__lede { margin-bottom: var(--fb-sp-5); }
.fb-btn--block { width: 100%; justify-content: center; }

.fb-threshold__hints {
    margin-bottom: var(--fb-sp-4);
    padding: var(--fb-sp-3) var(--fb-sp-4);
    background: var(--fb-parchment);
    border-radius: var(--fb-radius-sm);
}
.fb-threshold__hints ul { margin: var(--fb-sp-2) 0 0; padding-left: 1.1rem; }

/* Crispy renders Bootstrap form markup; the threshold card wants our own
   control skin rather than Bootstrap's default blue-focused one. */
.fb-threshold .form-control,
.fb-threshold .form-select,
.fb-form .form-control,
.fb-form .form-select,
.fb-card .form-control,
.fb-card .form-select {
    /* background-color, never the `background` shorthand: the shorthand resets
       background-image, and that image is the only thing drawing .form-select's
       caret (Bootstrap sets appearance:none). */
    background-color: var(--fb-card);
    border-color: var(--fb-border-strong);
    color: var(--fb-ink);
    border-radius: var(--fb-radius-sm);
}
.fb-threshold .form-control:focus,
.fb-threshold .form-select:focus,
.fb-form .form-control:focus,
.fb-form .form-select:focus {
    border-color: var(--fb-alpenglow);
    box-shadow: 0 0 0 3px rgba(217, 121, 63, 0.18);
}
.fb-threshold .form-label,
.fb-form .form-label {
    font-size: var(--fb-text-sm);
    font-weight: 700;
    color: var(--fb-ink);
    margin-bottom: var(--fb-sp-1);
}
.fb-threshold .form-text,
.fb-form .form-text { color: var(--fb-bark); font-size: var(--fb-text-sm); }
.fb-threshold .invalid-feedback,
.fb-form .invalid-feedback { color: var(--fb-danger); font-weight: 600; }

/* Native form controls follow the accent, instead of the browser/Bootstrap blue. */
input[type="checkbox"],
input[type="radio"],
input[type="range"],
progress {
    accent-color: var(--fb-ember);
}

/* Bootstrap draws its own checkbox rather than using the native one, so
   accent-color above does not reach it. */
.form-check-input:checked {
    background-color: var(--fb-ember);
    border-color: var(--fb-ember);
}
.form-check-input:focus {
    border-color: var(--fb-alpenglow);
    box-shadow: 0 0 0 3px rgba(217, 121, 63, 0.18);
}

/* Bootstrap's .nav-tabs (the markdown editor's Écrire / Aperçu switch). */
.nav-tabs {
    --bs-nav-tabs-link-active-bg: var(--fb-card);
    --bs-nav-tabs-link-active-color: var(--fb-ink);
    --bs-nav-tabs-link-active-border-color: var(--fb-border-strong) var(--fb-border-strong) var(--fb-card);
    --bs-nav-tabs-border-color: var(--fb-border);
    --bs-nav-link-color: var(--fb-bark);
    --bs-nav-link-hover-color: var(--fb-ember);
}

/* =========================================================================
   17. Stock-Bootstrap seams
   -------------------------------------------------------------------------
   Components we do not author the markup for -- crispy-forms' error blocks,
   widgets rendered as bare `{{ form.field }}` -- still have to look like the
   rest of the product. The --bs-* bridge in tokens.css covers most of it;
   these are the cases where Bootstrap hardcodes a value the bridge can't
   reach, or where there is no class at all to hook onto.
   ========================================================================= */

/* Crispy renders non-field errors as `.alert.alert-block.alert-danger`, which
   arrived as Bootstrap's cold pink inside a warm parchment card -- and it is the
   alert users see most often (a wrong password on the login form). Re-point the
   stock contextual alerts at the same grammar as .fb-alert: tinted ground, ink
   text, colour only in the left bar. */
.alert {
    padding: var(--fb-sp-3) var(--fb-sp-4);
    border: 1px solid var(--fb-border);
    border-left: 3px solid var(--fb-bark);
    border-radius: var(--fb-radius-sm);
    background-color: var(--fb-card);
    color: var(--fb-ink);
    font-size: 0.9375rem;
}
.alert-success { background-color: var(--fb-success-soft); border-left-color: var(--fb-success); color: var(--fb-ink); }
.alert-info    { background-color: var(--fb-info-soft);    border-left-color: var(--fb-info);    color: var(--fb-ink); }
.alert-warning { background-color: var(--fb-warning-soft); border-left-color: var(--fb-warning); color: var(--fb-ink); }
.alert-danger,
.alert-error   { background-color: var(--fb-danger-soft);  border-left-color: var(--fb-danger);  color: var(--fb-ink); }

/* Crispy puts a single error in a one-item bullet list. */
.alert ul { margin: 0; padding-left: 1.1rem; }
.alert ul:only-child li:only-child { list-style: none; margin-left: -1.1rem; }

/* Bootstrap's outline buttons hardcode $gray-600 (#6c757d) in their own
   --bs-btn-* locals, which the --bs-secondary bridge never reaches: 4.07:1 on
   parchment and 3.71:1 in Nightfall, both under AA. */
.btn-outline-secondary {
    --bs-btn-color: var(--fb-ink);
    --bs-btn-border-color: var(--fb-border-strong);
    --bs-btn-hover-color: var(--fb-card);
    --bs-btn-hover-bg: var(--fb-ember);
    --bs-btn-hover-border-color: var(--fb-ember);
    --bs-btn-active-color: var(--fb-card);
    --bs-btn-active-bg: var(--fb-ember);
    --bs-btn-active-border-color: var(--fb-ember);
    --bs-btn-disabled-color: var(--fb-bark);
    --bs-btn-disabled-border-color: var(--fb-border);
}
.btn-outline-primary {
    --bs-btn-color: var(--fb-ember);
    --bs-btn-border-color: var(--fb-ember);
    --bs-btn-hover-bg: var(--fb-ember);
    --bs-btn-hover-border-color: var(--fb-ember);
    --bs-btn-hover-color: var(--fb-card);
}
.btn-outline-danger {
    --bs-btn-color: var(--fb-danger);
    --bs-btn-border-color: var(--fb-danger);
    --bs-btn-hover-bg: var(--fb-danger);
    --bs-btn-hover-color: var(--fb-card);
}

/* `.badge.bg-secondary` hardcodes white text; the bridge flips --bs-secondary
   light in Nightfall, leaving white-on-light at 2.36:1. */
.badge.bg-secondary,
.badge.text-bg-secondary {
    background-color: var(--fb-panel) !important;
    color: var(--fb-ink) !important;
    border: 1px solid var(--fb-border);
    font-weight: 600;
}
.badge.text-bg-success { background-color: var(--fb-success-soft) !important; color: var(--fb-ink) !important; }
.badge.text-bg-danger  { background-color: var(--fb-danger-soft) !important;  color: var(--fb-ink) !important; }

/* Widgets rendered as bare `{{ form.field }}` (the relation-type selects, the
   presence date inputs, `{{ form.as_p }}`) carry no class at all and arrived as
   raw OS controls -- 22px tall next to 30px styled inputs. Style them by element
   inside our own containers rather than touching every form definition. */
.fb-form select, .fb-form input:not([type]), .fb-form input[type="text"],
.fb-form input[type="email"], .fb-form input[type="date"], .fb-form input[type="number"],
.fb-form input[type="password"], .fb-form textarea,
.fb-card select, .fb-card input[type="date"],
.fb-table select, .fb-table input[type="date"] {
    font: inherit;
    font-size: 0.9375rem;
    color: var(--fb-ink);
    background-color: var(--fb-card);
    border: 1px solid var(--fb-border-strong);
    border-radius: var(--fb-radius-sm);
    padding: 0.375rem 0.625rem;
}
.fb-form select:focus, .fb-form textarea:focus, .fb-form input:focus,
.fb-card select:focus, .fb-card input:focus,
.fb-table select:focus, .fb-table input:focus {
    outline: none;
    border-color: var(--fb-alpenglow);
    box-shadow: 0 0 0 3px rgba(217, 121, 63, 0.18);
}
/* Checkboxes and radios keep their native box -- the rule above would inflate them. */
.fb-form input[type="checkbox"], .fb-form input[type="radio"],
.fb-card input[type="checkbox"], .fb-card input[type="radio"] {
    padding: 0;
    border: revert;
    border-radius: revert;
}

/* Leaflet's attribution is the only blue text left in the app. */
.leaflet-container a.leaflet-popup-close-button,
.leaflet-control-attribution a { color: var(--fb-ember); }
.leaflet-control-attribution { background: color-mix(in srgb, var(--fb-card) 85%, transparent); color: var(--fb-bark); }

/* A row of related buttons that read as one control. */
.fb-btn-group { display: inline-flex; gap: 2px; }
.fb-btn-group .fb-btn { border-radius: 0; }
.fb-btn-group .fb-btn:first-child { border-radius: var(--fb-radius-sm) 0 0 var(--fb-radius-sm); }
.fb-btn-group .fb-btn:last-child { border-radius: 0 var(--fb-radius-sm) var(--fb-radius-sm) 0; }
.fb-btn-group .fb-btn:only-child { border-radius: var(--fb-radius-sm); }

/* Active segment of a toggle group. */
.fb-btn.active,
.cal-window-toggle.active,
.presence-view-toggle .fb-btn.active {
    background: var(--fb-ember);
    border-color: var(--fb-ember);
    color: var(--fb-card);
}
