/* Self-hosted fonts (R14) — @fontsource ships the .woff2 locally; Vite bundles
   them and serves them from our own origin. No Google Fonts / CDN request. */
@import '@fontsource/ibm-plex-sans/400.css';
@import '@fontsource/ibm-plex-sans/500.css';
@import '@fontsource/ibm-plex-sans/600.css';
@import '@fontsource/ibm-plex-sans/700.css';
@import '@fontsource/ibm-plex-mono/400.css';
@import '@fontsource/ibm-plex-mono/500.css';
@import '@fontsource/ibm-plex-mono/600.css';
/* No serif: a serif headline made every page read as a document, which is
   exactly the impression the redesign removes. One family, used assertively. */

/* Design tokens, then Tailwind v3 layers. */
@import './portal-tokens.css';
@import './admin-tokens.css';

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
    html {
        font-family: var(--font-sans);
        -webkit-font-smoothing: antialiased;
        text-rendering: optimizeLegibility;
        /* Stops iOS inflating body text when the phone is turned to landscape. */
        -webkit-text-size-adjust: 100%;
    }

    body {
        background: var(--bg);
        color: var(--text);
        font-size: var(--text-base);
        line-height: var(--lh-normal);
    }

    /* Uniform, visible focus for every interactive element (a11y §6.4/§9). */
    :focus-visible {
        outline: none;
        box-shadow: var(--focus-ring);
        border-color: var(--accent);
    }

    /* Data/IDs/IPs use tabular figures. */
    .font-mono {
        font-variant-numeric: tabular-nums;
    }

    /* Alpine: hide elements until initialised. */
    [x-cloak] {
        display: none !important;
    }
}

/* iOS zooms the whole page when a field smaller than 16px takes focus, and
 * then leaves the page zoomed — every tap into a form throws the layout about.
 *
 * There is no way to decline it: the meta-viewport switch that used to suppress
 * it also kills pinch-zoom, which takes a genuine accessibility feature away
 * from everyone to fix a cosmetic problem for some. Making the fields 16px on
 * touch devices removes the reason instead.
 *
 * !important, and deliberately: this has to beat every text-size utility on
 * every field in the application, and a rule that loses to `text-sm` on one
 * form is worth nothing. Checkboxes and radios are excluded — they have no text
 * to inflate and sizing them here would break their box.
 */
@media (pointer: coarse) {
    input:not([type='checkbox']):not([type='radio']),
    select,
    textarea {
        font-size: 16px !important;
    }
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        transition-duration: 0.001ms !important;
    }
}

/* ── The public website ───────────────────────────────────────────────────
 * The marketing page draws from the same tokens as the portal and the console
 * and differs only in rhythm. These four rules are what Tailwind cannot say:
 * a state class toggled by an observer, and the disclosure marker.
 */
.rv {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 0.55s var(--ease), transform 0.55s var(--ease);
}
.rv.in { opacity: 1; transform: none; }
/* Staggered siblings — the delay belongs to the element, not to the observer,
   so a card that scrolls in alone still arrives on its own schedule. Kept
   short: a row of three cards that takes a second to finish assembling reads
   as a slow page, not as a considered one. */
.rv.d1 { transition-delay: 0.07s; }
.rv.d2 { transition-delay: 0.14s; }

.logline { opacity: 0; transform: translateX(-10px); transition: opacity 0.45s var(--ease), transform 0.45s var(--ease); }
.logline.in { opacity: 1; transform: none; }

/* The FAQ disclosure. Safari needs the -webkit- selector as well, and without
   `list-style: none` Firefox keeps drawing its own triangle beside ours. */
.faq summary { list-style: none; cursor: pointer; }
.faq summary::-webkit-details-marker { display: none; }

@media (prefers-reduced-motion: reduce) {
    .rv, .logline { opacity: 1 !important; transform: none !important; }
}

/* ── Metric visuals ───────────────────────────────────────────────────────
 * The ring and the sparkline from the approved template. They live here
 * rather than as utilities because both need keyframes and a per-element
 * custom property, and Tailwind expresses neither.
 *
 * Every var() carries a fallback: an undefined custom property does not make
 * a browser skip the declaration, it computes to `unset` — which for a
 * stroke-dashoffset silently draws a full ring at every value.
 */
.metric-ring { transform: rotate(-90deg); }
.metric-ring .track { fill: none; stroke: var(--surface-hover); stroke-width: 7; }
.metric-ring .value {
    fill: none;
    stroke: var(--accent);
    stroke-width: 7;
    stroke-linecap: round;
    stroke-dasharray: var(--c, 157);
    stroke-dashoffset: var(--c, 157);
    animation: ring-sweep 1.1s cubic-bezier(.2, .7, .2, 1) .2s forwards;
}
@keyframes ring-sweep { to { stroke-dashoffset: var(--o, 0); } }

/* `fill: none` gehört an die LINIE, nicht an jeden Pfad: eine CSS-Regel schlägt
   ein Präsentationsattribut, und `.spark path { fill: none }` machte damit das
   fill="url(#…)" der Fläche wirkungslos — die Füllung verschwand still. */
.spark path { stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.spark .line {
    fill: none;
    stroke: var(--accent);
    stroke-dasharray: 400;
    stroke-dashoffset: 400;
    animation: spark-draw 1.3s cubic-bezier(.4, .1, .2, 1) .2s forwards;
}
.spark.muted .line { stroke: var(--text-muted); }
/* Die Farbe kommt jetzt über currentColor in den Verlauf; `fill` setzt das
   Bauteil selbst auf url(#…). Bleibt hier nur das Einblenden. */
.spark { color: var(--accent); }
.spark.muted { color: var(--text-muted); }
.spark .area { stroke: none; opacity: 0; animation: spark-fade .8s ease .9s forwards; }
@keyframes spark-draw { to { stroke-dashoffset: 0; } }
@keyframes spark-fade { to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
    .metric-ring .value { stroke-dashoffset: var(--o, 0) !important; }
    .spark .line { stroke-dashoffset: 0 !important; }
    .spark .area { opacity: 1 !important; }
}
