/* =====================================================================
   SudoExam page loader — the marker's ticks.

   While a page is fetched the registry's stationery is set to work: the
   tracked masthead and engraved double rule of the letterhead stand
   over a miniature candidate's script — red margin, pencil-grey
   answers, an EMPTY marks box — being marked in crimson ink. Ticks
   land down the margin one line at a time, a querying underline is
   dragged under the fourth answer (a marker reading, not a verdict),
   the nib rests poised over the untouched fifth, and before the paper
   could ever be finished the ink lifts and a fresh script begins.

   SUPERSESSION NOTE (2026-08-26). The 2026-08-09 rationale that used
   to live here — "no spinner, no ring and no tick; a success glyph has
   no business drawing itself during a state that has not succeeded" —
   is superseded by the owner's direction of 2026-08-26: "make it like
   marking exam paper." The craft principle survives one level up: the
   ticks are the marker's working on a miniature script, never a
   verdict on the page load. The one composition that must never exist
   in ANY frame — animated, entrance, frozen, or reduced — is a
   FINISHED paper: all lines ticked, a filled marks box, a total, a
   grade, or a stamp. Maximum ink on screen is 3 ticks + 1 query + 1
   untouched line + an empty box.

   Aliveness is proved quietly: the marking proceeds on one shared 8s
   beat, the caret breathes, and one hairline of light crosses the top
   of the page. Everything animated is transform and opacity only, so
   the lab machines composite it for free — nothing else is used to
   move a pixel — and nothing ever sweeps a track edge-to-edge, so
   nothing can be read as a bar filling toward 100%.

   Plain CSS on purpose. The custom app loads this next to app.css and
   the Filament panel loads the very same file, so the whole product
   shows one loader. Every colour is read through a variable with a
   literal fallback, because inside the panel the app's tokens do not
   exist.

   Two timings are load-bearing and should not be "tidied away". The
   veil does not paint for the first VEIL_PAINT ms (400 since
   2026-09-08 — see page-loader.js for the measurement), so an ordinary
   navigation shows the top hairline and never a full-screen flash; and
   it leaves more slowly than it arrives, so it reads as a sheet lifted
   off the new page rather than a layer that vanished.

   That first 180ms is owned by ONE class and one class only: the JS
   marks #sx-veil `on` at the click (which swallows it) and `paint`
   180ms later. Everything that means "the sheet is arriving" — its own
   opacity, the marking loop, and the rule that stands the hairline
   down — hangs off `paint`, never off `on`. Hang any of them back on
   `on` and the suppression rule below matches on the frame the
   hairline is switched on, so the bar's opacity target is 0 before it
   has painted anything and a fast navigation acknowledges the click
   with nothing at all.

   The marking loop carries a shared NEGATIVE lead: -2.6s of the 8s
   beat, on every inked element alike. Navigation often freezes
   painting the moment the browser commits, so the first frame `paint`
   shows — frequently the ONLY frame — must already be mid-marking:
   ticks standing on lines one and two, the nib at line three, never a
   blank form. Every stroke's window is a percentage of the ONE shared
   period; no stroke carries a delay of its own, so the page-turn reset
   can never drift out of step.
   ===================================================================== */

:root {
    --pl-font: var(--sxfont, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
    --pl-ink: var(--ink, #0f172a);
    --pl-slate: var(--slate, #475569);
    --pl-accent: var(--brand, #0a6e80);
    --pl-track: var(--line, #e2e8f0);
    /* the sheet is 96% opaque: the page underneath survives as a ghost,
       which is the difference between "covered" and "gone" */
    --pl-scrim: rgba(248, 250, 252, .96);
    --pl-rule-a: rgba(15, 23, 42, .28);
    --pl-rule-b: rgba(15, 23, 42, .11);
    /* the marker's pen, where it writes on the dark desk directly (the
       inline mark). On the script's ivory paper the ink is a literal —
       paper stays paper in the dark, so its ink must not brighten. */
    --pl-mark: #b42332;
}

/* ---------------------------------------------------------------------
   1. The hairline across the top of the page (in-page Livewire work).
   Deliberately untouched by the marking re-skin: it also runs during
   panel round trips, where crimson would read as an alarm — the
   registry's teal light travelling a rule, exactly as shipped. No
   track when it is off: an idle loader must leave no mark.

   --pl-end is the travel distance, in multiples of the segment's own
   width: 100 / segment% x 100%. At exactly that value the segment's
   left edge reaches the right end of the rule on the same frame its
   right edge would re-enter at the left, so the line is never empty
   for a beat. Change --pl-seg and you must recompute --pl-end.

   --pl-lead is negative on purpose. Navigation often freezes painting
   the moment the browser commits, so the loader is frequently a single
   still frame; the negative delay puts the light part-way along the
   rule at frame zero instead of off-canvas.
   --------------------------------------------------------------------- */
.loadbar {
    position: fixed; top: 0; left: 0; right: 0; height: 2px;
    overflow: hidden;
    z-index: 2147483000; pointer-events: none; background: transparent;
    opacity: 0; transition: opacity .2s linear;
    --pl-seg: 22%; --pl-end: 454.5%; --pl-beat: 1.8s; --pl-lead: -.6s;
}
.loadbar > i {
    position: absolute; top: 0; bottom: 0; left: 0;
    width: var(--pl-seg, 26%);
    transform: translateX(-100%);
    background: linear-gradient(90deg,
        transparent,
        var(--pl-accent, #0a6e80) 28%,
        var(--pl-accent, #0a6e80) 72%,
        transparent);
}
/* On at once, with no fade. Two reasons, and both are about the frame
   this is actually seen in: an acknowledgement of a click that ramps up
   over a fifth of a second has already missed the navigations it exists
   for; and, as the note above says, the browser frequently freezes
   painting the moment it commits, so the loader is often ONE still
   frame — a fade would leave that frame at a third of an opacity nobody
   can read. Only the entry is instant. Leaving still runs on the base
   transition above, or on the suppression rule's own, because both of
   those are the after-change style and it is the after-change style
   that owns a transition. */
.loadbar.on { opacity: 1; transition: none; }

/* Never two moving things at once. While the full-screen hold is up the
   composition is the whole statement, so the top hairline steps back.
   ID beats .loadbar.on without !important; where :has() is missing the
   worst case is simply that both remain visible.

   Keyed on `paint`, never on `on`. `on` is added at the click, so this
   rule would win the cascade on the hairline's very first frame: the
   bar would start at 0 with a target of 0, and 0 -> 0 does not animate.
   `paint` is added 180ms later, which is precisely when the sheet
   starts arriving and the hairline has had its moment — so the delay
   that used to sit on this transition is gone with it. */
body:has(#sx-veil.paint) #sx-loadbar { opacity: 0; transition: opacity .12s linear; }

/* ---------------------------------------------------------------------
   2. The inline mark, beside a caption (/verify, mark script, self-mark).
   One ruled line of the full-screen loader's script: the 2px rule stays
   as the spine (it is the family resemblance binding hairline, inline
   and veil into one object), with pencil answer-stubs standing on it
   and miniature two-stroke ticks landing stub by stub — and ALWAYS one
   stub the ink has not reached. Every cycle the ink lifts, so no tick
   ever persists beside a caption as its answer.

   The wrapper's layout belongs here, not in a style attribute on the
   Blade component, so this file can size the mark without stranding it.

   Base state — what shows with no animation running at all — is the
   reduced-motion still: ONE ticked stub of four (one of three at .sm),
   plainly mid-marking, claiming no percentage.
   --------------------------------------------------------------------- */
.sxloadwrap { display: inline-flex; align-items: center; gap: 12px; }
.sxloadwrap .label { font: 700 13px/1.4 var(--pl-font); color: var(--pl-ink, #0f172a); letter-spacing: -.005em; }
.sxloadwrap .label span { display: block; margin-top: 2px;
    font: 500 12px/1.5 var(--pl-font); color: var(--pl-slate, #475569); }

.sxload {
    position: relative; display: inline-block; vertical-align: middle;
    align-self: center; flex: none;
    width: 80px; height: 14px;
}
/* the spine: the same 2px rule the loader has always been */
.sxload::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
    background: var(--pl-track, #e2e8f0); }
/* the candidate's answers: pencil stubs on the rule (never move) */
.sxst { position: absolute; bottom: 4px; height: 2.5px; border-radius: 2px;
    background: rgba(15, 23, 42, .26); }
.sxst1 { left: 2px;  width: 12px; }
.sxst2 { left: 23px; width: 13px; }
.sxst3 { left: 44px; width: 11px; }
.sxst4 { left: 65px; width: 12px; }
/* the ticks: miniature two-stroke marks, one per stub, landing in turn.
   The fourth stub has no tick position at all — unreachable by design. */
.sxmt { position: absolute; bottom: 5px; width: 11px; height: 11px;
    transform-origin: 0 100%; }
.sxmt i { position: absolute; height: 1.8px; border-radius: 2px;
    background: var(--pl-mark, #b42332); }
.sxmt .sxs-s1 { left: 0;     top: 5.4px; width: 4.6px; transform: rotate(48deg);  transform-origin: 1px 50%; }
.sxmt .sxs-s2 { left: 2.6px; top: 7.8px; width: 8.6px; transform: rotate(-56deg); transform-origin: 1px 50%; }
.sxmt1 { left: 2px;  opacity: 1; }
.sxmt2 { left: 23px; opacity: 0; }
.sxmt3 { left: 44px; opacity: 0; }

.sxload.sm { width: 52px; }
.sxload.sm .sxst1 { left: 1px;  width: 10px; }
.sxload.sm .sxst2 { left: 20px; width: 11px; }
.sxload.sm .sxst3 { left: 39px; width: 10px; }
.sxload.sm .sxst4 { display: none; }
.sxload.sm .sxmt1 { left: 1px; }
.sxload.sm .sxmt2 { left: 20px; }
.sxload.sm .sxmt3 { display: none; }

/* ---------------------------------------------------------------------
   3. The full-screen loader.

   Entry: `on` flips display at once (so the click is swallowed
   immediately) while the sheet stays fully transparent; `paint` lands
   180ms later from the JS and is what makes it visible, the typography
   rising 80ms behind that. A 150ms navigation never reaches `paint`
   and therefore paints nothing at all.
   Exit: a 240ms accelerating fade, held on screen by a discrete display
   transition. Browsers without transition-behavior simply cut, which is
   the old behaviour and perfectly graceful.
   --------------------------------------------------------------------- */
.sxveil {
    position: fixed; inset: 0; z-index: 2147483001; box-sizing: border-box;
    display: none; place-items: center; padding: 24px; opacity: 0;
    background: var(--pl-scrim, rgba(248, 250, 252, .96));
    color: var(--pl-ink, #0f172a); font-family: var(--pl-font);
    cursor: progress; pointer-events: none; overscroll-behavior: contain;
    -webkit-user-select: none; user-select: none;
    transition: opacity .24s cubic-bezier(.4, 0, 1, 1), display .24s;
    transition-behavior: allow-discrete;
}
.sxveil.on { display: grid; pointer-events: auto; }
.sxveil.on.paint { opacity: 1; }

/* Deliberately NOT locking the body's scroll while the sheet is up.
   On Windows, where the scrollbar takes real width, `overflow: hidden`
   reflows the whole page by ~15px the moment it is applied and again
   when it is released — a visible jolt on every single click, on the
   exact hardware this runs on. Scrolling a page nobody can see for the
   second it is covered is the far smaller sin. `overscroll-behavior`
   above still stops the scroll chaining out of the sheet itself. */

.sxv-col { width: min(420px, 100%); margin-bottom: 5vh; text-align: left; }

.sxv-mast { margin: 0 0 13px; font: 700 11.5px/1 var(--pl-font); letter-spacing: .3em;
    text-transform: uppercase; color: var(--pl-ink, #0f172a); }
.sxv-mast span { color: var(--pl-accent, #0a6e80); }

/* the engraved double rule of a letterhead */
.sxv-rule { position: relative; height: 4px; margin: 0 0 20px; }
.sxv-rule::before, .sxv-rule::after { content: ''; position: absolute; left: 0; right: 0; height: 1px; }
.sxv-rule::before { top: 0; background: var(--pl-rule-a, rgba(15, 23, 42, .28)); }
.sxv-rule::after { bottom: 0; background: var(--pl-rule-b, rgba(15, 23, 42, .11)); }

.sxv-status { margin: 0; font: 600 20px/1.3 var(--pl-font); letter-spacing: -.014em;
    color: var(--pl-ink, #0f172a); }
.sxv-caret { display: inline-block; width: 2px; height: .92em; margin-left: .3em; vertical-align: -.06em;
    background: var(--pl-accent, #0a6e80); }

/* 34ch so the strings the JS writes set as two tidy lines rather than
   one long one under a 20px statement */
.sxv-note { margin: 7px 0 0; max-width: 34ch; font: 500 12.5px/1.6 var(--pl-font);
    color: var(--pl-slate, #475569); }

/* ---------------------------------------------------------------------
   THE SCRIPT. Where the centre rule used to run: a miniature
   candidate's script on the desk — ivory paper, a red margin rule, five
   ruled lines of abstract pencil answers, and a red-outlined marks box
   that is EMPTY in every frame of every state. Nothing on it is
   fabricated (the front-door rule): no candidate number, no words, no
   figures — bars of pencil grey only.

   It is PAPER, so it keeps its own light palette on the dark desk,
   exactly as the workbook editor pins a light sheet inside the dark
   panel — which is why every colour on the sheet is a literal, not a
   token that flips.

   Base state — no animation running — is the reduced-motion still, and
   also what a frozen navigation frame shows: ticks standing on lines
   one and two, the querying underline under line three, the nib
   resting at line four, line five untouched, marks box empty. A
   half-marked script cannot be mistaken for a finished one.
   --------------------------------------------------------------------- */
.sxv-script { position: relative; height: 140px; margin: 22px 0 0;
    background: #fffdf5; border: 1px solid rgba(15, 23, 42, .18); border-radius: 2px;
    box-shadow: 0 1px 0 rgba(15, 23, 42, .05), 0 14px 34px -22px rgba(15, 23, 42, .45);
    overflow: hidden; }

/* printed features of the script (never move) */
.sxs-margin { position: absolute; top: 0; bottom: 0; left: 36px; width: 1px;
    background: rgba(180, 35, 50, .34); }
.sxs-head { position: absolute; top: 12px; left: 46px; width: 32%; height: 3px;
    border-radius: 2px; background: rgba(15, 23, 42, .13); }
.sxs-box { position: absolute; top: 9px; right: 10px; width: 36px; height: 19px;
    border: 1px solid rgba(180, 35, 50, .42); border-radius: 2px; }
.sxs-row { position: absolute; left: 0; right: 0; height: 1px; background: rgba(15, 23, 42, .1); }
.sxr1 { top: 36px; } .sxr2 { top: 58px; } .sxr3 { top: 80px; } .sxr4 { top: 102px; } .sxr5 { top: 124px; }

/* the candidate's answers — pencil-grey, abstract (never move) */
.sxs-hand { position: absolute; left: 46px; right: 14px; display: flex; gap: 8px; }
.sxs-hand i { display: block; height: 3px; border-radius: 2px; background: rgba(15, 23, 42, .16); }
.sxh1 { top: 27px; } .sxh2 { top: 49px; } .sxh3 { top: 71px; } .sxh4 { top: 93px; } .sxh5 { top: 115px; }

/* the marker's ink — everything the pen lays down lives on this one
   layer, so ONE opacity fade lifts the whole marking for the page turn
   and the cycle restarts clean */
.sxs-ink { position: absolute; inset: 0; }

/* a tick is two strokes, drawn in pen order: short, then long. The
   rotation is static; only scaleX and opacity ever change. */
.sxs-tick { position: absolute; left: 9px; width: 18px; height: 16px; }
.sxt1 { top: 21px; } .sxt2 { top: 43px; } .sxt3 { top: 65px; }
.sxs-tick i { position: absolute; height: 2.5px; border-radius: 2px;
    background: #b42332; opacity: .92; }
.sxs-tick .sxs-s1 { left: 0;     top: 8px;    width: 8px;  transform-origin: 1.2px 50%; }
.sxs-tick .sxs-s2 { left: 4.6px; top: 12.2px; width: 15px; transform-origin: 1.2px 50%; }
.sxt1 .sxs-s1, .sxt2 .sxs-s1 { transform: rotate(48deg) scaleX(1); }
.sxt1 .sxs-s2, .sxt2 .sxs-s2 { transform: rotate(-56deg) scaleX(1); }
.sxt3 .sxs-s1 { transform: rotate(48deg) scaleX(0); }
.sxt3 .sxs-s2 { transform: rotate(-56deg) scaleX(0); }

/* the querying underline — a marker reading, not a verdict. In the
   still it sits under line three; while the loop runs it belongs to
   line four (repositioned inside the motion block below). */
.sxs-under { position: absolute; left: 46px; top: 77px; width: 56px; height: 2px;
    border-radius: 2px; background: #b42332; opacity: .8;
    transform: scaleX(1); transform-origin: 0 50%; }

/* the nib — a small crimson dash in the margin, resting one line below
   the last mark. Base: poised at line four. */
.sxs-nib { position: absolute; left: 26px; top: 27px; width: 2.5px; height: 10px;
    border-radius: 1px; background: #b42332; opacity: .85; transform: translateY(66px); }

.sxv-plate { margin: 12px 0 0; font: 700 10.5px/1.7 var(--pl-font); letter-spacing: .18em;
    text-transform: uppercase; color: var(--pl-slate, #475569); }
.sxv-plate span + span::before { content: '\00B7'; margin: 0 .5em 0 .4em; }

@media (max-width: 430px) {
    .sxv-status { font-size: 18px; }
    .sxv-mast { letter-spacing: .24em; }
    .sxv-plate { letter-spacing: .13em; }
    .sxv-plate span:nth-child(2) { display: none; }   /* keeps the plate on one line */
}

/* ---------------------------------------------------------------------
   4. Dark. Only the Filament panel can be dark; it puts class="dark" on
   <html>. html.dark outranks :root, so the tokens win whatever the
   source order. The desk darkens; THE SCRIPT STAYS IVORY PAPER — the
   workbook-editor rule, paper reads as paper on the desk — so nothing
   on the sheet is redeclared here. Only the inline mark, whose ink
   sits on the dark ground directly, brightens its pen to keep 2px
   strokes legible.
   --------------------------------------------------------------------- */
html.dark, .dark {
    --pl-ink: #e6eaf2;
    --pl-slate: #94a3b8;
    /* --pl-accent is deliberately NOT redeclared here. It reads
       var(--brand), and tools.css already swaps --brand to the dark
       accent under html.dark — the indigo literal that used to sit on
       this line was the one place the dark panel's loader still painted
       the retired palette, overriding a value that was already right. */
    --pl-track: rgba(148, 163, 184, .24);
    --pl-scrim: rgba(15, 23, 42, .96);
    --pl-rule-a: rgba(226, 232, 240, .34);
    --pl-rule-b: rgba(226, 232, 240, .14);
    --pl-mark: #ef6f7b;
}
.dark .sxveil { background: var(--pl-scrim, rgba(15, 23, 42, .96)); }
.dark .sxv-mast, .dark .sxv-status { color: var(--pl-ink, #e6eaf2); }
.dark .sxv-note, .dark .sxv-plate { color: var(--pl-slate, #94a3b8); }
.dark .sxv-script { border-color: rgba(0, 0, 0, .55); }
.dark .sxloadwrap .label { color: var(--pl-ink, #e6eaf2); }
.dark .sxloadwrap .label span { color: var(--pl-slate, #94a3b8); }
.dark .sxst { background: rgba(230, 234, 242, .34); }

/* ---------------------------------------------------------------------
   MOTION. Every animation in the file lives inside this one
   no-preference block, so the reduce block below never has to fight
   anything — the base styles ARE the reduced composition.

   The marking loop is gated on `.paint` (each appearance restarts on
   its own clean cycle) and every inked element shares the ONE 8s
   linear beat with the ONE -2.6s lead — windows are keyframe
   percentages, never per-stroke delays. The timeline:

     0%          a fresh script; nib poised at line one
     4-9%        tick one drawn (short stroke, then long)
     14%         nib steps to line two
     20-25%      tick two drawn
     30%         nib steps to line three     <- the -2.6s entrance frame
     38-43%      tick three drawn
     48%         nib steps to line four
     53-62%      the querying underline dragged under answer four
     66-88%      the nib's longest still beat, over untouched line five
     88-94%      the page turn: ALL ink lifts in one shared fade
     94-96%      strokes reset, fully transparent; nib returns
     97-100%     the fresh script fades in, clean

   Line five is never ticked. The marks box never fills.
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
    .loadbar > i { animation: sx-travel var(--pl-beat, 2.2s) linear var(--pl-lead, -.75s) infinite; }
    /* NO fill on the entry ramp. `.sxveil.on.paint` already declares
       opacity: 1 for after the ramp, so a forwards fill changes nothing
       the eye can see — but a finished animation's fill keeps COVERING
       the property, and a transition never starts on a property an
       animation still applies to. With `both` here, hide() snapped the
       sheet away instead of running the 240ms exit fade (measured in
       Chrome: gone in 1 frame with fill, 262ms without). */
    .sxveil.on.paint { animation: sx-veil-in .14s linear; }
    .sxv-col { animation: sx-rise .3s cubic-bezier(.2, .75, .3, 1) .26s both; }
    .sxv-caret { animation: sx-caret 1.6s ease-in-out infinite; }

    /* while the loop runs, the query belongs to line four */
    .sxs-under { top: 99px; }

    .sxveil.paint .sxs-ink { animation: sxs-page 8s linear -2.6s infinite; }
    .sxveil.paint .sxs-nib { animation: sxs-nib 8s linear -2.6s infinite; }
    .sxveil.paint .sxt1 .sxs-s1 { animation: sxs-t1a 8s linear -2.6s infinite; }
    .sxveil.paint .sxt1 .sxs-s2 { animation: sxs-t1b 8s linear -2.6s infinite; }
    .sxveil.paint .sxt2 .sxs-s1 { animation: sxs-t2a 8s linear -2.6s infinite; }
    .sxveil.paint .sxt2 .sxs-s2 { animation: sxs-t2b 8s linear -2.6s infinite; }
    .sxveil.paint .sxt3 .sxs-s1 { animation: sxs-t3a 8s linear -2.6s infinite; }
    .sxveil.paint .sxt3 .sxs-s2 { animation: sxs-t3b 8s linear -2.6s infinite; }
    .sxveil.paint .sxs-under { animation: sxs-query 8s linear -2.6s infinite; }

    /* inline: 4.8s, ticks landing stub by stub, all lifted each cycle;
       the shared -.6s lead means a still frame shows one tick standing */
    .sxmt1 { animation: sxm-t1 4.8s linear -.6s infinite; }
    .sxmt2 { animation: sxm-t2 4.8s linear -.6s infinite; }
    .sxmt3 { animation: sxm-t3 4.8s linear -.6s infinite; }
}

/* --------------------------------------------------------------------- */
@keyframes sx-travel {
    from { transform: translateX(-100%); }
    to   { transform: translateX(var(--pl-end, 300%)); }
}
@keyframes sx-caret  { 0%, 100% { opacity: 1; } 45%, 55% { opacity: .14; } }
@keyframes sx-veil-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes sx-rise   { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* the page turn: the whole marking lifts as one, holds clear while the
   strokes reset underneath it, then the next script fades in clean */
@keyframes sxs-page {
    0%, 88% { opacity: 1; }
    94%, 97% { opacity: 0; }
    100% { opacity: 1; }
}
/* the nib steps down the margin between fully-specified keyframes; its
   longest still beat (66-88%, ~1.8s) is poised over untouched line
   five. It returns home at 94-96%, while the ink layer is transparent. */
@keyframes sxs-nib {
    0%, 12%   { transform: translateY(0); }
    14%, 28%  { transform: translateY(22px); }
    30%, 46%  { transform: translateY(44px); }
    48%, 64%  { transform: translateY(66px); }
    66%, 94%  { transform: translateY(88px); }
    96%, 100% { transform: translateY(0); }
}
/* each stroke is laid in pen order and holds until the page turn; the
   reset (96%) happens only after the ink layer is fully transparent
   (94%). Rotation never changes — only scaleX. */
@keyframes sxs-t1a { 0%, 4%  { transform: rotate(48deg) scaleX(0); }  6%, 94%  { transform: rotate(48deg) scaleX(1); }  96%, 100% { transform: rotate(48deg) scaleX(0); } }
@keyframes sxs-t1b { 0%, 6%  { transform: rotate(-56deg) scaleX(0); } 9%, 94%  { transform: rotate(-56deg) scaleX(1); } 96%, 100% { transform: rotate(-56deg) scaleX(0); } }
@keyframes sxs-t2a { 0%, 20% { transform: rotate(48deg) scaleX(0); }  22%, 94% { transform: rotate(48deg) scaleX(1); }  96%, 100% { transform: rotate(48deg) scaleX(0); } }
@keyframes sxs-t2b { 0%, 22% { transform: rotate(-56deg) scaleX(0); } 25%, 94% { transform: rotate(-56deg) scaleX(1); } 96%, 100% { transform: rotate(-56deg) scaleX(0); } }
@keyframes sxs-t3a { 0%, 38% { transform: rotate(48deg) scaleX(0); }  40%, 94% { transform: rotate(48deg) scaleX(1); }  96%, 100% { transform: rotate(48deg) scaleX(0); } }
@keyframes sxs-t3b { 0%, 40% { transform: rotate(-56deg) scaleX(0); } 43%, 94% { transform: rotate(-56deg) scaleX(1); } 96%, 100% { transform: rotate(-56deg) scaleX(0); } }
/* the underline is dragged, not struck — a slower, reading hand */
@keyframes sxs-query {
    0%, 53% { transform: scaleX(0); }
    62%, 94% { transform: scaleX(1); }
    96%, 100% { transform: scaleX(0); }
}
/* inline ticks: landed, held, lifted — never left standing */
@keyframes sxm-t1 {
    0%, 6% { opacity: 0; transform: scaleX(.3); }
    10%, 88% { opacity: 1; transform: scaleX(1); }
    94%, 100% { opacity: 0; transform: scaleX(1); }
}
@keyframes sxm-t2 {
    0%, 34% { opacity: 0; transform: scaleX(.3); }
    38%, 88% { opacity: 1; transform: scaleX(1); }
    94%, 100% { opacity: 0; transform: scaleX(1); }
}
@keyframes sxm-t3 {
    0%, 62% { opacity: 0; transform: scaleX(.3); }
    66%, 88% { opacity: 1; transform: scaleX(1); }
    94%, 100% { opacity: 0; transform: scaleX(1); }
}

/* ---------------------------------------------------------------------
   5. Reduced motion. Every animation already lives inside the
   no-preference block above, so the base styles ARE this composition
   and nothing here fights anything: the veil is the half-marked script
   — ticks standing on lines one and two, the query under line three,
   the nib resting at line four, line five untouched, marks box empty —
   the inline mark is one ticked stub, and the hairline and the inline
   spine become a dotted leader, the printed convention for "to be
   completed". Plainly unfinished, claiming no percentage, entirely
   still.

   The animation: none list is belt and braces. .sxveil.on.paint is
   listed by name: it carries the entry animation and outranks a bare
   .sxveil.on, so leaving it out would let the fade survive here. The
   180ms hold before `paint` stays — a hold is not motion, and it is
   what keeps a fast navigation free of a full-screen flash for these
   readers too.
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .loadbar, .loadbar.on, .sxveil { transition: none; }
    .sxveil.on, .sxveil.on.paint, .sxv-col, .sxv-caret,
    .loadbar > i, .sxs-ink, .sxs-nib, .sxs-tick i, .sxs-under,
    .sxmt1, .sxmt2, .sxmt3 { animation: none; }
    .sxv-col { opacity: 1; transform: none; }
    .sxv-caret { opacity: .85; }
    .loadbar > i { display: none; }
    .loadbar {
        background: repeating-linear-gradient(90deg,
            var(--pl-accent, #0a6e80) 0 7px, transparent 7px 15px);
    }
    .sxload::after {
        background: repeating-linear-gradient(90deg,
            var(--pl-track, #e2e8f0) 0 5px, transparent 5px 10px);
    }
}

@media print {
    .loadbar, .sxveil { display: none !important; }
}

/* screen-reader-only text, repeated here so the loader stands alone */
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
