/* ════════════════════════════════════════════════════════════════════════
   Base stylesheet — the only CSS the public surfaces need. Loaded first by
   every layout:

     layouts/marketing.php  base.css + marketing.css
     layouts/guest.php      base.css + guest.css
     layouts/main.php       base.css + app.css

   Split out of app.css so the apex marketing pages and the auth/error pages
   stop pulling the whole authenticated app's stylesheet (~80 KB) for a
   landing page and a login form.

   What belongs here: design tokens, the reset, typography, form controls,
   the guest card + auth forms, and the app-wide accessibility rules. What
   does NOT: anything only an authenticated page can render — the shell, nav,
   modals, data tables, dashboards, editors. Those stay in app.css.

   Rule of thumb: if a logged-out visitor can't see it, it isn't base.
   ════════════════════════════════════════════════════════════════════════ */


/* ── Design tokens ────────────────────────────────────────────────────────────
   Single source of truth for the neutral-zinc palette + spacing/type/radius/shadow
   scales. Component rules reference these via var(); change a value here to
   retheme app-wide. */

:root {
    /* Ink / text — neutral zinc, dark → faint */
    --ink:            #18181b;
    --ink-muted:      #52525b;
    --ink-subtle:     #71717a;
    --ink-faint:      #a1a1aa;
    /* Legacy alias used across views as var(--text-muted, #888); the old #888
       fallback failed AA (3.5:1 on white). Point it at --ink-subtle (4.6:1). */
    --text-muted:     #71717a;

    /* Surfaces */
    --surface:        #fff;
    --bg:             #fafafa;
    --surface-alt:    #f4f4f5;
    --surface-hover:  #fafafa;

    /* Borders — subtle → strong */
    --border-subtle:  #f4f4f5;
    --border:         #e4e4e7;
    --border-strong:  #d4d4d8;

    /* What a borderless .btn-ghost* is filled with. A container painted --surface-alt overrides
       this pair so its ghost buttons do not disappear into it; see the note by .btn-ghost. */
    --ghost-fill:       var(--surface-alt);
    --ghost-fill-hover: var(--border);

    /* Accent — emerald. The single interactive/brand hue (links, buttons, focus,
       selected states). --accent-soft is the pale secondary; --accent-ring the
       lighter focus ring used on dark surfaces.

       Note the accent is emerald-700, not the brand emerald-600 (#059669) used on
       the guest gradient and the logo mark. #059669 measures 3.77:1 on white —
       under the 4.5:1 AA floor for link text and for white-on-fill button labels.
       #047857 clears it at 5.48:1. Keep any new interactive colour at or below
       that lightness; #059669 is for decoration only. */
    --accent:         #047857;
    --accent-hover:   #065f46;
    --accent-soft:    #6ee7b7;
    --accent-ring:    #a7f3d0;

    /* What a SOLID accent button is painted with, and the label that goes on it.
       Identical to the accent above, so nothing in the app moves -- the indirection exists for the
       public sign-up flow, where an organizer's own brand colour replaces them per event.

       Fill and accent are separate for a reason that only shows up once the colour is not ours: a
       button is painted the organizer's exact hex and its label flips to whichever of white or ink
       can be read on it, while TEXT has nothing to flip against and must darken instead. Collapse
       the two and a yellow event gets either an olive button or an unreadable link.
       See App\Branding\Palette.

       Anything overriding --accent for a surface must override these three too. A var() inside a
       custom property is substituted where it is DECLARED, so this --brand-fill computes against
       :root's --accent and keeps the emerald however the token is re-pointed further down the tree.
       Palette::cssVars() emits all of them together for exactly that reason. */
    --brand-fill:       var(--accent);
    --brand-fill-hover: var(--accent-hover);
    --brand-on-fill:    #fff;

    /* Status */
    --danger:         #dc2626;
    --danger-hover:   #b91c1c;
    --warn:           #f59e0b;
    --success:        #16a34a;
    /* Status tints and the ink that reads on them (pills, Needs attention), as the sibling apps
       have them. Each ink clears AA on its own tint. */
    --success-tint:   #dcfce7;   --success-ink: #166534;   /* 6.8:1 */
    --warn-tint:      #fef3c7;   --warn-ink:    #92400e;   /* 7.1:1 */
    --danger-tint:    #fee2e2;   --danger-ink:  #b91c1c;   /* 6.3:1 */

    /* Radius */
    --radius-sm:      4px;
    --radius:         6px;
    --radius-lg:      8px;

    /* Elevation */
    --shadow-sm:      0 1px 3px rgba(24,24,27,0.06);
    --shadow-md:      0 4px 14px rgba(24,24,27,0.08);

    /* Spacing scale (4/8/12/16/24/32) — adopted incrementally by later milestones */
    --space-1:        0.25rem;
    --space-2:        0.5rem;
    --space-3:        0.75rem;
    --space-4:        1rem;
    --space-5:        1.5rem;
    --space-6:        2rem;

    /* Type scale */
    --text-xs:        0.72rem;
    --text-sm:        0.8rem;
    --text-md:        0.875rem;
    --text-lg:        1rem;
}


/* ── Brand tokens (public surfaces) ───────────────────────────────────────────
   Shared by the apex marketing pages and the guest card so login/signup/404 read
   as the same product as the landing page. Scoped to the two public body classes,
   never :root, so the app's palette is untouched.

   --brand is emerald-600, one stop lighter than --accent. It is DECORATIVE only:
   large fills, gradients, the logo mark. It fails AA as body text — anything
   readable uses var(--accent). See the accent note above. */

body.marketing-layout,
body.guest-layout {
    --brand:       #059669;  /* emerald-600 — decorative fills, gradient start */
    --brand-deep:  #065f46;  /* emerald-800 — dark bands, gradient end */
    --brand-paper: #f0fdf4;  /* faint green-tinted panel ground */
    /* The logo mark's other two bars. public/img/logo-mark.svg is the source; these are copies
       for CSS, so the marketing hero can paint the mark's courses with `fill: var(...)`. */
    --brand-mid:   #56b597;
    --brand-pale:  #99cfbe;
}



/* ── The marketing site ───────────────────────────────────────────────────────
   Light, and green without gold. The site was a dark room with cream tickets
   and amber as its one button until 2026-10, and was judged too dark and too
   much like a green-and-gold sports uniform. What replaced it is white and a
   pale mint, with the logo mark's own three greens and one sky blue as the
   only other colour. The button is the ordinary emerald .btn-primary, so
   nothing here re-points --brand-fill any more.

   Marketing-layout ONLY: the guest layout is also the invitation, unsubscribe
   and error pages, which must stay a plain card on a green field.

   Measured on #fff / --mist / --mint-band:

     --ink      #18181b   17.7 / 16.6 / 15.9
     --ink-soft #3d4f49    9.0 /  8.5 /  8.1
     --accent   #047857    5.5 /  5.2 /  5.0
     --sky-ink  #0369a1    5.9 /  5.5 /  5.3

   --sky (#0ea5e9) is decorative and never carries text; --sky-ink is the one
   that may. */

body.marketing-layout {
    --mist:       #f1f9f5;  /* the hero's top, and the header over it */
    --mint-band:  #eaf5f0;  /* a flat band; the price ticket sits on it */
    --hairline:   #dde3df;
    --ink-soft:   #3d4f49;
    --sky:        #0ea5e9;
    --sky-ink:    #0369a1;

    /* The ticket. --paper is its stock, --perf the dashes of the tear-off. A
       ticket's notches are painted in the colour of the ground under it, so a
       ticket must sit on a flat ground: see marketing.css. */
    --paper:      #fffdf7;
    --paper-edge: #efe2cd;
    --perf:       #d9c7a8;

    /* Labels on the worked example and the ticket. A monospace doing a job, the
       way a printed stub sets its serial numbers, not standing in for code. */
    --mono: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}


/* ── Reset & Base ─────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

body {
    margin: 0;
    font-family: 'Poppins', sans-serif;
    font-size: 14px;
    background: var(--bg);

    color: var(--ink);
}


/* ── Typography & Links ───────────────────────────────── */

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); }

dl { display: grid; grid-template-columns: 140px 1fr; gap: 0.5rem 1rem; }
dt { font-weight: 600; color: var(--ink-muted); }
/* The UA gives <dd> a 40px inline-start margin, which indents every value away from
   its column edge — meaningless once the list is a two-column grid. */
dd { margin: 0; }


/* ── Buttons & Inputs ─────────────────────────────────── */

/* The `hidden` attribute has to beat the display rules below, and by default it does not: the
   browser's own [hidden] rule is a USER-AGENT style, so any author rule setting `display` wins
   over it no matter how unspecific — `button { display: inline-flex }` included. That left every
   `<button hidden>` in the app rendering as though it were visible.

   This used to say that as an author rule it needed no !important, because [hidden] is (0,1,0) and
   outranks a bare tag. The first half is true and the conclusion is not. (0,1,0) is also exactly
   what a CLASS scores, so every `.thing { display: flex }` in app.css TIES with it and then wins on
   source order, app.css being loaded after base.css. The evidence was already in the tree: app.css
   carried three one-line patches re-stating the attribute for one selector each (.modal-sidebar a,
   .modal-form-row, .alert), each with a comment working the same specificity out from scratch, and
   register.css had reached the right answer independently and said so at the top of the file. This
   comment used to name two more, .asset-panel and .chat-paywall-overlay, which had been deleted
   along with the features that used them.

   The one nobody had patched was .ai-assist--solo, and it is why a new SUB-EVENT modal showed the
   describe-it box on top of the name field: the JS hid it correctly and the CSS put it back. Any
   element could be next, so the rule is stated once, here, with the !important that makes an
   attribute meaning "this is not on the page" actually mean it. The three patches are gone.

   What this does NOT cover is an element shown by assigning style.display, since an inline style
   loses to !important. Nothing does that today — the ids driven by style.display and the ids
   toggled through .hidden are disjoint sets — and the two ways of hiding something should not be
   mixed on one element anyway. Use the attribute. */
[hidden] { display: none !important; }

button, input[type=submit], .btn {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    color: #fff;
    border: none;
    border-radius: 4px;
    padding: 0.4rem 1rem;
    cursor: pointer;
    font-size: 0.875rem;
    font-weight: 500;
    font-family: inherit;
    text-decoration: none;
    /* default: secondary */
    background: var(--ink-muted);
}

[data-lucide] {
    width: 1em;
    height: 1em;
    flex-shrink: 0;
    stroke-width: 2;
}
button:hover, input[type=submit]:hover, .btn:hover { background: #3f3f46; color: #fff; }
button:disabled, .btn:disabled { opacity: 0.4; cursor: not-allowed; }
/* Anchors styled as buttons can't take :disabled, so link-buttons in a locked panel opt out
   of pointer events instead. Same look either way. */
.btn[aria-disabled="true"] { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
/* Inputs had no disabled state at all, which read as editable-but-unresponsive in locked panels. */
input:disabled, select:disabled, textarea:disabled {
    background: var(--surface-alt);
    color: var(--ink-subtle);
    cursor: not-allowed;
}

/* solid */
/* Two details here are load-bearing once the fill is not ours.

   `color` has to be stated on BOTH lines. Without it these inherit #fff from `button` and
   `button:hover` above, which is right for every colour we ship and wrong for a brand yellow. The
   rest state is fine at (0,1,0) over `button`'s (0,0,1) -- but `button:hover` is (0,1,1), which
   .btn-primary alone does NOT beat, so the hover rule has to restate it at (0,2,0).

   The border is --accent, not the fill. A pale fill has no edge against a white page -- #FFD700
   measures 1.40:1 and #FAFAFA 1.04:1, well under the 3:1 WCAG 1.4.11 asks of a control's boundary
   -- so the outline is what makes the button a button, and --accent is the one colour guaranteed to
   provide it (it clears 4.5:1 on white by construction). This is also what lets the fill stay the
   organizer's exact hex instead of being darkened until its own edge shows. For our emerald the two
   are the same colour and nothing changes. */
.btn-primary             { background: var(--brand-fill); border: 1px solid var(--brand-fill-edge, var(--accent)); color: var(--brand-on-fill); }
.btn-primary:hover       { background: var(--brand-fill-hover); color: var(--brand-on-fill); }

.btn-danger              { background: var(--danger); }
.btn-danger:hover        { background: var(--danger-hover); color: #fff; }

/* outline */
/* NOTE: there is deliberately no .btn-secondary. It was removed once its only remaining callers
   turned out to be the dark tool cards, where a muted-grey outline on a near-black card was
   effectively invisible. Use .btn-ghost for a low-emphasis action on light, or .btn-subtle
   on a dark surface. */
.btn-outline-primary     { background: transparent; color: var(--accent); border: 1px solid var(--accent); }
.btn-outline-primary:hover { background: var(--accent); color: #fff; }

.btn-sm { padding: 0.2rem 0.6rem; font-size: 0.8rem; }

/* filled, borderless — light grey, for low-emphasis actions.
   The fill is a token rather than --surface-alt directly, because these have no border and so are
   ENTIRELY the fill: dropped onto a container painted --surface-alt they vanish, which is what an
   .inset-panel and a .picker-row--set both do. Such a container re-points --ghost-fill one step
   darker and everything inside it reappears, with no change to the box (a border would move every
   icon button 2px and only where it happened to be nested). Grep --ghost-fill for the containers
   that do this. */
.btn-ghost { background: var(--ghost-fill); color: var(--ink-muted); border: none; }
.btn-ghost:hover { background: var(--ghost-fill-hover); color: var(--ink); }

/* ghost, but the icon/text picks up a contextual color on hover — deliberately no fill change,
   so the hover reads as "this one is dangerous" rather than as a second button state. */
.btn-ghost-primary { background: var(--ghost-fill); color: var(--ink-muted); border: none; }
.btn-ghost-primary:hover { background: var(--ghost-fill); color: var(--accent); }

.btn-ghost-danger { background: var(--ghost-fill); color: var(--ink-muted); border: none; }
.btn-ghost-danger:hover { background: var(--ghost-fill); color: var(--danger); }

/* icon-only sizing: equal padding instead of the default rectangular text padding */
.btn-icon { padding: 0.4rem; }


/* ── Inputs ───────────────────────────────────────────── */

input:not([type=submit]):not([type=checkbox]):not([type=radio]),
select,
textarea {
    padding: 0.4rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    font-family: inherit;
    font-size: 0.875rem;
    color: var(--ink);
    background: #fff;
    outline: none;
}
input:not([type=submit]):not([type=checkbox]):not([type=radio]):focus,
select:focus,
textarea:focus {
    border-color: var(--ink-faint);
}

/* A required field left empty on submit -- set/cleared by AjaxModal (public/js/app.js).
   The <input> selectors must repeat the base rule's three :not() clauses above. Each :not()
   counts its argument's specificity, so that base rule scores (0,3,1) and sets the `border`
   SHORTHAND -- a plain `input.has-error` (0,1,1) loses to it outright and the error border
   never appeared. select/textarea have no such guard and would be fine either way; they carry
   the same shape only so the three stay visibly parallel. */
input:not([type=submit]):not([type=checkbox]):not([type=radio]).has-error,
select.has-error,
textarea.has-error {
    border-color: var(--danger);
}
input:not([type=submit]):not([type=checkbox]):not([type=radio]).has-error:focus,
select.has-error:focus,
textarea.has-error:focus {
    border-color: var(--danger);
    box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.15);
}

/* Help text under a field. Lives here rather than with the modal rules because the
   auth forms use it too, and guest pages never load app.css.
   For prose that is NOT attached to a form field — a page's intro paragraph, an empty-state
   line — use .text-muted below; this one is for form help. */
.form-text {
    font-size: 0.8rem;
    color: var(--ink-subtle);
    margin-top: 0.35rem;
    width: 100%;
}

/* Quieter body prose: page intros, empty-state copy, secondary lines in a table cell.
   Colour only — it keeps the surrounding text size, which is what separates it from .form-text.
   Sits in base.css because guest and standalone pages (auth/signup-sent.php,
   email/unsubscribe.php) use it and never load app.css.
   NOTE: this class was used in ~30 places for a long time with no rule behind it at all, so
   everything wearing it rendered at full body ink. Do not remove it again. */
.text-muted {
    color: var(--ink-subtle);
}


/* ── Layout: Guest (centered card) ────────────────────── */

body.guest-layout {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
}

.guest-card {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 2.5rem 3rem;
    width: 100%;
    max-width: 420px;
    box-shadow: 0 2px 12px rgba(24,24,27,0.07);
}

/* The card's heading. The UA default (2em, 0.67em margins) is far too loud for a
   420px card, and every auth view relied on the guest-layout stylesheet to fix it
   — which meant the invitation and error pages, which load the same layout, got
   the raw UA size. Sized here so all of them agree. */
.guest-card h1 {
    margin: 0 0 0.5rem;
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: -0.01em;
}
.guest-card p { line-height: 1.6; }

/* The trailing line under a card's form — "Don't have an account? Sign up",
   "Back to sign in". Replaces the style="margin-top:1.25rem" that was repeated
   inline across nine auth views. Add .text-muted for the quieter variants. */
.guest-foot {
    margin-top: 1.25rem;
    margin-bottom: 0;
    font-size: 0.875rem;
}


/* Auth forms — semantic stacked fields (labels above full-width inputs). */
.auth-form { display: flex; flex-direction: column; gap: 1.15rem; }
.auth-form .form-field { display: flex; flex-direction: column; gap: 0.3rem; }
/* A hint sits in the same flex column as its label and control, so .form-text's own margin-top
   stacks on top of the 0.3rem gap and pushes it nearly twice as far from the input as the label is
   above it. The gap already does that spacing; the margin only made the pair look uneven. The
   0.15rem added to the gap above buys back the separation BETWEEN fields that this loses. */
.auth-form .form-text { margin-top: 0; }
.auth-form .form-field label {
    font-size: 0.82rem;
    font-weight: 500;
    color: var(--ink-muted);
}
/* Its own spacing, rather than relying on a space in whatever template wrote it -- some do, some
   don't, and one built in JavaScript has no incidental whitespace at all. */
.form-optional { margin-left: 0.35rem; }
.auth-form .form-field label .form-optional { color: var(--ink-faint); font-weight: 400; }
.auth-form input:not([type=checkbox]),
.auth-form select { width: 100%; }
/* A checkbox and its label on one line. Un-scoped from .auth-form because it is a general control
   -- the settings modals use it too, and a second copy of these five lines under another prefix is
   how a component quietly becomes two components that drift. */
.form-check {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.875rem;
    color: var(--ink-muted);
}
.form-check input { width: auto; }
.auth-form .form-actions {
    margin-top: 0.25rem;
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
}
/* Password field with a show/hide toggle. */
.password-input { position: relative; display: flex; }
.password-input input { padding-right: 2.5rem; }
.password-toggle {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    padding: 0 0.7rem;
    background: none;
    border: none;
    color: var(--ink-muted);
    cursor: pointer;
}
.password-toggle:hover { background: none; color: var(--ink); }
.password-toggle svg { width: 17px; height: 17px; }
/* Edge draws its own reveal eye inside password fields, on top of ours. There's no way to ask
   whether a browser has one, so ours is the only one, the same everywhere. */
.password-input input::-ms-reveal,
.password-input input::-ms-clear { display: none; }

/* Segmented code entry -- one box per character. Built by public/js/code-input.js around the
   real input, which becomes type=hidden and stays the submitted field. */
.code-input {
    display: grid;
    grid-template-columns: repeat(var(--code-len), minmax(0, 1fr));
    gap: 0.5rem;
}
.code-input input {
    min-width: 0;
    min-height: 2.75rem;
    padding: 0.5rem 0;
    text-align: center;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 1.15rem;
}
.code-input--mask input { -webkit-text-security: disc; }

.auth-link-sm { font-size: 0.875rem; }
.auth-error   { color: var(--danger); font-size: 0.875rem; }
.auth-success { color: var(--success); font-size: 0.875rem; }

/* Text/link button (no chrome) — e.g. "Resend code". */
.btn-link {
    background: none;
    border: none;
    padding: 0;
    color: var(--ink-muted);
    text-decoration: underline;
    cursor: pointer;
    font-size: 0.875rem;
    font-weight: 500;
}
.btn-link:hover { background: none; color: var(--accent); }


/* ── Honeypot (public form bot trap) ──────────────────────────────────────────
   The `_hp` field in views/partials/form-guard.php. Moved off-canvas rather than
   display:none or visibility:hidden — a bot that skips hidden inputs would never
   trip the trap, and this way it stays a real, focusable-by-nothing field. The
   wrapper carries aria-hidden and the input tabindex="-1", so assistive tech and
   keyboard users never reach it either. */

.form-guard {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}


/* ── Accessibility ────────────────────────────────────────────────────────────
   Keyboard focus, skip link, and reduced-motion support. Base rather than app so
   the marketing and auth pages keep their focus indicators (WCAG 2.4.7) and honour
   prefers-reduced-motion (WCAG 2.3.3). The dark-surface ring overrides for the
   sidebar and app footer stay in app.css — those elements only exist there. */

/* Visible focus ring for keyboard users only (:focus-visible), so mouse clicks
   don't show a ring but Tab navigation does. Emerald-700 reads 5.5:1 on the app's
   light surfaces. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible,
.btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* The main content wrapper is focused via the skip link; it's not an interactive
   target, so suppress the ring there. */
#main-content:focus-visible { outline: none; }

/* Skip-to-content link: off-screen until focused, then slides into view. */
.skip-link {
    position: fixed;
    top: 0.5rem;
    left: 0.5rem;
    z-index: 1100;
    background: var(--ink);
    color: #fff;
    padding: 0.5rem 0.9rem;
    border-radius: 6px;
    font-size: 0.875rem;
    font-weight: 500;
    transform: translateY(-160%);
    transition: transform 0.15s ease;
}
.skip-link:focus { transform: translateY(0); color: #fff; }

/* ── The missing course ──────────────────────────────────────────────────────
   views/partials/missing-course.php: the mark with its middle bar gone, on the
   app's 404 and a registrant's. Here because both of those stylesheets load
   this file, and one drawing animated two ways would drift.

   The fills are custom properties so a surface can repaint it without restating
   the shapes; register.css swaps them for its accent family.

   One authored moment: the two bars lay in from the right the way the landing
   page's courses do, and only then does the empty slot between them draw. With
   motion reduced, the drawing is simply the drawing. */
.gone {
    --gone-top:    #99cfbe;
    --gone-bottom: #059669;
    --gone-slot:   #56b597;
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
}
.gone-bar-1 { fill: var(--gone-top); }
.gone-bar-3 { fill: var(--gone-bottom); }
.gone-slot {
    fill: none;
    stroke: var(--gone-slot);
    stroke-width: 3;
    stroke-dasharray: 9 8;
    stroke-linecap: round;
    animation: gone-slot .7s ease-out .75s both;
}
.gone-bar { transform-box: fill-box; animation: gone-lay .9s cubic-bezier(0.16, 1, 0.3, 1) both; }
.gone-bar-3 { animation-delay: .12s; }
@keyframes gone-lay  { from { opacity: 0; transform: translateX(48px); } }
@keyframes gone-slot { from { opacity: 0; } }
/* The global rule below shortens these but keeps their delays, which would
   leave the slot missing for most of a second first. */
@media (prefers-reduced-motion: reduce) {
    .gone-bar, .gone-slot { animation: none; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}
