/* gp-editor.css — theme overrides for Quill's editor chrome.
 *
 * Pairs with wwwroot/js/gp-editor.js: that file configures the editor, this one makes it look like the
 * rest of the site. Load it AFTER quill.snow.css on any page that hosts an editor.
 *
 * WHY IT EXISTS. quill.snow.css ships fixed light-theme colours and hard 1px borders around both the
 * toolbar and the container, which read as two stark boxes stacked inside a dark card. Everything here
 * either removes that chrome or maps it onto the project's own tokens, so the editor sits inside a
 * .gp-card the way the hand-rolled toolbar it replaced did.
 *
 * DO NOT EDIT quill.snow.css TO ACHIEVE ANY OF THIS. The vendored file stays byte-identical to the
 * published build so it can be replaced wholesale on upgrade — see wwwroot/lib/quill/VERSION.txt.
 *
 * The ql- prefix is THIRD-PARTY: neither gp- nor page-specific, so it does not fit UI/ClassNaming.md's
 * two categories. Same situation as rich-text.css, and for the same reason — these are class
 * names Quill emits, not names the project chose.
 */

/* --- Toolbar shell -------------------------------------------------------------------------- */
/* Replaces Quill's full border with the single bottom divider the previous toolbar used, so the
   toolbar reads as a row of the surrounding card rather than as a separate box.
   position: relative anchors the save indicator that gp-editor.js moves in here. */
.ql-toolbar.ql-snow {
    position: relative;
    box-sizing: border-box;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem;
    padding: 0.375rem 0.625rem;
    padding-right: 1.5rem; /* room for the save indicator */
    border: none;
    border-bottom: 1px solid var(--line-soft);
    font-family: inherit;
}

/* Quill's own 15px group gap is far too wide once the buttons carry borders. A thin rule between
   groups replaces it, matching the .roster-editor-separator the old toolbar used. */
.ql-toolbar.ql-snow .ql-formats {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    margin: 0;
}
/* Separator on the LEFT of every group after the first, rather than on the right of every group but
   the last. :last-of-type would have matched the save indicator gp-editor.js appends — also a span —
   leaving a stray rule after the final button group. This form cannot be broken by what else the
   toolbar contains. */
.ql-toolbar.ql-snow .ql-formats + .ql-formats {
    margin-left: 0.125rem;
    padding-left: 0.5rem;
    border-left: 1px solid var(--line);
}

/* --- Buttons -------------------------------------------------------------------------------- */
/* Deliberately mirrors .btn + .btn-sm + .btn-sq from site.css rather than inventing a look. The values
   are repeated rather than shared because Quill emits bare <button> elements and cannot be given the
   project's classes — if .btn ever changes, these four properties must be brought across by hand. */
.ql-toolbar.ql-snow button,
.ql-toolbar.ql-snow .ql-picker-label {
    color: var(--ink-2);
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: 0.25rem;
    box-shadow: 0 1px 0 rgba(255,255,255,0.03) inset, 0 1px 2px rgba(0,0,0,0.3);
    transition: all 0.15s;
}

.ql-toolbar.ql-snow button {
    width: 1.75rem;
    height: 1.75rem;
    padding: 0.25rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    float: none; /* Quill floats its buttons; the flex toolbar above does the layout instead */
}

.ql-toolbar.ql-snow button:hover,
.ql-toolbar.ql-snow .ql-picker-label:hover {
    background: var(--bg-3);
    border-color: var(--ink-4);
    color: var(--ink);
}
.ql-toolbar.ql-snow button:active { transform: translateY(1px); }

/* Active format. Uses the accent the way .btn-active does, kept lighter because several can be lit at
   once and the full .btn-active glow would make the toolbar noisy. */
.ql-toolbar.ql-snow button.ql-active,
.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label {
    color: var(--ink);
    background: var(--bg);
    border-color: color-mix(in oklab, var(--accent) 55%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--accent) 35%, transparent);
}

/* --- Icons ---------------------------------------------------------------------------------- */
/* Quill's SVGs carry hardcoded #444 strokes and fills. Mapping them onto the ink scale is what makes
   the icons legible on a dark card — without this they are near-invisible. */
.ql-snow .ql-stroke { stroke: var(--ink-2); }
.ql-snow .ql-fill,
.ql-snow .ql-stroke.ql-fill { fill: var(--ink-2); }
.ql-snow .ql-thin,
.ql-snow .ql-stroke.ql-thin { stroke: var(--ink-3); }

.ql-toolbar.ql-snow button:hover .ql-stroke,
.ql-toolbar.ql-snow button.ql-active .ql-stroke { stroke: var(--ink); }
.ql-toolbar.ql-snow button:hover .ql-fill,
.ql-toolbar.ql-snow button.ql-active .ql-fill { fill: var(--ink); }

/* --- Pickers (header / font / size / align / colour) ---------------------------------------- */
.ql-snow .ql-picker { color: var(--ink-2); font-size: 0.8rem; height: auto; }

.ql-snow .ql-picker-label {
    display: inline-flex;
    align-items: center;
    padding: 0.25rem 1.25rem 0.25rem 0.5rem;
    line-height: 1.3;
}
.ql-snow .ql-picker-label::before { line-height: 1.3; }
.ql-snow .ql-picker-label svg { right: 0.25rem; }

/* Quill sizes these by their widest option, which leaves three very wide boxes on the row. */
.ql-snow .ql-picker.ql-header { width: 6rem; }
.ql-snow .ql-picker.ql-font { width: 6.5rem; }
.ql-snow .ql-picker.ql-size { width: 5.5rem; }
.ql-snow .ql-picker.ql-video-size { width: 6.5rem; }

/* The dropdown panel. White by default, which is the most jarring part of the untouched theme. */
.ql-snow .ql-picker-options {
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: 0.25rem;
    box-shadow: var(--sh-card);
    padding: 0.25rem;
}
.ql-snow .ql-picker.ql-expanded .ql-picker-options { z-index: 30; }
.ql-snow .ql-picker-item { color: var(--ink-2); border-radius: 0.25rem; padding: 0.25rem 0.375rem; }
.ql-snow .ql-picker-item:hover { color: var(--ink); background: var(--bg-3); }
.ql-snow .ql-picker-item.ql-selected { color: var(--ink); background: var(--bg-3); }

/* The colour grids are swatches, not text — the padding above would break their layout. */
/*  SUPERSEDED 2026-10-01 — the grid grew from 7 columns to 9 (gp-editor.js COLOR_GRID).
.ql-snow .ql-color-picker .ql-picker-options { width: 10rem; padding: 0.25rem; }
*/
/*  SUPERSEDED 2026-10-01 — the grid gained an eleventh column (pure blue beside Quill's).
.ql-snow .ql-color-picker .ql-picker-options { width: calc(10 * 20px + 0.5rem); padding: 0.25rem; }
*/
/* Exactly 11 swatches per line: each is 16px wide plus Quill's 2px margin either side (20px).
   KEEP IN STEP with the grid-template-columns below and with COLOR_GRID's column count. */
.ql-snow .ql-color-picker .ql-picker-options { width: calc(11 * 20px + 0.5rem); padding: 0.25rem; }
.ql-snow .ql-color-picker .ql-picker-item { padding: 0; border: 1px solid var(--line); }

/* The class and rarity tables below the grid (2026-10-01). The first swatch of each starts a new line
   and carries the table's heading as a ::before, because Quill maps a swatch to its colour by its
   position among the options' children and NO heading element may be inserted there (gp-editor.js
   markColorGroups). The top margin is the room the heading sits in. */
/*  SUPERSEDED 2026-10-01 — with Quill's floats, the swatches after the first one lined up with the TOP
    of its margin, so the rest of the row sat in the heading's space and covered it.
.ql-snow .ql-color-picker .ql-picker-item.gp-color-group-start {
    clear: left;
    position: relative;
    margin-top: 1.25rem;
}
*/
/* A GRID, not Quill's floats: the start swatch's margin makes its row taller, and align-items: end keeps
   every other swatch in that row level with it, below the heading. Quill shows the options with
   display: block at (0,3,0); this is (0,4,0). Floats are ignored on grid items. */
.ql-snow .ql-color-picker.ql-expanded .ql-picker-options {
    display: grid;
    /* grid-template-columns: repeat(10, 20px); — SUPERSEDED 2026-10-01, eleventh column */
    grid-template-columns: repeat(11, 20px);
    align-items: end;
}
.ql-snow .ql-color-picker .ql-picker-item.gp-color-group-start {
    grid-column-start: 1;
    position: relative;
    margin-top: 1.25rem;
}
.ql-snow .ql-color-picker .ql-picker-item.gp-color-group-start::before {
    content: attr(data-gp-group);
    position: absolute;
    left: -1px;
    bottom: calc(100% + 0.25rem);
    color: var(--ink-3);
    font-size: 0.7rem;
    line-height: 1;
    white-space: nowrap;
    pointer-events: none;
}

/* --- Editing surface ------------------------------------------------------------------------ */
/* The container keeps its border removed and hands padding to .ql-editor, which is the element Quill
   actually types into. (0,2,0), so this wins over .roster-announcement-editor's (0,1,0) padding on
   specificity. That rule moved to pages/roster-manage.css on 2026-09-29 and now loads AFTER this file,
   so source order no longer helps; the old wording here said "Loaded after site.css ... at equal
   specificity", which was never the reason. */
.ql-container.ql-snow {
    border: none;
    font-family: inherit;
    font-size: inherit;
    padding: 0;
}
/*  SUPERSEDED 2026-08-11 — the editor grew with the document, without bound.
.ql-editor {
    padding: 0.625rem 0.75rem;
    min-height: 6rem;
    line-height: 1.6;
    color: var(--ink);
}
*/

/*  A BOUNDED, USER-RESIZABLE BOX.
 *
 * Quill core already ships the scrolling half: .ql-editor carries `height: 100%` and
 * `overflow-y: auto`. Nothing was missing except a DEFINITE height for that 100% to resolve against —
 * with an auto-height parent it computed to auto, which is the only reason the editor grew with the
 * document.
 *
 * THE HEIGHT AND THE HANDLE GO ON .ql-editor, NOT ON .ql-container, and that is the whole trick.
 * `resize` only works on an element whose overflow is something other than `visible`, and Quill's
 * link/video tooltip is a CHILD OF .ql-container — the snow theme builds it with quill.addContainer(),
 * which appends into the container. Bounding the CONTAINER would therefore clip that popover against
 * the box edges. .ql-editor is the tooltip's SIBLING, so bounding it constrains the text and leaves
 * the popover free to overhang.
 *
 * overflow-y is restated here rather than inherited from quill.snow.css. It is what makes `resize`
 * legal on this element, and a vendored file that gets replaced wholesale on upgrade is not where
 * that dependency should live.
 *
 * min-height is the DRAG FLOOR — without it the handle drags the editor away to nothing.
 *
 * 24rem is the long-form default (blog post, team document). Surfaces whose content is short by
 * nature override it to 12rem: see faq.css and the roster announcement block in pages/roster-manage.css. An override
 * must target `#host .ql-editor`, because a bare id selector reaches the CONTAINER, not this element.
 */
.ql-editor {
    padding: 0.625rem 0.75rem;
    min-height: 6rem;
    height: 24rem;
    overflow-y: auto;
    resize: vertical;
    line-height: 1.6;
    color: var(--ink);
}
.ql-editor.ql-blank::before {
    color: var(--ink-4);
    font-style: normal;
    left: 0.75rem;
    right: 0.75rem;
}

/* --- Link / video entry tooltip -------------------------------------------------------------- */
/* Quill's own popover, used by the link and video buttons. Untouched it is a white box with a blue
   link on a dark page. */
.ql-snow .ql-tooltip {
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: 0.25rem;
    box-shadow: var(--sh-card);
    color: var(--ink-2);
    padding: 0.375rem 0.625rem;
    z-index: 40;
}
.ql-snow .ql-tooltip input[type=text] {
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: 0.25rem;
    color: var(--ink);
    padding: 0.25rem 0.375rem;
    font-family: inherit;
    font-size: 0.8rem;
}
.ql-snow .ql-tooltip a.ql-action,
.ql-snow .ql-tooltip a.ql-remove,
.ql-snow .ql-tooltip a.ql-preview { color: var(--accent-glow); }

/* --- Video embeds (added 2026-09-18) --------------------------------------------------------- */
/* Quill creates the video iframe with no width and no height, so without these it takes the
   browser's default 300x150 box. 16:9, and a share of the text width set by the video size format:
   no class is Full (the default), gp-video-small is half, gp-video-medium three-quarters.

   THESE RULES EXIST TWICE — here for the editor, and in rich-text.css for every display surface —
   because no stylesheet is loaded by both an editor page and a display page. Change the widths in
   both files or neither. Alignment needs nothing here: quill.snow.css already carries the video's
   centre and right margins, and it loads first. */
.ql-editor .ql-video { width: 100%; height: auto; aspect-ratio: 16 / 9; border: 0; }
.ql-editor .ql-video.gp-video-small { width: 50%; }
.ql-editor .ql-video.gp-video-medium { width: 75%; }

/* THE VIDEO TAKES NO CLICKS IN THE EDITOR (2026-09-18). An iframe swallows every click into its own
   document, so a live video could never be selected. With pointer events off, a click on it lands on
   the editor and gp-editor.js selects the video under the pointer. The cost, decided: a video does
   not play inside the editor. rich-text.css has no such rule, so it plays on every display page. */
.ql-editor .ql-video { pointer-events: none; }

/* A preset narrower than the text is unreadable on a phone. The site's 48em breakpoint. */
@media (max-width: 48em) {
    .ql-editor .ql-video.gp-video-small,
    .ql-editor .ql-video.gp-video-medium { width: 100%; }
}

/* The video size picker. Quill labels a picker from ::before content keyed on data-value, the way
   quill.snow.css labels the text size and header pickers; an unlabelled picker renders empty. */
.ql-snow .ql-picker.ql-video-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-video-size .ql-picker-item::before { content: 'Full'; }
.ql-snow .ql-picker.ql-video-size .ql-picker-label[data-value="small"]::before,
.ql-snow .ql-picker.ql-video-size .ql-picker-item[data-value="small"]::before { content: 'Small'; }
.ql-snow .ql-picker.ql-video-size .ql-picker-label[data-value="medium"]::before,
.ql-snow .ql-picker.ql-video-size .ql-picker-item[data-value="medium"]::before { content: 'Medium'; }

/* Disabled while the selection is not on a video — gp-editor.js sets aria-disabled. The label then
   names what the picker is for, rather than showing "Full" beside ordinary text as though the
   paragraph had a size. Attribute selector, no mirror class (UI/ClassNaming.md). */
.ql-toolbar.ql-snow .ql-picker.ql-video-size[aria-disabled="true"] {
    opacity: 0.45;
    pointer-events: none;
}
.ql-snow .ql-picker.ql-video-size[aria-disabled="true"] .ql-picker-label::before { content: 'Video size'; }

/* The emoji picker and the blank button (2026-10-01). Neither is a format: both insert plain text
   (gp-editor.js EMOJI / insertText). Quill builds a picker and a button for them but has no
   icon for either, so their faces are drawn here the way the video size picker's labels are. */

/* The emoji picker. Its label is always the smiley, never the last emoji chosen: the picker is a
   command, and showing a chosen value would read as a format applied to the text. Each item shows
   its own character, taken from data-value, so adding an emoji to EMOJI needs no rule here. */
/*  SUPERSEDED 2026-10-01 — sized for Quill's dropdown arrow, which is now hidden.
.ql-snow .ql-picker.ql-gp-emoji { width: 2.75rem; }
*/
/* Button-sized, with NO dropdown arrow: Quill draws its arrow SVG into every text picker's label, but
   this one sits among icon buttons and looks like one of them (the colour and align pickers, which
   are icon pickers, have none either). The size is this file's toolbar button size (1.75rem square,
   0.25rem padding); the general label padding leaves room for the arrow and is overridden here. */
.ql-snow .ql-picker.ql-gp-emoji { width: 1.75rem; }
.ql-toolbar.ql-snow .ql-picker.ql-gp-emoji .ql-picker-label {
    width: 1.75rem;
    height: 1.75rem;
    padding: 0.25rem;
    justify-content: center;
}
.ql-snow .ql-picker.ql-gp-emoji .ql-picker-label svg { display: none; }
.ql-snow .ql-picker.ql-gp-emoji .ql-picker-label::before,
.ql-snow .ql-picker.ql-gp-emoji .ql-picker-label[data-value]::before {
    content: '\263A';
    font-size: 1rem;
}
.ql-snow .ql-picker.ql-gp-emoji .ql-picker-item::before {
    content: attr(data-value);
    font-size: 1rem;
    line-height: 1.4;
}

/* The blank button. U+2800 itself is invisible, so the face is the visible-space sign U+2423. Text in
   place of an SVG, so it takes the icons' ink colours through `color` rather than stroke/fill. */
.ql-toolbar.ql-snow button.ql-gp-blank { color: var(--ink-2); }
.ql-toolbar.ql-snow button.ql-gp-blank:hover,
.ql-toolbar.ql-snow button.ql-gp-blank:focus-visible { color: var(--ink); }
.ql-toolbar.ql-snow button.ql-gp-blank::before {
    content: '\2423';
    display: block;
    font-size: 1rem;
    line-height: 1;
}

/*  SUPERSEDED 2026-09-18 (the same day) — the layer held a select button per video until selection
    became a click anywhere on the video.
   The select layer: one select button per video, and the outline on the selected one.
   gp-editor.js adds it with quill.addContainer, so it sits in .ql-container OUTSIDE .ql-editor and
   never becomes document content. It covers the editor box and clips to it, so a button scrolled out
   of the text never floats over the toolbar. It takes no pointer events itself; only the buttons do,
   so text selection, the video's own controls and the resize handle all still work beneath it. */

/* The select layer: the outline on the selected video. gp-editor.js adds it with quill.addContainer,
   so it sits in .ql-container OUTSIDE .ql-editor and never becomes document content. It covers the
   editor box and clips to it, so an outline scrolled out of the text never floats over the toolbar.
   It takes no pointer events, so every click reaches the editor beneath it. */
.gp-video-layer {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    z-index: 1;
}
/*  SUPERSEDED 2026-09-18 — the select button was removed the same day it was added.
.gp-video-layer .gp-video-handle {
    position: absolute;
    margin: 0.375rem 0.375rem 0 0;
    pointer-events: auto;
}
*/
.gp-video-layer .gp-video-outline {
    position: absolute;
    pointer-events: none;
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 0.125rem;
}

/* --- The save flash (added 2026-09-18) ------------------------------------------------------- */
/* "Saved", written by gpEditor.flashSaved into the FIRST element of an editor's Save row (in front of
   every control, never between two — a flash between Delete and Save reads as a label), then faded.
   UI/RichTextEditor.md rule 2 — which forbade this text until 2026-09-18, by instruction reversed.
   The header indicator still reports the state; this is the momentary confirmation at the button.

   A RESERVED WIDTH, so the row does not jump when the text appears and goes: the element is always
   there, empty while idle. align-self because the control rows are flex rows that stretch their
   items. Colour: the ok green the indicator turns on a save. Timing: visible ~1.5 s, fading ~1 s. */
.gp-save-flash {
    display: inline-block;
    min-width: 3.25em;
    align-self: center;
    text-align: right;
    font-size: 0.8rem;
    color: var(--ok);
    opacity: 0;
}
.gp-save-flash.is-shown { animation: gp-save-flash 2.5s ease-in forwards; }
@keyframes gp-save-flash {
    0%, 60% { opacity: 1; }
    100% { opacity: 0; }
}
/* No fade for a reader who asked for less motion: shown for the same time, then gone. */
@media (prefers-reduced-motion: reduce) {
    .gp-save-flash.is-shown { animation: gp-save-flash-still 2.5s linear forwards; }
}
@keyframes gp-save-flash-still {
    0%, 99% { opacity: 1; }
    100% { opacity: 0; }
}

/* --- Save indicator -------------------------------------------------------------------------- */
/* gp-editor.js moves the page's status element into the toolbar so it sits on the same row as the
   buttons, as it did before. Absolute rather than margin-left:auto because the toolbar wraps to two
   rows at narrow widths and auto-margin would strand the dot on whichever row happened to be last. */
.ql-toolbar.ql-snow .roster-save-indicator {
    position: absolute;
    top: 0.75rem;
    right: 0.625rem;
    margin-left: 0;
}
