/* Doorway Count Me In — activity styles.
   1em = 1/50th of activity width (set by doorway.js onResize).
   All sizing in em; no px except borders.
*/

/* --- Welcome screen image ---
   Constrained max-width / max-height so the illustration stays at a sensible
   size when the activity is rendered in flow mode (the parent is no longer
   letterboxed, so a percent-based width could blow up at large viewports). */

.welcome_image {
    width: 75%;
    max-width: 30em;
    max-height: 12em;
    margin: var(--dw-space-default) auto;
}

.welcome_image svg {
    width: 100%;
    height: auto;
    max-height: 12em;
    display: block;
}

/* --- Welcome subtitle (was .welcome_description; now uses shared .sub_title) --- */

.doorway_welcome .sub_title {
    text-align: center;
    font-size: 1.25em;
    margin: var(--dw-space-narrow) auto var(--dw-space-default);
    max-width: 30em;
    line-height: 1.4;
}

/* --- Screen layout --- */

/* Flex-column skeleton comes from .dw-activity-screen in shared/doorway.css. */
.cmi-screen {
    overflow-y: auto;
    padding: var(--dw-space-tight) var(--dw-space-default) var(--dw-space-narrow);
    gap: var(--dw-space-snug);
}

/* --- Activity screen layout ---
   Vertical block (topbar + grid) is centred together in the available height
   so the guide text, Found/Score readouts and number grid shift down as one
   unit (matches Flash where the prompt sits ~140 px above a centred grid).
   `justify-content: center` requires the children to be content-sized, so the
   grid drops `flex:1` below and uses its own `--cmi-avail-h`-driven max-width.
*/
.cmi-activity-layout {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    gap: var(--dw-space-snug);
    box-sizing: border-box;
    padding: 0;
    min-height: 0;
    justify-content: center;
}

/* --- Guide text ---
   Lives inline in the activity topbar (alongside the nav-header buttons)
   rather than on its own row, so the grid below can claim the remaining
   vertical space. text-align:center centres the prompt in the flex slot.
   Mode-2 override below re-aligns the whole cluster (guide + Found + Score)
   to the top-right corner to match Flash.
*/

.cmi-guide-text {
    flex: 1;
    text-align: center;
    font-size: 1em;
    font-weight: bold;
    margin: 0;
    padding: 0;
    min-height: 0;
}

/* --- Number grid ---
   10 columns × N rows of square (aspect-ratio:1) cells. The grid claims the
   leftover vertical space (flex:1) and is centred horizontally (align-self:
   center). To keep cells from overflowing vertically, max-width is clamped by
   both 44em and the height-based budget: cell_height = (avail_h - (N-1)*gap)/N,
   total_width = 10*cell_height + 9*gap. JS sets --cmi-rows on the grid each
   time it builds (Math.ceil(numberRange/10)) so the height budget tracks the
   chosen range. --cmi-avail-h is a conservative estimate of the height left
   after the topbar + padding (activity area is ~28em high; topbar ~2.2em,
   gap+padding ~1em → ~25em).
*/

.cmi-number-grid {
    --cmi-rows: 2;
    --cmi-avail-h: 25em;
    /* The gap doubles as visual breathing room for the Stations step-marker
       outline (0.2em orange) on adjacent buttons; keeping the outline width
       under the gap avoids collisions between the marker and its neighbours. */
    --cmi-gap: 0.5em;

    /* Rows are children — a flex column of .cmi-number-row containers, each
     * of which lays out its own 10 buttons horizontally. Row wrapping is
     * needed so switch scanning can paint a .scan_highlight ring around a
     * whole row at the row-scan level (row groups need a real DOM box for
     * the shared scanner's box-shadow ring to attach to). */
    display: flex;
    flex-direction: column;
    gap: var(--cmi-gap);
    padding: 0;
    /* Centre rows vertically within the available height (flex:1 on the
     * parent gives the grid the leftover vertical space; justify-content:
     * center centres the row block, instead of pinning it to the top). */
    justify-content: center;
    align-self: center;
    flex: 0 1 auto;
    min-height: 0;
    width: 100%;
    max-width: min(
        44em,
        calc(
            (var(--cmi-avail-h) - var(--cmi-gap) * (var(--cmi-rows) - 1))
            / var(--cmi-rows) * 10 + var(--cmi-gap) * 9
        )
    );
}

/* One horizontal row of number buttons. Flex row with 10 equal-share
 * children; children keep aspect-ratio:1 so cell height tracks cell width.
 * position:relative so the shared .scan_highlight box-shadow (drawn on
 * this element when it is the current row-scan target) sits above adjacent
 * rows. */
.cmi-number-row {
    display: flex;
    flex-direction: row;
    gap: var(--cmi-gap);
    width: 100%;
    position: relative;
}

/* Row-scan ring: tighten the shared 0.5em gold ring to match the button
 * focus-visible outline weight (0.25em) so the row-level scan cue reads
 * as a single unit without swamping the individual button cells inside
 * it. Colour matches the button focus ring (#ffaa00) for consistency. */
.cmi-number-row.scan_highlight {
    --scan-highlight-colour: #ffaa00;
    --scan-highlight-thickness: 0.25em;
}

/* Number-grid buttons match the welcome page illustration: flat solid-colour
   squares, no rounded corners. Odd tiles use the salmon pink fill (#ffb0b0)
   and even tiles the sky-blue fill (#a4e1ff) — same palette as the welcome
   SVG `<rect>` elements (which use `stroke="none"`). Hover/active variants
   use a brightness filter; switch-access focus-visible keeps a separate
   orange focus ring so scanning is still clearly indicated.
   The transparent 0.25em border is the mode-3 "claimed" green border sized
   in advance: box-sizing: border-box + transition: border-color keeps the
   tile footprint stable when .cmi-done fades the colour in. */
.cmi-number-btn {
    aspect-ratio: 1;
    min-width: 0;
    /* Equal-share of the parent row's width — 10 buttons per row. width:0
     * is the standard flex-basis idiom that lets `flex: 1 1 0` distribute
     * space evenly without content-based min-content quirks. */
    flex: 1 1 0;
    width: 0;
    font-size: var(--dw-font-size-subheading);
    font-weight: bold;
    border: 0.25em solid transparent;
    border-radius: 0;
    box-sizing: border-box;
    background: transparent;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    color: #000;
    transition: border-color 0.25s ease-out;
}

.cmi-number-btn.cmi-compact {
    font-size: 1em;
}

/* Odd buttons — welcome-page pink. */
.cmi-number-btn.cmi-odd {
    background-color: #ffb0b0;
}

/* Even buttons — welcome-page light blue. */
.cmi-number-btn.cmi-even {
    background-color: #a4e1ff;
}

/* Hover/active states: darken the sprite slightly. Flash uses 3 distinct fill
   colours per state; here we approximate with a brightness filter so we keep
   using the single sprite asset. Already-claimed (aria-disabled) tiles skip
   the hover feedback so they read as "done", not "still pressable". */
@media (hover: hover) {
    .cmi-number-btn:hover { filter: brightness(0.92); }
}
.cmi-number-btn:active { filter: brightness(0.82); }

.cmi-number-btn[aria-disabled="true"] {
    cursor: default;
}
.cmi-number-btn[aria-disabled="true"]:active {
    filter: none;
}
@media (hover: hover) {
    .cmi-number-btn[aria-disabled="true"]:hover {
        filter: none;
    }
}

/* Focus ring for keyboard / switch-access scanning. */
.cmi-number-btn:focus-visible {
    outline: 0.25em solid #ffaa00;
    outline-offset: 0.1em;
}

/* Highlight animation — correct button press.
   Flash reference (frame_06.as): a "highlight" MovieClip is attached at the button position
   on a correct answer, then fades out via setInterval(removeHighlight, 300) which plays the
   clip's fade-out frames. Replicated here as a single CSS keyframe animation that pulses
   Flash green (#009900) and fades back. JS adds .cmi-highlight and listens for animationend
   to remove the class — no setTimeout sequencing.
   Informational content: the result (correct vs wrong) is carried by audio + live region
   + score/done DOM state. The pulse is decorative reinforcement only. */
@keyframes cmi-highlight-pulse {
    0%   { outline: 0.3em solid #009900; transform: scale(1);    filter: brightness(1.25) saturate(1.4); }
    30%  { outline: 0.3em solid #009900; transform: scale(1.12); filter: brightness(1.25) saturate(1.4); }
    100% { outline: 0 solid transparent; transform: scale(1);    filter: brightness(1); }
}

.cmi-number-btn.cmi-highlight {
    animation: cmi-highlight-pulse 0.6s ease-out;
}

/* Stations step marker — orange outline on the currently-active
   non-multiple. Only ever set on one button at a time: the next Stations
   press clears it before applying the next state (either another orange
   step, or the green multiples treatment). Flash uses no animation here,
   so nor do we. */
.cmi-number-btn.cmi-highlight-step {
    outline: 0.2em solid #ff7f00;
}

/* Mode 3: already-pressed buttons in the times-table-stations sequence get a
   thick green border (matching the welcome-page highlight stations) rather
   than being greyed out — the tile reads as "claimed/done", not "disabled".
   Meaning is carried by `aria-disabled` + live region; the green border is
   decorative reinforcement. The button stays focusable (no native `disabled`)
   so the learner Tabs across the intermediate numbers to reach the next
   station themselves — the counting motion the exercise is meant to teach.
   Border width + transition + box-sizing live on the base .cmi-number-btn
   selector so the fade-in reads as a colour change against an already-sized
   border. */
.cmi-number-btn.cmi-done {
    border-color: #2a9300;
}

/* --- Activity-screen top bar ---
   Flash: top strip has back-pencil (left), "Found:0" + Repeat (right).
   HTML5 reproduces the right-hand side here; the back is provided by the shared
   navigation header. The guide text fills the row alongside the nav buttons
   (flex:1 on .cmi-guide-text) so Repeat/Found/Score sit at the right edge.
   padding-left clears the absolutely-positioned nav-header buttons.
   Mode-2 override below collapses the guide to a right-aligned inline label
   so the whole cluster (guide + Found + Score) sits in the top-right corner.
*/
.cmi-activity-topbar {
    display: flex;
    align-items: center;
    gap: var(--dw-space-default);
    min-height: 2.2em;
    padding-left: 6.5em;
    padding-right: var(--dw-space-default);
    /* Positioning context for the score panel, which is anchored here so it
     * always tracks the topbar regardless of how far it is vertically centred. */
    position: relative;
}

/* Mode 2 (Find the Number): Flash places the guide prompt, "Found:N" counter
   and Score toggle all together in the top-right corner (above the centred
   number grid).  Right-align the row and drop the guide's flex-grow so it
   shrinks to its own text width rather than filling the slot.  The nav-header
   buttons are absolutely positioned in the top-left, so removing padding-left
   here is safe — the right-aligned cluster stays clear of them. */
.cmi-activity-layout[data-cmi-mode="2"] .cmi-activity-topbar {
    justify-content: flex-end;
    padding-left: var(--dw-space-default);
}

.cmi-activity-layout[data-cmi-mode="2"] .cmi-guide-text {
    flex: 0 0 auto;
    text-align: right;
}

.cmi-found-counter {
    font-size: 1em;
    font-weight: bold;
    padding: 0.1em var(--dw-space-narrow);
    white-space: nowrap;
}

.cmi-found-label {
    margin-right: var(--dw-space-tight);
}

/* --- Repeat button (mode 3) --- */

/* Inherits .main_button; sized for the top-bar */
.cmi-repeat-btn {
    margin: 0;
}

/* Repeat button entry animation: fade + grow in when mode 3 sequence completes.
   The button becoming visible is what carries the meaning (combined with "well done"
   audio + live region). The animation is purely decorative.
   Applied as a one-shot keyframe on .cmi-repeat-appear; JS adds the class right after
   removing hidden_panel and listens for animationend to remove the class. */
@keyframes cmi-repeat-appear {
    0%   { opacity: 0; transform: scale(0.7); }
    100% { opacity: 1; transform: scale(1); }
}

.cmi-repeat-btn.cmi-repeat-appear {
    animation: cmi-repeat-appear 0.35s ease-out;
}

/* Mode 3 two-step confirm: first Space past the end of the counting
   sequence adds this class to arm Repeat; the next Space fires it.
   Mirrors the orange step outline used on number buttons
   (.cmi-highlight-step) so switch users read the same "scan cursor
   is here" meaning in both places. */
.cmi-repeat-btn.cmi-repeat-armed {
    outline: 0.2em solid #ff7f00;
}

/* --- Prompt-replay button (mouth/lips icon) ---
   Flash places a red mouth/lips clip at bottom-right of the activity area;
   clicking it re-fires the current mode's prompt (sayPressAnyNumber /
   sayFindTheNumber / sayCountIn). We use the Flash sprite (pictures/mouth.svg)
   directly — no chrome around it, just the lips on the white stage.
*/
.cmi-replay-btn {
    position: absolute;
    right: 1em;
    bottom: 1em;
    width: 3.2em;
    height: 2.4em;
    padding: 0;
    border: none;
    background: transparent;
    cursor: pointer;
}

.cmi-replay-btn img {
    display: block;
    width: 100%;
    height: 100%;
}

@media (hover: hover) {
    .cmi-replay-btn:hover { filter: brightness(0.9); }
}
.cmi-replay-btn:active { filter: brightness(0.8); }

.cmi-replay-btn:focus-visible {
    outline: 0.2em solid var(--dw-color-focus-ring);
    outline-offset: 0.15em;
}

/* Eye-gaze mode: the mouth doubles as a dwellable prompt-replayer, so it
   grows to side-rail scale and gets a visible target disc behind the lips.
   position:relative is required by shared/dwell.css for its indicator
   overlay; the dwell ring inset:0 fills the whole target. */
.cmi-activity-layout.cmi-eye-gaze .cmi-replay-btn {
    width: 5.6em;
    height: 5.6em;
    bottom: 0.6em;
    right: 0.6em;
    border: 0.2em solid #c33;
    border-radius: 50%;
    background: #fff2f2;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: visible;
}

.cmi-activity-layout.cmi-eye-gaze .cmi-replay-btn img {
    width: 70%;
    height: 70%;
}

/* --- Score area (mode 2) --- */

.cmi-score-toggle-btn {
    padding: var(--dw-space-tight) var(--dw-space-comfy);
    font-size: 1em;
    border: 2px solid var(--dw-color-text-muted);
    border-radius: 0.3em;
    background: #eee;
    cursor: pointer;
    white-space: nowrap;
}

.cmi-score-toggle-btn:focus-visible {
    background: var(--dw-color-border-faint);
    outline: 0.2em solid var(--dw-color-focus-ring);
}
@media (hover: hover) {
    .cmi-score-toggle-btn:hover {
        background: var(--dw-color-border-faint);
        outline: 0.2em solid var(--dw-color-focus-ring);
    }
}

/* Score overlay — speech-bubble anchored to the topbar (its containing block).
   Drops below the topbar by default (top: 100%); JS adds .cmi-score-panel--above
   when there is enough room above the topbar to show without covering the grid.
   `right: var(--dw-space-default)` aligns the panel's right edge with the topbar's
   content edge, matching the Score button's right edge.
   `width: max-content` lets the panel size to the longest row ("Total Found:").
   The ::before/::after pseudo-elements draw the speech-bubble triangle. */
.cmi-score-panel {
    position: absolute;
    top: 100%;
    right: var(--dw-space-default);
    background: #fffff0;
    border: 2px solid var(--dw-color-border-muted);
    border-radius: 0.5em;
    padding: var(--dw-space-default) 0.9em;
    z-index: 10;
    font-size: 1em;
    line-height: 1.45;
    width: max-content;
    max-width: calc(100% - 1em);
    box-sizing: border-box;
}

/* Speech-bubble triangle — overlapping pair of CSS triangles so the inner
   (#fffff0) wedge sits over the outer (#888) wedge with a 2px offset, matching
   the panel's grey 2px border.  Positioned at the top-right of the bubble,
   pointing up toward the Score toggle button. */
.cmi-score-panel::before,
.cmi-score-panel::after {
    content: "";
    position: absolute;
    width: 0;
    height: 0;
    border-style: solid;
    right: 0.6em;
}

.cmi-score-panel::before {
    /* Outer (border) triangle — upward-pointing */
    top: -0.6em;
    border-width: 0 0.55em 0.6em 0.55em;
    border-color: transparent transparent var(--dw-color-border-muted) transparent;
}

.cmi-score-panel::after {
    /* Inner (fill) triangle — sits 2px below the outer to mimic a 2px border */
    top: calc(-0.6em + 2px);
    border-width: 0 0.55em 0.6em 0.55em;
    border-color: transparent transparent #fffff0 transparent;
}

/* Flipped variant: panel appears above the topbar when there is enough room.
   JS adds this class after measuring available space; the triangle flips to
   point downward toward the Score button. */
.cmi-score-panel--above {
    top: auto;
    bottom: 100%;
}

.cmi-score-panel--above::before {
    /* Outer (border) triangle — downward-pointing */
    top: auto;
    bottom: -0.6em;
    border-width: 0.6em 0.55em 0 0.55em;
    border-color: var(--dw-color-border-muted) transparent transparent transparent;
}

.cmi-score-panel--above::after {
    /* Inner (fill) triangle — sits 2px above the outer to mimic a 2px border */
    top: auto;
    bottom: calc(-0.6em + 2px);
    border-width: 0.6em 0.55em 0 0.55em;
    border-color: #fffff0 transparent transparent transparent;
}

/* Score panel entry animation: fade + slight slide when toggled on.
   Visibility is the carrier of meaning (panel showing/hiding); animation is decorative.
   JS adds .cmi-score-appear on toggle-on and listens for animationend to remove it. */
@keyframes cmi-score-appear {
    0%   { opacity: 0; transform: translateY(-0.5em); }
    100% { opacity: 1; transform: translateY(0); }
}

@keyframes cmi-score-appear-above {
    0%   { opacity: 0; transform: translateY(0.5em); }
    100% { opacity: 1; transform: translateY(0); }
}

.cmi-score-panel.cmi-score-appear {
    animation: cmi-score-appear 0.25s ease-out;
}

.cmi-score-panel--above.cmi-score-appear {
    animation: cmi-score-appear-above 0.25s ease-out;
}

.cmi-score-panel p {
    margin: 0;
}

.cmi-score-label {
    font-weight: bold;
}

/* --- Flash stage scene layout ---
   Flash stage: 800×600 px, white background (#ffffff).
   Flash activity frame layout:
     - Top strip (y 0–65 px / 0–4 em): back button (left), guide text (centre), score button (right)
     - Main area (y 65–520 px / 4–24 em): number grid
     - Bottom-right (y ≈537 px / ≈25 em): mouth/replay button
   buttonO (odd):  Flash fill #ffb0b0 (salmon), border #fe9301 (orange) — see .cmi-odd above.
   buttonE (even): Flash fill #a4e1ff (sky blue), border #fe9301 (orange) — see .cmi-even above.
   Background: white (#ffffff) — no stage gradient. The blue band (#c6d7ff) visible in some
   frames is part of the loader progress bar, not the activity background.
*/

/* Activity screen: white background matching Flash stage */
#dwActivityScreen {
    background: #ffffff;
}

/* --- Options grid layout --- */

/* Headphones/speakers banner above the first fieldset (Flash options-screen header). */
.cmi-headphones-notice {
    text-align: center;
    margin: 0 0 var(--dw-space-comfy);
    font-style: italic;
}

/* Stations sub-panel description sits alone in the sub-panel, so drop the
   top margin that .dw-scan-note normally uses to separate it from the
   controls above. */
.cmi-stations-scan-note {
    margin-top: 0;
}

/* --- Eye-gaze mode ---
   Additive to switch scanning: when enabled, large static dwell targets
   flank the number grid and the bottom-right mouth icon is scaled up to
   side-rail size and made dwellable. The user dwells (gaze or hover) on
   a target; a progress ring fills over the configured window (default
   800 ms) and fires on completion.
   Mode 1 (Hear):     right rail only — Next cycles AND speaks the newly-
                      highlighted number.
   Modes 2 and 3:     dwell triangle — Previous (left rail), Next (right
                      rail), Select (bottom-centre bay under the grid).
                      Mode 2: Previous/Next cycle the highlight in
                      opposite directions; Select scores it.
                      Mode 3: Previous steps back / Next steps forward in
                      the counting sequence; Select fires the current
                      station. On completion, Select morphs to Repeat. */

.cmi-gaze-rail {
    /* Fixed-width column flanking the grid. Absolute positioning keeps the
       central grid's layout unchanged when the rails are hidden.
       flex-direction:column stacks Next above Pick (mode 2) with a visible
       gap; align-items:center centres each cell horizontally in the rail. */
    position: absolute;
    top: 3.5em;    /* clear the topbar guide text + chrome */
    bottom: 4em;   /* clear the bottom-right mouth button */
    width: 6.5em;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.6em;
    z-index: 5;
    pointer-events: none;
}

.cmi-gaze-rail-left  { left:  0.4em; }
.cmi-gaze-rail-right { right: 0.4em; }

/* Bottom-centre bay hosting the Select button under the number grid.
   Sits in clear space below the grid so it reads as a separate control,
   not glued to the last row of cells. right offset clears the mouth
   replay button at the bottom-right. */
.cmi-gaze-select-bay {
    position: absolute;
    left: 0;
    right: 7em;
    bottom: 0.6em;
    display: flex;
    justify-content: center;
    align-items: center;
    pointer-events: none;
    z-index: 5;
}

/* The side-rail button itself. Large, round, high-contrast — safe targets even
   with imprecise gaze calibration. position:relative so the dwell SVG can fill
   the button via position:absolute inset. */
.cmi-gaze-btn {
    position: relative;
    width: 5.6em;
    height: 5.6em;
    border: 0.2em solid #1a4d80;
    border-radius: 50%;
    background: #e8f3ff;
    color: #1a4d80;
    font-weight: bold;
    font-size: 1em;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    pointer-events: auto;
    /* The dwell completion flash is a box-shadow pulse outside the host
     * element; overflow:visible lets it reach past the circular border. */
    overflow: visible;
}

.cmi-gaze-btn.cmi-gaze-select {
    background: #e3fbd7;
    border-color: #2a9300;
    color: #184600;
}

@media (hover: hover) {
    .cmi-gaze-btn:hover { filter: brightness(0.95); }
}
.cmi-gaze-btn:active { filter: brightness(0.88); }

.cmi-gaze-btn:focus-visible {
    outline: 0.25em solid #ffaa00;
    outline-offset: 0.15em;
}

/* Dwell indicator: geometry and animation come from shared/dwell.css,
   injected by shared/dwell.js. The ring has a transparent centre so the
   label reads through; position:relative on the label keeps it anchored
   in flow above the absolutely-positioned ring overlay. */
.cmi-gaze-btn-label {
    position: relative;
    z-index: 1;
}

/* Cycle highlight — the "you are here" ring on the currently-highlighted
   number button in eye-gaze mode. Visually distinct from the mode-3 orange
   step marker and the scan ring: a thicker purple ring with a soft halo. */
.cmi-number-btn.cmi-gaze-selected {
    outline: 0.3em solid #6a2fbf;
    outline-offset: 0.05em;
    box-shadow: 0 0 0 0.15em rgba(106, 47, 191, 0.35);
}

/* When eye-gaze mode is on (.cmi-eye-gaze on the layout container), the number
   grid is clamped so the rail(s) sit in clear space rather than overlapping
   grid cells. Rails themselves are shown via .hidden_panel toggle in JS; no
   CSS gating needed.
   Mode 1: only the right rail is visible, so the grid is shifted left to put
   leftover horizontal space on the left.
   Modes 2 and 3: both rails plus the bottom-centre Select bay, so the grid
   stays centred and `--cmi-avail-h` is narrowed to leave room below for
   Select. */
.cmi-activity-layout.cmi-eye-gaze .cmi-number-grid {
    max-width: min(
        38em,
        calc(
            (var(--cmi-avail-h) - var(--cmi-gap) * (var(--cmi-rows) - 1))
            / var(--cmi-rows) * 10 + var(--cmi-gap) * 9
        )
    );
    align-self: flex-start;
    margin-left: 0.5em;
}

.cmi-activity-layout.cmi-eye-gaze[data-cmi-mode="2"] .cmi-number-grid,
.cmi-activity-layout.cmi-eye-gaze[data-cmi-mode="3"] .cmi-number-grid {
    --cmi-avail-h: 18em;
    max-width: min(
        30em,
        calc(
            (var(--cmi-avail-h) - var(--cmi-gap) * (var(--cmi-rows) - 1))
            / var(--cmi-rows) * 10 + var(--cmi-gap) * 9
        )
    );
    align-self: center;
    margin-left: 0;
}

/* Eye-gaze option row — sits at the bottom of the Accessibility fieldset,
   below the scan sub-panel. flex-wrap lets the subtitle span drop below the
   checkbox + label on narrow option-screen widths. */
.cmi-eye-gaze-row {
    display: flex;
    align-items: center;
    gap: var(--dw-space-tight);
    margin-top: var(--dw-space-default);
    flex-wrap: wrap;
}

/* --- Reduced motion ---
   The dwell ring's sweep is the visible dwell clock, driven by the
   same JS timer that fires the action, so its fill animation stays at
   its configured duration. Everything else collapses to the usual
   decorative-motion near-zero. */

@media (prefers-reduced-motion: reduce) {
    *:not(.dw-dwell-fill), *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}
