/* Application styles. Also the file that loads Bulma: the @import below puts bulma.css into the
 * cascade layer `bulma`, and every rule in this file is unlayered. Unlayered rules beat layered
 * ones regardless of specificity and load order, so a plain single-class selector here overrides
 * Bulma - no doubling, no borrowing Bulma's class for weight.
 *
 * The doubled and borrowed selectors below - .box.combat-chat-box, .scene-image-hidden
 * .scene-image-hidden, html body - predate the layer, from when this file loaded before bulma.css
 * and a specificity *tie* went to the later sheet. They still win and are left alone; a new rule
 * does not need the trick.
 *
 * `cursor: pointer` appears five times rather than once as a utility class. Each sits with the
 * feature it belongs to and the reasons differ - a drag handle, three kinds of <summary> whose
 * marker does not imply a hand, a clickable image. Collecting them would move the decision into
 * five templates and buy no behaviour.
 */

/* CSS requires @import before every rule in the file, which is why Bulma is loaded here at the top
   rather than by a second <link> in shared/head.jte - HTML has no way to put a <link> into a layer. */
@import url("bulma.css") layer(bulma);

/* ============================================================================================
   Basics
   ============================================================================================ */

/* Bulma's own danger colour rather than `red`, and no background at all: the white one this used to
   carry painted a white block onto the dark scheme, which Bulma applies from prefers-color-scheme. */
.error { color: var(--bulma-danger); }

/* `is-tiny` is not a Bulma size - it has three (is-small, is-medium, is-large) and this is not one of
   them. Five buttons have carried the class since the chat room was first built: the roster's "aktiv
   setzen" and "[x]" in room/creaturePanel.jte, the channel box's delete and restore, and the kick
   button in room/view.jte. All five therefore rendered at Bulma's *default* size, a head taller than
   the is-small controls they stand among - the roster's two buttons per row were the loudest thing in
   the right column. A class that does nothing fails quietly, which is why it lasted this long.

   Defined rather than replaced with is-small: all five sit in a repeating row - one per creature, one
   per channel, one per user - where the action is secondary to the name beside it. That is a real step
   below is-small, and now the name says it.

   Bulma sizes a button by font-size alone and keeps the control height at a flat 2.5em, which at this
   font would still be a 28px box around 11px of text. Height off, padding instead - the same move the
   dice buttons make inline - so the box follows the font. */
.button.is-tiny {
    font-size: 0.7rem;
    height: auto;
    padding: 0.15em 0.6em;
    line-height: 1.4;
}

/* Bulma's minireset sets `html { box-sizing: border-box }` and then `*, *::before, *::after
   { box-sizing: inherit }`, which is how every element in the page ends up on the border box. That
   inheritance follows the *flat* tree, and a <details> slots its children through a UA shadow root:
   the flat-tree parent of everything below the summary is a <slot> the author `*` rule cannot match,
   so it sits on the initial `content-box` and the whole subtree inherits that instead.

   The visible damage was in creature/fragments/rollSection.jte: the difficulty <select> asks Bulma for
   `height: 2.5em` and, as a content box, added its padding and border on top - 42px inside a 30px
   wrapper, drawn over the first row of the value table below it. Two more turned up on measuring the
   pages that carry a <details> before and after this rule. /modifier/list overflowed its window by
   90px, because a `width: 100%` control in a table cell was that plus its padding; and the simulator's
   freeform attribute grid fitted two Bulma columns per row where three were asked for, for the same
   reason. The sidebar is built from <details> too and does not move - it sizes nothing in percent.

   Re-anchoring the direct children puts their subtrees back on border-box, since `inherit` then has
   the right value to take. <summary> is included because it is slotted the same way. */
details > * {
    box-sizing: border-box;
}

/* ============================================================================================
   Boxes that scroll instead of growing the page (room view and combat screen)
   ============================================================================================ */

/* Combat screen: the three columns are meant to fit on one screen, so the chat and the character
   sheet scroll inside their own box instead of growing the page. The height comes from the viewport
   so the layout follows the window rather than a fixed pixel count. */
.combat-scroll {
    max-height: calc(100vh - 14rem);
    overflow-y: auto;
}

/* The chat box on the combat screen: the whole box is capped at the window, and the message list is
   the only part that gives. Capping #messages alone was not enough - the heading, the dice bar and
   the send form sit in the same box, so the box still grew past the fold and the input the player
   types into ended up off screen. flex + min-height:0 is what lets the list shrink below the height
   of its content and take the scrollbar instead.

   The doubled selector predates the bulma layer (see the file header), from when Bulma's plain
   ".box { display: block }" beat a single class on load order and would have left the box a block -
   capped in height, with the message list spilling out of it and down the page. Still wins, stays. */
.box.combat-chat-box {
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 14rem);
}

.box.combat-chat-box > * {
    flex: 0 0 auto;
}

/* Das Knopfraster unter „Verwaltung" auf gameSystem/show.

   Bulmas .button ist white-space: nowrap. In einer Viertelspalte lief eine zu lange Beschriftung
   damit nicht etwa in eine zweite Zeile, sondern aus ihrem Knopf heraus und über den Nachbarn - bei
   1024px Fensterbreite betraf das die Hälfte des Rasters. Umbrechen zu erlauben kostet an schmalen
   Fenstern eine höhere Zeile und an breiten gar nichts.

   Zwei Klassen (0,2,0) schlagen Bulmas .button (0,1,0) unabhängig von der Ladereihenfolge, hier
   braucht es die verdoppelte Klasse also nicht. min-height hält die einzeiligen Knöpfe auf der
   Höhe, die sie ohne diese Regel hätten, damit eine Zeile aus kurzen und langen Beschriftungen
   nicht ausfranst. */
.admin-button-grid .button {
    white-space: normal;
    height: auto;
    min-height: 2.5em;
}

/* Die beiden ausklappbaren Kästen der Raumansicht (Kanäle, Benutzer im Raum). Sie starten verborgen
   und werden über den Raumknoten der Seitenleiste eingeblendet - siehe roomSidebar.js.

   Die doppelte Klasse stammt aus der Zeit vor der bulma-Ebene (siehe Dateikopf), als Bulmas
   ".box { display: block }" einen einzelnen Klassenselektor bei Gleichstand schlug und diese Regel
   still gar nichts getan hätte. Sie gewinnt weiterhin und bleibt. */
.room-side-box-hidden.room-side-box-hidden {
    display: none;
}

/* Same idea for the chat column of the normal room view (room/view.jte), which had no cap at all: the
   message list grew with the log, the page grew with it, and the dice bar and send form - a second box
   below the message box - ended up arbitrarily far down. Here the *column* is the flex container, since
   the two boxes are siblings: the message box gives, the send box keeps its natural height.

   The cap is a custom property set by view.jte from the column's measured top offset, because the
   header box above the columns renders a user-supplied description and has no predictable height. The
   constant is only the fallback for a page whose script did not run.

   Two classes like the combat rules above: predates the bulma layer, still wins, stays. */
.column.room-chat-column {
    display: flex;
    flex-direction: column;
    max-height: var(--room-chat-max, calc(100vh - 18rem));
}

.column.room-chat-column > .box {
    flex: 0 0 auto;
}

.column.room-chat-column > .box:first-child {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 6rem;
}

/* Inside the message box only the list gives; the heading above it keeps its height. */
.column.room-chat-column > .box:first-child > * {
    flex: 0 0 auto;
}

/* Both screens end in the same three declarations, because both ask the same thing of their message
   list: be the part that gives, never collapse to nothing, take the scrollbar. It is written once
   with both selectors rather than twice.

   Each of them out-specifies the `> *` rule above it by the one extra class .messages, so this wins
   wherever it stands - the position below them is for reading order, not for the cascade. */
.box.combat-chat-box .messages,
.column.room-chat-column > .box:first-child > .messages {
    flex: 1 1 auto;
    min-height: 6rem;
    overflow-y: auto;
}

/* ============================================================================================
   Chat lines (room/messages.jte, on both screens)
   ============================================================================================ */

/* A chat message is a row in a log, and it was wearing Bulma's .message - the notification card, with
   `mb-3 p-3` on top. Two one-line messages stood 60px apart: 24px of text, 12px of padding below it,
   12px of margin, 12px of padding above the next one. The class stays - three Selenium tests find
   chat lines by `#messages .message` - but the spacing is the row's own: one line plus enough air
   for the stripe below to read as a band rather than as a rule.

   The horizontal padding is what keeps the stripe off the text; the vertical one is deliberately far
   smaller than it, since it is the axis the complaint was about.

   The margin has to be said out loud: dropping `mb-3` did not leave zero behind, it uncovered Bulma's
   own `.message:not(:last-child) { margin-bottom: 1.5rem }`, which is 24px - worse than what was
   removed. Same specificity as this rule, so it is the unlayered position that wins it (file header). */
.messages .message {
    margin-bottom: 0;
    padding: 0.15rem 0.5rem;
}

/* Alternating rows. A scheme colour rather than a fixed one, so the dark theme gets a band lighter
   than its background instead of a second rule saying so.

   `-ter` and not the `-bis` Bulma's own striped tables use: those stripe rows one line high, where
   two percent of lightness is enough to tell them apart. A chat line runs to four lines, and at 98%
   on white the band was measurably there and visually absent - which is the wrong kind of subtle,
   because separating a long message from the next one is the whole job here. -ter is 96%/13% against
   the box's 100%/9%: still no colour, but a band you can see.

   nth-child over nth-of-type: the fragment renders these divs and nothing else into #messages, and a
   deleted message is skipped server-side rather than hidden, so the children *are* the visible rows.
   Numbering from the top means the stripes shift by one whenever the oldest message scrolls out of
   the fetched window - harmless, because the stripe carries no meaning; it only separates rows. */
.messages .message:nth-child(even) {
    background-color: var(--bulma-scheme-main-ter);
}

/* The initiative track is a wrapping grid (columns is-multiline in combatTurnOrder.jte), so it grows
   downwards with every extra creature and pushes the action bar below the fold. Capping it keeps the
   actions in view. The cap follows the viewport but never drops below one card row - a card is a 6rem
   portrait plus name, hit points and tags - so a short window still shows a full row, not a sliver. */
.combat-order-scroll {
    max-height: max(16rem, calc(100vh - 28rem));
    overflow-y: auto;
}

/* ============================================================================================
   Turn-order cards
   ============================================================================================ */

/* The GM's "take this creature out of the fight" ✕ sits in the card's own top right corner instead
   of in a row of buttons underneath it, so it reads as belonging to that creature. Bulma's .box has
   1.25rem of padding, which is the room the ✕ sits in - it overlaps nothing. */
.combat-card {
    position: relative;
}

.combat-card .combat-card-remove {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
}

/* The game master opens a creature in the edit panel by clicking its portrait and name. Only that
   block is the handle: the card also carries the hit point field, the remove ✕ and the effect tags'
   own ✕, and a click on any of those bubbles - taking the whole card would fire on all of them. */
.combat-card-handle {
    cursor: pointer;
}

/* Which creature the edit panel below is showing. An outline rather than a background: the card
   already uses its background for two other states (its turn, and down at 0 hit points), and those
   have to stay readable while it is open. */
.combat-card.combat-card-selected {
    outline: 2px solid var(--bulma-link);
    outline-offset: 1px;
}

/* Heroes or monsters (docs/plans/kampfreihen.adoc, Inkrement 1): an edge on the card, because the
   background is taken - its turn, and down - and the outline above means "open in the panel". */
/* The scene's main image on the campaign board (docs/plans/foundry-kampagnen.adoc): the star marks
   it and the tile is outlined; the tile keeps its size, since the palette and the cards share one
   grid and a bigger tile would push the others around. */
.board-image-tile .board-tile-star {
    position: absolute;
    top: 0.2rem;
    left: 0.2rem;
    border: none;
    background: rgba(255, 255, 255, 0.85);
    border-radius: 50%;
    width: 1.5rem;
    height: 1.5rem;
    line-height: 1.5rem;
    padding: 0;
    cursor: pointer;
    color: var(--bulma-grey);
}

.board-image-tile .board-tile-star.is-main {
    color: var(--bulma-warning);
}

.board-image-tile.board-image-main {
    outline: 2px solid var(--bulma-warning);
    outline-offset: 1px;
}

.combat-card.combat-card-heroes {
    border-left: 4px solid var(--bulma-info);
}

.combat-card.combat-card-monsters {
    border-left: 4px solid var(--bulma-danger);
}

/* The row picture (Inkrement 2): four lanes under their labels. A lane keeps the card grid; the
   label is what turns the grid into "Gegner hinten". Empty lanes stay, so the picture keeps its
   shape when a row empties. */
.combat-lane {
    min-height: 1rem;
    margin-bottom: 0.25rem;
}

.combat-lane-label {
    border-bottom: 1px solid var(--bulma-border);
}

/* ============================================================================================
   Creature sheet
   ============================================================================================ */

/* The foldable value blocks of the room panel (creature/fragments/rollSection.jte). Native <details>,
   so the folding costs no javascript; all this adds is the hand cursor the marker alone does not give
   and a little air between the blocks. */
.creature-section > summary {
    cursor: pointer;
}

.creature-section + .creature-section {
    margin-top: 0.5rem;
}

/* The check-roll difficulty select repeats on every attribute, skill and saving throw row of the
   creature sheet. Bulma reserves 2.5em at the end of every select for its own arrow
   (bulma.css ".select select:not([multiple])") plus 0.75em at the start, so the control is far wider
   than its content - and unlike the is-fullwidth selects elsewhere this one sizes itself to that
   content, widening the whole table column with it. Tighten the reserve and pull the arrow in to
   match, so the select still reads as a dropdown.
   The extra class beats Bulma's own selectors on specificity, so the load order of the two
   stylesheets does not matter here. */
.select.is-compact:not(.is-multiple):not(.is-loading)::after {
    inset-inline-end: 0.6em;
    height: 0.5em;
    width: 0.5em;
}

.select.is-compact select:not([multiple]) {
    padding-inline-start: 0.5em;
    padding-inline-end: 1.4em;
}

/* And the same treatment on the other axis. Bulma's control height is a flat 2.5em, which at the
   0.75rem of `is-small` is a 30px box around 12px of text - fine for a form field standing on its
   own, twice the height it needs beside a table of 18px rows. Height off, padding instead, so the
   box follows the font rather than a constant: it lands near 22px and shrinks with is-small.
   Both elements, because the wrapper carries a height of its own and only sizing both keeps the
   arrow centred on the text. */
.select.is-compact:not(.is-multiple),
.select.is-compact select:not([multiple]) {
    height: auto;
}

.select.is-compact select:not([multiple]) {
    padding-block: 0.15em;
}

/* The difficulty above each roll table (creature/fragments/rollSection.jte). Right-aligned so it
   stands over the dice column - the column whose buttons roll against it.
   The width reset is the exception the "selects look like inputs" block below names: stretching this
   one to the panel width would push its label to the far left, half a panel away from the control it
   belongs to. */
.roll-difficulty {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.4em;
    margin-bottom: 0.25rem;
}

.roll-difficulty .select,
.roll-difficulty .select select {
    width: auto;
}

/* ============================================================================================
   Layout
   ============================================================================================ */

/* Bulma has fifths and halves but no 30% column. Same declaration and same breakpoint as Bulma's own
   fraction classes (bulma.css, "@media screen and (min-width: 769px), print"), so this column stacks
   below 769px exactly like its neighbours do. */
@media screen and (min-width: 769px), print {
    .column.is-three-tenths {
        flex: none;
        width: 30%;
    }
}

/* ============================================================================================
   Mass-edit tables and the list filter
   ============================================================================================ */

/* Mass-edit tables (the list pages of flags, skills, traits and qualities). Compact mode drops the
   columns that are rarely touched while entering a batch of new elements; the inputs stay in the
   DOM, so a save from compact mode still submits their values. */
.mass-edit.is-compact .is-full-only {
    display: none;
}

.mass-edit .input.mass-edit-ranking,
.mass-edit .input.mass-edit-number {
    width: 6em;
}

.mass-edit td {
    vertical-align: middle;
}

/* The name column needs a floor, and the host pages need the whole window.
 *
 * Bulma gives every .input `width: 100%`, so a name field is exactly as wide as its cell and
 * contributes *nothing* to the table's auto layout - the column ends up with whatever slack the
 * selects and the 6em number inputs leave over, and long names (the natural combat skills are the
 * worst) scroll inside the input instead of showing. A min-width on the cell is what actually
 * reserves the space; widening the page alone does not move the slack here.
 *
 * The other half is on the templates: the twelve pages that host a mass-edit table use
 * `container is-fluid` rather than a plain `.container`, whose 960/1152/1344px cap left most of a
 * wide window unused once the 15rem sidebar padding below had been taken off. is-fluid keeps a
 * 32px gutter, which is the narrow padding to the navigation these tables want. */
.mass-edit td.mass-edit-name {
    min-width: 16rem;
}

/* A select is as wide as its widest option, so the "granted by" column - which lists every
   grantable skill by its full name - pushes the name column back out. Same reason the difficulty
   select is capped further down. */
.mass-edit .select.mass-edit-grant select {
    max-width: 14rem;
}

/* ============================================================================================
   Selects look like inputs
   ============================================================================================ */

/* Bulma already gives .control, .input, .textarea and .select the same --bulma-input-* variables
 * (bulma.css ".control, .input, .textarea, .select"), so colour, border and radius match already.
 * Two things it does not carry across:
 *
 * 1. The inset shadow. ".textarea, .input" sets `box-shadow: inset 0 0.0625em ...` literally, while
 *    ".select select" is left on the shared control rule's `box-shadow: none`. Bulma declares the
 *    value as --bulma-input-shadow and then does not use it for the select, so applying it here is
 *    using Bulma's own variable rather than restating its number - and it keeps following the theme.
 *
 * 2. Width. An .input is `width: 100%`; a .select wrapper is `display: inline-block` and sizes to its
 *    widest option, so next to an input it reads as a different kind of control. It now stretches the
 *    same way, everywhere - in a table cell it fills the column, in a stacked form it fills the field.
 *    `max-width: 100%` stays Bulma's, so nothing overflows its container.
 *
 *    Deliberately global rather than restricted to stacked fields. The narrow variants that are
 *    narrow *on purpose* say so for themselves and still do: .mass-edit-grant caps the "granted by"
 *    column below, .roll-difficulty puts the sheet's difficulty dropdown back beside its label, and a
 *    select in a .field.is-grouped is bounded by the control it sits in rather than by the row.
 *
 * Unlayered, so no doubled selector is needed to beat the bulma layer - see the file header. */
.select select {
    box-shadow: var(--bulma-input-shadow);
}

.select,
.select select {
    width: 100%;
}

/* ...with one exception, and it is not a preference. A table lays its columns out from the intrinsic
   width of their contents, and a `width: 100%` control has none - exactly the trap the name column
   above is pinned against. Left at 100% inside a mass-edit table the column collapses to its header
   text while Bulma still reserves 2.5em for the arrow, so the arrow is drawn over the value and the
   value cannot be read at all: the skill table's "Typ" showed a bare chevron, the modifier tables
   showed "Elf" and "Konstitutio" with the arrow on top of them.
   `is-fullwidth` still wins, because there it is the author asking for it. */
.mass-edit .select:not(.is-fullwidth),
.mass-edit .select:not(.is-fullwidth) select {
    width: auto;
}

/* Search-as-you-type (shared/listFilter.jte + listFilter.js): the row stays in the DOM and, on a
   mass-edit table, keeps submitting its inputs - the batch would otherwise stop saving what is
   filtered away. */
.is-filtered-out {
    display: none;
}

.list-filter-count {
    white-space: nowrap;
}

/* ============================================================================================
   Dice and combat configuration (/game-system/create and /edit)
   ============================================================================================ */

/* rollConfigFields.jte, rollSpecFields.jte, combatConfigFields.jte.

   Bulma's .select is an inline-block wrapper that positions the dropdown arrow, so a <p class="help">
   following it sat on the same line and the select hung over its own explanation. Rather than
   restructure the ~15 places that pair the two, the wrapper becomes a block inside this region only -
   the layout everywhere else (the compact selects on the creature sheet, the mass-edit tables) is
   deliberately inline and must stay that way. */
.roll-config .select {
    display: block;
}

/* The actual cause of the overlap, and it is not the inline-block above: Bulma pins the *wrapper* to
   `height: 2.5em` (.select:not(.is-multiple)) while the <select> inside sizes itself from its own
   font, padding and border. Measured on this page the wrapper reserves 40px and the select draws 56,
   so it hangs 16px out of its own box and over whatever follows - here the <p class="help"> that
   explains the field. Letting the wrapper size to its content is the fix; the arrow is centred on the
   wrapper, so it follows.

   Scoped to this region rather than to `.select` globally: the same 40-vs-56 mismatch exists wherever
   Bulma's select is used, but every other place either has no help text under it or has been laid out
   around the overlap already, and a global height change would move all of them at once. */
.roll-config .select:not(.is-multiple) {
    height: auto;
}

.roll-config .select + .help {
    margin-top: 0.35rem;
}

/* A <summary> renders as a block, and a heading inside it would break onto its own line and lose the
   disclosure triangle's alignment - hence .is-inline on the h2/h3 plus the hand cursor the marker
   alone does not give. Same reasoning as .creature-section > summary above. */
.roll-config summary,
.roll-spec > summary,
.roll-spec-group > summary {
    cursor: pointer;
}

.roll-spec,
.roll-spec-group {
    margin-top: 0.75rem;
    padding-left: 0.75rem;
    border-left: 2px solid var(--bulma-border);
}

/* The nested tables carry their own padding; without this the groups drift steadily to the right as
   the nesting deepens. */
.roll-spec > table,
.roll-spec-group > table {
    margin-bottom: 0;
}

/* ============================================================================================
   Wizard step strip
   ============================================================================================ */

/* The step strip of the Würfel- und Kampfsystem assistant (gameSystem/wizard/layout.jte).

   Bulma's .tabs keeps its list on one line and scrolls it horizontally; with twelve steps that hides
   most of them behind an off-screen edge, which defeats the point of a strip you are meant to jump
   around in. Wrapping is the whole change - two short rows of tabs read fine, a scrollbar under a
   navigation aid does not.

   This is the block the old load order - before bulma.css moved into its cascade layer - was
   silently eating: written as `.wizard-tabs ul` it tied with Bulma's `.tabs ul` and lost, so nothing
   here had any effect until the class was doubled to .tabs.wizard-tabs. The doubled form stays.

   Taking .tabs out of flex layout, rather than letting its ul shrink: Bulma sizes that ul as a
   non-shrinking flex item, which pins it to its max-content width - and the max-content width of a
   wrapping flex container is one line's worth, so it would never wrap however the wrapping is set.
   As a block the ul is simply as wide as what holds it and its flex-wrap means what it says, and
   the overflow that produced the scrollbar is gone with the scroll container. white-space is reset
   along with it, since an inherited nowrap would put the list back on one line.

   The bottom border Bulma draws under .tabs ul assumes one row, so it is dropped here: on a wrapped
   list it would draw a line through the middle of the strip. Each tab keeps its own border. */
.tabs.wizard-tabs {
    display: block;
    overflow: visible;
    white-space: normal;
}

.tabs.wizard-tabs ul {
    flex-wrap: wrap;
    border-bottom: none;
}

/* ============================================================================================
   Bilder: passend einpassen statt verzerren
   ============================================================================================ */

/* Ein Bild, das in seinen Platz eingepasst wird, ohne sein Seitenverhältnis zu verlieren.
   Ausdrücklich *nicht* Bulmas .image.is-16by9: dessen Seitenverhältnis gehört auf den umgebenden
   Container, dessen Kind Bulma dann füllend streckt. Steht die Klasse am <img> selbst, greift der
   Kindselektor nie, aber das aspect-ratio landet auf dem Bild und schlägt sein eigenes - ein
   Hochformat wurde dadurch breitgezogen. Die Kachelgröße macht der Aufrufer über max-height. */
.image-fit {
    width: auto;
    height: auto;
    max-width: 100%;
    object-fit: contain;
}

/* ============================================================================================
   Scene panel: images, mobs and loot
   ============================================================================================ */

/* Ein Szenen-Bild, das der Spielleiter noch zurückhält. Nur er sieht es überhaupt - bei den Spielern
   fehlt es im HTML -, und die Blässe sagt ihm auf einen Blick, was am Tisch noch niemand kennt.
   Doppelte Klasse aus der Zeit vor der bulma-Ebene (siehe Dateikopf); gewinnt weiterhin, bleibt. */
.scene-image-hidden.scene-image-hidden {
    opacity: 0.45;
    filter: grayscale(70%);
}

/* Dasselbe eine Zeile weiter: ein Mob oder ein Gegenstand, den die Spieler noch nicht sehen. Gleiche
   Aussage, gleiche Optik, und gleiches Spezifitätsproblem - deshalb wieder doppelt. */
.scene-hidden-row.scene-hidden-row {
    opacity: 0.55;
    filter: grayscale(70%);
}

/* Szenen-Bilder sind anklickbar (großes Bild im Dialog) - der Zeiger sagt das. */
.scene-image {
    cursor: pointer;
}

/* ============================================================================================
   Scene board (/campaign/{id}/board): palette tiles, scene cards, drop feedback
   ============================================================================================ */

/* Eine Kachel der Palette: greifbar, und der Zeiger sagt es. */
.board-tile {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.6rem;
    margin-bottom: 0.3rem;
    border: 1px solid var(--bulma-border, #dbdbdb);
    border-radius: 0.4rem;
    cursor: grab;
    user-select: none;
}

.board-tile:active,
.board-handle:active {
    cursor: grabbing;
}

/* Bild-Kacheln: klein, nebeneinander, in Palette und Karte gleich. Der Löschknopf sitzt in der Ecke. */
.board-image-tiles {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.board-image-tile {
    position: relative;
    width: 72px;
    padding: 0.2rem;
    margin: 0;
    justify-content: center;
}

.board-image-tile img {
    max-width: 100%;
    max-height: 64px;
    display: block;
    cursor: pointer;
}

.board-tile-delete {
    position: absolute;
    top: -0.3rem;
    right: -0.3rem;
}

/* Der Griff einer Szenen-Karte oder eines Textblocks - nur er ist ziehbar, nicht die ganze Karte,
   damit Knöpfe und Bild-Vorschau darin normal klickbar bleiben. */
.board-handle {
    cursor: grab;
    color: var(--bulma-text-weak, #7a7a7a);
    user-select: none;
}

.board-text-row {
    display: flex;
    align-items: center;
    padding: 0.2rem 0.4rem;
    border-radius: 0.3rem;
}

.board-mob {
    padding: 0.4rem 0.6rem;
    margin-bottom: 0.3rem;
    border: 1px dashed var(--bulma-border, #dbdbdb);
    border-radius: 0.4rem;
}

.board-loot-chip {
    cursor: grab;
}

/* Rückmeldung beim Ziehen: das Ziel, über dem die Kachel gerade schwebt ... */
.board-drop-hover {
    outline: 2px solid var(--bulma-link, #485fc7);
    outline-offset: 2px;
}

/* ... und beim Umsortieren die Kante, an der eingefügt würde. */
.board-insert-before {
    box-shadow: 0 -3px 0 0 var(--bulma-link, #485fc7);
}

.board-insert-after {
    box-shadow: 0 3px 0 0 var(--bulma-link, #485fc7);
}

/* Bulma gibt .modal-content 640px. Ein Kreaturenblatt mit Attributen, Fertigkeiten und Merkmalen
   braucht mehr; die mitgenannte Bulma-Klasse stammt aus der Zeit vor der bulma-Ebene und bleibt. */
.modal-content.creature-sheet-modal-content {
    width: 90vw;
    max-width: 60rem;
}

/* ============================================================================================
   Changelog (start page and /old-changes)
   ============================================================================================ */

/* Die Änderungsliste auf der Startseite. Jeder Eintrag ist ein <details>, startet also eingeklappt;
   der Knopf daneben klappt alle auf einmal auf. */
.changelog-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.changelog-entry {
    border: 1px solid var(--bulma-border, #dbdbdb);
    border-radius: 4px;
    margin-bottom: 0.5rem;
    padding: 0.5rem 0.75rem;
}

/* Eigener Marker statt des eingebauten Dreiecks: Einträge ohne weiteren Text sind kein <details>,
   und nur mit gleich breitem Marker hier und gleich breitem Einzug dort fangen alle Zeilen an
   derselben Stelle an. */
.changelog-entry > summary {
    cursor: pointer;
    list-style: none;
}

.changelog-entry > summary::-webkit-details-marker {
    display: none;
}

.changelog-entry > summary::before {
    content: "\25b8";
    display: inline-block;
    width: 1.2em;
    color: var(--bulma-text-weak, #7a7a7a);
}

.changelog-entry[open] > summary::before {
    content: "\25be";
}

.changelog-entry-plain {
    padding-left: calc(0.75rem + 1.2em);
}

.changelog-subject {
    font-weight: 600;
}

.changelog-meta {
    color: var(--bulma-text-weak, #7a7a7a);
    font-size: 0.8rem;
    margin-left: 0.5rem;
    white-space: nowrap;
}

/* Commit-Texte sind auf Zeilenumbrüche hin geschrieben - pre-wrap erhält sie, ohne wie <pre> aus
   dem Container zu laufen. */
.changelog-body {
    white-space: pre-wrap;
    margin-top: 0.5rem;
}

/* The way to the list of older changes, below the list of recent ones. */
.changelog-more {
    margin-top: 0.75rem;
}

/* ============================================================================================
   Die Seitenleiste (shared/menu.jte). docs/plans/navigation.adoc begründet den Aufbau.

   `html body` stammt aus der Zeit vor der bulma-Ebene, als ein schlichtes `body { ... }` gegen
   Bulmas Minireset (`body { margin: 0; padding: 0 }`) bei Gleichstand verlor; das Fehlerbild war
   eine korrekt aussehende Leiste über einem Inhalt, der darunter statt daneben lag. Seit der Ebene
   (siehe Dateikopf) gewänne auch die einfache Form; die stärkere schadet nicht und bleibt.
   ============================================================================================ */
html body {
    padding-left: 15rem;
}

/* Und die Regel, ohne die der Versatz oben Inhalte VERSCHLUCKT statt sie nur zu verschieben.

   Bulma klemmt waagerechten Überlauf ab - `html { overflow-x: var(--bulma-body-overflow-x) }`, und
   die Variable steht auf `hidden`. Solange der Inhalt die volle Fensterbreite hatte, fiel das nie
   auf. Nimmt die Leiste ihm 15rem weg, ragt jede Tabelle, die breiter ist als der Rest, über den
   rechten Rand - und dort ist sie nicht etwa abgeschnitten zu sehen, sondern weg, ohne Scrollbalken,
   der sie zurückholt.

   Gemessen an der Rassenliste, der breitesten Tabelle der Anwendung: 1180px Tabelle, 1114px
   Restbreite bei einem 1366px-Fenster. Ihre Aktionsspalte - Anzeigen, Bearbeiten, Löschen - lag
   damit vollständig im abgeklemmten Bereich und war mit keiner Geste erreichbar.

   Aufgefallen ist es Selenium, und zwar mit einem Fehlerbild, das in die Irre führt: `displayed=false`
   und leerer Text für ein <a>, das der Server nachweislich ausliefert und dessen berechneter Stil
   `display: inline-flex; visibility: visible` sagt. Wer das für ein Testproblem hält, baut die
   Zusicherung um und lässt den Fehler stehen.

   Die Eigenschaft direkt, und `:root` statt `html` - beides aus der Zeit vor der bulma-Ebene:
   damals verlor eine eigene `--bulma-body-overflow-x` auf `:root` gegen Bulmas gleichnamige, weil
   bulma.css später geladen wurde. Seit der Ebene gewänne auch die Variable; der direkte Weg bleibt,
   weil er dasselbe ohne Umweg sagt. */
:root {
    overflow-x: auto;
}

.app-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 15rem;
    z-index: 30;
    display: flex;
    flex-direction: column;
    background-color: var(--bulma-scheme-main-bis);
    border-right: 1px solid var(--bulma-border-weak);
}

.sidebar-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem;
    border-bottom: 1px solid var(--bulma-border-weak);
}

.sidebar-brand {
    font-weight: 700;
}

.sidebar-toggle {
    border: none;
    background: none;
    cursor: pointer;
    font-size: 1.1rem;
    line-height: 1;
    padding: 0.25rem 0.4rem;
    color: inherit;
}

/* Der Mittelteil scrollt, Kopf und Fuß bleiben stehen: der Baum des aktiven Spielsystems wird
   aufgeklappt länger als jeder Bildschirm. */
.sidebar-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 0.5rem 0;
}

.sidebar-foot {
    border-top: 1px solid var(--bulma-border-weak);
    padding: 0.5rem 0;
}

.sidebar-foot-row {
    display: flex;
    gap: 0.25rem;
    padding: 0.25rem 0.75rem;
}

/* Ein Eintrag des Baums. `display: block` und nicht flex, damit die Beschriftung eines langen
   Systemnamens abschneiden kann, ohne das Symbol mitzunehmen. */
.nav-item {
    display: block;
    padding: 0.3rem 0.75rem;
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.nav-item:hover {
    background-color: var(--bulma-background);
    color: inherit;
}

.nav-item.is-current {
    background-color: var(--bulma-link);
    color: var(--bulma-link-invert);
}

.nav-item.is-hint {
    color: var(--bulma-text-weak);
    font-size: 0.85rem;
}

/* Die Einträge des Umschalters sind <button>, damit das Formular ohne JavaScript auskommt. Sie
   müssen wie ein .nav-item aussehen; Bulmas .button bringt Rahmen, Höhe und Zentrierung mit, die
   hier alle falsch sind. Eigene Klasse ohne Bulma-Gegenstück, also reicht die einfache Form. */
.nav-item-button {
    width: 100%;
    border: none;
    background: none;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.is-active-system {
    font-weight: 700;
}

.nav-group > summary {
    cursor: pointer;
    padding: 0.3rem 0.75rem;
    white-space: nowrap;
    font-weight: 600;
}

/* Jede Ebene rückt ein. Der Baum ist bis zu vier Ebenen tief (System > Charakter > Attribute),
   ohne Einzug wäre nicht zu sehen, was wozu gehört. */
.nav-children {
    padding-left: 0.75rem;
}

.nav-children .nav-group > summary {
    font-weight: 400;
}

.nav-icon {
    display: inline-block;
    width: 1.25rem;
}

/* Ein Spielsystemname darf lang sein und die Leiste darf davon nicht umbrechen, also abschneiden.
   In der 15rem-Spalte ist die Schranke enger als in der alten waagerechten Leiste. */
.nav-system-label {
    display: inline-block;
    max-width: 9.5rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

/* Der Raumname im Raumknoten - dasselbe Abschneiden wie beim Spielsystem, aber enger: rechts daneben
   steht noch der Bearbeiten-Stift, und der Knoten sitzt eine Ebene tiefer als „Räume". */
.nav-room-label {
    display: inline-block;
    max-width: 7.5rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

/* Der Stift springt in die Raumbearbeitung. Nur der Besitzer bekommt ihn überhaupt gerendert. */
.nav-room-edit {
    float: right;
    padding: 0 0.25rem;
    opacity: 0.6;
}

.nav-room-edit:hover {
    opacity: 1;
}

/* --- Die Symbolschiene -----------------------------------------------------------------------

   `data-sidebar` steht am <html>, nicht an der Leiste: sidebar.js setzt es blockierend im <head>,
   wo es die Leiste selbst noch gar nicht gibt. Derselbe Aufbau wie bei `data-theme`.

   Beschriftungen verschwinden mit `display: none` und nicht mit `visibility` oder Breite 0: sie
   sollen keinen Platz beanspruchen, und ein Rest-Kasten würde die Symbole aus der Mitte drücken. */
[data-sidebar="rail"] body {
    padding-left: 3.5rem;
}

[data-sidebar="rail"] .app-sidebar {
    width: 3.5rem;
}

[data-sidebar="rail"] .sidebar-brand,
[data-sidebar="rail"] .sidebar-foot-row,
[data-sidebar="rail"] .nav-system-label,
    /* Der Benutzername neben „Profil" trägt .is-size-7, also eine eigene Schriftgröße - das font-size: 0
       weiter unten schluckt ihn deshalb NICHT, und in der Schiene stand „☺ (admi". */
[data-sidebar="rail"] .sidebar-foot > .nav-item .is-size-7 {
    display: none;
}

[data-sidebar="rail"] .sidebar-head {
    justify-content: center;
    padding: 0.75rem 0;
}

/* In der Schiene trägt jede Zeile nur noch ihr Symbol. Die Einträge OHNE Symbol - die Blätter tief
   im Baum - haben in der Schiene ohnehin nichts zu suchen; sie erscheinen im Ausklappmenü. */
[data-sidebar="rail"] .sidebar-scroll > .nav-item,
[data-sidebar="rail"] .sidebar-scroll > .nav-group > summary,
[data-sidebar="rail"] .sidebar-foot > .nav-item {
    text-align: center;
    padding: 0.4rem 0;
    font-size: 0;              /* schluckt die Beschriftung ... */
    list-style: none;          /* ... und das Dreieck des <summary> */
}

[data-sidebar="rail"] .sidebar-scroll > .nav-group > summary::marker,
[data-sidebar="rail"] .sidebar-scroll > .nav-group > summary::-webkit-details-marker {
    content: "";
    display: none;
}

[data-sidebar="rail"] .nav-icon {
    font-size: 1.1rem;         /* ... das Symbol holt sich seine Größe zurück */
    width: auto;
}

/* Das Ausklappmenü. sidebar.js hält in der Schiene jedes <details> offen - ein geschlossenes
   verbirgt seine Kinder auf Browserebene und CSS käme nicht daran -, also wird hier nur noch
   ein- und ausgeblendet.

   position: fixed und nicht absolute: die Leiste selbst ist fixed und beschneidet mit ihrem
   scrollenden Mittelteil, ein absolut positioniertes Untermenü bliebe darin gefangen. */
[data-sidebar="rail"] .sidebar-scroll > .nav-group > .nav-children {
    display: none;
}

[data-sidebar="rail"] .sidebar-scroll > .nav-group:hover > .nav-children,
[data-sidebar="rail"] .sidebar-scroll > .nav-group:focus-within > .nav-children {
    display: block;
    position: fixed;
    left: 3.5rem;
    min-width: 12rem;
    max-height: 80vh;
    overflow-y: auto;
    z-index: 40;
    padding: 0.25rem 0;
    background-color: var(--bulma-scheme-main-bis);
    border: 1px solid var(--bulma-border-weak);
    box-shadow: var(--bulma-shadow);
}

/* --- Schmale Fenster -------------------------------------------------------------------------

   Unter Bulmas Desktop-Grenze schiebt die aufgeklappte Leiste den Inhalt nicht mehr beiseite,
   sondern legt sich darüber: bei 768px wären 15rem ein Fünftel der Breite, dauerhaft belegt.
   Der Innenabstand des Inhalts bleibt deshalb bei der Schienenbreite, egal welchen Zustand
   sidebar.js gesetzt hat.

   Die Leiste ganz aus dem Bild zu schieben wäre die andere Möglichkeit gewesen und ist verworfen:
   der Umschalter sitzt IN der Leiste und wäre mitgefahren. */
@media screen and (max-width: 1023px) {
    html body,
    [data-sidebar="rail"] body {
        padding-left: 3.5rem;
    }

    [data-sidebar="wide"] .app-sidebar {
        box-shadow: var(--bulma-shadow);
    }
}

/* --- Kampfsimulator --------------------------------------------------------------------------

   Das Gruppenfeld in der Kämpferliste. Bulmas .input ist auf 100% der Zelle gestellt, und eine
   Spalte in voller Breite für zwei Buchstaben lässt die Tabelle auseinanderfallen; das mitbenannte
   .input stammt aus der Zeit vor der bulma-Ebene und bleibt. */
.input.simulator-team-input {
    max-width: 7rem;
}

/* Die zuklappbaren Attribut- und Fertigkeitsblöcke des Baukastens (simulator/freeformFields.jte).
   Natives <details>; das hier ist nur die Hand, die der Marker allein nicht gibt, und etwas Luft -
   dieselbe Begründung wie bei .creature-section > summary weiter oben. Kein display auf dem
   <summary>: das braucht `list-item`, sonst verschwindet das Dreieck. */
.freeform-fold > summary {
    cursor: pointer;
    margin-bottom: 0.5em;
}

.freeform-fold + .freeform-fold {
    margin-top: 0.75rem;
}

/* Die Fehlermeldungen aus js/htmxErrors.js. Ein Stapel unten rechts, damit zwei Fehler hintereinander
   nicht übereinander liegen, und über allem - eine Meldung, die hinter einem Dialog verschwindet, ist
   keine. Das Element entsteht erst beim ersten Fehler; ohne Fehler steht hier nichts im Dokument. */
#htmx-error-toasts {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 60;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-width: min(28rem, calc(100vw - 2rem));
}

/* Bulmas .notification bringt seinen eigenen Abstand mit; der Stapel setzt ihn über gap. */
.htmx-error-toast {
    margin-bottom: 0;
}
