/*
 * Structural rules only: every colour, font and radius comes from the --app-*
 * tokens declared in themes.css, which is where the five themes live.
 */
body {
    background-attachment: fixed;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

.bg-gradient-primary {
    background: var(--app-primary-gradient) !important;
}

.card {
    border-radius: var(--app-radius);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.card:hover {
    transform: translateY(-2px);
    box-shadow: var(--app-card-shadow-hover) !important;
}

/* A transformed card becomes the containing block of the fixed menus inside
   it (see initFloatingDropdowns), which would clip them again: a card with an
   open menu stays where it is. */
.card:has(.dropdown-menu.show),
.card:has(.dropdown-menu.show):hover {
    transform: none;
}

.rounded-pill {
    border-radius: 50px !important;
}

.btn-primary {
    background: var(--app-primary-gradient);
    border: none;
}

.btn-primary:hover,
.btn-primary:focus-visible {
    background: var(--app-primary-gradient-hover);
}

.alert {
    border-radius: 10px;
}

.form-control, .form-select {
    border-radius: 8px;
}

.table-hover tbody tr:hover {
    background-color: var(--app-primary-soft);
}

.footer {
    margin-top: auto;
}

/* ===================================================================== Frame
   Header: the brand (back to the dashboard), two menus and the avatar. It keeps
   the theme gradient, which is what tells the five themes apart at a glance. */
.app-header {
    position: sticky;
    top: 0;
    z-index: 1020;
    padding-top: env(safe-area-inset-top);
    box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.12);
}

.app-header-inner {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    min-height: 3.75rem;
}

.app-brand {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    color: #fff;
    text-decoration: none;
    font-family: 'Fredoka', var(--app-heading-font);
    font-size: 1.6rem;
    font-weight: 600;
    letter-spacing: -0.02em;
}

.app-brand:hover,
.app-brand:focus-visible {
    color: #fff;
}

/* The logo turtles in their light palette (icon board), made for a dark ground. */
.app-brand img {
    transition: transform 0.25s ease;
}

.app-brand:hover img {
    transform: translateX(3px) rotate(-6deg);
}

.app-menus {
    align-items: center;
    gap: 0.25rem;
}

.app-menu-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 0.9rem;
    color: rgba(255, 255, 255, 0.88);
    background: transparent;
    border: 0;
    border-radius: 999px;
    font-weight: 600;
    text-decoration: none;
}

.app-menu-toggle .fa-chevron-down {
    font-size: 0.65rem;
    opacity: 0.8;
}

.app-menu-toggle:hover,
.app-menu-toggle:focus-visible,
.app-menu-toggle[aria-expanded="true"] {
    color: #fff;
    background: rgba(255, 255, 255, 0.16);
}

.app-menu-toggle.active {
    color: #fff;
    background: rgba(255, 255, 255, 0.24);
}

.app-user {
    margin-left: auto;
}

.app-user-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.2rem 0.75rem 0.2rem 0.2rem;
    color: #fff;
    background: rgba(255, 255, 255, 0.14);
    border: 0;
    border-radius: 999px;
    font-weight: 600;
}

.app-user-toggle:hover,
.app-user-toggle:focus-visible,
.app-user-toggle[aria-expanded="true"] {
    background: rgba(255, 255, 255, 0.26);
}

.app-user-toggle > img,
.app-user-toggle > .avatar-monogram {
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85);
    font-size: 14px !important;
}

.app-user-toggle .fa-chevron-down {
    font-size: 0.65rem;
}

/* On a phone the avatar alone is the button. */
@media (max-width: 767.98px) {
    .app-user-toggle {
        padding: 0.2rem;
    }
}

.app-dropdown {
    min-width: 15rem;
    padding: 0.4rem;
    border: 1px solid var(--app-border);
    border-radius: calc(var(--app-radius) * 0.8);
    box-shadow: var(--app-card-shadow-hover);
}

.app-dropdown .dropdown-item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.55rem 0.75rem;
    border-radius: calc(var(--app-radius) * 0.5);
    font-weight: 500;
}

.app-dropdown .dropdown-item i {
    color: var(--app-primary);
}

.app-dropdown .dropdown-item.active,
.app-dropdown .dropdown-item:active {
    color: var(--app-primary);
    background: var(--app-primary-soft);
}

.app-dropdown-who {
    display: flex;
    flex-direction: column;
    padding: 0.4rem 0.75rem 0.6rem;
    margin-bottom: 0.25rem;
    border-bottom: 1px solid var(--app-border);
}

.app-dropdown-who small {
    color: var(--app-text-muted);
}

/* Bottom bar of a phone: three daily screens and the full menu. It sits on the
   page surface, the header already carries the theme colour. */
.app-tabbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1030;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    padding: 0.3rem 0.5rem calc(0.3rem + env(safe-area-inset-bottom));
    background: var(--app-surface);
    border-top: 1px solid var(--app-border);
    box-shadow: 0 -0.25rem 1rem rgba(0, 0, 0, 0.08);
}

.app-tab {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    padding: 0.35rem 0;
    color: var(--app-text-muted);
    background: transparent;
    border: 0;
    font-size: 0.72rem;
    font-weight: 600;
    text-decoration: none;
}

.app-tab i {
    display: grid;
    place-items: center;
    width: 3.2rem;
    height: 1.9rem;
    font-size: 1.1rem;
    border-radius: 999px;
    transition: background-color 0.2s ease;
}

.app-tab.active {
    color: var(--app-primary);
}

.app-tab.active i {
    background: var(--app-primary-soft);
}

@media (max-width: 991.98px) {
    body.has-tabbar {
        padding-bottom: calc(4.4rem + env(safe-area-inset-bottom));
    }

    body.has-tabbar .footer {
        display: none;
    }

    /* Floating add buttons stay above the bar. */
    body.has-tabbar .agenda-fab {
        bottom: calc(5rem + env(safe-area-inset-bottom));
    }
}

.offcanvas.app-offcanvas {
    height: auto;
    max-height: 85vh;
    background: var(--app-surface);
    color: var(--app-text);
    border-top-left-radius: calc(var(--app-radius) * 1.3);
    border-top-right-radius: calc(var(--app-radius) * 1.3);
    padding-bottom: env(safe-area-inset-bottom);
}

.app-offcanvas-group {
    margin: 0.25rem 0 0.6rem;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--app-text-muted);
}

.app-offcanvas-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.5rem;
    margin-bottom: 1.25rem;
}

.app-offcanvas-link {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    min-height: 5.25rem;
    padding: 0.6rem 0.4rem;
    color: var(--app-text);
    background: var(--app-surface-alt);
    border-radius: var(--app-radius);
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1.15;
    text-align: center;
    text-decoration: none;
}

.app-offcanvas-link i {
    font-size: 1.3rem;
    color: var(--app-primary);
}

.app-offcanvas-link.active {
    background: var(--app-primary-soft);
    box-shadow: inset 0 0 0 2px var(--app-primary);
}

/* Mobile: tighter page gutters, the cards need the width more than the margin. */
@media (max-width: 575.98px) {
    .app-main {
        padding-top: 1rem !important;
    }

    .app-brand {
        font-size: 1.3rem;
    }

    .app-brand img {
        width: 34px;
        height: 34px;
    }
}

/* Avatars: a picture when one is picked, a monogram otherwise. Both are
   produced by getAvatarDisplay() with the member colour. */
.avatar-monogram {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    text-transform: uppercase;
}

.color-preview {
    vertical-align: text-bottom;
}

/* Colour picker: a dropdown with a live preview swatch. */
.color-picker .swatch-preview {
    width: 36px;
    border: 1px solid var(--app-border-strong);
    border-right: 0;
    border-radius: 8px 0 0 8px;
}

/* Keep a tall form reachable: the modal never grows taller than the viewport,
   the body scrolls, the header and footer stay pinned. Every modal of the app
   wraps its header, body and footer in a <form>, and that form is the only child
   of .modal-content, so it is the flex item that must be allowed to shrink
   (min-height:0) and it must be a column itself for the body to scroll inside
   it. Bootstrap's own modal-dialog-scrollable cannot do this on its own: it
   expects .modal-body to be the flex item, and here the form is in between. */
.modal-content > form {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    margin: 0;
}

.modal-content > form > .modal-header,
.modal-content > form > .modal-footer {
    flex-shrink: 0;
}

.modal-content > form > .modal-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--app-scrollbar) transparent;
}

.modal-content > form > .modal-body::-webkit-scrollbar {
    width: 8px;
}

.modal-content > form > .modal-body::-webkit-scrollbar-thumb {
    background: var(--app-scrollbar);
    border-radius: 4px;
}

/* Avatar preview in the member form, and its placeholder monogram. */
.avatar-preview {
    object-fit: cover;
    flex-shrink: 0;
}

.avatar-preview-placeholder {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    font-size: 26px;
    flex-shrink: 0;
}

/* Avatar picker popup: a tappable grid, the chosen image gets a ring. */
.avatar-picker-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 4px;
}

.avatar-option {
    padding: 0;
    border: 2px solid transparent;
    border-radius: 10px;
    background: var(--app-surface-alt);
    cursor: pointer;
    transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.avatar-option img {
    display: block;
    border-radius: 8px;
    object-fit: cover;
}

.avatar-option:hover {
    transform: scale(1.06);
    border-color: var(--app-border-strong);
}

.avatar-option.active {
    border-color: var(--app-primary);
    box-shadow: 0 0 0 2px var(--app-surface), 0 0 0 4px var(--app-primary);
}

.avatar-option:focus-visible {
    outline: 2px solid var(--app-primary);
    outline-offset: 2px;
}

/* Native avatar picker <dialog>: it sits in the top layer, above the member
   modal, and never interferes with its open/close state. */
.avatar-dialog {
    border: 1px solid var(--app-border);
    background: var(--app-surface);
    color: var(--app-text);
    border-radius: 12px;
    padding: 0;
    overflow: hidden;
    width: min(380px, calc(100vw - 2rem));
    max-height: 70vh;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
}

.avatar-dialog::backdrop {
    background: rgba(0, 0, 0, 0.45);
}

.avatar-dialog-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--app-border);
}

.avatar-dialog-title {
    margin: 0;
    font-size: 1.1rem;
}

.avatar-dialog-body {
    max-height: 45vh;
    overflow-y: auto;
    padding: 1rem 1.25rem;
    scrollbar-width: thin;
}

.avatar-dialog-body::-webkit-scrollbar {
    width: 6px;
}

.avatar-dialog-body::-webkit-scrollbar-thumb {
    background: var(--app-scrollbar);
    border-radius: 4px;
}

.avatar-dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    padding: 0.75rem 1.25rem;
    border-top: 1px solid var(--app-border);
}


/* Theme picker: each card carries its own data-theme, so the preview below is
   painted by the very tokens a real page would use. The form works without the
   live preview JavaScript, the radio input is the only state. */
.theme-picker {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 0.75rem;
    position: relative;
    margin: 0 0 1rem;
    padding: 0;
    border: 0;
}

.theme-option {
    position: relative;
    display: block;
    height: 100%;
    padding: 0;
    border: 2px solid var(--app-border);
    border-radius: var(--app-radius);
    background: var(--app-surface);
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.theme-option:hover {
    border-color: var(--app-primary);
}

.theme-option:focus-within {
    box-shadow: 0 0 0 0.25rem var(--app-focus);
}

.theme-option.selected {
    border-color: var(--app-primary);
    box-shadow: 0 0 0 2px var(--app-surface), 0 0 0 4px var(--app-primary);
}

/* The radio itself stays reachable by the keyboard but visually collapses into
   the tick badge, so the card reads as the control. */
.theme-option-input {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    opacity: 0;
}

.theme-tick {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    display: none;
    width: 1.5rem;
    height: 1.5rem;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--app-primary);
    color: var(--app-primary-contrast);
    font-size: 0.75rem;
}

.theme-option-input:checked ~ .theme-tick {
    display: flex;
}

.theme-preview {
    display: block;
    padding: 0.6rem;
    border-radius: calc(var(--app-radius) - 4px);
    background: var(--app-bg);
    color: var(--app-text);
    font-family: var(--app-body-font);
}

.theme-preview-bar {
    display: block;
    height: 0.5rem;
    border-radius: 4px;
    background: var(--app-primary-gradient);
}

.theme-preview-body {
    display: flex;
    gap: 0.4rem;
    margin-top: 0.5rem;
}

.theme-preview-card {
    flex: 1;
    height: 2rem;
    border-radius: 6px;
    background: var(--app-surface);
    border: 1px solid var(--app-border);
}

.theme-preview-button {
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    background: var(--app-primary-gradient);
}

.theme-preview-title {
    display: block;
    margin: 0.5rem 0 0;
    font-family: var(--app-heading-font);
    color: var(--app-heading-color);
    font-size: 0.95rem;
}

.theme-preview-muted {
    display: block;
    color: var(--app-text-muted);
    font-size: 0.75rem;
}

/* The picker layout is meaningless on a very narrow screen. */
@media (max-width: 768px) {
    .theme-picker {
        grid-template-columns: 1fr;
    }
}

@media (max-width: 768px) {
    /* Sortable headers and the action group stay usable on a phone. */
    .table td, .table th {
        padding: 0.5rem;
    }

    .btn-group-sm > .btn {
        padding: 0.2rem 0.4rem;
    }
}


/* ==================================================================== Agenda
   Each calendar is its own vertical list, and the lists sit side by side
   separated by a gap. When there are more of them than the screen is wide, the
   track scrolls sideways and snaps list by list, which is what the arrows and
   the keyboard do too: one calendar at a time instead of a squeezed matrix.

   Each list scrolls on its own height-wise and carries its own header, so a
   calendar with a busy week never stretches the others.

   The lists and the "a venir" panel share one grid box: the track takes the
   column that is left, the panel sits in a fixed one on its right, and the two
   arrows stay over the track because they are placed in its column. A narrow
   screen gives the panel its own row under the lists, which is where the three
   windows end up stacked. */

.agenda-carousel {
    position: relative;
    display: grid;
    /* Declared here and not as a fallback value: the next arrow reads the same
       two variables to step over the panel, and a fallback would let the two
       drift apart. */
    --agenda-aside-width: 19rem;
    --agenda-aside-gap: 0.75rem;
    grid-template-columns: minmax(0, 1fr) var(--agenda-aside-width);
    gap: var(--agenda-aside-gap);
    align-items: start;
}

.agenda-carousel-track {
    grid-column: 1;
    grid-row: 1;
}

/* The arrows take no grid area on purpose: an absolutely positioned grid child
   that names an area is placed against that area, while app.js places them
   against the padding box of the carousel. Left unplaced, their containing
   block is that padding box whatever column or row the track ends up in (the
   pinned default calendar moves it). */
.agenda-carousel-nav {
    grid-column: auto;
    grid-row: auto;
}

.agenda-upcoming {
    grid-column: 2;
    grid-row: 1;
}

/* The member's default calendar, pinned outside the track on its left: it keeps
   the width of one list and never scrolls away. The previous arrow steps over
   it through the pair of variables below, as the next one steps over the panel. */
.agenda-carousel {
    --agenda-pinned-width: 0rem;
    --agenda-pinned-gap: 0rem;
}

.agenda-carousel.has-pinned {
    --agenda-pinned-width: 18rem;
    --agenda-pinned-gap: 0.75rem;
    grid-template-columns: var(--agenda-pinned-width) minmax(0, 1fr) var(--agenda-aside-width);
}

.agenda-carousel.has-pinned .agenda-carousel-track {
    grid-column: 2;
}

.agenda-carousel.has-pinned .agenda-upcoming {
    grid-column: 3;
}

.agenda-pinned {
    grid-column: 1;
    grid-row: 1;
    display: flex;
    min-width: 0;
    padding: 0.15rem 0 0.75rem;
}

/* Wins over the phone rule that sizes a list to 82vw for the swipe: the pinned
   list is not swiped, it takes its whole column. */
.agenda-pinned .agenda-list {
    flex: 1 1 auto;
    min-width: 0;
    max-width: none;
}

/* With a pinned list the track is narrower: two lists fill it until the screen
   is wide enough for three again. */
.agenda-carousel.has-pinned .agenda-carousel-track .agenda-list {
    flex-basis: calc((100% - 0.75rem) / 2);
}

@media (min-width: 1600px) {
    .agenda-carousel.has-pinned .agenda-carousel-track .agenda-list {
        flex-basis: calc((100% - 1.5rem) / 3);
    }
}

@media (max-width: 1199.98px) {
    .agenda-carousel,
    .agenda-carousel.has-pinned {
        grid-template-columns: minmax(0, 1fr);
        /* The panel moves under the lists, so the next arrow no longer has to
           step over it: zeroing the width here is what brings it back to the
           right edge of the track. */
        --agenda-aside-width: 0rem;
        --agenda-aside-gap: 0rem;
        --agenda-pinned-width: 0rem;
        --agenda-pinned-gap: 0rem;
    }

    /* On a narrow screen the pinned list gets its own row before the track,
       full width, then the track, then "A venir". */
    .agenda-pinned {
        grid-column: 1;
        grid-row: 1;
    }

    .agenda-carousel.has-pinned .agenda-carousel-track {
        grid-column: 1;
        grid-row: 2;
    }

    .agenda-upcoming,
    .agenda-carousel.has-pinned .agenda-upcoming {
        grid-column: 1;
        grid-row: 2;
    }

    .agenda-carousel.has-pinned .agenda-upcoming {
        grid-row: 3;
    }
}

.agenda-carousel-track {
    display: flex;
    gap: 0.75rem;
    overflow-x: auto;
    padding: 0.15rem 0.15rem 0.75rem;
    scroll-snap-type: x proximity;
    scrollbar-color: var(--app-scrollbar) transparent;
    scroll-padding-left: 0.15rem;
}

/* Three lists fill the width; a fourth one is what starts the carousel, so the
   track only scrolls when the family owns more than three calendars. */
.agenda-list {
    display: flex;
    flex: 0 0 calc((100% - 1.5rem) / 3);
    flex-direction: column;
    min-width: 0;
    background: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius);
    box-shadow: var(--app-card-shadow);
    scroll-snap-align: start;
    overflow: hidden;
    transition: opacity 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}

@media (max-width: 991.98px) {
    .agenda-list {
        flex-basis: calc((100% - 0.75rem) / 2);
    }
}

/* The head is painted in the colour of the calendar, so a list is recognised
   before its name is read. --agenda-head-ink is decided in PHP, because the
   palette holds shades light and dark enough to need both inks. */
.agenda-list-head {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.7rem 0.75rem;
    color: var(--agenda-head-ink, #0b1220);
    background: var(--agenda-color, var(--app-primary));
    border-bottom: 1px solid var(--app-border);
}

/* The actions button sits on the calendar's own colour, so it takes its ink from
   it rather than from a Bootstrap variant, which would clash with some shades.
   It carries no border: the coloured header already frames it, and a second
   outline around a dot of punctuation is noise. */
.agenda-list-head .agenda-head-action {
    color: inherit;
}

.agenda-list-head .agenda-head-action:hover,
.agenda-list-head .agenda-head-action.show {
    color: inherit;
    background: rgba(127, 127, 127, 0.25);
}

.agenda-head-action {
    border: 0;
}

.agenda-list-body {
    flex: 1 1 auto;
    padding: 0.25rem 0.5rem 0.5rem;
    overflow-y: auto;
    scrollbar-color: var(--app-scrollbar) transparent;
}

.agenda-cal-heading {
    flex: 1 1 auto;
    min-width: 0;
}

.agenda-cal-title {
    margin: 0;
    font-family: var(--app-heading-font);
    font-size: 0.95rem;
    font-weight: 600;
    color: inherit;
    overflow-wrap: anywhere;
}

/* Slightly transparent rather than a second ink: the line under the name only
   has to be readable, and the colour is what identifies the calendar. */
.agenda-cal-meta {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    margin-top: 0.15rem;
    font-size: 0.7rem;
    color: inherit;
    opacity: 0.85;
}

/* The day is a separator inside the list: a sticky row of its own, so the events
   of a day stay visually grouped when a week is long. */
.agenda-list-day {
    padding-top: 0.35rem;
}

.agenda-list-day + .agenda-list-day {
    margin-top: 0.2rem;
    border-top: 1px solid var(--app-border);
}

.agenda-list-day-label {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    margin: 0;
    padding: 0.3rem 0.1rem;
    background: var(--app-surface);
    font-weight: 600;
}

.agenda-list-day.is-today .agenda-list-day-label {
    background: var(--app-primary-soft);
}

/* One add button per day and per calendar. It stays out of the way until the
   row is hovered or the button itself is reached, but a touch screen has no
   hover, so there it is always shown. */
.agenda-day-add {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 1.4rem;
    height: 1.4rem;
    padding: 0;
    margin-left: auto;
    color: var(--agenda-color, var(--app-primary));
    background: transparent;
    border: 1px solid currentColor;
    border-radius: 50%;
    font-size: 0.65rem;
    opacity: 0;
    transition: opacity 0.15s ease;
}

.agenda-list-day:hover .agenda-day-add,
.agenda-day-add:focus-visible {
    opacity: 1;
}

.agenda-day-add:hover {
    color: #fff;
    background: var(--agenda-color, var(--app-primary));
}

.agenda-day-add:focus-visible {
    outline: 2px solid var(--app-primary);
    outline-offset: 1px;
}

@media (hover: none) {
    .agenda-day-add {
        opacity: 1;
    }
}

.agenda-day-name {
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--app-text-muted);
}

.agenda-day-number {
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--app-text);
}

.agenda-day-number small {
    font-size: 0.65rem;
    font-weight: 400;
    color: var(--app-text-muted);
}

.agenda-list-day.is-today .agenda-day-name,
.agenda-list-day.is-today .agenda-day-number {
    color: var(--app-primary);
}

.agenda-list-items {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 0.1rem 0 0.25rem;
}

.agenda-list-empty {
    margin: 0;
    padding: 0.15rem 0.1rem 0.35rem;
    font-size: 0.7rem;
    color: var(--app-text-muted);
}

.agenda-event {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
    width: 100%;
    padding: 0.4rem 0.5rem;
    text-align: left;
    background: var(--app-surface);
    color: var(--app-text);
    border: 1px solid var(--app-border);
    border-left: 3px solid var(--agenda-color, var(--app-primary));
    border-radius: calc(var(--app-radius) - 4px);
    cursor: pointer;
    transition: background-color 0.15s ease, box-shadow 0.15s ease;
}

/* An occurrence that diverges from its series is marked with a dashed outline
   so the difference is readable without opening the event. */
.agenda-event.is-modified {
    border-style: dashed;
    border-left-style: solid;
}

.agenda-event.is-modified .agenda-event-time {
    color: var(--app-primary);
}

.agenda-event:hover {
    background: var(--app-surface-alt);
    box-shadow: var(--app-card-shadow);
}

.agenda-event:focus-visible {
    outline: none;
    box-shadow: 0 0 0 0.2rem var(--app-focus);
}

.agenda-event-time {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.68rem;
    color: var(--app-text-muted);
}

.agenda-event-time .fa-repeat {
    font-size: 0.62rem;
}

.agenda-event-icon {
    font-size: 0.85rem;
    line-height: 1;
}

.agenda-event-title {
    font-size: 0.82rem;
    font-weight: 600;
    overflow-wrap: anywhere;
}

/* Calendar order modal. One row per calendar, in the order they will be posted:
   the grip is the only part that drags, the two buttons move the row too, and
   the insertion point is painted as a line rather than as a moved row, so the
   list the user is reading stays still. */
.agenda-order-list {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.agenda-order-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.6rem;
    background: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius);
}

.agenda-order-item.is-dragging {
    opacity: 0.4;
}

.agenda-order-item.is-drop-before {
    box-shadow: 0 -2px 0 0 var(--app-primary);
}

.agenda-order-grip {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    color: var(--app-text-muted);
    cursor: grab;
}

.agenda-order-dot {
    flex: 0 0 auto;
    width: 0.65rem;
    height: 0.65rem;
    background: var(--agenda-color, var(--app-primary));
    border-radius: 50%;
}

.agenda-order-name {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.agenda-order-move {
    display: flex;
    gap: 0.15rem;
}

/* Icon-only steps: small enough to sit at the end of the row without opening it
   up, so they keep the padding of a ghost button and only show their border on
   hover or on focus. */
.agenda-order-step {
    display: grid;
    place-items: center;
    width: 1.7rem;
    height: 1.7rem;
    padding: 0;
    color: var(--app-text-muted);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--app-radius);
    font-size: 0.7rem;
    cursor: pointer;
}

.agenda-order-step:hover {
    color: var(--app-primary);
    border-color: var(--app-border-strong);
}

.agenda-order-step:focus-visible {
    color: var(--app-primary);
    outline: 2px solid var(--app-focus);
    outline-offset: 1px;
}

/* Carousel arrows. They only exist while there is something to scroll to, the
   script toggles d-none for that. initCarousels() places them: centred on the
   bottom edge of the list header and on the outer edges of the visible lists.
   The offsets below are only where they wait before the script runs. */
.agenda-carousel-nav {
    position: absolute;
    top: 2.6rem;
    z-index: 4;
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    color: var(--app-text);
    background: var(--app-surface);
    border: 1px solid var(--app-border-strong);
    border-radius: 50%;
    box-shadow: var(--app-card-shadow);
    cursor: pointer;
}

.agenda-carousel-nav:disabled {
    opacity: 0.35;
    cursor: default;
}

/* An unplaced absolutely positioned child is placed against the padding box of
   its grid container, not against a column, so each arrow steps over what stands
   between it and the track by itself: the pinned default calendar on the left,
   "A venir" on the right. */
.agenda-carousel-prev {
    left: calc(var(--agenda-pinned-width) + var(--agenda-pinned-gap) - 0.35rem);
}

.agenda-carousel-next {
    right: calc(var(--agenda-aside-width) + var(--agenda-aside-gap) + 0.35rem);
}

/* The add button lives on the page, not on every day cell: the calendar is
   chosen in the form, so one button for the whole week is enough. */
.agenda-fab {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 1030;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.85rem 1.1rem;
    color: #fff;
    background: var(--app-primary);
    border: 0;
    border-radius: 999px;
    box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.25);
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.agenda-fab:hover,
.agenda-fab:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 0.75rem 1.5rem rgba(0, 0, 0, 0.3);
}

.agenda-fab-label {
    font-size: 0.9rem;
    font-weight: 600;
}

/* "A venir": one card for the three windows, sitting next to the lists on a
   wide screen and under them on a narrow one. It does not scroll on its own
   like the lists do, because each window shows a handful of events and says how
   many it holds back, so the panel cannot grow with the length of a series. */
.agenda-upcoming {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    padding: 0.9rem 0.95rem;
    background: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius);
    box-shadow: var(--app-card-shadow);
}

.agenda-upcoming-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.25rem 0.5rem;
}

.agenda-upcoming-title {
    margin: 0;
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: 0.01em;
}

/* The window the panel really covers, spelled out: the three groups are counted
   from today and not from the week being read. */
.agenda-upcoming-since {
    margin: 0;
    font-size: 0.7rem;
    color: var(--app-text-muted);
}

.agenda-upcoming-group-title {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0 0 0.4rem;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--app-text-muted);
}

.agenda-upcoming-count {
    padding: 0.05rem 0.4rem;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0;
    color: var(--app-text-muted);
    background: var(--app-surface-alt);
    border-radius: 999px;
}

.agenda-upcoming-items {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Same button as in the lists, so the form opens the same way, but one line
   tall: the column mixes every calendar, and the calendar is already named by
   the coloured bar, so a second line would only make the panel taller. */
.agenda-upcoming-item {
    display: flex;
    align-items: baseline;
    gap: 0.45rem;
    width: 100%;
    padding: 0.35rem 0.55rem 0.35rem 0.6rem;
    text-align: left;
    background: var(--app-surface);
    border: 1px solid var(--app-border);
    border-left: 3px solid var(--agenda-color, var(--app-primary));
    border-radius: var(--app-radius);
    cursor: pointer;
    transition: box-shadow 0.15s ease, border-color 0.15s ease;
}

.agenda-upcoming-item:hover,
.agenda-upcoming-item:focus-visible {
    box-shadow: var(--app-card-shadow);
}

.agenda-upcoming-item.is-modified {
    background: var(--app-primary-soft);
}

/* Fixed width, so every entry of the column lines its labels up and the eye
   finds the title at the same x on every row. */
.agenda-upcoming-when {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: baseline;
    gap: 0.35rem;
    font-size: 0.7rem;
    color: var(--app-text-muted);
    white-space: nowrap;
}

.agenda-upcoming-day {
    font-weight: 700;
    text-transform: capitalize;
}

.agenda-upcoming-clock {
    display: inline-flex;
    align-items: baseline;
    gap: 0.25rem;
}

.agenda-upcoming-title {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--app-text);
    /* The title yields first: a long one is truncated rather than wrapped, which
       is what keeps every entry one line tall. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.agenda-upcoming-empty,
.agenda-upcoming-more {
    margin: 0;
    font-size: 0.72rem;
    color: var(--app-text-muted);
}

.agenda-upcoming-more {
    margin-top: 0.4rem;
    font-style: italic;
}

@media (max-width: 575.98px) {
    .agenda-list {
        min-width: 82vw;
        max-width: 82vw;
        flex-basis: 82vw;
    }

    .agenda-fab {
        right: 0.9rem;
        padding: 0.8rem;
    }

    body:not(.has-tabbar) .agenda-fab {
        bottom: 0.9rem;
    }

    /* The label is the first thing to go when the button has to sit over the
       content of a small screen. */
    .agenda-fab-label {
        display: none;
    }

    .agenda-day-name {
        font-size: 0.6rem;
    }
}

/* ================================================================ Emoji picker
   The panel is a fixed layer placed by the script right above (or below) the
   button that opened it, so it floats over the event form. It cannot live inside
   the form: modal-dialog-scrollable turns the modal body into a scroll container
   and would clip a panel nested in it.

   Without JS the panel stays closed and every category is listed in the markup,
   but the button cannot open it, so an event remains creatable without an icon. */

.emoji-panel {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 1090;
    display: none;
    flex-direction: column;
    width: min(21rem, calc(100vw - 1.5rem));
    max-height: calc(100vh - 1.5rem);
    background: var(--app-surface);
    border: 1px solid var(--app-border-strong);
    border-radius: var(--app-radius);
    box-shadow: 0 0.75rem 1.5rem rgba(0, 0, 0, 0.2);
}

.emoji-panel.is-open {
    display: flex;
}

.emoji-panel-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.5rem 0.6rem;
    border-bottom: 1px solid var(--app-border);
    font-size: 0.8rem;
}

.emoji-cats {
    flex: 0 0 auto;
    display: none;
    gap: 0.15rem;
    padding: 0.4rem 0.5rem;
    overflow-x: auto;
    border-bottom: 1px solid var(--app-border);
    scrollbar-color: var(--app-scrollbar) transparent;
}

.emoji-cat-tab {
    display: grid;
    flex: 0 0 auto;
    place-items: center;
    width: 2rem;
    height: 2rem;
    color: var(--app-text-muted);
    background: transparent;
    border: 0;
    border-radius: var(--app-radius);
    cursor: pointer;
}

.emoji-cat-tab:hover {
    color: var(--app-text);
    background: var(--app-surface-alt);
}

.emoji-cat-tab.is-active {
    color: #fff;
    background: var(--app-primary);
}

/* The script marks the picker as ready once it has taken over the tabs, which is
   what switches the panel from the no-JS "everything listed" layout to one
   category at a time. */
.emoji-panel.is-ready .emoji-cats {
    display: flex;
}

.emoji-panel.is-ready .emoji-cat {
    display: none;
}

.emoji-panel.is-ready .emoji-cat.is-active {
    display: block;
}

.emoji-cat {
    padding: 0.4rem 0.5rem;
}

.emoji-cats-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-color: var(--app-scrollbar) transparent;
}

.emoji-cat-label {
    margin: 0 0 0.35rem;
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--app-text-muted);
}

.emoji-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(2.1rem, 1fr));
    gap: 0.1rem;
}

.emoji-btn {
    display: grid;
    place-items: center;
    aspect-ratio: 1;
    padding: 0;
    font-size: 1.2rem;
    line-height: 1;
    background: transparent;
    border: 1px solid transparent;
    border-radius: calc(var(--app-radius) - 4px);
    cursor: pointer;
}

.emoji-btn:hover,
.emoji-btn:focus-visible {
    background: var(--app-surface-alt);
    border-color: var(--app-border-strong);
    outline: none;
}

.emoji-btn.is-selected {
    background: var(--app-primary-soft);
    border-color: var(--app-primary);
}

/* The reset sits in the panel, so the panel grows by one row once it is laid
   out. Kept out of the flow of the emoji list above. */
.emoji-panel-foot {
    flex: 0 0 auto;
    padding: 0.4rem 0.5rem;
    border-top: 1px solid var(--app-border);
}

/* ======================================================== Event, category and calendar form
   The title, its icon button and the category share one input group: a whole
   event is named and sorted in a single line, and the icon button opens the
   picker right above the row it fills. The calendar form holds the same button
   on its own line, so the button sizes itself the same way in both. */

.agenda-icon-btn {
    flex: 0 0 auto;
    min-width: 2.75rem;
    font-size: 1.1rem;
    line-height: 1;
}

/* Set programmatically when the choice is cleared: textContent empties the span,
   so the placeholder glyph comes back from the class instead of the markup. */
.agenda-icon-btn [data-emoji-current].is-empty::after {
    content: "\f118";
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
}

/* The category is the tail of the group, wide enough to read a title but never
   wide enough to push the title out of reach on a phone. Its options carry their
   title only: a native select cannot render the category icon, and the icon is
   already shown by the button that fills it in. */
.agenda-category-select {
    flex: 0 1 12rem;
    max-width: 12rem;
}

/* Recurrence is opt-in: the zone holding the model and the dates of a series
   opens with the checkbox that precedes it, in CSS as well as in the script, so a
   series can still be set without JavaScript. The form is the anchor: the toggle
   and the zone are not siblings once the block sits inside the collapsible
   "when" zone. The script's own d-none still wins where it hides the zone,
   Bootstrap marking it !important. */
[data-event-recurrence-zone] {
    display: none;
}

[data-event-form]:has([data-event-recurrence-toggle] > [data-event-recurring]:checked) [data-event-recurrence-zone] {
    display: block;
}

/* The collapsible "where and when" block: the answer the day button already gave
   is written out on the left, the button that brings the fields back on the
   right. */
.agenda-when-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.agenda-when-summary {
    min-width: 0;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--app-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* "Toutes les [N] semaine(s)": a short number field read inside the sentence. */
.agenda-interval-input {
    width: 5rem;
}

/* The note button sits right of the "where and when" one; a dot on it says a
   note is written while the field is folded away. */
.agenda-note-toggle {
    position: relative;
}

.agenda-note-dot {
    position: absolute;
    top: -0.25rem;
    right: -0.25rem;
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    background: var(--app-primary);
    border: 2px solid var(--bs-body-bg, #fff);
}

.agenda-note-dot[hidden] {
    display: none;
}

/* Reminders of a calendar or of an event: one wrapping row per reminder. */
.reminders-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.reminders-list:not(:empty) {
    margin-bottom: 0.6rem;
}

.reminder-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}

.reminder-kind {
    width: auto;
    min-width: 11rem;
}

.reminder-hours {
    width: auto;
}

.reminder-hours .form-control {
    width: 4.5rem;
    flex: 0 0 auto;
}

.reminder-at {
    display: inline-flex;
    gap: 0.4rem;
}

.reminder-at .form-select {
    width: auto;
}

.reminder-row [data-reminder-part][hidden] {
    display: none !important;
}

.reminder-remove {
    margin-left: auto;
}

.agenda-when-bar:has(+ [data-event-when-zone].d-none) .agenda-when-summary {
    color: var(--app-heading-color);
}

/* The weekday boxes are Bootstrap btn-check pairs: the labels are the visible
   controls and toggle the boxes natively, so no script is involved here. */
.agenda-weekdays .btn {
    min-width: 2.5rem;
}

.agenda-weekdays .btn-check:checked + .btn {
    color: #fff;
    background: var(--app-primary);
    border-color: var(--app-primary);
}

/* ================================================================== Categories
   The list the family head arranges, shown as rows. Each row carries its icon in
   a chip: the icon is the family's shorthand for the category, and the select of
   the event form only lists the titles. */

.agenda-categories-empty {
    font-size: 0.875rem;
    color: var(--app-text-muted);
}

.agenda-category-emoji {
    display: grid;
    flex: 0 0 auto;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    font-size: 1rem;
    line-height: 1;
    background: var(--app-surface-alt);
    border-radius: var(--app-radius);
}


/* ================================================================== Dashboard
   The member's own home page: three columns that are always there, each one
   showing what its filter lets through.

   The grid gives the two narrow columns a third of the width each and leaves the
   middle one for the group carousel. Below 1200px the columns stack in reading
   order, which is the order they are meant to be read in.

   The columns of a row are stretched to the tallest one: no column bounds its
   own content, so the row is as tall as the fullest column and the others grow
   to match, their content filling the card. */

.dashboard-columns {
    display: grid;
    grid-template-columns: 1fr 1.15fr 1fr;
    gap: 1rem;
    align-items: stretch;
}

@media (max-width: 1399.98px) {
    .dashboard-columns {
        grid-template-columns: 1fr 1fr;
    }
}

@media (max-width: 991.98px) {
    .dashboard-columns {
        grid-template-columns: 1fr;
    }
}

.dashboard-column {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* The head carries the column's name and its range. The range is the small badge,
   so it is read after the name and never competes with it. */
.dashboard-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.9rem 1.1rem;
    border-bottom: 1px solid var(--app-border);
}

.dashboard-count {
    flex: 0 0 auto;
    padding: 0.1rem 0.5rem;
    font-size: 0.7rem;
    font-weight: 700;
    white-space: nowrap;
    color: var(--app-text-muted);
    background: var(--app-surface-alt);
    border-radius: 999px;
}

.dashboard-column-empty {
    padding: 1.25rem 1.1rem;
    font-size: 0.85rem;
    color: var(--app-text-muted);
}

/* Today's rows are a single grid line: a colour swatch, a time, a title, and the
   calendar it comes from. The source column is dropped first when a row runs out
   of width, because the title is what the member is looking for. */
.dashboard-list {
    margin: 0;
}

.dashboard-row {
    display: grid;
    grid-template-columns: 1.25rem 4.25rem minmax(0, 1fr) auto;
    gap: 0.5rem;
    align-items: baseline;
    padding: 0.5rem 1.1rem;
    border-bottom: 1px solid var(--app-border);
}

.dashboard-row:last-child {
    border-bottom: 0;
}

.dashboard-swatch {
    display: inline-block;
    width: 0.65rem;
    height: 0.65rem;
    border-radius: 50%;
    vertical-align: middle;
}

.dashboard-time {
    font-variant-numeric: tabular-nums;
    font-size: 0.8125rem;
    color: var(--app-text-muted);
}

.dashboard-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dashboard-flag {
    font-size: 0.65rem;
    color: var(--app-text-muted);
}

.dashboard-source {
    font-size: 0.8125rem;
    color: var(--app-text-muted);
    white-space: nowrap;
}

/* Narrow screen: the calendar name goes, the hours stay (an event without
   hours leaves its column empty, and the auto column then takes no room). */
@media (max-width: 575.98px) {
    .dashboard-row {
        grid-template-columns: 1.25rem auto minmax(0, 1fr);
    }

    .dashboard-time {
        white-space: nowrap;
    }

    .dashboard-source {
        display: none;
    }
}

/* Groups column: the agenda's lists and carousel, resized for a column of its own.
   The box takes the height the row gives the card, and the track and its lists
   follow, so the group fills the column down to its bottom edge.

   The lists are inset from the card so the arrows can sit on their left and
   right edges, half inside and half outside, the way the agenda's arrows sit on
   the edges of the week. --dashboard-groups-inset is the distance from the card
   to the list, which is where the centre of each arrow goes. */
.dashboard-groups {
    --dashboard-groups-inset: 1.25rem;
    position: relative;
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    padding: 0 calc(var(--dashboard-groups-inset) - 0.15rem);
}

.dashboard-group-track {
    flex: 1 1 auto;
    padding: 0.75rem 0.15rem 0.75rem;
}

/* One group fills the column: a carousel is what reaches the next group, and a
   third of a group would make the day headings unreadable. */
.dashboard-group-track .agenda-list {
    flex: 0 0 100%;
    box-shadow: none;
    border: 0;
}

/* The head leaves room for the arrows centred on its bottom corners, so the
   list of calendars under the name is never hidden under one of them. */
.dashboard-group-list .agenda-list-head {
    padding-inline: 1.35rem;
}


/* The calendar a row comes from, named only when the group mixes several: with a
   single calendar the colour bar already says it, and the name would be noise. */
.agenda-event-source {
    display: block;
    font-size: 0.65rem;
    font-weight: 400;
    color: var(--app-text-muted);
}

/* The dashboard reads, it does not edit, so its rows carry the agenda's look
   without its pointer. */
.dashboard-event {
    cursor: default;
}

.dashboard-event:hover {
    background: var(--app-surface);
    box-shadow: none;
}

/* Last column. Its length is bounded by the controller (a few events per day and
   a count of the rest), not by a height: the column is as tall as its content,
   and the row lines the other two up on it. */
.dashboard-upcoming {
    display: flex;
    flex-direction: column;
    gap: 0.8rem;
    padding: 0.75rem 1.1rem 1rem;
}

.dashboard-upcoming-day {
    min-width: 0;
}

.dashboard-upcoming-label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0 0 0.35rem;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--app-text-muted);
}

/* Configuration screen: the tick lists and the group editor. */

.dashboard-filter {
    max-height: 11rem;
    overflow-y: auto;
    padding: 0.75rem;
    background: var(--app-surface-alt);
    border-radius: var(--app-radius);
}

.dashboard-member {
    height: 100%;
}

/* Group list of the account: the whole row opens the edition modal. */
.group-row {
    cursor: pointer;
}

.group-row:focus-visible {
    outline: 2px solid var(--app-focus);
    outline-offset: -2px;
}

/* ================================================================= Checklists
   A task is a button that looks like a tick box and a label: the whole line is
   the target, which is what a thumb on a phone needs. */

.list-row {
    cursor: pointer;
}

.checklist-table .checklist-progress-cell {
    width: 14rem;
}

.card .progress {
    height: 0.5rem;
}

.checklist-tasks {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.checklist-task {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    width: 100%;
    padding: 0.55rem 0.7rem;
    text-align: left;
    color: var(--app-text);
    background: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius);
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.checklist-task:hover {
    border-color: var(--app-border-strong);
}

.checklist-task:focus-visible {
    outline: 2px solid var(--app-focus);
    outline-offset: 1px;
}

.checklist-task:disabled {
    opacity: 0.6;
    cursor: progress;
}

.checklist-box {
    display: grid;
    flex: 0 0 auto;
    place-items: center;
    width: 1.35rem;
    height: 1.35rem;
    font-size: 0.75rem;
    color: transparent;
    border: 2px solid var(--app-border-strong);
    border-radius: 0.35rem;
}

.checklist-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

.checklist-by {
    flex: 0 0 auto;
    font-size: 0.72rem;
    color: var(--app-text-muted);
}

.checklist-task.is-done {
    background: var(--app-surface-alt);
}

.checklist-task.is-done .checklist-box {
    color: var(--app-primary-contrast);
    background: var(--app-primary);
    border-color: var(--app-primary);
}

.checklist-task.is-done .checklist-label {
    color: var(--app-text-muted);
    text-decoration: line-through;
}

/* Editor rows of the modal. */
.checklist-task-rows {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.checklist-task-bullet {
    color: var(--app-text-muted);
}

.checklist-facts dt {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--app-text-muted);
}

.checklist-facts dd {
    margin-bottom: 0.9rem;
}

.checklist-facts dd:last-child {
    margin-bottom: 0;
}

.checklist-child-name {
    flex: 0 0 6rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Dashboard row of pinned checklists, under the three columns. */
.dashboard-checklists {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
    gap: 1rem;
    margin-top: 1rem;
    align-items: stretch;
}

.dashboard-checklists .card-body {
    padding: 0.75rem 1.1rem 1rem;
}

.dashboard-card-head a {
    color: inherit;
    text-decoration: none;
}

.dashboard-card-head a:hover {
    text-decoration: underline;
}

/* ===================================================================== Tokens
   A pile seen from the side: each token is a flat disc whose darker lower edge
   gives it its thickness, and every token overlaps the one under it. The tokens
   already spent since the last refill stay as dashed outlines on top, so the
   child sees both what is left and what the pile held. */

.token-pile {
    --token-w: 4rem;
    --token-h: 1.6rem;
    --token-overlap: -0.8rem;
    display: flex;
    flex: 0 0 auto;
    flex-direction: column;
    align-items: center;
    width: calc(var(--token-w) + 1.5rem);
    padding-top: 0.25rem;
}

.token-pile--sm {
    --token-w: 2.4rem;
    --token-h: 0.9rem;
    --token-overlap: -0.68rem;
    width: calc(var(--token-w) + 0.5rem);
}

.token-coin {
    position: relative;
    display: block;
    flex: 0 0 auto;
    width: var(--token-w);
    height: var(--token-h);
    padding: 0;
    background: var(--token-color);
    border: 1px solid rgba(0, 0, 0, 0.25);
    border-radius: 50%;
    box-shadow: inset 0 calc(var(--token-h) * -0.22) 0 rgba(0, 0, 0, 0.28), 0 1px 2px rgba(0, 0, 0, 0.2);
    transition: transform 0.18s ease, opacity 0.18s ease;
}

.token-coin + .token-coin {
    margin-top: var(--token-overlap);
}

/* About to be spent: the token fades out of the top of the pile, leaving only
   its outline, so the pile shows what will be left after the spend. */
.token-coin.is-picked {
    background: transparent;
    border-style: dashed;
    border-color: var(--token-color);
    box-shadow: none;
    opacity: 0.35;
    transform: translateY(-0.2rem);
}

.token-coin.is-spent {
    background: transparent;
    border: 2px dashed var(--token-color);
    box-shadow: none;
    opacity: 0.55;
}

.token-more {
    margin-top: 0.35rem;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--app-text-muted);
}

.token-chip {
    display: inline-block;
    width: 0.9rem;
    height: 0.9rem;
    margin-right: 0.2rem;
    vertical-align: -0.1rem;
    background: var(--token-color);
    border: 1px solid rgba(0, 0, 0, 0.25);
    border-radius: 50%;
    box-shadow: inset 0 -0.15rem 0 rgba(0, 0, 0, 0.25);
}

.token-chip--sm {
    width: 0.7rem;
    height: 0.7rem;
}

.dashboard-tokens {
    margin-top: 1.5rem;
}

/* Adult's summary of the children's tokens: name, count, then a bar. */
.token-summary {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.token-summary-row {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0.25rem 0.5rem;
    align-items: center;
}

.token-summary-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.token-summary-count {
    font-variant-numeric: tabular-nums;
    color: var(--bs-secondary-color);
    font-size: 0.875rem;
}

.token-summary-bar {
    grid-column: 1 / -1;
    display: block;
    height: 0.4rem;
    background: var(--bs-secondary-bg);
    border-radius: 1rem;
    overflow: hidden;
}

.token-summary-bar > span {
    display: block;
    height: 100%;
    border-radius: inherit;
}

.token-card-body {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
}

.token-card-side {
    flex: 1 1 auto;
    min-width: 0;
}

.token-purpose {
    font-weight: 600;
}

.member-tokens {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.member-token {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
}

.member-token-balance {
    margin-left: 0.35rem;
    color: var(--app-text-muted);
}

.member-token-count {
    width: 5rem;
}

.member-token-moves li {
    padding: 0.2rem 0;
    border-bottom: 1px solid var(--app-border);
}

.member-token-moves li:last-child {
    border-bottom: 0;
}

.min-w-0 {
    min-width: 0;
}

/* The - and + of the spend form: wide enough for a child's finger. */
.token-stepper {
    max-width: 11rem;
}

.token-stepper .btn {
    min-width: 2.75rem;
}

.token-stepper .form-control {
    font-weight: 700;
}

/* =============================================================== Informations
   The lists reuse the agenda's columns and carousel. There is no side panel
   here, so the two variables the next arrow reads are zeroed. */

.info-carousel {
    grid-template-columns: minmax(0, 1fr);
    --agenda-aside-width: 0rem;
    --agenda-aside-gap: 0rem;
}

.info-narrow {
    max-width: 34rem;
}

.info-device-input {
    max-width: 20rem;
}

.info-list-body {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    padding: 0.6rem 0.6rem 0.4rem;
}

.info-list-foot {
    padding: 0 0.6rem 0.6rem;
}

/* A block is only a description: the text keeps its line breaks, the grip says
   it can be dragged, and the arrows do the same from the keyboard. */
.info-block {
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
    padding: 0.55rem 0.4rem 0.55rem 0.5rem;
    background: var(--app-surface);
    border: 1px solid var(--app-border);
    border-left: 3px solid var(--agenda-color, var(--app-primary));
    border-radius: var(--app-radius);
    cursor: pointer;
}

.info-block:hover {
    border-color: var(--app-border-strong);
    border-left-color: var(--agenda-color, var(--app-primary));
}

.info-block:focus-visible {
    outline: 2px solid var(--app-focus);
    outline-offset: 1px;
}

.info-block.is-dragging {
    opacity: 0.4;
}

.info-block-grip {
    flex: 0 0 auto;
    padding-top: 0.1rem;
    color: var(--app-text-muted);
    cursor: grab;
}

.info-block-text {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    font-size: 0.9rem;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.info-block-moves {
    display: flex;
    flex: 0 0 auto;
    flex-direction: column;
}

.info-block-moves .btn {
    padding: 0 0.3rem;
    font-size: 0.7rem;
    line-height: 1.2;
    color: var(--app-text-muted);
}

.info-block-moves .btn:disabled {
    visibility: hidden;
}

/* Recovery code: large, monospaced, easy to copy by hand. */
.info-recovery-code {
    padding: 0.9rem;
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    word-break: break-word;
    background: var(--app-surface-alt);
    border: 2px dashed var(--app-border-strong);
    border-radius: var(--app-radius);
}

.info-recovery-meta {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.2rem 0.8rem;
}

.info-recovery-meta dd {
    margin: 0;
}

/* Printing the recovery sheet prints that sheet and nothing else. */
@media print {
    body.info-printing * {
        visibility: hidden;
    }

    body.info-printing .info-recovery-sheet,
    body.info-printing .info-recovery-sheet * {
        visibility: visible;
    }

    body.info-printing .info-recovery-sheet {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        border: 0;
        box-shadow: none;
    }

    body.info-printing .info-recovery-sheet .modal-footer,
    body.info-printing .info-recovery-sheet .form-check {
        display: none;
    }
}

/* Folded technical details under a vault error: what went wrong, step by step. */
.info-diagnostic summary {
    font-size: 0.8rem;
    cursor: pointer;
}

.info-diagnostic pre {
    max-height: 16rem;
    margin: 0.5rem 0 0;
    padding: 0.6rem;
    overflow: auto;
    font-size: 0.72rem;
    white-space: pre-wrap;
    word-break: break-word;
    background: rgba(0, 0, 0, 0.06);
    border-radius: var(--app-radius);
}

/* ===================================================== Installer and maintenance
   Pages shown before the database is usable: no navigation, one card centred on
   the brand green, whatever theme the family will pick later. */
.setup-body {
    min-height: 100vh;
    background: radial-gradient(circle at 20% 0%, #1C9AA3 0%, #14808A 35%, #0B4F5C 100%);
    color: #17383F;
}

.setup-shell {
    max-width: 34rem;
    margin: 0 auto;
    padding: 2.5rem 1rem 3rem;
}

.setup-brand {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin-bottom: 1.25rem;
    color: #fff;
    font-family: var(--app-heading-font);
    font-size: 2.4rem;
    font-weight: 600;
    letter-spacing: -0.02em;
}


.setup-card {
    background: #fff;
    border-radius: 1.25rem;
    padding: 1.75rem;
    box-shadow: 0 1.5rem 3rem rgba(4, 40, 47, 0.35);
}

.setup-card-wide {
    max-width: none;
}

.setup-title {
    font-family: var(--app-heading-font);
    font-size: 1.6rem;
    margin-bottom: 1.25rem;
}

.setup-section {
    margin-bottom: 1.5rem;
}

.setup-section legend {
    font-size: 1rem;
    font-weight: 700;
    color: #14808A;
    border-bottom: 1px solid rgba(20, 128, 138, 0.2);
    padding-bottom: 0.25rem;
    margin-bottom: 0.75rem;
}

.setup-env {
    font-size: 0.8rem;
}

.update-pending {
    border-left: 4px solid #f0ad4e;
    background: #fff8ec;
    border-radius: 0.5rem;
    padding: 0.75rem 1rem;
}


/* ============================================================ Public home page
   The hero is the logo board itself: the five turtles beside the "ohana"
   wordmark and its line, on the sea-glass ground of the theme. */
.home-hero {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 2.5rem;
    margin: 2rem auto 3rem;
}

.home-logo {
    width: clamp(9rem, 26vw, 15rem);
    height: auto;
}

.home-wordmark {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.home-title {
    margin: 0;
    font-family: 'Fredoka', var(--app-heading-font);
    font-size: clamp(4rem, 12vw, 8.25rem);
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.02em;
    color: var(--app-heading-color);
}

.home-tagline {
    margin: 0 0 1.5rem 0.4rem;
    font-family: 'Fredoka', var(--app-heading-font);
    font-size: clamp(1.15rem, 2.4vw, 1.6rem);
    font-weight: 500;
    color: #14808A;
}

.home-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-left: 0.4rem;
}

@media (max-width: 767.98px) {
    .home-hero {
        flex-direction: column;
        gap: 1rem;
        text-align: center;
    }

    .home-tagline,
    .home-actions {
        margin-left: 0;
        justify-content: center;
    }
}

.home-features {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 1.5rem 2rem;
    max-width: 66rem;
    margin: 0 auto;
    padding: 1.75rem;
    background: var(--app-surface);
    border-radius: calc(var(--app-radius) * 1.4);
    box-shadow: var(--app-card-shadow);
}

.home-feature i {
    font-size: 1.4rem;
    color: var(--app-primary);
}

.home-feature h2 {
    margin: 0.5rem 0 0.35rem;
    font-size: 1.1rem;
}

.home-feature p {
    margin: 0;
    color: var(--app-text-muted);
    line-height: 1.5;
}

@media (max-width: 991.98px) {
    .home-features {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 575.98px) {
    .home-features {
        grid-template-columns: 1fr;
        padding: 1.25rem;
    }
}

.is-public .footer {
    background: transparent;
}

/* A service announced in the menu but not built yet. */
.coming-soon {
    max-width: 26rem;
    margin: 3rem auto;
    padding: 2.5rem 1.5rem;
    text-align: center;
    background: var(--app-surface);
    border-radius: var(--app-radius);
    box-shadow: var(--app-card-shadow);
}

.coming-soon i {
    font-size: 2.5rem;
    color: var(--app-primary);
    margin-bottom: 0.75rem;
}

.home-secondary {
    background: var(--app-surface);
}

/* ============================================================ Agenda search */
.agenda-search-results {
    list-style: none;
    margin: 0;
    padding: 0;
}

.agenda-search-hit {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 0.75rem;
    color: var(--app-text);
    text-decoration: none;
    border-radius: calc(var(--app-radius) * 0.6);
}

.agenda-search-hit:hover,
.agenda-search-hit:focus-visible {
    background: var(--app-primary-soft);
    color: var(--app-text);
}

.agenda-search-hit > span:last-child {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.agenda-search-hit small {
    color: var(--app-text-muted);
}

.agenda-search-dot {
    flex: 0 0 auto;
    width: 0.8rem;
    height: 0.8rem;
    border-radius: 50%;
    background: var(--agenda-color);
}

/* The occurrence a search result led to. */
.agenda-event.is-focused {
    outline: 3px solid var(--app-primary);
    outline-offset: 2px;
    animation: agenda-focus 1.2s ease 0.3s 2;
}

@keyframes agenda-focus {
    50% {
        outline-color: transparent;
    }
}

@media (prefers-reduced-motion: reduce) {
    .agenda-event.is-focused {
        animation: none;
    }
}

/* Rename pencil beside the family name: present, but quieter than the title. */
.family-rename {
    padding: 0.25rem 0.4rem;
    color: var(--app-text-muted);
    opacity: 0.6;
    font-size: 0.95rem;
}

.family-rename:hover,
.family-rename:focus-visible {
    color: var(--app-primary);
    opacity: 1;
}

.min-w-0 {
    min-width: 0;
}

.account-color {
    display: inline-block;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 50%;
    border: 1px solid var(--app-border);
}

/* The three tabs stay on one line on a phone and scroll sideways if needed. */
@media (max-width: 575.98px) {
    .account-tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
        overflow-y: hidden;
        scrollbar-width: none;
    }

    .account-tabs .nav-link {
        white-space: nowrap;
        padding-left: 0.75rem;
        padding-right: 0.75rem;
    }
}

/* ===================================================================== Budget
   Amounts are tabular figures aligned right; what is left turns to the danger
   colour once overspent. One markup for every width: a line is a grid that
   lays its three amounts in columns on a wide screen and under the label, with
   their name, on a phone. */
.budget-amount {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.is-over,
.budget-amount.is-over {
    color: var(--app-danger);
}

.budget-meter {
    display: block;
    height: 0.4rem;
    border-radius: 999px;
    background: var(--app-surface-alt);
    overflow: hidden;
}

.budget-meter > span {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: var(--app-primary-gradient);
}

.budget-meter.is-over > span {
    background: var(--app-danger);
}

.budget-progress-col {
    width: 9rem;
}

.budget-back {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.5rem;
    color: var(--app-text-muted);
    text-decoration: none;
    font-weight: 600;
}

.budget-summary {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.75rem 1.5rem;
    padding: 1.1rem 1.25rem;
    margin-bottom: 1.25rem;
    background: var(--app-surface);
    border-radius: var(--app-radius);
    box-shadow: var(--app-card-shadow);
}

.budget-figure {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.budget-figure-label {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--app-text-muted);
}

.budget-figure-value {
    font-family: var(--app-heading-font);
    font-size: clamp(1.15rem, 3.5vw, 1.75rem);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--app-heading-color);
    white-space: nowrap;
}

.budget-figure-left.is-over .budget-figure-value {
    color: var(--app-danger);
}

.budget-summary-meter {
    grid-column: 1 / -1;
}

.budget-section {
    margin-bottom: 1rem;
    background: var(--app-surface);
    border-radius: var(--app-radius);
    box-shadow: var(--app-card-shadow);
}

.budget-section-head {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    padding-right: 0.5rem;
    border-bottom: 1px solid var(--app-border);
}

.budget-section-toggle {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.75rem;
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.85rem 1rem;
    color: var(--app-text);
    text-align: left;
    background: transparent;
    border: 0;
}

.budget-chevron {
    font-size: 0.8rem;
    color: var(--app-text-muted);
    transition: transform 0.2s ease;
}

.budget-section-toggle.collapsed .budget-chevron {
    transform: rotate(-90deg);
}

.budget-section-title {
    font-family: var(--app-heading-font);
    font-size: 1.15rem;
    font-weight: 600;
    color: var(--app-heading-color);
    margin-right: auto;
}

.budget-section-totals {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1rem;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.budget-section-totals small {
    font-weight: 500;
    color: var(--app-text-muted);
}

.budget-section-menu {
    color: var(--app-text-muted);
}

.budget-lines {
    padding: 0.25rem 1rem 0.5rem;
}

.budget-line {
    display: grid;
    grid-template-columns: minmax(0, 1fr) repeat(3, 8.5rem) 8.25rem;
    align-items: center;
    gap: 0.5rem 1rem;
    padding: 0.6rem 0;
}

.is-section-mode .budget-line {
    grid-template-columns: minmax(0, 1fr) 8.5rem 2.5rem;
}

.budget-line > .budget-amount {
    text-align: right;
}

.budget-line-wrap + .budget-line-wrap,
.budget-line-head + .budget-line-wrap {
    border-top: 1px solid var(--app-border);
}

.budget-line-head {
    padding: 0.4rem 0 0.25rem;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--app-text-muted);
}

.budget-line-head > span:not(:first-child) {
    text-align: right;
}

.budget-line-label {
    font-weight: 600;
    overflow-wrap: anywhere;
}

.budget-line-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.35rem;
}

.budget-spent-toggle {
    padding: 0;
    color: var(--app-link);
    font: inherit;
    font-variant-numeric: tabular-nums;
    text-decoration: underline dotted;
    background: none;
    border: 0;
}

.budget-spent-toggle small {
    color: var(--app-text-muted);
}

.budget-add-line {
    padding-left: 0;
    font-weight: 600;
    text-decoration: none;
}

.budget-expenses {
    list-style: none;
    margin: 0 0 0.6rem;
    padding: 0.25rem 0.75rem;
    background: var(--app-surface-alt);
    border-radius: calc(var(--app-radius) * 0.6);
}

.budget-expense {
    display: grid;
    grid-template-columns: 6rem minmax(0, 1fr) auto auto;
    align-items: center;
    gap: 0.75rem;
    padding: 0.4rem 0;
    font-size: 0.9rem;
}

.budget-expense + .budget-expense {
    border-top: 1px dashed var(--app-border);
}

.budget-expense-date {
    color: var(--app-text-muted);
    font-variant-numeric: tabular-nums;
}

.budget-expense-comment {
    overflow-wrap: anywhere;
}

.budget-section-expenses {
    padding: 0.75rem 1rem 1rem;
    border-top: 1px solid var(--app-border);
}

.budget-amount-input {
    font-variant-numeric: tabular-nums;
    text-align: right;
}

/* Phone: the label takes the first row, the amounts the second with their
   names, the actions stay at the end of the first row. */
@media (max-width: 767.98px) {
    .budget-summary {
        gap: 0.5rem 1rem;
        padding: 0.9rem 1rem;
    }

    .budget-line-head {
        display: none;
    }

    .budget-line,
    .is-section-mode .budget-line {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 0.35rem 0.75rem;
    }

    .budget-line-label {
        grid-column: 1 / 3;
        grid-row: 1;
    }

    .budget-line-actions {
        grid-column: 3;
        grid-row: 1;
    }

    .budget-line > .budget-amount {
        grid-row: 2;
        text-align: left;
        font-size: 0.95rem;
    }

    .budget-line > .budget-amount::before {
        content: attr(data-label);
        display: block;
        font-size: 0.7rem;
        font-weight: 600;
        color: var(--app-text-muted);
    }

    .budget-line-head + .budget-line-wrap {
        border-top: 0;
    }

    .budget-section-totals {
        width: 100%;
        padding-left: 1.15rem;
        font-size: 0.85rem;
    }

    .budget-expense {
        grid-template-columns: 4.6rem minmax(0, 1fr) auto auto;
        gap: 0.5rem;
    }
}

/* Markdown of an information block (markdown.js): compact, inside a card. */
.info-md {
    white-space: normal;
}

.info-md > :last-child {
    margin-bottom: 0;
}

.info-md p,
.info-md ul,
.info-md ol,
.info-md blockquote,
.info-md pre {
    margin: 0 0 0.5rem;
}

.info-md ul,
.info-md ol {
    padding-left: 1.2rem;
}

.info-md .info-md-heading {
    margin: 0.2rem 0 0.35rem;
    font-size: 1rem;
    font-weight: 600;
}

.info-md h4.info-md-heading {
    font-size: 1.05rem;
}

.info-md blockquote {
    padding-left: 0.6rem;
    border-left: 3px solid var(--app-border-strong);
    color: var(--app-text-muted);
}

.info-md code {
    padding: 0.05rem 0.3rem;
    border-radius: 4px;
    background: var(--app-surface-alt);
    color: var(--app-text);
    font-size: 0.85em;
}

.info-md pre {
    padding: 0.5rem 0.6rem;
    border-radius: 6px;
    background: var(--app-surface-alt);
    white-space: pre-wrap;
}

.info-md pre code {
    padding: 0;
    background: none;
}

.info-md a {
    overflow-wrap: anywhere;
}

.info-md-image {
    display: block;
    max-width: 100%;
    max-height: 16rem;
    margin: 0.25rem 0;
    border-radius: 6px;
}

.info-md hr {
    margin: 0.5rem 0;
}

/* Markdown editor: two tabs and a row of formatting buttons above the field. */
.md-editor-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem;
    margin-bottom: 0.4rem;
}

.md-editor-tabs {
    display: inline-flex;
    padding: 0.2rem;
    border-radius: 999px;
    background: var(--app-surface-alt);
}

.md-editor-tabs button {
    padding: 0.25rem 0.85rem;
    color: var(--app-text-muted);
    font-weight: 600;
    background: transparent;
    border: 0;
    border-radius: 999px;
}

.md-editor-tabs button.active {
    color: var(--app-primary);
    background: var(--app-surface);
    box-shadow: var(--app-card-shadow);
}

.md-editor-tools {
    display: flex;
    flex-wrap: wrap;
    gap: 0.15rem;
}

.md-editor-tools button {
    width: 2.1rem;
    height: 2.1rem;
    color: var(--app-text);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
}

.md-editor-tools button:hover,
.md-editor-tools button:focus-visible {
    border-color: var(--app-border-strong);
    background: var(--app-surface-alt);
}

.md-editor textarea {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.9rem;
}

.md-editor-preview {
    min-height: 15.5rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--app-border-strong);
    border-radius: 8px;
    overflow-y: auto;
}

/* A task row: the tick button, and for an adult the child it is given to. */
.checklist-task-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.checklist-task-item > form[data-checklist-toggle] {
    flex: 1 1 auto;
    min-width: 0;
}

.checklist-assign {
    flex: 0 0 9.5rem;
}

.checklist-assign .form-select.is-saved {
    border-color: var(--app-primary);
    box-shadow: 0 0 0 0.2rem var(--app-focus);
}

.checklist-assignee {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin-left: auto;
    padding: 0.1rem 0.55rem;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--app-text-muted);
    background: var(--app-surface-alt);
    border-radius: 999px;
    white-space: nowrap;
}

.checklist-assignee.is-me {
    color: #fff;
    background: var(--app-primary);
}

@media (max-width: 575.98px) {
    .checklist-assign {
        flex-basis: 7.5rem;
    }
}

/* ================================================================== Messages
   A page of its own, outside the application: the setup layout's teal ground
   and one card holding the composer and the thread. */
.msg-card {
    padding: 1.25rem;
}

.msg-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

.msg-pin-note {
    display: flex;
    gap: 0.5rem;
    padding: 0.6rem 0.8rem;
    margin-bottom: 1rem;
    font-size: 0.88rem;
    color: #6b4a00;
    background: #fff5df;
    border-radius: 10px;
}

.msg-pin {
    letter-spacing: 0.4em;
    font-variant-numeric: tabular-nums;
}

.msg-compose {
    margin-bottom: 1.25rem;
}

.msg-compose textarea {
    font-size: 1rem;
    resize: vertical;
}

.msg-compose-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 0.5rem;
}

.msg-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.msg-item {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
}

.msg-bubble {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.55rem 0.8rem;
    background: #EEF6F4;
    border-radius: 4px 14px 14px 14px;
}

.msg-item.is-mine .msg-bubble {
    background: #DDF0EC;
}

.msg-meta {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    font-size: 0.82rem;
}

.msg-meta span {
    color: #567075;
}

.msg-body {
    margin: 0.15rem 0 0;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Agenda reopening on a kept position or on an event: laid out but not shown
   until app.js has placed it (see the inline script of the layout). */
html.is-restoring body {
    visibility: hidden;
}

/* Day alignment turned off (gear menu of the agenda): a flex track stretches
   its lists to the tallest one by default; here each keeps its own height. */
.agenda-layout.is-unaligned .agenda-carousel-track {
    align-items: flex-start;
}

/* One line under the tasks of a checklist to add a task without the form. */
.checklist-quick-add {
    margin-top: 0.75rem;
}

/* Take back / give tokens from the adult's summary on the home page. */
.token-summary-adjust {
    grid-column: 1 / -1;
    display: flex;
    gap: 0.35rem;
    align-items: center;
}

.token-summary-adjust .form-control {
    width: 4rem;
    flex: 0 0 auto;
}

/* ------------------------------------------------------------------ */
/* Agenda, month view: the grid, then the list of the chosen day. On a
   wide screen the two sit side by side; on a phone the list comes under
   the grid and a cell shows dots instead of titles. */
.agenda-month-layout {
    display: grid;
    gap: 1rem;
    padding-bottom: 5rem; /* room for the floating add button */
}

@media (min-width: 992px) {
    .agenda-month-layout {
        grid-template-columns: minmax(0, 2fr) minmax(16rem, 1fr);
        align-items: start;
    }
}

.agenda-month {
    padding: 0.5rem;
    overflow: hidden;
    touch-action: pan-y;
}

.agenda-month.is-leaving-left,
.agenda-month.is-leaving-right {
    transition: transform 0.2s ease, opacity 0.2s ease;
    opacity: 0.4;
}

.agenda-month.is-leaving-left {
    transform: translateX(-12%);
}

.agenda-month.is-leaving-right {
    transform: translateX(12%);
}

.agenda-month-weekdays,
.agenda-month-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 0.25rem;
}

.agenda-month-weekdays span {
    text-align: center;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--app-text-muted);
    padding-bottom: 0.25rem;
}

.agenda-month-day {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
    min-height: 3.25rem;
    padding: 0.25rem;
    border: 1px solid var(--app-border);
    border-radius: calc(var(--app-radius) - 6px);
    background: var(--app-surface);
    color: var(--app-text);
    text-align: left;
    overflow: hidden;
}

.agenda-month-day.is-other-month {
    opacity: 0.45;
}

.agenda-month-day.is-selected {
    border-color: var(--app-primary);
    box-shadow: 0 0 0 2px var(--app-primary) inset;
}

.agenda-month-number {
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1.2;
}

.agenda-month-day.is-today .agenda-month-number {
    display: inline-flex;
    align-self: flex-start;
    align-items: center;
    justify-content: center;
    min-width: 1.4rem;
    height: 1.4rem;
    border-radius: 50%;
    background: var(--app-primary);
    color: #fff;
}

.agenda-month-marks {
    display: flex;
    flex-wrap: wrap;
    gap: 0.15rem;
}

/* Phone: a dot per event. */
.agenda-month-chip {
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    background: var(--agenda-color, var(--app-primary));
}

.agenda-month-chip-text {
    display: none;
}

.agenda-month-chip.is-hidden-calendar,
.agenda-month-chip.is-overflow,
.agenda-month-event.is-hidden-calendar {
    display: none !important;
}

.agenda-month-more {
    font-size: 0.65rem;
    color: var(--app-text-muted);
    line-height: 1;
}

/* Wider screens: short titles in the cells. */
@media (min-width: 768px) {
    .agenda-month-day {
        min-height: 6.5rem;
    }

    .agenda-month-marks {
        flex-direction: column;
        flex-wrap: nowrap;
    }

    .agenda-month-chip {
        width: auto;
        height: auto;
        border-radius: 3px;
        background: transparent;
        border-left: 3px solid var(--agenda-color, var(--app-primary));
        padding-left: 0.25rem;
        font-size: 0.72rem;
        line-height: 1.25;
    }

    .agenda-month-chip-text {
        display: block;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
}

.agenda-month-days {
    padding: 0.75rem;
}

.agenda-month-daylist-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.6rem;
}

.agenda-month-events {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.agenda-month-event.is-readonly {
    cursor: default;
}

/* Agenda toolbar: on a phone it takes the whole row under the title, the view
   switch and the search on the left, the gear pushed to the right edge. */
@media (max-width: 767.98px) {
    .agenda-toolbar {
        width: 100%;
    }
}

/* Family tree: people as tappable cards (walking the tree on a phone), the
   unions of a person with their children indented below, and the diagram in
   a frame that scrolls both ways. */
.gn-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: .5rem;
}

.gn-list {
    display: grid;
    gap: .5rem;
}

.gn-card {
    display: flex;
    align-items: center;
    gap: .75rem;
    min-height: 3.5rem;
    padding: .5rem .75rem;
    border: 1px solid var(--bs-border-color);
    border-radius: .75rem;
    background: var(--bs-body-bg);
    color: inherit;
    text-decoration: none;
}

a.gn-card:hover,
a.gn-card:focus-visible {
    border-color: var(--bs-primary);
    color: inherit;
}

.gn-card-unknown {
    border-style: dashed;
    color: var(--bs-secondary-color);
}

.gn-card-text {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
}

.gn-card-name {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.gn-card-meta {
    font-size: .8125rem;
    color: var(--bs-secondary-color);
}

.gn-card-chevron {
    color: var(--bs-secondary-color);
    font-size: .8rem;
}

.gn-avatar {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
    font-weight: 600;
    font-size: .9rem;
}

.gn-avatar-lg {
    width: 3.5rem;
    height: 3.5rem;
    font-size: 1.2rem;
}

.gn-card-deceased .gn-avatar {
    background: var(--bs-secondary-bg);
    color: var(--bs-secondary-color);
}

.gn-union + .gn-union {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--bs-border-color);
}

.gn-children {
    margin: .75rem 0 0 1.25rem;
    padding-left: .75rem;
    border-left: 2px solid var(--bs-border-color);
}

.gn-scroll {
    overflow: auto;
    max-height: 75vh;
    -webkit-overflow-scrolling: touch;
    background: var(--bs-tertiary-bg);
}

/* Full window diagram: on the page background, no scrollbars (dragged with
   the mouse, swiped on a phone), and the page itself does not scroll. */
.gn-scroll.gn-full {
    max-height: none;
    background: transparent;
    scrollbar-width: none;
    cursor: grab;
    touch-action: pan-x pan-y;
}

.gn-scroll.gn-full::-webkit-scrollbar {
    display: none;
}

.gn-scroll.gn-full.is-dragging {
    cursor: grabbing;
    user-select: none;
}

.gn-scroll.gn-full.is-dragging a {
    cursor: grabbing;
}

body.gn-fullpage .footer {
    display: none;
}

body.gn-fullpage,
body.gn-fullpage .app-main {
    padding-bottom: 0 !important;
}

.gn-canvas {
    width: max-content;
    min-width: 100%;
    padding: .5rem;
}

.gn-canvas svg {
    display: block;
    max-width: none;
    margin: 0 auto;
}

.gn-canvas a {
    text-decoration: none;
}

@media (min-width: 576px) {
    .w-sm-auto {
        width: auto !important;
    }
}

.gn-sex {
    font-size: .8em;
    margin-left: .25rem;
}

.gn-sex-m {
    color: #4a7fb8;
}

.gn-sex-f {
    color: #b85c8b;
}

.gn-member-mark {
    font-size: .75em;
    margin-left: .25rem;
    color: var(--bs-primary);
}

/* Mon heritage of a member whose death the family confirmed, read only on
   the informations page. */
.info-legacy-title {
    padding-left: .5rem;
    border-left: 4px solid var(--bs-border-color);
}

.info-legacy-block {
    display: block;
    cursor: default;
    margin-bottom: .5rem;
}

.death-banner {
    margin-bottom: .75rem;
}
