.application-body.application-events {
    /* Styling for the Events app. Compiled into the per-app block
       .application-body.application-events { … } by
       scripts/compile_sass.py:generate_registry_scss(). The CSS field on the
       configuration App record is appended on top at compile time. */

    & {
        /* The orange family (--colour-kikaron-orange in the Usugami theme), with
           Calendar, Radio, Apps and Favourites (Daniel, 2026-09-25). */
        --app-colour: var(--app-colour-events, #F37021);

        /* The FullCalendar 7.x "classic" theme is re-skinned to Kikaron colours
           and its toolbar buttons restyled to match Kikaron toolbar controls in
           assets/base/components/_events-fullcalendar-theme.scss. */

        /* The bell is gone from an event PAGE. There it sat in the
           sidebar beside a document that is one event: a counter of invitations
           waiting elsewhere, on the page for the one you are reading. The
           calendar is where a pile of invitations means something, and that is
           where the bell lives. */
        &:has(.kik-document.type-event) aside.sidebar .kik-events-invites-trigger {
            display: none;
        }

        /* …and BOTH the bell and the + go on the history page. The bell for the
           same reason as above and then some — it opens the list of invitations
           still waiting,
           which is the one thing this page is not; and the + because creating an
           event has nothing to do with reading what you were invited to. A
           control that belongs to another screen is worse on a screen about the
           same subject than on an unrelated one: it looks like it acts here. */
        &:has(.kik-events-invite-history) aside.sidebar .kik-events-invites-trigger,
        &:has(.kik-events-invite-history) aside.sidebar .icon.create,
        &:has(.kik-events-trash) aside.sidebar .kik-events-invites-trigger,
        &:has(.kik-events-trash) aside.sidebar .icon.create {
            display: none;
        }

        /* Sidebar 'extras' styling comes from the shared
           partials/quaive_sidebar_extras.html include — no app-specific CSS. */

        /* Cards reuse the canonical .event-card styling (components/_event-card.scss
           + _sidebar.scss) so they match the events portlet / team spaces exactly. */
        .events-upcoming {
            padding: 1rem;
        }
        .events-upcoming-batch {
            display: contents;
        }
        .events-upcoming-more {
            display: block;
            padding: 0.5rem 0;
            opacity: 0.6;
        }

        /* ---- Upcoming cards: the markers get a grid column of their own ---
           (Daniel, 2026-08-26.) A cake or a camera in front of the title reads
           as part of the name, so the markers sit at the card's trailing edge,
           centred on the whole card. A COLUMN, not position: absolute — the
           card is already a grid (link-list), and the thumbnail took the same
           route (events-fullcalendar-theme.css): rows that carry markers grow
           a trailing auto track, so title and byline keep cells of their own
           and nothing can overlap. The grid is re-stated per combination —
           with and without a byline, with and without a thumbnail.

           Keyed on .has-card-glyphs, stamped by the TEMPLATE — Gecko misses a
           child-combinator :has() on the initial parse (the markers sat
           auto-placed in an implicit row until the sidebar's first refetch
           re-injected the list), and the server knows perfectly well at render
           time whether the card carries markers.

           The :has() twin stays beside it as the fallback, for a fragment
           cached before the class existed.

           SCOPED ON #events-upcoming, NOT .events-upcoming-batch. That wrapper
           exists in the RESPONSE but never in the live page: the sentinel's
           inject reads `source: #events-upcoming-batch-1` with no `::element`,
           which takes that element's CHILDREN — the <h2> and <ul> land in
           #events-upcoming and the wrapper is left behind. A rule keyed on the
           wrapper therefore matched in every hand-built test (innerHTML keeps
           it) and in nothing the reader actually sees: the marker fell to
           auto-placement and sat in a row of its own under the title.
           #events-upcoming is the container that survives, because the inject
           targets what is inside it. */
        #events-upcoming .pat-link-list.tile-icons li a:is(.has-card-glyphs, :has(> .events-card-glyphs)) {
            grid-template-columns: calc(var(--bullet-space) * 2) 1fr auto;
            grid-template-areas: "icon title marks";
        }
        #events-upcoming .pat-link-list.tile-icons li a:is(.has-card-glyphs, :has(> .events-card-glyphs)):has(.item-byline) {
            grid-template-areas:
                "icon title marks"
                "icon byline marks";
        }
        /* A card with a hero image leads with it, full width, above everything
           else — so the glyph column keeps its place and the picture takes a row
           of its own across all three tracks. The plain (glyph-less) case is the
           same shape and lives in events-fullcalendar-theme.css; the two have to
           agree, or a card grows or loses its picture depending on whether it
           happens to carry a camera. */
        #events-upcoming .pat-link-list.tile-icons li a:is(.has-card-glyphs, :has(> .events-card-glyphs)):has(> .item-thumb) {
            grid-template-columns: calc(var(--bullet-space) * 2) 1fr auto;
            grid-template-areas:
                "thumb thumb thumb"
                "icon  title  marks";
        }
        #events-upcoming .pat-link-list.tile-icons li a:is(.has-card-glyphs, :has(> .events-card-glyphs)):has(> .item-thumb):has(.item-byline) {
            grid-template-areas:
                "thumb thumb  thumb"
                "icon  title  marks"
                "icon  byline marks";
        }
        /* The picture itself, for every card in the sidebar — glyphs or not, so
           it is scoped to the container rather than to one of the two grids
           above. events-fullcalendar-theme.css carries the same values for the
           card as it appears in the dashboard portlet, which is outside this
           container. */
        #events-upcoming .pat-link-list.tile-icons li a:has(> .item-thumb) > .item-thumb {
            grid-area: thumb;
            width: 100%;
            aspect-ratio: 16 / 9;
            object-fit: cover;
            border-radius: var(--border-radii-medium, 6px);
            margin: 0 0 8px;
            display: block;
        }
        .events-card-glyphs {
            grid-area: marks;
            align-self: center;
            display: flex;
            align-items: center;
            gap: 0.25em;
            line-height: 1;
            /* The clearance from the card's edge the absolute version kept —
               the card's own 8px padding plus this ≈ the same seat. */
            margin-inline-end: 1rem;
            /* Half-strength row ink: at full weight the marker competes with
               the title, and in the calendar's own colour it reads as a second
               accent next to the date tile and border already painted from it.

               currentColor rather than a literal (2026-09-01): this was
               `rgba(0,0,0,0.5)`, which is half-strength ink only on a light
               plate — on a dark theme it is a black cake on a near-black card,
               which is to say no cake. The dashboard's copy of these markers
               (static/base/base.css) has always inherited the row's ink for the
               same reason; this is the app-scoped surface catching up. */
            color: color-mix(in srgb, currentColor 50%, transparent);
        }
        .events-card-glyphs .events-event-glyph {
            margin: 0;
        }

        /* ---- Event detail ------------------------------------------------- */
        .events-detail {
            padding: 1.5rem;
            max-width: 48rem;
        }
        .events-detail-title {
            margin: 0.5rem 0 1rem;
        }
        .events-detail-meta {
            display: grid;
            grid-template-columns: max-content 1fr;
            gap: 0.35rem 1rem;
            margin: 0 0 1.5rem;
        }
        .events-detail-meta dt {
            font-weight: 600;
            opacity: 0.7;
        }
        .events-detail-meta dd {
            margin: 0;
        }

        /* ---- Grid tile glyphs: birthday + Jewish-holiday ------------------ */
        /* FC7 emits only hashed class names and drops our custom event
           classNames, so events-fullcalendar.js (eventDidMount) prepends a stable
           ``.events-event-glyph`` span to each tile's title, keyed off the feed's
           ``extendedProps.glyph``. These rules paint the symbol — mirroring the
           calendar app (components/_calendar.scss): a fontello cake/tart glyph for
           birthdays, a Star of David (U+2721) for Jewish holidays. */
        .events-event-glyph {
            display: inline-block;
            margin-right: 4px;
            margin-left: 4px;

            body[dir=rtl] & {
                margin-right: 0;
                margin-left: 4px;
            }
        }
        .events-glyph-birthday:before {
            /* The house birthday drawing as a mask, in the 1em a glyph took (core/icons.py). */
            content: "";
            display: inline-block;
            width: 1em;
            height: 1em;
            vertical-align: -0.15em;
            background-color: currentColor;
            -webkit-mask: var(--kik-svg-birthday) center / contain no-repeat;
            mask: var(--kik-svg-birthday) center / contain no-repeat;
            animation: heartbeat 1s;
        }
        /* The fontello set has no Star of David, so use the Unicode codepoint
           (U+2721) in the default font — renders on every OS. */
        .events-glyph-jewish-holiday:before {
            content: '\2721';
            font-style: normal;
            font-weight: normal;
        }
        /* Latin cross (U+271D) for the Christian package's feasts, same idea. */
        .events-glyph-christian-holiday:before {
            content: '\271D';
            font-style: normal;
            font-weight: normal;
        }
        /* Star and crescent (U+262A) for the Islamic package's observances. */
        .events-glyph-islamic-holiday:before {
            content: '\262A\FE0E';
            font-style: normal;
            font-weight: normal;
        }
        /* Camera marker for events whose location holds a video-conference
           link (or whose video-conference URL is set). */
        .events-glyph-video-conf:before {
            /* The house videocam drawing as a mask, in the 1em a glyph took (core/icons.py). */
            content: "";
            display: inline-block;
            width: 1em;
            height: 1em;
            vertical-align: -0.15em;
            background-color: currentColor;
            -webkit-mask: var(--kik-svg-videocam) center / contain no-repeat;
            mask: var(--kik-svg-videocam) center / contain no-repeat;
        }
        /* Suggested slot for an unfinished task with a time estimate
           (events/task_slots.py) — the tasks app's own note glyph, so the block
           says at a glance that it came from the task list and isn't an
           appointment. The block's white fill and dashed outline are inline
           styles from the feed + eventDidMount: FC7 drops these class names from
           the tile itself, and only the glyph span survives. */
        .events-glyph-task-slot:before {
            content: "";
            display: inline-block;
            width: 1em;
            height: 1em;
            vertical-align: -0.15em;
            background-color: currentColor;
            -webkit-mask: var(--kik-svg-draft) center / contain no-repeat;
            mask: var(--kik-svg-draft) center / contain no-repeat;
        }
        /* A PINNED slot is drawn like any other event, so the one thing that says
           it came from the task list is the Reminders app's own mark on its title.
           Painted as a MASK rather than an image: the tile's text colour carries
           it, which keeps it legible on a filled block in any calendar colour and
           in either brand. The stock icon (not a brand override) — at this size
           only the silhouette reads, and this stylesheet ships to every brand. */
        .events-glyph-reminders {
            width: 1em;
            height: 1em;
            vertical-align: -0.1em;
            background-color: currentColor;
            -webkit-mask: url(/assets/libraries/kikaron/app-icons/artefacts/reminders.svg) center / contain no-repeat;
            mask: url(/assets/libraries/kikaron/app-icons/artefacts/reminders.svg) center / contain no-repeat;
        }

        /* ---- The project code badge --------------------------------------- */
        /* An event filed on a project carries that project's short capitals in
           the top-right corner of its tile (events-calendar.js, eventDidMount).
           The name would not fit on a tile at any size the grid can spare; the
           code is what the abbreviation exists for, and the corner is where the
           eye can sweep a week's worth of them in one pass.

           PLACED ABSOLUTELY rather than laid into FC's header row: FC7 emits
           only hashed class names, so there is no header row this stylesheet can
           name that will still be there after the next FullCalendar release. The
           tile is the containing block — FC positions every one of them — and
           ``.kik-fc-event-coded`` reserves the corner so the title and the time
           stop short of the badge instead of running under it. */
        .kik-fc-event-coded {
            /* THE THREE MEASUREMENTS THE CORNER IS BUILT FROM, in pixels because
               the plate's own type is (see below) and because the timestamp they
               are sized against is 11px whatever the tile's font-size is.

               ``slot`` is the whole corner the plate occupies — its widest self
               plus the air before it and the 1px inset after it. ``clear`` is the
               strip at the start that the timestamp needs and never gives up: a
               full "10:00–12:00" measures 71px at 11px, so 76px is that plus a
               breath. ``floor`` is the tile width at which what is left over
               stops being a shortened code and becomes a coloured nub sitting on
               the time — roughly two characters' worth. Every rule below is one
               of these three. */
            --kik-fc-code-slot: 47px;
            --kik-fc-code-clear: 76px;
            --kik-fc-code-floor: 102px;

            position: relative;
            /* The corner the badge sits in, taken out of the tile's own content
               box so the time and the title stop short of it rather than running
               under it — and given back in full once the tile is too narrow to
               carry a plate at all, which is what the second term is for.

               On the TILE, not on its children: FC's children here are the inner
               content AND the absolutely-placed resize strips, and a padding
               meant for the first one turned the month view's leading dot into a
               bar the width of the row. */
            padding-inline-end: min(
                clamp(0px, calc(100% - var(--kik-fc-code-clear)), var(--kik-fc-code-slot)),
                max(0px, calc((100% - var(--kik-fc-code-floor)) * 1000)));
        }

        /* …EXCEPT ON A TILE THAT STACKS. A timegrid tile puts the time on its own
           line and the title under it (events-calendar.js marks it), so the badge
           shares a line with the time alone. Reserving the corner over the tile's
           whole height there took 47px off every line of the title, and FC clips
           a title that does not fit — mid-word, with no ellipsis to say so
           (Daniel, 2026-09-17). The title gets the full width back; the badge
           still gives way to the time on a narrow tile, which is its own rule
           below. */
        .kik-fc-event-coded-stacked {
            padding-inline-end: 0;
        }

        /* A stacked tile's title may use the height it has (events-calendar.js
           marks the title element): FC keeps a title on one line and clips it, so
           a long one was cut mid-word on a tile with room for three more lines. */
        .kik-fc-event-title-wrap {
            white-space: normal;
            overflow-wrap: anywhere;
        }
        .events-event-code {
            position: absolute;
            /* Daniel's own values (2026-09-16), verbatim — a 1px inset at the
               corner, and the type sized in PIXELS rather than in ems of the
               tile. That last one is the load-bearing difference: an em-sized
               badge grows with whatever font-size the surrounding view happens to
               give a tile, and the badge is a fixed small mark on every view. Do
               not "restore" the relative units. */
            top: 1px;
            inset-inline-end: 1px;
            z-index: 1;
            border-radius: 0.25rem;
            /* THE PLATE GIVES WAY, NOT THE TIME (Daniel, 2026-09-16). A code is
               up to eight characters and a timegrid column can be sixty pixels
               wide, so the two do collide — and when they do it is the
               abbreviation that truncates, because the timestamp is what somebody
               is actually reading off the tile.

               Same expression as the reserve above, so the two can never disagree
               about where the corner ends: whichever is smaller, the slot or what
               is left once the timestamp has its strip. The second term is the
               switch — multiplying the deficit by 1000 makes it swamp the first
               the moment the tile is wider than ``floor`` and go hard negative
               the moment it is not, so the plate is either a legible two
               characters or nothing at all, with no band in between where it
               shrinks to a stub.

               CSS and not a measurement in eventDidMount: a width computed at
               mount is wrong the moment the pane is resized or a sidebar opens,
               and this re-resolves on its own. */
            max-width: min(
                calc(min(var(--kik-fc-code-slot), 100% - var(--kik-fc-code-clear)) - 7px),
                calc((100% - var(--kik-fc-code-floor)) * 1000));
            overflow: hidden;
            text-overflow: ellipsis;
            /* A PLATE OF THE TILE'S OWN COLOUR, TAKEN DOWN. Not a neutral wash:
               mixing toward black keeps the badge in the event's family, so a
               week of tiles reads as tiles with a corner rather than as tiles
               with a foreign chip stuck on. White ink because the plate is the
               darkest thing on the tile at any calendar colour. */
            background: color-mix(in srgb, var(--fc-event-color, var(--app-colour)) 74%, #000);
            color: #fff;
            padding-block: 0.12em 0.16em;
            /* The horizontal padding is thrown by the same switch, and it has to
               be: max-width cannot shrink a box below its own padding, so without
               this the plate bottoms out as a 7px coloured nub on top of the time
               instead of disappearing. (Split out of the padding shorthand for
               exactly that reason — as a shorthand it came after this and undid
               it.) */
            padding-inline: min(0.4em, max(0px, calc((100% - var(--kik-fc-code-floor)) * 1000)));
            font-size: 9px;
            line-height: 11px;
            font-weight: 700;
            letter-spacing: 0.03em;
            text-transform: uppercase;
            pointer-events: none;
            white-space: nowrap;
        }

        /* More-menu category filtering is enforced server-side now: the toggle
           form persists the pick on the user's profile and both the FullCalendar
           feed (EventsJsonView) and the upcoming list (_upcoming_page) omit the
           de-selected categories, so there's nothing to hide with CSS. The grid
           updates live via a per-user realtime refetch push; the sidebar via the
           form's pat-inject. */

        /* ---- Travel-time strips ------------------------------------------- */
        /* A hatched block sitting immediately before a timed event with a real
           address: how long it takes to get there, and so what time you leave.
           Added as a FullCalendar event by libraries/kikaron/events-travel, which
           also builds the markup below in eventDidMount (FC7 drops our class
           names from tiles, so the JS puts .kik-travel-block on the element).

           The hatching is the point, not decoration: a solid block would read as
           another appointment, and the whole claim of a travel strip is that this
           time is NOT booked — it is time being spent getting somewhere. Each
           mode hatches differently (bike leans one way, public transport the
           other, the car is denser), so the strip says which way you are going
           without needing its label to be legible at 20 minutes tall. */
        .kik-travel-block {
            /* THE INK IS THE APP COLOUR TAKEN DOWN (Daniel, 2026-08-31). At full
               strength it is the app's own orange on a white plate over hatching
               of the same hue — the lightest ink anywhere on the grid, and the
               one asked to carry the smallest type on it. A fifth of black keeps
               the family and gives the glyph and the minutes an edge.

               The HATCHING keeps the undarkened colour on purpose: it is the
               texture that says "this time is not booked", and darkening a weave
               turns it back into something that reads as a second event. */
            /* The SHADE the ink darkens toward, and the hatch strength, are
               theme knobs: on a dark plate the ink lightens toward white
               instead, and the weave runs a little stronger to keep reading. */
            --kik-travel-ink: color-mix(in srgb, var(--app-colour) 78%, var(--kik-travel-ink-shade, black) 22%);
            --kik-travel-hatch: color-mix(in srgb, var(--app-colour) var(--kik-travel-hatch-strength, 34%), transparent);
            /* …and the answer to a pointer on an alternative: darker still, which
               is the whole of the hover (see button.kik-travel-chip). */
            --kik-travel-ink-strong: color-mix(in srgb, var(--app-colour) 55%, var(--kik-travel-ink-shade, black) 45%);

            /* Not a calendar event: a DIV the JS positions in the event column
               (inline top/height/left/right), sized straight from the column's
               px-per-minute scale — see events-travel.js for why FC's own event
               layout could not draw this. */
            position: absolute;
            z-index: 1;
            color: var(--kik-travel-ink);
            /* A plate under the hatching, so the strip reads over whatever the
               grid draws behind it, and the label stays legible where the
               hatching runs densest (Daniel, 2026-08-26). */
            background-color: var(--kik-travel-plate, rgba(255, 255, 255, 0.8));
            border-radius: var(--border-radii-small);
            /* The halo is the plate's own tone: it lifts the glyphs off the
               hatching without introducing a second colour — white on light
               themes, the dark plate on a dark one. */
            text-shadow: 0 0 2px var(--kik-travel-halo, white), 0 0 2px var(--kik-travel-halo, white), 0 0 2px var(--kik-travel-halo, white),
                         0 0 2px var(--kik-travel-halo, white), 0 0 2px var(--kik-travel-halo, white), 0 0 2px var(--kik-travel-halo, white);
            /* Visible on purpose: a five-minute journey is a sliver of hatching,
               and its label (anchored to the strip's bottom, below) rides up over
               the free time above rather than being clipped away. */
            overflow: visible;

            /* Bike: stripes leaning forward, open and light. */
            &.kik-travel-mode-bicycle {
                background-image: repeating-linear-gradient(45deg,
                    var(--kik-travel-hatch) 0 2px, transparent 2px 7px);
            }
            /* Public transport: the same weight, leaning the other way — the two
               are told apart by direction, which survives any block height. */
            &.kik-travel-mode-transit {
                background-image: repeating-linear-gradient(-45deg,
                    var(--kik-travel-hatch) 0 2px, transparent 2px 7px);
            }
            /* Car: denser and heavier. It is the fastest and the least free. */
            &.kik-travel-mode-car {
                background-image: repeating-linear-gradient(45deg,
                    var(--kik-travel-hatch) 0 4px, transparent 4px 8px);
            }
        }

        .kik-travel-strip {
            display: flex;
            position: absolute;
            inset-inline: 0;
            bottom: 0;
            align-items: center;
            justify-content: space-between;
            gap: 6px;
            height: 100%;
            /* Two bounds, one reason each. The min-height keeps the label legible
               when the journey is short — the box grows UPWARD (bottom-anchored),
               over free time, never into the event. The max-width keeps the lead
               and the alternatives within one glance in the day view, where the
               column is the whole grid and space-between would park the
               alternatives half a screen away. */
            min-height: 15px;
            max-width: 300px;
            padding: 0 4px;
            font-size: 11px;
            line-height: 1.2;
            white-space: nowrap;
        }

        .kik-travel-others {
            display: flex;
            align-items: center;
            gap: 4px;
            /* The alternatives are an aside — smaller, and tinted toward white
               rather than dimmed with opacity, so they render crisp over the
               hatching while still never competing with the lead. A tenth of
               white now rather than a fifth: the ink they are mixed from is
               already darker, and an aside still has to be readable at 10px over
               a weave. */
            font-size: 0.92em;
            opacity: 1;
            color: color-mix(in srgb, var(--kik-travel-ink) 90%, white 10%);
        }

        .kik-travel-chip {
            display: inline-flex;
            align-items: center;
            gap: 3px;
            margin: 0;
            padding: 0;
            border: 0;
            background: none;
            color: inherit;
            font: inherit;

            &.is-lead {
                font-weight: 600;
            }

            /* A mode nobody could route (no timetable here, no road there) keeps
               its place and shows a dash. Dropping it would say "there is no bus",
               which is a different claim and one we cannot make. */
            &.is-unknown .kik-travel-time {
                opacity: 0.7;
            }
        }

        button.kik-travel-chip {
            /* Clicking an alternative makes it the wide one and stores that choice
               on the event. No pointer cursor — house rule; it is a button, and
               buttons look like buttons.

               SO THE POINTER IS ANSWERED IN INK: the mode under it goes darker
               than any other mark on the strip, which is the same thing that will
               happen to it permanently if the click lands — it becomes the lead.
               The answer and the outcome are the same move, which is what makes it
               legible without a word.

               Not an underline any more. That said "link", which this is not, and
               the house has been taking underlines off its hovers in favour of an
               answer the control itself gives (0aa6553ae9, the cluster items). The
               dead `opacity: 1` went with it — the only dimmed chips are the
               unroutable ones, and those are spans, never buttons. */
            @media (hover: hover) {
                &:hover {
                    color: var(--kik-travel-ink-strong);
                }
            }
        }

        .kik-travel-icon:before {
            font-family: fontello;
            font-style: normal;
            font-weight: normal;
        }
        .kik-travel-bicycle .kik-travel-icon:before { content: var(--glyph-bicycle); }
        .kik-travel-transit .kik-travel-icon:before { content: var(--glyph-bus); }
        /* The fontello set ships no plain car; the taxi glyph IS a car silhouette
           at this size, and nothing else in the set reads as one. */
        .kik-travel-car .kik-travel-icon:before { content: var(--glyph-taxi); }
    }

        /* THE CREATE/EDIT PANELS' HEIGHT FLOORS MOVED TO THE MODAL COMPONENT
           (Daniel, 2026-09-04). ``#modal-create-fields`` / ``#modal-edit-fields``
           and the lazy anchor they replace are reserved by
           libraries/kikaron/modal, keyed off ``.kik-modal-create-event`` /
           ``.kik-modal-edit-event`` on the dialog. Here they compiled into
           ``.application-body.application-events``, so the SAME panel opened from
           another app — Mail's "Voeg toe" on a detected event — stood in that
           app's body and opened at the wrong height. */

        /* INVITATIONS WAITING — the toolbar disc and its menu.
           ------------------------------------------------------------------ */

        /* The counter's own disc and its seat on the corner live with the badge,
           in patterns/context-menu — a trigger carrying a count is a fact about
           menus rather than about this one. */

        /* THE PANEL KEEPS ITS OWN SIZE. A menu of rows is shrunk to 0.85 on a
           large screen — worth it for a list of words, wrong for cards carrying
           three lines and a control apiece. Taking the shrink off is the whole
           change; nothing here is scaled UP.

           And zoom is not a free knob: the panel's own
           ``max-height: calc(100dvh - 100px)`` is computed in the ZOOMED
           coordinate space, so a zoom above 1 lets the box grow taller than the
           screen and it never needs the scrollbar it would otherwise show — the
           list simply ran off the bottom. At 1 the two agree again. */
        @container screen style(--screen-small: false) {
            .kik-events-invites-panel .context-menu-content {
                zoom: 1;
                /* THE HEIGHT HAS TO COME DOWN WITH THE ZOOM. The panel's
                   max-height is stated once, for the zoomed case: at 0.85 the box
                   it allowed was 0.85 of that on screen, and the 100px of slack
                   was measured against that. Taking the zoom off gives the full
                   value back — a box taller than the space under the trigger,
                   which is why the list ran off the bottom with no scrollbar.
                   Multiplying the limit by the zoom that is no longer applied
                   reproduces exactly the on-screen height it always had. */
                max-height: calc(0.85 * (100dvh - 100px));
            }
        }

        /* An invitation well: title, the when/where under it, the answer below.
           The whole of the reading matter is one link (header included); the
           answer is deliberately outside it. */
        .kik-events-invite-well {
            /* The env chip's wash, on a card. Same construction as
               libraries/kikaron/global-header (kik-env-badge): the ink washed
               over white for the ground, and a ring instead of a border, so the
               edge is a tint of the same colour rather than a grey line. Paler
               than the chip's 12%: the chip is a stamp the size of a letter and
               has to hold its colour at that size, where this is a card the eye
               rests on and reads three lines from.

               Built from --color-accent-hover, not --colour-accent: inside an
               application body the latter is reassigned to that app's own tint
               (application-body.css), and the events tint is orangered — the card
               would come out washed RED here and washed orange everywhere else.
               This token is the brand orange at :root and nothing reassigns it. */
            --kik-invite-colour: var(--color-accent-hover, #ea6c1a);
            background: color-mix(in srgb, var(--kik-invite-colour) 6%, #fff);
            box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--kik-invite-colour) 35%, transparent);

            /* The title and what it says belong together — a well's stock gap
               between header and panel is page-module air, far too much at this
               size. */
            --pat-well-padding-top: 10px;
            --pat-well-padding-bottom: 12px;
            --pat-well-padding-left: 12px;
            --pat-well-padding-right: 12px;
            --pat-well-padding: 12px;

            > .well-title-group {
                margin-bottom: 0;
                padding-bottom: 0;
            }
            > .panel-content {
                padding-top: 2px;
            }
        }
        .kik-events-invite-well .well-header {
            font-size: 1em;
            line-height: 1.25;
            text-wrap: balance;
            margin: 0;
        }
        /* Both halves of the link — the header's and the body's — read as text,
           not as a blue link: the well IS the target, so underlining parts of it
           says less than the whole box already does. */
        .kik-events-invite-well .kik-events-invite-open {
            display: flex;
            flex-direction: column;
            gap: 1px;
            text-decoration: none;
            color: inherit;
        }
        .kik-events-invite-well .kik-events-invite-when,
        .kik-events-invite-well .kik-events-invite-where,
        .kik-events-invite-well .kik-events-trash-removed {
            font-size: 0.85em;
            color: var(--colour-byline);
        }
        /* A trashed event's well is the invitation well in grey: the orange wash
           says "something is asked of you", and nothing is asked here — it is a
           row that WAS on the calendar. The three lines stack like the link's
           halves do, without being a link. The removal line sits apart from the
           when/where, which describe the event; this one describes what
           happened to it. */
        .kik-events-trash-well {
            /* Grey is the fallback only: each well is handed the colour of the
               calendar the event was on as an inline --kik-invite-colour
               (event_trash_wells.html), and an inline value wins over this. A
               notch stronger wash than an invitation's 6%: here the tint is the
               information — which calendar — and two calendars a few hue steps
               apart have to tell apart at a glance. */
            --kik-invite-colour: var(--colour-byline, #6b7280);
            background: color-mix(in srgb, var(--kik-invite-colour) 10%, #fff);
            > .panel-content {
                display: flex;
                flex-direction: column;
                gap: 1px;
            }
            .kik-events-trash-removed {
                margin-top: 8px;
            }
            /* The header bar is a grid of title and functions, and the functions
               column gives way to a long title — the invitation history's answer
               pill can lose a few letters to that, a button that reads "H…"
               cannot. The bar keeps the width its one control needs; the title
               wraps instead (it is text-wrap: balance already). */
            > .well-title-group > .pat-toolbar {
                min-width: max-content;
            }
            .kik-events-trash-restore {
                flex: none;
                max-width: none;
                overflow: visible;
            }
        }
        /* AIR BEFORE THE DECISION. The answer is a different kind of thing from
           the two lines above it — those describe the event, this one asks
           something — so it is separated rather than stacked. */
        .kik-events-invite-well .rsvp {
            margin-top: 14px;
        }
        /* THE ANSWER AS A STATE, AND AS THE WAY TO CHANGE IT. On the history
           page the answer is what the row IS — so it takes the shape a state
           already has here: a pat-select coloured by what is selected, exactly
           like a workflow state on a toolbar (patterns/components/toolbar has
           .state-published, .state-draft and the rest doing precisely this).

           THROUGH --pat-select-box-*, WHICH IS THE ONLY THING THAT PAINTS HERE.
           Inside a .pat-toolbar the visible box is the LABEL, drawn from
           --pat-select-box-background-colour with its text and arrow from
           --pat-select-box-arrow-text-colour; the <select> inside it is not what
           anyone sees. Colouring that select — the first go at this — measured
           perfectly in devtools and showed nothing on screen, because the label's
           box is painted over it.

           The workflow tokens rather than three colours of this app's own — green
           for a yes and red for a no mean the same here as they do on a document,
           and a fourth palette for the same three ideas is how a theme starts
           disagreeing with itself. Never answered is the exception: it is not a
           state anything reached, so it wears a neutral mixed from the page's own
           ink and follows whatever theme is on.

           WRITTEN AT THE TOOLBAR'S OWN WEIGHT. patterns/components/toolbar sets
           these very tokens for every .pat-select in a bar — the toolbar button's
           white — through `.pat-toolbar .toolbar-section > .pat-select:not(.bare)`,
           four classes deep. A plain `.kik-events-invite-answer` here is three
           (the app wrapper included) and loses, silently: the class was on the
           element, the custom properties were declared, and the box went on
           painting the default. Matching its shape puts this above it. */
        .pat-toolbar .toolbar-section > .pat-select.kik-events-invite-answer {
            --kik-invite-answer-colour: var(--colour-published);
            --pat-select-box-background-colour: var(--kik-invite-answer-colour);
            --pat-select-box-background-colour-hover:
                color-mix(in srgb, var(--kik-invite-answer-colour) 88%, black);
            --pat-select-box-arrow-text-colour: white;
            --pat-select-box-arrow-text-colour-hover: white;

            &.option-yes { --kik-invite-answer-colour: var(--colour-published); }
            &.option-maybe { --kik-invite-answer-colour: var(--colour-draft); }
            &.option-no { --kik-invite-answer-colour: var(--colour-closed); }
            &.option-none {
                --kik-invite-answer-colour: color-mix(in srgb, var(--colour-base-text) 45%, transparent);
            }

            /* The select itself carries no ground of its own — the label is the
               box. It only has to put its words in the same ink. */
            select {
                background-color: transparent;
                color: var(--pat-select-box-arrow-text-colour);
                border: none;
                box-shadow: none;
                white-space: nowrap;
            }

            /* PAST, SO NOT A DECISION ANY MORE. The form draws a disabled select
               as a flat grey field, and the pattern drops the arrow on a box that
               holds one (`:has([disabled])`). The box keeps its state colour
               either way: it still has to read as the state it is. */
            &.is-disabled select:disabled {
                background: transparent !important;
                color: var(--pat-select-box-arrow-text-colour);
                box-shadow: none;
                cursor: default;
            }
        }
        /* A history well's header bar carries that one control and nothing else,
           so it asks for no height of its own beyond it. */
        .kik-events-invite-past .well-title-group .pat-toolbar {
            min-height: 0;
        }
        /* GLYPHS ONLY, no words. The voting bar drops its labels below a width
           threshold — but only inside a .pat-toolbar, and this one is in a
           popover, so the words stayed and the pill squeezed them until they
           clipped mid-word ("Accepte", "Misschie", "Wijs af" → "Wijs"). A clipped
           word is worse than no word: it reads as a different word.

           Same mechanism the toolbar uses (quaive/voting-bar): font-size: 0
           collapses the text node while the glyph, sized back up on the :before,
           centres on its own — and the words stay in the DOM for a screen reader. */
        .kik-events-invite-well .rsvp .voting-bar .pat-checklist {
            display: flex;
            gap: 4px;
            /* The three answers SPREAD: equal shares of the whole control rather
               than three marks huddled at its left end. The bar is the width of
               the card either way — this is what it is the width of it FOR. */
            > label {
                flex: 1 1 0;
                width: auto;
                min-width: 0;
            }
        }
        .kik-events-invite-well .rsvp .voting-bar .pat-checklist label {
            font-size: 0;
            padding-left: 0;
            padding-right: 0;
        }
        .kik-events-invite-well .rsvp .voting-bar .pat-checklist label:before {
            font-size: var(--body-font-size);
            margin: 0;
        }

        /* THE IN-PAGE ANSWER — phone only.
           ------------------------------------------------------------------
           It sits above the date module: answering is the one thing this page
           asks of an invitee, so it comes before what the event says about
           itself. On a wider screen the toolbar carries the same control and
           two of them is the same question asked twice — hidden there, at the
           breakpoint the toolbar's own rules use (681px, phone-app-carousel). */
        #event-toolbar-rsvp-events-central-section {
            /* Narrower than the modules it leads: the answer reads as a control
               over the page, not as another block of it. */
            max-width: 80%;
            /* The same air the content area takes below the hero, so the gap to
               the first module matches the gap above the control. Named, not
               measured — quaive/document-body composes its padding from this
               token, and a hard-coded twin would drift the first time it moves. */
            margin-bottom: var(--small-screen-padding);
        }
        @media (min-width: 681px) {
            #event-toolbar-rsvp-events-central-section {
                display: none;
            }
        }

        /* AN UNANSWERED INVITATION IS AN OUTLINE, NOT A TILE.
           ------------------------------------------------------------------
           It is on the calendar because somebody asked, not because this person
           said yes — so it is drawn as a shape waiting to be filled: white
           ground, a dashed edge, silver text. A grid that draws it like every
           other event says the week is fuller than it is.

           The class comes from the feed (``classNames``), which is where a fact
           about an event belongs. FC7 does not apply those to the element on its
           own; events-calendar.js puts them back on mount, so this rule has
           something to match.

           ``!important`` because the tile's colour is written inline from the
           feed, and an inline style is the one thing a class cannot out-rank. */
        .invite-pending {
            /* Painted through the property the theme paints FROM, not over the
               top of it: the classic theme fills and outlines a tile with
               ``background-color: var(--fc-event-color)`` and
               ``border-color: var(--fc-event-color)`` on hashed classes, so
               setting the variable reaches every one of them at once instead of
               chasing selectors whose names are generated.

               !important because the feed writes the colour inline on the
               element, and an inline custom property beats a stylesheet without
               it. The border is stated after, or it would go white with the
               fill. */
            /* The card's own tone, not literal white — the outline metaphor
               holds on a dark calendar too (the token is unset on light themes,
               so this is #fff there). */
            --fc-event-color: var(--kik-fc-view-background, #fff) !important;
            --fc-event-contrast-color: silver !important;
            border-style: dashed !important;
            border-width: 1px !important;
            border-color: silver !important;
            color: silver !important;
        }
}
