/* ==========================================================================
   Swiss Fleet Management — depth, motion and finish
   --------------------------------------------------------------------------
   Loaded AFTER site.css. This file adds nothing new to the vocabulary: it
   takes the classes site.css already defines and gives them elevation, hover
   behaviour, focus states and a little life. Because it only ever adds or
   overrides at equal specificity, removing this file leaves a working — if
   flatter — console behind.

   Two rules held throughout:
     · every colour resolves through a token, so the tenant accent and the
       light/dark switch keep working;
     · every motion is switched off under prefers-reduced-motion, and nothing
       here is load-bearing for comprehension.

   Contents
     01  Motion and elevation tokens
     02  Rich iconography
     03  Cards: lift, spotlight, accent hairline
     04  Stat tiles
     05  Tables
     06  Buttons
     07  Sidebar and topbar
     08  Forms
     09  Badges, chips, dots, meters
     10  Page header and rich text
     11  Toasts, alerts, empty states
     12  Timeline
     13  Skeletons
     14  Map surfaces
     15  Scroll reveal and entrance
     16  Focus, selection, scrollbars
     17  Motion preferences
   ========================================================================== */


/* ==========================================================================
   01  Motion and elevation tokens
   ========================================================================== */

:root {
    /* Easing. --ease-out for things arriving, --ease-spring for anything the
       pointer is directly pushing around. */
    --ease-out: cubic-bezier(.22, .61, .36, 1);
    --ease-spring: cubic-bezier(.34, 1.56, .64, 1);
    --ease-in-out: cubic-bezier(.65, .05, .36, 1);

    --dur-fast: 120ms;
    --dur-mid: 220ms;
    --dur-slow: 420ms;

    /* A deeper elevation scale than site.css carries, for hover states. */
    --lift-1: 0 2px 4px rgba(0, 20, 51, .06), 0 1px 2px rgba(0, 20, 51, .08);
    --lift-2: 0 10px 24px -12px rgba(0, 20, 51, .26), 0 2px 6px rgba(0, 20, 51, .06);
    --lift-3: 0 26px 56px -22px rgba(0, 20, 51, .34), 0 4px 12px rgba(0, 20, 51, .08);
    --lift-accent: 0 12px 28px -14px var(--c-accent-soft);

    /* The sheen that runs across a primary button on hover. */
    --sheen: linear-gradient(105deg,
        transparent 20%,
        rgba(255, 255, 255, .22) 42%,
        rgba(255, 255, 255, .34) 50%,
        rgba(255, 255, 255, .22) 58%,
        transparent 80%);
}

:root[data-theme='dark'] {
    --lift-1: 0 2px 4px rgba(0, 6, 18, .5), 0 1px 2px rgba(0, 6, 18, .6);
    --lift-2: 0 12px 26px -12px rgba(0, 6, 18, .78), 0 2px 6px rgba(0, 6, 18, .5);
    --lift-3: 0 28px 60px -22px rgba(0, 6, 18, .9), 0 4px 12px rgba(0, 6, 18, .6);
}


/* ==========================================================================
   02  Rich iconography

   A tinted, rounded container for a Bootstrap icon. Takes its colour from the
   tone vocabulary, so .sf-ico.tone-ok is green and .sf-ico.tone-danger is red
   with no extra rules.
   ========================================================================== */

.sf-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: 0 0 auto;
    border-radius: var(--radius);
    background: var(--tone-soft, var(--c-accent-soft));
    color: var(--tone, var(--c-accent-text));
    border: 1px solid transparent;
    font-size: .95rem;
    line-height: 1;
    transition:
        transform var(--dur-mid) var(--ease-spring),
        box-shadow var(--dur-mid) var(--ease-out),
        border-color var(--dur-mid) var(--ease-out);
}

.sf-ico--sm { width: 26px; height: 26px; font-size: .8rem; border-radius: var(--radius-sm); }
.sf-ico--lg { width: 46px; height: 46px; font-size: 1.25rem; border-radius: var(--radius-lg); }
.sf-ico--xl { width: 60px; height: 60px; font-size: 1.6rem; border-radius: var(--radius-lg); }

.sf-ico--round { border-radius: var(--radius-pill); }

/* Solid variant: full tone behind an inverted glyph. */
.sf-ico--solid {
    background: var(--tone, var(--c-accent));
    color: var(--c-text-invert);
}

/* Outlined variant for quieter rows. */
.sf-ico--outline {
    background: transparent;
    border-color: var(--c-border);
    color: var(--tone, var(--c-text-dim));
}

/* A ring that softly breathes — reserved for something genuinely live. */
.sf-ico--live::after {
    content: '';
    position: absolute;
    inset: -4px;
    border-radius: inherit;
    border: 1px solid var(--tone, var(--c-accent));
    opacity: .45;
    animation: sf-ring 2.4s var(--ease-in-out) infinite;
}

.sf-ico--live { position: relative; }

@keyframes sf-ring {
    0%   { transform: scale(.92); opacity: .5; }
    70%  { transform: scale(1.12); opacity: 0; }
    100% { transform: scale(1.12); opacity: 0; }
}

/* Icons lift with the row or card that owns them. */
.sf-card:hover .sf-ico,
.sf-tile:hover .sf-ico,
.sf-table tbody tr:hover .sf-ico {
    transform: translateY(-1px) scale(1.04);
    box-shadow: var(--lift-1);
}

/* Bootstrap Icons sit a hair high inside buttons and links. */
.sf-btn > .bi,
.sf-nav__link > .bi,
.sf-link > .bi { vertical-align: -.08em; }


/* ==========================================================================
   03  Cards: lift, spotlight, accent hairline
   ========================================================================== */

.sf-card {
    position: relative;
    isolation: isolate;
    transition:
        transform var(--dur-mid) var(--ease-out),
        box-shadow var(--dur-mid) var(--ease-out),
        border-color var(--dur-mid) var(--ease-out);
}

/* A hairline of accent across the top edge, drawn on hover. Scale rather than
   opacity so it wipes outward from the centre. */
.sf-card::before {
    content: '';
    position: absolute;
    inset: 0 0 auto 0;
    height: 2px;
    border-radius: var(--radius) var(--radius) 0 0;
    background: linear-gradient(90deg,
        transparent,
        var(--tone, var(--c-accent)) 22%,
        var(--tone, var(--c-accent)) 78%,
        transparent);
    opacity: 0;
    transform: scaleX(.4);
    transition:
        opacity var(--dur-mid) var(--ease-out),
        transform var(--dur-slow) var(--ease-out);
    pointer-events: none;
    z-index: 2;
}

.sf-card:hover {
    box-shadow: var(--lift-2);
    border-color: var(--c-border-strong);
}

.sf-card:hover::before { opacity: .9; transform: scaleX(1); }

/* A card that IS a link earns the full lift. Deliberately not
   `.sf-card:has(a:hover)`: most list pages are a table of links inside a card,
   and lifting the whole card as the pointer crosses a row would make every
   table twitch. */
a.sf-card:hover,
a > .sf-card:hover { transform: translateY(-2px); box-shadow: var(--lift-3); }

/* Pointer spotlight. effects.js writes --mx / --my as percentages; with no JS
   the gradient simply sits in the middle and stays invisible at 0 opacity. */
.sf-card::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: radial-gradient(
        420px circle at var(--mx, 50%) var(--my, 50%),
        var(--c-hover),
        transparent 60%);
    opacity: 0;
    transition: opacity var(--dur-slow) var(--ease-out);
    pointer-events: none;
    z-index: 0;
}

.sf-card:hover::after { opacity: .7; }

/* Keep real content above the spotlight. */
.sf-card__header,
.sf-card__body,
.sf-card__footer { position: relative; z-index: 1; }

.sf-card__title { letter-spacing: -.01em; }

/* The muted card is informational, not interactive: no lift, no spotlight. */
.sf-card--muted:hover { transform: none; box-shadow: var(--shadow-1); }
.sf-card--muted::after { content: none; }


/* ==========================================================================
   04  Stat tiles
   ========================================================================== */

.sf-tile {
    position: relative;
    overflow: hidden;
    transition:
        transform var(--dur-mid) var(--ease-out),
        box-shadow var(--dur-mid) var(--ease-out),
        border-color var(--dur-mid) var(--ease-out);
}

/* Tone bar down the left, widening on hover. */
.sf-tile::before {
    content: '';
    position: absolute;
    inset: 0 auto 0 0;
    width: 3px;
    background: var(--tone, var(--c-accent));
    opacity: .7;
    transition: width var(--dur-mid) var(--ease-out), opacity var(--dur-mid) var(--ease-out);
}

.sf-tile:hover {
    transform: translateY(-3px);
    box-shadow: var(--lift-2);
    border-color: var(--c-border-strong);
}

.sf-tile:hover::before { width: 5px; opacity: 1; }

/* A very soft wash of the tone in the far corner. */
.sf-tile::after {
    content: '';
    position: absolute;
    right: -30px;
    top: -30px;
    width: 120px;
    height: 120px;
    border-radius: 50%;
    background: var(--tone-soft, var(--c-accent-soft));
    opacity: 0;
    transition: opacity var(--dur-slow) var(--ease-out);
    pointer-events: none;
}

.sf-tile:hover::after { opacity: .55; }

.sf-tile__label,
.sf-tile__value,
.sf-tile__meta,
.sf-tile__delta { position: relative; z-index: 1; }

.sf-tile__value {
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
    transition: color var(--dur-mid) var(--ease-out);
}

.sf-tile:hover .sf-tile__value { color: var(--tone, var(--c-text)); }

/* Counting up looks wrong mid-flight if the width jumps around. */
[data-sf-count] { font-variant-numeric: tabular-nums; }

/* Every stat tile already labels itself with a Bootstrap icon. Promoting that
   bare glyph to a tinted chip is the single change that makes the whole
   console read as designed rather than assembled, and it costs no markup:
   the label is already a flex row with a gap. */
.sf-tile__label i {
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    margin-right: 1px;
    border-radius: var(--radius-sm);
    border: 1px solid color-mix(in srgb, var(--tone, var(--c-accent)) 26%, transparent);
    background: var(--tone-soft, var(--c-accent-soft));
    color: var(--tone, var(--c-accent));
    font-size: 12px;
    transition:
        transform var(--dur-mid) var(--ease-spring),
        background var(--dur-mid) var(--ease-out),
        border-color var(--dur-mid) var(--ease-out);
}

.sf-tile:hover .sf-tile__label i {
    transform: translateY(-1px) scale(1.06);
    border-color: color-mix(in srgb, var(--tone, var(--c-accent)) 52%, transparent);
}


/* ==========================================================================
   05  Tables
   ========================================================================== */

.sf-table tbody tr {
    transition:
        background-color var(--dur-fast) var(--ease-out),
        box-shadow var(--dur-fast) var(--ease-out);
}

.sf-table tbody tr:hover {
    background: var(--c-hover);
    box-shadow: inset 0 0 0 1px var(--c-border-faint);
}

/* The accent row already carries an inset bar; thicken it on hover. */
.sf-table__row-accent { transition: box-shadow var(--dur-fast) var(--ease-out); }
.sf-table__row-accent:hover { box-shadow: inset 4px 0 0 var(--tone, var(--c-accent)), inset 0 0 0 1px var(--c-border-faint); }

/* The sticky head and its opaque background are site.css's job; nothing to add
   here but a containing block for anything positioned inside the wrap. */
.sf-table-wrap { position: relative; }

.sf-table__link {
    position: relative;
    transition: color var(--dur-fast) var(--ease-out);
}

/* Underline that grows from the left rather than appearing all at once. */
.sf-table__link::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -2px;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--dur-mid) var(--ease-out);
}

.sf-table tbody tr:hover .sf-table__link::after,
.sf-table__link:hover::after { transform: scaleX(1); }

.sf-table tbody tr:hover .sf-table__link { color: var(--c-accent-text); }

/* Row actions stay out of the way until the row is under the pointer. */
.sf-table__actions { transition: opacity var(--dur-fast) var(--ease-out); }

@media (hover: hover) and (min-width: 900px) {
    .sf-table tbody tr .sf-table__actions { opacity: .45; }
    .sf-table tbody tr:hover .sf-table__actions,
    .sf-table tbody tr:focus-within .sf-table__actions { opacity: 1; }
}

.sf-num { font-variant-numeric: tabular-nums; }


/* ==========================================================================
   06  Buttons
   ========================================================================== */

.sf-btn {
    position: relative;
    overflow: hidden;
    transition:
        transform var(--dur-fast) var(--ease-out),
        box-shadow var(--dur-mid) var(--ease-out),
        background-color var(--dur-fast) var(--ease-out),
        border-color var(--dur-fast) var(--ease-out),
        color var(--dur-fast) var(--ease-out);
}

.sf-btn:hover { transform: translateY(-1px); }
.sf-btn:active { transform: translateY(0) scale(.985); transition-duration: 60ms; }

.sf-btn--primary { box-shadow: var(--lift-1); }
.sf-btn--primary:hover { box-shadow: var(--lift-2), var(--lift-accent); }

/* The sheen. A single pass on hover, not a loop — a button that shimmers
   forever is a button nobody trusts. */
.sf-btn--primary::before,
.sf-btn--danger::before,
.sf-btn--success::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--sheen);
    transform: translateX(-120%);
    transition: transform 620ms var(--ease-out);
    pointer-events: none;
}

.sf-btn--primary:hover::before,
.sf-btn--danger:hover::before,
.sf-btn--success:hover::before { transform: translateX(120%); }

/* Ripple origin written by effects.js. */
.sf-btn .sf-ripple {
    position: absolute;
    border-radius: 50%;
    transform: scale(0);
    background: currentColor;
    opacity: .22;
    pointer-events: none;
    animation: sf-ripple 560ms var(--ease-out) forwards;
}

@keyframes sf-ripple {
    to { transform: scale(2.6); opacity: 0; }
}

/* The leading icon nudges forward on hover — small, but it makes a button
   feel like it is going somewhere. */
.sf-btn > .bi { transition: transform var(--dur-mid) var(--ease-spring); }
.sf-btn:hover > .bi:first-child { transform: translateX(-1px); }
.sf-btn--block:hover > .bi:first-child { transform: translateX(2px); }

.sf-icon-btn {
    transition:
        transform var(--dur-fast) var(--ease-spring),
        background-color var(--dur-fast) var(--ease-out),
        color var(--dur-fast) var(--ease-out);
}

.sf-icon-btn:hover { transform: translateY(-1px); }
.sf-icon-btn:active { transform: scale(.94); }

/* Busy state: site.js swaps the label, this keeps the button from feeling dead. */
.sf-btn[aria-busy='true'] { pointer-events: none; opacity: .85; }
.sf-btn[aria-busy='true']::after {
    content: '';
    position: absolute;
    inset: auto 0 0 0;
    height: 2px;
    background: linear-gradient(90deg, transparent, currentColor, transparent);
    animation: sf-indeterminate 1.1s var(--ease-in-out) infinite;
}

@keyframes sf-indeterminate {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(100%); }
}


/* ==========================================================================
   07  Sidebar and topbar
   ========================================================================== */

.sf-nav__link {
    position: relative;
    transition:
        background-color var(--dur-fast) var(--ease-out),
        color var(--dur-fast) var(--ease-out),
        padding-left var(--dur-mid) var(--ease-out);
}

/* Active marker slides in from the left edge. */
.sf-nav__link::before {
    content: '';
    position: absolute;
    left: 0;
    top: 50%;
    height: 0;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: var(--c-accent);
    transform: translateY(-50%);
    transition: height var(--dur-mid) var(--ease-spring);
}

.sf-nav__link:hover::before { height: 40%; }
.sf-nav__link.is-active::before,
.sf-nav__link[aria-current='page']::before { height: 62%; }

.sf-nav__link:hover { padding-left: calc(var(--space-3) + 3px); }

/* Collapsed, the link is an icon centred in 60px: site.css zeroes the inline
   padding and the nudge above would shove the glyph off centre. */
.sf-shell.is-collapsed .sf-nav__link:hover { padding-left: 0; }

.sf-nav__link > .bi {
    transition: transform var(--dur-mid) var(--ease-spring), color var(--dur-fast) var(--ease-out);
}

.sf-nav__link:hover > .bi { transform: scale(1.14); color: var(--c-accent); }

.sf-nav__count {
    transition: transform var(--dur-mid) var(--ease-spring);
}

.sf-nav__link:hover .sf-nav__count { transform: scale(1.08); }

/* Group headings fade up slightly as the sidebar opens on mobile. */
.sf-nav__heading { transition: color var(--dur-mid) var(--ease-out); }

.sf-brand {
    transition: transform var(--dur-mid) var(--ease-spring);
}

.sf-brand:hover { transform: translateY(-1px); }

.sf-brand__mark {
    transition:
        transform var(--dur-slow) var(--ease-spring),
        box-shadow var(--dur-mid) var(--ease-out);
}

.sf-brand:hover .sf-brand__mark {
    transform: rotate(-8deg) scale(1.06);
    box-shadow: var(--lift-2);
}

.sf-topbar {
    transition: box-shadow var(--dur-mid) var(--ease-out);
}

/* Shadow appears once the page is scrolled; effects.js sets the attribute. */
.sf-topbar[data-scrolled='true'] { box-shadow: var(--lift-2); }

.sf-bell { transition: transform var(--dur-fast) var(--ease-spring); }
.sf-bell:hover { transform: scale(1.06); }

/* An arriving alert rings the bell once. */
.sf-bell.is-ringing > .bi { animation: sf-ring-bell 820ms var(--ease-in-out); transform-origin: top center; }

@keyframes sf-ring-bell {
    0%, 100% { transform: rotate(0); }
    15% { transform: rotate(14deg); }
    30% { transform: rotate(-12deg); }
    45% { transform: rotate(9deg); }
    60% { transform: rotate(-6deg); }
    75% { transform: rotate(3deg); }
}

.sf-bell__count { transition: transform var(--dur-mid) var(--ease-spring); }
.sf-bell.is-ringing .sf-bell__count { transform: scale(1.25); }

.sf-user { transition: background-color var(--dur-fast) var(--ease-out); }
.sf-user__caret { transition: transform var(--dur-mid) var(--ease-out); }
.sf-user[aria-expanded='true'] .sf-user__caret { transform: rotate(180deg); }

/* The live-connection dot actually breathes while connected. */
.sf-conn__dot { transition: background-color var(--dur-mid) var(--ease-out); }
.sf-conn[data-state='connected'] .sf-conn__dot { animation: sf-breathe 2.6s var(--ease-in-out) infinite; }

@keyframes sf-breathe {
    0%, 100% { box-shadow: 0 0 0 0 var(--c-green-soft); }
    50%      { box-shadow: 0 0 0 5px transparent; }
}


/* ==========================================================================
   08  Forms
   ========================================================================== */

.sf-input,
.sf-select,
textarea.sf-input {
    transition:
        border-color var(--dur-fast) var(--ease-out),
        box-shadow var(--dur-fast) var(--ease-out),
        background-color var(--dur-fast) var(--ease-out);
}

.sf-input:hover:not(:disabled),
.sf-select:hover:not(:disabled) { border-color: var(--c-border-strong); }

.sf-input:focus,
.sf-select:focus,
textarea.sf-input:focus {
    box-shadow: 0 0 0 3px var(--c-accent-soft), var(--lift-1);
}

/* The leading icon takes the accent while the field has focus. */
.sf-input-group { position: relative; }

.sf-input-group__icon {
    transition: color var(--dur-fast) var(--ease-out), transform var(--dur-mid) var(--ease-spring);
}

.sf-input-group:focus-within .sf-input-group__icon {
    color: var(--c-accent-text);
    transform: scale(1.08);
}

.sf-input-group__button { transition: color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-spring); }
.sf-input-group__button:hover { color: var(--c-accent-text); transform: scale(1.1); }

.sf-label { transition: color var(--dur-fast) var(--ease-out); }
.sf-field:focus-within .sf-label { color: var(--c-accent-text); }

.sf-check__input,
.sf-switch__input { transition: transform var(--dur-fast) var(--ease-spring); }
.sf-check__label:hover .sf-check__input,
.sf-check:hover .sf-check__input { transform: scale(1.06); }

.sf-switch__track { transition: background-color var(--dur-mid) var(--ease-out); }

/* A validation message should arrive, not blink into place. */
.sf-error:not(:empty) {
    display: block;
    animation: sf-slide-down var(--dur-mid) var(--ease-out);
}

@keyframes sf-slide-down {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}


/* ==========================================================================
   09  Badges, chips, dots, meters
   ========================================================================== */

.sf-badge {
    transition: transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-fast) var(--ease-out);
}

.sf-table tbody tr:hover .sf-badge,
.sf-card:hover .sf-badge { transform: translateY(-1px); }

.sf-badge--solid { box-shadow: 0 1px 2px rgba(0, 20, 51, .18); }

.sf-chip {
    transition:
        transform var(--dur-fast) var(--ease-spring),
        background-color var(--dur-fast) var(--ease-out),
        border-color var(--dur-fast) var(--ease-out);
}

.sf-chip:hover { transform: translateY(-1px); border-color: var(--c-border-strong); }
.sf-chip__x { transition: transform var(--dur-fast) var(--ease-spring), color var(--dur-fast) var(--ease-out); }
.sf-chip__x:hover { transform: rotate(90deg); color: var(--c-red); }

.sf-dot--pulse { animation: sf-pulse 2s var(--ease-in-out) infinite; }

@keyframes sf-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: .55; transform: scale(.88); }
}

.sf-meter__fill {
    transition: width var(--dur-slow) var(--ease-out);
    position: relative;
    overflow: hidden;
}

/* A slow diagonal texture so a filling bar reads as active. */
.sf-meter__fill::after {
    content: '';
    position: absolute;
    inset: 0;
    background-image: linear-gradient(
        45deg,
        rgba(255, 255, 255, .16) 25%, transparent 25%,
        transparent 50%, rgba(255, 255, 255, .16) 50%,
        rgba(255, 255, 255, .16) 75%, transparent 75%);
    background-size: 14px 14px;
    opacity: 0;
    transition: opacity var(--dur-mid) var(--ease-out);
}

.sf-meter:hover .sf-meter__fill::after { opacity: 1; animation: sf-stripes .9s linear infinite; }

@keyframes sf-stripes {
    from { background-position: 0 0; }
    to   { background-position: 28px 0; }
}


/* ==========================================================================
   10  Page header and rich text
   ========================================================================== */

.sf-page-header { position: relative; }

.sf-page-title {
    letter-spacing: -.022em;
    background: linear-gradient(180deg, var(--c-text) 40%, color-mix(in srgb, var(--c-text) 76%, var(--c-accent)) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

/* color-mix is well supported, but a browser without it must not render an
   invisible heading. */
@supports not (background: color-mix(in srgb, red 50%, blue)) {
    .sf-page-title { background: none; color: var(--c-text); }
}

.sf-page-subtitle { max-width: 76ch; }

/* The 2px rule under a page title, from the brochure. */
.sf-page-header__text::after {
    content: '';
    display: block;
    width: 44px;
    height: 2px;
    margin-top: var(--space-3);
    border-radius: 2px;
    background: linear-gradient(90deg, var(--c-accent), transparent);
    transition: width var(--dur-slow) var(--ease-out);
}

.sf-page-header:hover .sf-page-header__text::after { width: 84px; }

/* An eyebrow above a heading: small, letterspaced, upper. */
.sf-eyebrow {
    display: inline-block;
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--c-text-faint);
    margin-bottom: var(--space-2);
}

/* Long-form copy: the pages that explain rather than list. */
.sf-prose { max-width: 72ch; color: var(--c-text-dim); line-height: 1.7; }
.sf-prose > * + * { margin-top: var(--space-4); }
.sf-prose h2, .sf-prose h3 { color: var(--c-text); letter-spacing: -.012em; }
.sf-prose strong { color: var(--c-text); font-weight: 600; }
.sf-prose code { background: var(--c-surface-2); padding: .12em .38em; border-radius: var(--radius-sm); }

.sf-prose blockquote {
    margin: 0;
    padding: var(--space-3) var(--space-5);
    border-left: 3px solid var(--c-accent);
    background: var(--c-surface-2);
    border-radius: 0 var(--radius) var(--radius) 0;
    color: var(--c-text);
}

/* A lead paragraph, for the top of an explanatory screen. */
.sf-lead { font-size: 1.05rem; line-height: 1.6; color: var(--c-text-dim); max-width: 68ch; }

.sf-link {
    position: relative;
    text-decoration: none;
    background-image: linear-gradient(currentColor, currentColor);
    background-size: 0 1px;
    background-position: 0 100%;
    background-repeat: no-repeat;
    transition: background-size var(--dur-mid) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

.sf-link:hover { background-size: 100% 1px; }


/* ==========================================================================
   11  Toasts, alerts, empty states
   ========================================================================== */

.sf-toast {
    animation: sf-toast-in var(--dur-slow) var(--ease-spring);
    box-shadow: var(--lift-3);
    transition: transform var(--dur-mid) var(--ease-out), opacity var(--dur-mid) var(--ease-out);
}

.sf-toast:hover { transform: translateX(-3px); }
.sf-toast.is-leaving { opacity: 0; transform: translateX(22px); }

@keyframes sf-toast-in {
    from { opacity: 0; transform: translateX(28px) scale(.98); }
    to   { opacity: 1; transform: translateX(0) scale(1); }
}

.sf-toast__close { transition: transform var(--dur-fast) var(--ease-spring), opacity var(--dur-fast) var(--ease-out); }
.sf-toast__close:hover { transform: rotate(90deg); opacity: 1; }

.sf-alert {
    position: relative;
    overflow: hidden;
    animation: sf-slide-down var(--dur-mid) var(--ease-out);
}

.sf-alert__icon { transition: transform var(--dur-mid) var(--ease-spring); }
.sf-alert:hover .sf-alert__icon { transform: scale(1.08); }

.sf-empty__icon {
    animation: sf-float 5.5s var(--ease-in-out) infinite;
    transition: transform var(--dur-mid) var(--ease-spring);
}

@keyframes sf-float {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-5px); }
}

.sf-empty__title { letter-spacing: -.012em; }


/* ==========================================================================
   12  Timeline
   ========================================================================== */

.sf-timeline__item { transition: background-color var(--dur-fast) var(--ease-out); }
.sf-timeline__item:hover { background: var(--c-hover); }

/* The node on the spine grows when its entry is hovered. */
.sf-timeline__item::before { transition: transform var(--dur-mid) var(--ease-spring), box-shadow var(--dur-mid) var(--ease-out); }
.sf-timeline__item:hover::before { transform: scale(1.35); box-shadow: 0 0 0 4px var(--c-accent-soft); }


/* ==========================================================================
   13  Skeletons
   ========================================================================== */

.sf-skeleton {
    position: relative;
    overflow: hidden;
}

.sf-skeleton::after {
    content: '';
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, var(--c-hover), transparent);
    animation: sf-shimmer 1.5s var(--ease-in-out) infinite;
}

@keyframes sf-shimmer {
    100% { transform: translateX(100%); }
}


/* ==========================================================================
   14  Map surfaces
   ========================================================================== */

.sf-map-panel {
    box-shadow: var(--lift-3);
    transition: transform var(--dur-mid) var(--ease-out), box-shadow var(--dur-mid) var(--ease-out);
}

.sf-asset-row {
    position: relative;
    transition: background-color var(--dur-fast) var(--ease-out), padding-left var(--dur-mid) var(--ease-out);
}

.sf-asset-row::before {
    content: '';
    position: absolute;
    left: 0;
    top: 6px;
    bottom: 6px;
    width: 0;
    border-radius: 0 2px 2px 0;
    background: var(--tone, var(--c-accent));
    transition: width var(--dur-mid) var(--ease-out);
}

.sf-asset-row:hover::before,
.sf-asset-row.is-selected::before { width: 3px; }
.sf-asset-row:hover { background: var(--c-hover); padding-left: calc(var(--space-3) + 3px); }

.sf-marker { transition: transform var(--dur-mid) var(--ease-spring); }
.sf-marker:hover { transform: scale(1.18); z-index: 500; }

.sf-replay__range { accent-color: var(--c-accent); }


/* ==========================================================================
   15  Scroll reveal and entrance

   effects.js adds .sf-reveal to the blocks it watches and flips .is-in when
   they enter the viewport. Without JS nothing is ever hidden: the base state
   here is only applied once JS has marked the document as capable.
   ========================================================================== */

html.sf-js .sf-reveal {
    opacity: 0;
    transform: translateY(10px);
    transition:
        opacity var(--dur-slow) var(--ease-out),
        transform var(--dur-slow) var(--ease-out);
}

html.sf-js .sf-reveal.is-in { opacity: 1; transform: none; }

/* Tiles in a row arrive one after another rather than all together. */
html.sf-js .sf-tiles > .sf-reveal:nth-child(1) { transition-delay: 0ms; }
html.sf-js .sf-tiles > .sf-reveal:nth-child(2) { transition-delay: 55ms; }
html.sf-js .sf-tiles > .sf-reveal:nth-child(3) { transition-delay: 110ms; }
html.sf-js .sf-tiles > .sf-reveal:nth-child(4) { transition-delay: 165ms; }
html.sf-js .sf-tiles > .sf-reveal:nth-child(5) { transition-delay: 220ms; }
html.sf-js .sf-tiles > .sf-reveal:nth-child(6) { transition-delay: 275ms; }

/* The page settles in as a whole. Anything the observer has taken over is
   excluded: an animation's filled opacity outranks a normal declaration, so
   leaving both on one element would cancel the reveal. */
.sf-main > *:not(.sf-reveal) { animation: sf-page-in var(--dur-slow) var(--ease-out) both; }

@keyframes sf-page-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}


/* ==========================================================================
   16  Focus, selection, scrollbars
   ========================================================================== */

:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

.sf-btn:focus-visible,
.sf-nav__link:focus-visible,
.sf-icon-btn:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: 3px;
    box-shadow: 0 0 0 5px var(--c-accent-soft);
}

::selection { background: var(--c-accent-soft); color: var(--c-text); }

* { scrollbar-color: var(--c-border-strong) transparent; }

::-webkit-scrollbar-thumb {
    transition: background-color var(--dur-mid) var(--ease-out);
}

::-webkit-scrollbar-thumb:hover { background-color: var(--c-text-faint); }


/* ==========================================================================
   17  Motion preferences

   Everything above is decoration. Someone who has asked for less motion gets
   the console with none of it, and loses no information doing so.
   ========================================================================== */

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

    html.sf-js .sf-reveal { opacity: 1; transform: none; }

    .sf-btn:hover,
    .sf-tile:hover,
    .sf-card:hover,
    .sf-icon-btn:hover,
    .sf-brand:hover .sf-brand__mark { transform: none; }

    .sf-empty__icon,
    .sf-dot--pulse,
    .sf-conn[data-state='connected'] .sf-conn__dot { animation: none; }
}
