/* The unified character sheet (static/js/character-sheet.js). One column,
   mobile first at 360px. Tokens from tokens.css; no bare colours.

   Every selector is scoped under [data-cs-root], the attribute mount() sets on
   the root. lobby.css no longer carries cs- rules of its own, and this
   stylesheet loads on every page from base.html. Scoping keeps the component
   winning inside its own subtree regardless of load order. Root-level
   state is written [data-cs-root].cs-locked ..., never [data-cs-root] .cs-locked,
   or the lock dimming stops working. */
/* container-type, not a viewport media query: what the layout answers to is
   the sheet's own width. A 318px game rail was getting the 640px-viewport wide
   layout and crammed six stat tiles into 46px each. Every breakpoint in this
   file is an @container query on this element. */
[data-cs-root] { container-type: inline-size; display: flex; flex-direction: column; gap: 16px; font-family: var(--font-ui); color: var(--text); }
/* Scoped reset. components.css writes `.modal-body p { color: var(--text-dim) }`,
   which matched .cs-prose inside the Session Zero modal and dimmed the story to
   the grey of its own label, while the open textarea stayed bright. The sheet
   owns its typography; a host stylesheet reaching an element by tag does not.
   .cs-eyebrow and .cs-prose-label carry their colour on a class, which outranks
   this rule, so they keep it. */
[data-cs-root].cs p, [data-cs-root].cs h2 { margin: 0; color: inherit; }
[data-cs-root] :where(h3, h4) { margin: 0; }
[data-cs-root] .cs-eyebrow {
    margin: 0 0 8px; font-size: 0.75rem; letter-spacing: 0.02em;    color: var(--text-dim); font-weight: 600;
}

/* Header */
[data-cs-root] .cs-header { display: flex; gap: 12px; align-items: flex-start; }
[data-cs-root] .cs-portrait {
    flex: 0 0 56px; width: 56px; height: 56px; border-radius: var(--radius-md); overflow: hidden;
    background: var(--white-line); display: flex; align-items: center; justify-content: center;
}
[data-cs-root] .cs-portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }
[data-cs-root] .cs-portrait-tappable { cursor: pointer; }
[data-cs-root] .cs-portrait-blank { width: 24px; height: 24px; border-radius: 999px; background: rgba(var(--white-rgb), 0.12); }
[data-cs-root] .cs-head-main { flex: 1; min-width: 0; }
/* A column at 40px for the same reason the identity parts are one: the h2 IS
   the field wrapper tapTarget resolves, and browser QA measured it at 26px of
   text at 390. justify-content keeps the title on the row's centre line and
   wrap()'s error text stays under it, not beside it. */
[data-cs-root] .cs-name {
    margin: 0; font-family: var(--font-reading); font-size: 1.35rem; line-height: 1.2; font-weight: 600;
    display: flex; flex-direction: column; justify-content: center; align-items: flex-start; min-height: 40px;
}
/* One row holds race, class and the pills (renderHeader appends them here).
   They used to be two rows, each 20px of text propped up by a transparent
   ::after expander, and the pill row's expander painted over the identity
   row's: the effective hit height at 390 was 24px, not the 44 the two rules
   claimed. A real 40px row each part fills needs no overlay, and the header
   comes out 13px shorter than the two rows it replaces. */
[data-cs-root] .cs-identity {
    margin-top: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; min-height: 40px;
    font-size: 0.85rem; color: var(--text-dim);
}
/* A column, not a row: wrap() puts the field's `.cs-error-text` after the value
   inside every wrapper, and as a row a shown error sat beside "Human" instead
   of under it. justify-content keeps the value on the row's centre line. */
[data-cs-root] .cs-identity-part {
    display: inline-flex; flex-direction: column; justify-content: center; align-items: flex-start; min-height: 40px;
}
/* The wrapper is what tapTarget resolves to (closest('[data-cs-field]')), so
   the wrapper is what has to be 40px: a 24px chip centred in the row left the
   8px above and below it landing on .cs-identity, which carries no
   data-cs-field, and the tap opened nothing. The wrapper fills the row and the
   chip visual moves to the value node inside it, which is what is drawn and
   what an open editor replaces. */
[data-cs-root] .cs-pill {
    display: inline-flex; flex-direction: column; justify-content: center; align-items: flex-start;
    align-self: stretch; min-height: 40px; padding: 0; background: transparent; font-size: 0.75rem;
}
[data-cs-root] .cs-pill > [data-cs-value] {
    display: inline-flex; align-items: center; gap: 4px; min-height: 24px; padding: 2px 8px;
    border-radius: 999px; background: var(--white-line); color: var(--text);
}
/* The empty prompt carries data-cs-value too, so the rule above (0,3,0) beat
   .cs-empty (0,2,0) and drew "+ Level" in the full text colour, as loud as a
   value that exists. Same weight, declared after, so the dim wins back. */
[data-cs-root] .cs-pill > .cs-empty { color: var(--text-dim); }
[data-cs-root] .cs-status .cs-value { text-transform: capitalize; }
[data-cs-root] .cs-owner { margin-top: 4px; font-size: 0.75rem; color: var(--text-dim); }

/* Lock bar and coach mark */
[data-cs-root] .cs-lock {
    padding: 6px 10px; border-radius: var(--radius-sm); font-size: 0.78rem;
    background: var(--accent-subtle); color: var(--accent); border-left: 2px solid var(--accent);
}
/* One dim line, no box: it is a hint, not a notice, and it goes after the
   first save (the script hides it) or after one session (localStorage). */
[data-cs-root] .cs-coach { padding: 0 2px; font-size: 0.75rem; color: var(--text-dim); }
[data-cs-root].cs-locked .cs-editable, [data-cs-root].cs-locked .cs-empty, [data-cs-root].cs-locked .cs-add, [data-cs-root].cs-locked .cs-stat.cs-editable { opacity: 0.5; cursor: not-allowed; }

/* Values */
[data-cs-root] .cs-editable { cursor: pointer; border-radius: var(--radius-sm); }
[data-cs-root] .cs-editable:hover, [data-cs-root] .cs-empty:hover { outline: 1px dashed var(--accent-half); outline-offset: 2px; }
/* One empty-state affordance for every inline prompt: a dim tappable line that
   owns a 40px row of its own. A tile prints its label under the prompt and must
   not grow past its filled neighbours, so it opts out of the height; the name
   slot keeps the serif title size instead of dropping to 0.85rem mono. */
[data-cs-root] .cs-empty { display: inline-flex; align-items: center; min-height: 40px; color: var(--text-dim); cursor: pointer; font-size: 0.85rem; }
/* An empty tile keeps the filled tile's shape, its number slot dimmed ("-",
   or 0 for a coin), so a row of tiles reads as one row whatever is set. */
[data-cs-root] .cs-vital-empty .cs-vital-value { color: var(--text-dim); font-weight: 400; }
[data-cs-root] .cs-name .cs-empty { font: inherit; color: var(--text-dim); }
/* The whole field answers a tap, so the whole field shows the hand. The
   :not() mirrors tapTarget(): a field holding stats or an add button opens
   nothing from its padding, so its padding must not offer to. */
[data-cs-root] .cs-field:has(.cs-editable, .cs-empty):not(:has([data-cs-stat], [data-cs-add])) { cursor: pointer; }
[data-cs-root].cs-locked .cs-field:has(.cs-editable, .cs-empty):not(:has([data-cs-stat], [data-cs-add])) { cursor: not-allowed; }
[data-cs-root] .cs-saving [data-cs-value], [data-cs-root] .cs-saving.cs-stat { animation: cs-pulse 0.9s ease-in-out infinite; }
@keyframes cs-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
[data-cs-root] .cs-saved > [data-cs-value]::after, [data-cs-root] .cs-saved .cs-vital-value::after { content: ' \2713'; color: var(--accent); font-size: 0.8em; }
[data-cs-root] .cs-error-text { margin-top: 4px; font-size: 0.75rem; color: var(--warning); }
[data-cs-root] .cs-error [data-cs-value] { outline: 1px solid var(--warning); outline-offset: 2px; }

/* Vitals */
[data-cs-root] .cs-vitals { display: flex; flex-wrap: wrap; gap: 8px; }
[data-cs-root] .cs-vitals > * { flex: 1 1 100%; min-width: 0; }
/* A lone vital and a lone coin share one row instead of two full-width cells. */
[data-cs-root] .cs-vitals > .cs-tiles:has(> .cs-vital:only-child) { flex: 1 1 120px; }
[data-cs-root] .cs-hp { margin-bottom: 0; }
[data-cs-root] .cs-hp-bar { height: 8px; border-radius: 999px; background: rgba(var(--white-rgb), 0.08); overflow: hidden; }
[data-cs-root] .cs-hp-fill { height: 100%; background: var(--accent); transition: width 0.3s ease; }
[data-cs-root] .cs-hp-fill.low { background: var(--warning); }
[data-cs-root] .cs-hp-fill.critical { background: var(--danger-strong); }
[data-cs-root] .cs-hp-text { margin-top: 4px; font-size: 1.25rem; font-weight: 600; display: flex; gap: 4px; align-items: center; }
/* Each end of the bar's label is a target in its own right, and the wrapper is
   what tapTarget resolves (closest('[data-cs-field]')), so the wrapper is what
   has to be 40px. It used to be 20px of text propped up by a transparent
   ::after, which grows nothing the finger can find: browser QA measured
   16x22 at 390. The pills' treatment, applied here. */
[data-cs-root] .cs-hp-text .cs-field {
    display: inline-flex; flex-direction: column; justify-content: center; align-items: center;
    min-height: 40px; min-width: 40px;
}
/* The box is 40px so the finger has something to hit; the digit still starts
   where the bar does. Centred, a single digit sat 15px in from the bar's left
   edge and the row read as two chips around a slash. */
[data-cs-root] .cs-hp-text .cs-hp-cur { align-items: flex-start; }
[data-cs-root] .cs-hp-label { color: var(--text-dim); font-size: 0.8rem; font-weight: 400; margin-left: 4px; }
/* auto-fit, not a fixed four: three vitals left a dead fourth column a quarter
   of the row wide on every sheet that has three. An empty track collapses and
   the filled ones stretch, so three tiles fill a 584px modal and a 318px rail
   alike, and a fourth vital still gets its own column wherever 4 x 84 fits.
   The max stays 1fr HERE on purpose: auto-fit counts repetitions by the max
   track sizing function when that is definite, so a 240px ceiling on this rule
   gave every rail and pane a single 240px column and stacked the vitals one
   per row. The ceiling belongs to wide containers only, below. */
[data-cs-root] .cs-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(84px, 1fr)); gap: 6px; }
/* 72px keeps 5e's four coins on one row at phone width (4 x 72 + gaps fits
   358px) and one coin a full-width cell, while a world with six coins wraps
   to a second row instead of squeezing every label to a letter column.
   Coins are counted, not read at a glance, so their cells are shorter. */
[data-cs-root] .cs-purse { grid-template-columns: repeat(auto-fit, minmax(72px, 1fr)); }
[data-cs-root] .cs-purse .cs-vital { min-height: 48px; padding: 6px 2px; }
[data-cs-root] .cs-vital {
    display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 8px 4px; min-height: 56px;
    border-radius: var(--radius-md); background: rgba(var(--white-rgb), 0.04); text-align: center;
}
[data-cs-root] .cs-vital-value { font-size: 1rem; font-weight: 600; line-height: 1.15; }
/* nowrap plus an ellipsis cut "Armour tier" to "ARMOUR TI..." in every tile
   under about 105px (a 308px creator rail, a 318px game rail). A label that
   wraps says the whole word; the tile keeps its min-height, so a one-line
   label still sits in a 56px tile beside a two-line one. */
[data-cs-root] .cs-vital-label {
    font-size: 0.75rem; color: var(--text-dim); margin-top: 2px;
    white-space: normal; overflow-wrap: anywhere; text-align: center; line-height: 1.2; max-width: 100%;
}

/* Attributes */
[data-cs-root] .cs-stat-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
/* Generated from abilityGridColumns(n, 3) in static/js/utils.js for n = 1..12,
   so no ability is orphaned on its own row; tests/test_ability_grid_columns_js.mjs
   holds this table to the function. The bare rule above is the > 12 fallback.
   Keep minmax(0, 1fr), not a bare 1fr: a long label must not blow the track out. */
[data-cs-root] .cs-stat-grid[data-ability-count="1"] { grid-template-columns: repeat(1, minmax(0, 1fr)); }
[data-cs-root] .cs-stat-grid[data-ability-count="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
[data-cs-root] .cs-stat-grid[data-ability-count="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
[data-cs-root] .cs-stat-grid[data-ability-count="4"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
[data-cs-root] .cs-stat-grid[data-ability-count="5"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
[data-cs-root] .cs-stat-grid[data-ability-count="6"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
[data-cs-root] .cs-stat-grid[data-ability-count="7"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
[data-cs-root] .cs-stat-grid[data-ability-count="8"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
[data-cs-root] .cs-stat-grid[data-ability-count="9"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
[data-cs-root] .cs-stat-grid[data-ability-count="10"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
[data-cs-root] .cs-stat-grid[data-ability-count="11"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
[data-cs-root] .cs-stat-grid[data-ability-count="12"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
[data-cs-root] .cs-stat {
    display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 8px 4px; min-height: 56px;
    border-radius: var(--radius-md); background: rgba(var(--white-rgb), 0.04);
}
[data-cs-root] .cs-stat-label { font-size: 0.75rem; color: var(--text-dim);letter-spacing: 0.02em; }
[data-cs-root] .cs-stat-score { font-size: 1.1rem; font-weight: 600; line-height: 1.15; }
[data-cs-root] .cs-stat-mod { font-size: 0.75rem; color: var(--accent); }

/* Blobs, lists, chips */
[data-cs-root] .cs-section { padding: 12px 0 0; border-top: 1px solid var(--white-line); }
/* A nested list inside Story or Connections is a sub-list, not a third
   top-level section: it drops the hairline and takes the sub-label's type, so
   "// Known NPCs" reads under "// Connections" instead of beside it. */
[data-cs-root] .cs-section-nested { border-top: 0; padding-top: 6px; }
[data-cs-root] .cs-section-nested .cs-eyebrow { font-size: 0.75rem; text-transform: none; letter-spacing: 0; margin-bottom: 4px; }
[data-cs-root] .cs-kv { margin: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 10px; }
/* Spelled out because a kv row used to carry .cs-row as well, and .cs-row is
   declared below at the same weight: the row wrapped and the key truncated
   under a value on its own line. renderBlob writes one class now; these
   declarations are the whole rule either way. */
[data-cs-root] .cs-kv-row {
    display: flex; flex-wrap: nowrap; justify-content: space-between; align-items: baseline; gap: 6px;
    padding: 4px 8px; border-radius: var(--radius-sm); background: rgba(var(--white-rgb), 0.04); font-size: 0.82rem;
}
/* The key wraps rather than cuts: "Sleight of hand" in a 2-column 300px rail
   ended as "Sleight o..." and "Animal handling" as "Animal ha...". */
[data-cs-root] .cs-kv-row dt { flex: 1 1 auto; color: var(--text-dim); min-width: 0; overflow-wrap: anywhere; line-height: 1.25; }
[data-cs-root] .cs-kv-row dd { flex: 0 0 auto; margin: 0; font-weight: 600; text-align: right; white-space: nowrap; }
/* A free-form blob's value can be a phrase ("1/1, half on a long rest"); held
   on one line it took the whole cell and the key went down it one letter a
   line. Here the key keeps a floor and the value wraps under it instead. */
[data-cs-root] .cs-kv:not(.cs-kv-keyed) .cs-kv-row { flex-wrap: wrap; row-gap: 2px; }
[data-cs-root] .cs-kv:not(.cs-kv-keyed) .cs-kv-row dt { min-width: 8ch; }
[data-cs-root] .cs-kv:not(.cs-kv-keyed) .cs-kv-row dd { flex: 0 1 auto; min-width: 0; white-space: normal; overflow-wrap: anywhere; margin-left: auto; }
/* Keyed ledgers (5e skills and saves): one column of tappable rows, the label
   in the text colour because it is the row's name and not a caption, the
   modifier in tabular figures so "+5" sits under "-1". Group headers name the
   governing ability; a dot before the label says proficient; a row without a
   value carries a dim "+" where its number will go. */
[data-cs-root] .cs-kv.cs-kv-keyed:not(.cs-kv-strip) { grid-template-columns: minmax(0, 1fr); gap: 2px; }
[data-cs-root] .cs-kv-keyed-row { min-height: 40px; padding: 4px 10px; align-items: center; }
[data-cs-root] .cs-kv-keyed-row dt { color: var(--text); font-weight: 500; display: flex; align-items: center; gap: 8px; }
[data-cs-root] .cs-kv-mod { font-variant-numeric: tabular-nums; min-width: 3ch; }
[data-cs-root] .cs-kv-unset { color: var(--text-dim); font-weight: 400; }
[data-cs-root] .cs-kv-odd { color: var(--text-dim); font-weight: 400; font-size: 0.75rem; max-width: 9ch; overflow: hidden; text-overflow: ellipsis; }
[data-cs-root] .cs-kv-missing dt { color: var(--text-dim); font-weight: 400; }
[data-cs-root] .cs-prof-dot { width: 8px; height: 8px; flex: 0 0 8px; border-radius: 999px; background: var(--accent); }
[data-cs-root] .cs-kv-group { grid-column: 1 / -1; padding: 10px 10px 2px; }
[data-cs-root] .cs-kv-group:first-child { padding-top: 2px; }
[data-cs-root] .cs-kv-group-label { font-size: 0.75rem; letter-spacing: 0.02em;color: var(--text-dim); }
[data-cs-root] .cs-kv-keyed-row.cs-editable:hover { outline: 0; background: rgba(var(--white-rgb), 0.08); }
[data-cs-root] .cs-kv-editing { background: rgba(var(--white-rgb), 0.08); outline: 1px solid var(--accent-half); }
/* Two classes deep on purpose: .cs-editor and .cs-editor-inline are declared
   after this and would otherwise give the box its 100% width and frame back. */
[data-cs-root] .cs-kv-row .cs-kv-input {
    width: 4ch; max-width: 4ch; min-width: 0; min-height: 0; height: calc(1.25em + 2px); padding: 0; text-align: right;
    font-weight: 600; background: transparent; border: 0; border-bottom: 1px solid var(--accent); border-radius: 0;
}
/* Saves under the tiles: the same six columns as the ability grid, the
   ability's short label over its modifier, tucked against the tiles it
   derives from with a sub-label instead of an eyebrow. */
[data-cs-root] .cs-saves { margin-top: -4px; }
[data-cs-root] .cs-sub-label { font-size: 0.75rem; color: var(--text-dim); margin: 0 0 4px; }
[data-cs-root] .cs-kv-strip.cs-stat-grid { display: grid; gap: 4px; }
[data-cs-root] .cs-kv-strip .cs-kv-keyed-row { flex-direction: column; justify-content: center; gap: 0; padding: 4px 2px; min-height: 40px; text-align: center; }
[data-cs-root] .cs-kv-strip .cs-kv-keyed-row dt { font-size: 0.75rem; letter-spacing: 0.02em;color: var(--text-dim); font-weight: 500; gap: 4px; }
[data-cs-root] .cs-kv-strip .cs-kv-keyed-row dd { text-align: center; }
[data-cs-root] .cs-kv-strip .cs-kv-row .cs-kv-input { text-align: center; }
[data-cs-root] .cs-rows { display: flex; flex-direction: column; gap: 4px; }
[data-cs-root] .cs-row { display: flex; flex-wrap: wrap; gap: 2px 8px; padding: 6px 8px; border-radius: var(--radius-sm); background: rgba(var(--white-rgb), 0.04); font-size: 0.85rem; }
[data-cs-root] .cs-row-name { font-weight: 500; }
[data-cs-root] .cs-row-desc { flex-basis: 100%; color: var(--text-dim); font-family: var(--font-reading); font-size: 0.9rem; line-height: 1.45; }
[data-cs-root] .cs-chips { display: flex; flex-wrap: wrap; gap: 6px; }
[data-cs-root] .cs-chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 10px; border-radius: 999px; background: var(--white-line); font-size: 0.8rem; }
[data-cs-root] .cs-list-tools { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
[data-cs-root] .cs-add {
    padding: 8px 12px; min-height: 40px; border: 0; border-radius: var(--radius-md);
    background: var(--white-line); color: var(--text); font: inherit; font-size: 0.85rem; cursor: pointer;
}
[data-cs-root] .cs-done { margin-left: auto; background: transparent; color: var(--accent); }
/* An empty list is one prompt line, so it takes the section's hairline and
   nothing else. */
[data-cs-root] .cs-section-empty { padding-top: 6px; }

/* Story */
[data-cs-root] .cs-story, [data-cs-root] .cs-connections { padding-top: 12px; border-top: 1px solid var(--white-line); }
[data-cs-root] .cs-prose-label { margin: 12px 0 4px; font-size: 0.75rem; color: var(--text-dim); font-weight: 600; }
[data-cs-root] .cs-prose { margin: 0; font-family: var(--font-reading); font-size: 0.95rem; line-height: 1.5; }

/* Editors (Task 3) */
[data-cs-root] .cs-editor { font: inherit; color: var(--text); background: var(--white-line); border: 1px solid var(--accent-half); border-radius: var(--radius-sm); padding: 6px 8px; min-height: 40px; width: 100%; box-sizing: border-box; }
/* `width: auto` on a number input is the UA intrinsic width, about 13.5em: the
   stat editor opened 259px wide over a 73px tile and covered its neighbours,
   clipped by the rail it sat in. Nothing inline may exceed its own box; the
   slot it opens in sets the width. */
[data-cs-root] .cs-editor-inline { width: 100%; max-width: 100%; min-width: 4ch; min-height: 0; padding: 2px 6px; }
/* The UA spinner arrows are a 13px target the sheet already answers with its
   own stepper, and in a tile they sit on top of the number. Off on every
   number the sheet opens, inline or full width. */
[data-cs-root] .cs-editor[type="number"] { appearance: textfield; -moz-appearance: textfield; }
[data-cs-root] .cs-editor[type="number"]::-webkit-outer-spin-button,
[data-cs-root] .cs-editor[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; appearance: none; margin: 0; }
/* The score edits as itself: same size and weight, an underline instead of a
   box, so the tile stays the tile and the modifier under it keeps its place. */
[data-cs-root] .cs-stat .cs-editor-inline {
    width: 3ch; max-width: 3ch; min-width: 0; min-height: 0; height: calc(1.15em + 2px); padding: 0; text-align: center;
    font-size: 1.1rem; font-weight: 600; line-height: 1.15; background: transparent; border: 0; border-bottom: 1px solid var(--accent); border-radius: 0;
    appearance: textfield; -moz-appearance: textfield;
}
[data-cs-root] .cs-stat .cs-editor-inline::-webkit-outer-spin-button,
[data-cs-root] .cs-stat .cs-editor-inline::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* Header slots: the race editor at its intrinsic width pushed the class beside
   it 80px along, and the level pill grew from 25px to 214px around its input. */
[data-cs-root] .cs-identity .cs-editor-inline { width: auto; max-width: 100%; }
[data-cs-root] .cs-pill .cs-editor-inline { width: 4ch; min-height: 0; padding: 0 4px; font-size: inherit; }
/* A touch keyboard zooms the page on a field under 16px. The stat and vital
   editors are already that size; these are the ones that inherit small text. */
@media (pointer: coarse) {
    [data-cs-root] .cs-editor:not(.cs-stepper), [data-cs-root] .cs-pill .cs-editor-inline { font-size: 1rem; }
}
/* A tile edits in the middle of itself, the way it reads: the number's own
   size and weight, an underline, no box, so the row keeps its 56px and the
   neighbours do not stretch. */
[data-cs-root] .cs-vital .cs-editor:not(.cs-stepper) {
    width: 4ch; max-width: 100%; min-width: 0; min-height: 0; height: calc(1.15em + 2px); padding: 0; text-align: center;
    font-size: 1rem; font-weight: 600; line-height: 1.15; background: transparent; border: 0; border-bottom: 1px solid var(--accent); border-radius: 0;
}
[data-cs-root] .cs-vital .cs-editor-textarea, [data-cs-root] .cs-vital select.cs-editor, [data-cs-root] .cs-vital .cs-segments { width: auto; height: auto; }
[data-cs-root] .cs-vital select.cs-editor { min-height: 36px; padding: 4px 6px; border: 1px solid var(--accent-half); border-radius: var(--radius-sm); background: var(--white-line); font-size: inherit; font-weight: 400; }
/* The HP numbers edit in place too, inside the "12 / 20" they belong to. */
[data-cs-root] .cs-hp-cur .cs-editor, [data-cs-root] .cs-hp-max .cs-editor {
    width: 4ch; max-width: 4ch; min-width: 0; min-height: 0; height: calc(1.15em + 2px); padding: 0; text-align: center;
    font: inherit; font-weight: 600; background: transparent; border: 0; border-bottom: 1px solid var(--accent); border-radius: 0;
}
[data-cs-root] .cs-editor-textarea { min-height: 96px; resize: vertical; font-family: var(--font-reading); }
[data-cs-root] .cs-stepper { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; }
[data-cs-root] .cs-stepper button { width: 40px; height: 40px; flex: 0 0 40px; border: 0; border-radius: 999px; background: rgba(var(--white-rgb), 0.08); color: var(--text); font-size: 1.1rem; cursor: pointer; }
/* An open stepper is far wider than one of four tiles in a 360px panel. Inside
   the tile it either overflowed its neighbours or wrapped to three lines and
   stretched the whole row to 130px. It leaves the tile instead: the tile row
   makes 52px of room under itself (base.css gives the buttons a 44px floor
   under a coarse pointer) and the stepper lies across that strip, the
   full width of the row, with the tapped tile outlined so the pair reads as
   one control. The number in the tile stays visible and moves with each tap. */
[data-cs-root] .cs-tiles { position: relative; }
[data-cs-root] .cs-tiles:has(.cs-editing) { padding-bottom: 52px; }
[data-cs-root] .cs-vital.cs-editing { outline: 1px solid var(--accent-half); }
[data-cs-root] .cs-vital.cs-editing .cs-stepper {
    position: absolute; left: 0; right: 0; bottom: 0; height: 44px; max-width: none;
    display: flex; align-items: center; justify-content: center; gap: 8px;
    padding: 0; border: 0; background: transparent;
}
[data-cs-root] .cs-vital.cs-editing .cs-stepper .cs-editor-inline {
    width: 6ch; max-width: 6ch; height: 40px; min-height: 40px; padding: 2px 4px; text-align: center; font-size: 1rem; font-weight: 600;
    background: var(--white-line); border: 1px solid var(--accent-half); border-radius: var(--radius-sm);
}
[data-cs-root] .cs-segments { display: inline-flex; gap: 4px; flex-wrap: wrap; }
[data-cs-root] .cs-segment { min-height: 40px; padding: 6px 10px; border: 0; border-radius: var(--radius-sm); background: var(--white-line); color: var(--text); font: inherit; font-size: 0.85rem; cursor: pointer; }
[data-cs-root] .cs-segment[aria-pressed="true"] { background: var(--accent); color: var(--bg-elevated); }
/* The rows editor is a list, not a box. It used to draw its own amber frame
   and fill around rows that each framed two more inputs: three nested borders
   and 0px between rows, so three entries read as one block of six inputs. The
   frame comes off and the rhythm goes between the rows. */
[data-cs-root] .cs-rows-editor { border: 0; background: transparent; padding: 0; display: flex; flex-direction: column; gap: 10px; }
/* One card per row, in the row's own fill: the editor looks like the list it
   edits, and the inputs inside carry no boxes of their own. The name is the
   row's mono weight, the description the reading serif under a hairline, the
   remove disc top right. The grip stays out of the way below 480px, where the
   card has no column to spare; base.css's 44px floor on the disc is why the
   last track is auto and not a hard 40px. */
[data-cs-root] .cs-rows-editor { gap: 6px; }
[data-cs-root] .cs-row-edit {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-auto-rows: auto; gap: 0 4px; align-items: start;
    padding: 4px 4px 4px 8px; border-radius: var(--radius-sm); background: rgba(var(--white-rgb), 0.04);
}
[data-cs-root] .cs-row-edit .cs-editor {
    min-height: 40px; padding: 8px 4px; background: transparent; border: 0; border-radius: 0; width: 100%; box-sizing: border-box; font-size: 0.85rem;
}
[data-cs-root] .cs-row-edit .cs-editor:focus { outline: 0; box-shadow: none; background: rgba(var(--white-rgb), 0.04); }
[data-cs-root] .cs-row-edit .cs-editor-name, [data-cs-root] .cs-row-edit .cs-editor-key { font-weight: 500; }
[data-cs-root] .cs-row-edit .cs-editor-value { grid-column: 1; }
[data-cs-root] .cs-row-edit .cs-editor-desc {
    grid-column: 1 / -1; min-height: 36px; padding: 6px 4px 8px; resize: none; overflow: hidden; field-sizing: content;
    border-top: 1px solid var(--white-line); font-family: var(--font-reading); font-size: 0.9rem; line-height: 1.45; color: var(--text-dim);
}
[data-cs-root] .cs-row-edit .cs-editor-desc:focus { color: var(--text); }
[data-cs-root] .cs-row-grip { display: none; color: var(--text-dim); cursor: grab; line-height: 40px; text-align: center; font-size: 0.8rem; opacity: 0.6; }
[data-cs-root] .cs-row-remove { grid-column: 2; grid-row: 1; width: 40px; height: 40px; border: 0; border-radius: 999px; background: transparent; color: var(--text-dim); font-size: 1.1rem; cursor: pointer; }
[data-cs-root] .cs-row-remove:hover { background: var(--white-line); }

/* Actions. Two labelled buttons measured 387px against a 347px lobby aside and
   stacked; they share the row now and give way at the label, not at the row. */
[data-cs-root] .cs-actions { display: flex; gap: 8px; flex-wrap: nowrap; padding-top: 12px; border-top: 1px solid var(--white-line); }
[data-cs-root] .cs-action {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px; flex: 1 1 0; min-width: 0;
    min-height: 40px; padding: 8px 12px; border: 0; border-radius: var(--radius-md); font: inherit; font-size: 0.85rem; cursor: pointer;
}
[data-cs-root] .cs-action-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
[data-cs-root] .cs-action-icon { display: inline-flex; flex: 0 0 16px; }
[data-cs-root] .cs-action-icon svg { width: 16px; height: 16px; display: block; }
[data-cs-root] .cs-action-primary { background: var(--accent); color: var(--bg-elevated); font-weight: 600; }
[data-cs-root] .cs-action-quiet { background: var(--white-line); color: var(--text); }
[data-cs-root] .cs-action-danger { background: rgba(var(--danger-rgb), 0.18); color: var(--error); }
/* Narrow: an action that brought an icon becomes that icon, named by the
   aria-label renderActions writes from the same label. An action with no icon
   keeps its text, because a nameless blank button is worse than a tight one. */
@container (max-width: 479px) {
    [data-cs-root] .cs-action-iconed { flex: 0 0 40px; width: 40px; padding: 0; border-radius: 999px; }
    [data-cs-root] .cs-action-iconed .cs-action-label { display: none; }
}

@container (min-width: 480px) {
    /* Wide: the same table, generated from abilityGridColumns(n, 6). The width
       is the sheet's own, so a side rail keeps the narrow table it needs. */
    [data-cs-root] .cs-stat-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="1"] { grid-template-columns: repeat(1, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="5"] { grid-template-columns: repeat(5, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="6"] { grid-template-columns: repeat(6, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="7"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="8"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="9"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="10"] { grid-template-columns: repeat(5, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="11"] { grid-template-columns: repeat(6, minmax(0, 1fr)); }
    [data-cs-root] .cs-stat-grid[data-ability-count="12"] { grid-template-columns: repeat(6, minmax(0, 1fr)); }
    [data-cs-root] .cs-kv { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    /* Room for the grip: a third column on the card's left. */
    [data-cs-root] .cs-row-edit { grid-template-columns: 20px minmax(0, 1fr) auto; padding-left: 4px; }
    [data-cs-root] .cs-row-grip { display: block; grid-column: 1; grid-row: 1; }
    [data-cs-root] .cs-row-edit .cs-editor-name, [data-cs-root] .cs-row-edit .cs-editor-key, [data-cs-root] .cs-row-edit .cs-editor-value { grid-column: 2; }
    [data-cs-root] .cs-row-edit .cs-editor-desc { grid-column: 2 / -1; }
    [data-cs-root] .cs-row-remove { grid-column: 3; }
}

/* Wide only: two vitals in the DM command center's 1075px column stretched to
   522px each around a two-character number. A ceiling on the track and a start
   packing cap them at 240px. 800px is above every rail, pane and modal the
   sheet mounts in, so nothing narrower can be counted into one column by the
   definite max. */
@container (min-width: 800px) {
    [data-cs-root] .cs-tiles { grid-template-columns: repeat(auto-fit, minmax(120px, 240px)); justify-content: start; }
    [data-cs-root] .cs-purse { grid-template-columns: repeat(auto-fit, minmax(84px, 160px)); }
    [data-cs-root] .cs-vitals > .cs-tiles:has(> .cs-vital:only-child) { flex: 0 0 240px; }
    [data-cs-root] .cs-vitals > .cs-purse:has(> .cs-vital:only-child) { flex: 0 0 160px; }
}
