/*
 * Platform root for logiaura.com — the discovery layer of ZZZ-zadanie/10. Static by design: no PHP,
 * no database, no session, no sign-in. One script and one only: /theme.js, which stamps the reader's
 * chosen theme on <html> before the first paint. The Content-Security-Policy in .htaccess admits that
 * one file and still denies everything else. Anything beyond it — a form, an analytics tag, a widget —
 * is out of scope by rule, not by oversight: the interested party writes to the address in the footer.
 *
 * Palette and type stack come from the applications (laravel/resources/css/app.css, PLT-037) so the
 * platform is visibly one brand. The mark is polychrome, so everything else stays quiet and it remains
 * the only saturated element. The wash behind the hero is the mark's own hues, measured off
 * brand/logo-512.png rather than eyeballed, at a tenth of their strength.
 *
 * The theme has three states, exactly as the applications do it: light is the default, the dark block
 * follows the operating system unless the reader asked for light, and data-theme on <html> overrides
 * both. There the attribute is written by the server from a cookie; here there is no server, so
 * /theme.js writes it from localStorage.
 */

/* --- tokens ------------------------------------------------------------- */
/*
 * Surface and ink keep the applications' names so the family reads as one system. Two tokens are new.
 *
 * --on-brand is the label on a brand-coloured fill. It has to follow the THEME, not the operating
 * system: keyed on the media query alone, a reader who forced light under a dark OS got #0b1220 on
 * #0b5cad — 2.8:1, on the page's primary call to action.
 *
 * --focus was #f59e0b in both schemes. Measured against the light surface that is 2.00:1, and WCAG 2.2
 * asks for at least 3:1 from a focus indicator, so the light value is now a darker amber at 4.68:1.
 * The dark value goes to #fbbf24, the same one the applications use, at 11.22:1.
 */
:root {
    --surface: #f6f7f9;
    --panel: #ffffff;
    --ink: #0f172a;
    --muted: #475569;
    --line: #d5dae2;
    --brand: #0b5cad;
    --brand-strong: #08427c;
    --on-brand: #ffffff;
    --focus: #b45309;
    --focus-edge: #0f172a;
    --shadow: 15 23 42;

    /* the mark, measured off brand/logo-512.png — the violet arm was missing from the wash until now */
    --hue-red: #fc3116;
    --hue-yellow: #fad305;
    --hue-green: #26f841;
    --hue-cyan: #00d6f0;
    --hue-blue: #0666fc;
    --hue-violet: #9604fc;
    --wash: 0.10;
    --edge: 0.9;

    /* space, radius, type: a scale, so a later change is one edit and not a search for literals */
    --s1: 0.5rem;
    --s2: 0.75rem;
    --s3: 1rem;
    --s4: 1.5rem;
    --s5: 2rem;
    --s6: 3rem;
    --s7: 4rem;
    /* the distance from one section to the next, spent symmetrically on both sides of a rule so
       the rule sits midway between the two blocks it divides instead of hugging one of them */
    --flow: clamp(var(--s6), 7vw, var(--s7));
    --r-sm: 8px;
    --r-md: 10px;
    --r-lg: 20px;
    --t-micro: 0.75rem;
    --t-small: 0.9375rem;
    --t-body: 1.0625rem;
    --t-card: clamp(1.25rem, 1.14rem + 0.55vw, 1.5rem);
    --t-h1: clamp(2.25rem, 1.42rem + 4.1vw, 4.25rem);

    color-scheme: light;
}

/* The dark values are declared once and applied twice: once for "follow the system, unless the reader
   overrode it", once for the explicit choice. Duplication is deliberate and matches the applications. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --surface: #0b1220;
        --panel: #131d30;
        --ink: #e9eff8;
        --muted: #9fb0c8;
        --line: #253248;
        --brand: #5aa4f0;
        --brand-strong: #8cc0f5;
        --on-brand: #0b1220;
        --focus: #fbbf24;
        --focus-edge: #05080f;
        --shadow: 0 0 0;
        --wash: 0.12;
        --edge: 1;
        color-scheme: dark;
    }
}
:root[data-theme="dark"] {
    --surface: #0b1220;
    --panel: #131d30;
    --ink: #e9eff8;
    --muted: #9fb0c8;
    --line: #253248;
    --brand: #5aa4f0;
    --brand-strong: #8cc0f5;
    --on-brand: #0b1220;
    --focus: #fbbf24;
    --focus-edge: #05080f;
    --shadow: 0 0 0;
    --wash: 0.12;
    --edge: 1;
    color-scheme: dark;
}
/* Scrollbars, form controls and the canvas before CSS lands follow the chosen state, not the system. */
:root[data-theme="light"] { color-scheme: light; }

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

/* the hero wash bleeds 5 % past each edge by design; clip it so it can never widen the document */
html { -webkit-text-size-adjust: 100%; overflow-x: clip; }

/*
 * The column keeps the footer on the floor of the viewport when the page is shorter than the window,
 * which on this page it usually is. Without it the footer ended mid-screen with a band of bare
 * background beneath it, and the page read as unfinished rather than short.
 *
 * Safe against the hero wash: .hero is not a child of body, so it never becomes a flex item, and
 * `display: flex` alone opens no stacking context, so the wash's z-index: -1 still paints above the
 * body background instead of disappearing behind it.
 */
body {
    margin: 0;
    min-height: 100vh;
    min-height: 100svh;   /* the SMALL viewport: dvh grows when a phone toolbar retracts and would
                             push the footer below the fold on the one case this rule exists for */
    display: flex;
    flex-direction: column;
    background: var(--surface);
    color: var(--ink);
    font: 400 var(--t-body)/1.65 ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
}
main { flex: 1 0 auto; }

::selection { background: color-mix(in srgb, var(--brand) 22%, transparent); }
/*
 * A two-colour indicator: a dark edge hugging the control, then a gap, then the amber ring. The ring
 * alone measures 4.68:1 against the light surface and 11.22:1 against the dark one, which satisfies
 * SC 1.4.11 wherever it sits on the page background — but a ring drawn tight against a --brand fill
 * would be 1.33:1, so the dark edge is what guarantees separation on any surface a control may have.
 *
 * No border-radius here. The value it used to carry pinned every focused control to 4px, so a 10px
 * button visibly snapped square the instant it took focus; outline already follows the element's own
 * radius.
 */
:focus-visible {
    outline: 3px solid var(--focus);
    outline-offset: 3px;
    box-shadow: 0 0 0 2px var(--focus-edge);
}

/* the accessible name of a control, for readers who cannot see which label is showing */
.vh {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

.skip { position: absolute; left: -9999px; }
.skip:focus {
    left: 1rem; top: 1rem; z-index: 2;
    padding: var(--s2) var(--s3); border-radius: var(--r-sm);
    background: var(--panel); color: var(--ink);
    box-shadow: 0 8px 24px rgb(var(--shadow) / 18%);
}

.wrap { max-width: 68rem; margin: 0 auto; padding: 0 var(--s4); }

/* --- header ------------------------------------------------------------- */
header { position: relative; z-index: 1; }
header .wrap {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
    min-height: 5rem;
}
.brand { display: inline-flex; align-items: center; gap: 0.625rem; }
.brand img { width: 30px; height: 30px; display: block; }
.brand span { font-size: var(--t-body); font-weight: 650; letter-spacing: -0.01em; }

/* The last control carries 0.875rem of padding, so its label would sit inset from the gutter while
   the brand mark opposite it sits flush. Hang it out so the top and bottom right edges agree. */
.controls { display: inline-flex; align-items: center; gap: 0.125rem; margin-right: -0.875rem; }
.lang, .theme {
    display: inline-flex; align-items: center; gap: var(--s1);
    min-height: 2.75rem; padding: 0 0.875rem; border-radius: var(--r-sm);
    color: var(--muted); text-decoration: none;
    font: inherit; font-size: var(--t-small); font-weight: 500;
    transition: color 0.15s, background-color 0.15s;
}
.lang:hover, .theme:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 6%, transparent); }

/*
 * The theme button exists in the markup of both locales but is invisible until /theme.js has actually
 * run and set data-js on <html>. A blocked policy, a 404, a network failure or a browser without
 * JavaScript therefore leaves no dead control on the page — only the operating system's own preference,
 * which the stylesheet has always honoured. The reveal is CSS so the script never touches the button.
 */
.theme { display: none; border: 0; background: transparent; cursor: pointer; }
:root[data-js] .theme { display: inline-flex; }

/*
 * All three labels are in the HTML and CSS shows exactly one. The hidden two are display:none, so they
 * leave the accessibility tree as well and a screen reader announces one name, not three. That is why
 * the script carries no Slovak, no English and no aria-label handling at all.
 */
.theme .t { display: none; }
:root:not([data-theme]) .theme .t-system { display: inline; }
:root[data-theme="light"] .theme .t-light { display: inline; }
:root[data-theme="dark"] .theme .t-dark { display: inline; }
:root[data-theme] .theme { color: var(--ink); }

/*
 * The glyph hangs off a span that is aria-hidden, not off the button itself. Generated content on the
 * button would be folded into its accessible name in Chrome, and a screen reader would read the moon
 * before the word. Here the name is exactly the visually hidden prefix plus the one visible label.
 */
.theme .glyph::before { content: "\25D1"; font-size: 1.0625rem; line-height: 1; }
:root[data-theme="light"] .theme .glyph::before { content: "\2600"; }
:root[data-theme="dark"] .theme .glyph::before { content: "\263E"; }

/* --- hero --------------------------------------------------------------- */
.hero { position: relative; padding: clamp(3.5rem, 11vw, 7.5rem) 0 clamp(3rem, 8vw, 5rem); }
.hero::before {
    content: "";
    position: absolute; inset: -30% -5% auto -5%; height: 34rem; z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(38% 62% at 10% 34%, rgb(252 49 22 / var(--wash)), transparent 70%),
        radial-gradient(34% 56% at 32% 14%, rgb(250 211 5 / var(--wash)), transparent 70%),
        radial-gradient(40% 62% at 52% 40%, rgb(38 248 65 / var(--wash)), transparent 70%),
        radial-gradient(44% 64% at 72% 16%, rgb(0 214 240 / var(--wash)), transparent 70%),
        radial-gradient(38% 58% at 88% 44%, rgb(6 102 252 / var(--wash)), transparent 70%),
        radial-gradient(36% 56% at 99% 20%, rgb(150 4 252 / var(--wash)), transparent 70%);
}

/*
 * The brand line and its explanation, one block so they arrive together.
 *
 * What stood here was an uppercase, letter-spaced 12px label reading "Priemyselná operačná
 * platforma" — a category descriptor, the exact thing this site's headline was written to avoid,
 * sitting in the first line of the page. A claim is a sentence, so it is set as one: no capitals,
 * no tracking, full ink, at body size. Small enough that the headline underneath still owns the
 * screen, confident enough that it does not read as a caption.
 */
.claim { margin: 0 0 var(--s5); }
.claim-line {
    margin: 0;
    font-size: var(--t-card); font-weight: 650; letter-spacing: -0.015em;
    color: var(--ink);
}
/* Wide enough that the sentence breaks where it wants to rather than where a tight measure forces
   it; at 42ch the Slovak line split after "v jednej", which reads as an accident. */
.claim-note {
    margin: 0.4375rem 0 0; max-width: 58ch;
    font-size: var(--t-body); line-height: 1.55; color: var(--muted); text-wrap: pretty;
}

/*
 * The headline is one sentence of trouble and one of relief, and the type carries that rather than
 * decorating it: the problem is set smaller and in the muted ink, the answer at full size and full
 * strength, so the resolution literally brightens. No rule, no arrow, no illustration.
 */
h1 {
    margin: 0 0 var(--s4);
    font-weight: 700; line-height: 1.04; letter-spacing: -0.032em;
}
/*
 * The measure is on the beat, not on the h1. Balancing the whole headline lets the browser pull a word
 * from the first sentence onto the second sentence's line, which destroys the two-beat reading; each
 * beat balances inside itself instead. 26ch is sized for the longest of the four, "Production already
 * knows.", so it sets on one line at the top of the type scale — ch is the width of a zero, which is
 * narrower than the average glyph in this weight, so a tighter measure wraps a headline that fits.
 */
.beat { display: block; max-width: 26ch; text-wrap: balance; }
.problem { font-size: calc(var(--t-h1) * 0.72); color: var(--muted); letter-spacing: -0.026em; }
.answer { font-size: var(--t-h1); color: var(--ink); }

/*
 * One action, not two. The second used to be a "sign in" that jumped to an anchor three hundred
 * pixels below, where two sign-in buttons already sit on the product cards. A hero that offers the
 * same thing twice weakens both offers; the prospect writes to us here, the user signs in there.
 */
.actions { display: flex; flex-wrap: wrap; gap: var(--s2); margin: var(--s5) 0 0; }
/*
 * The user agent's rule for [hidden] is display:none, and .cta's own display would beat it on cascade
 * order. Restate it, or the call to action shows up as a dead control on a page whose script did not
 * run — the same trap the theme button has, for the same reason.
 */
.cta[hidden] { display: none; }
/*
 * A button, not a link: it performs an action rather than going somewhere, and the address it needs
 * does not exist until it is pressed. A button inherits none of the page's typography and arrives
 * with its own border and its own native rendering, so all of that is reset here — `font: inherit`
 * has to come before the size and weight or the shorthand wipes them, and `appearance: none` is for
 * Safari on iOS, which rounds and shades a native control even when the background is set.
 */
.cta {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 3.25rem; padding: 0 var(--s4);
    appearance: none; -webkit-appearance: none;
    border: 0; border-radius: var(--r-md);
    background: var(--brand); color: var(--on-brand);
    font: inherit; font-size: var(--t-body); font-weight: 600;
    cursor: pointer;
    transition: background-color 0.15s;
}
.cta:hover { background: var(--brand-strong); }

/* --- products ----------------------------------------------------------- */
/*
 * This names the section, so it carries more weight than the hero's eyebrow: at 12px in --muted it was
 * the smallest thing on a page whose body text is 17px, and it read as a caption rather than a title.
 */
.products-heading {
    margin: 0 0 var(--s2);
    font-size: var(--t-small); font-weight: 650; letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--ink);
}
/* the one line that says why three separate products share one front door */
.pairing { margin: 0 0 var(--s4); max-width: 46rem; color: var(--muted); text-wrap: pretty; }

/*
 * The cards and the colophon used to share one auto-fit declaration, so that both broke to a single
 * column at the same width and stood on the same vertical seam. With three cards against the
 * colophon's two blocks that promise cannot be kept — above 1008px the cards want three tracks and
 * the colophon two — so the declaration is split and this comment says what is actually true now.
 *
 * Auto-fit is gone with it, and deliberately. Three cards in an auto-fit grid produce a dead band
 * between 680px and 1007px: two tracks fit, both are occupied, and the third card hangs alone in a
 * second row at half width with nothing beside it. One column then three, with no two-column state,
 * has no such band at any width.
 */
.products {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--s4);
    /* A single column across the full 68rem wrap would stretch each card into a band. Cap it at the
       width a card actually wants to be. */
    max-width: 34rem;
    margin: 0 0 var(--flow);
    padding: 0;
    list-style: none;
}
@media (min-width: 58rem) {
    .products { grid-template-columns: repeat(3, 1fr); max-width: none; }
}
.colophon {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
    column-gap: var(--s4);
}
.card {
    position: relative; overflow: hidden;
    display: flex; flex-direction: column; gap: var(--s2);
    padding: var(--s5);
    border: 1px solid var(--line); border-radius: var(--r-lg);
    background: var(--panel);
    transition: border-color 0.18s, box-shadow 0.18s, transform 0.18s;
}
/*
 * The only place besides the mark and the wash where the spectrum appears, and only while the card is
 * being used: a two-pixel edge on hover and on focus-within, so a keyboard reaches it too. At rest the
 * page stays quiet and the mark keeps its monopoly on colour.
 */
.card::before {
    content: "";
    position: absolute; inset: 0 0 auto; height: 2px;
    background: linear-gradient(90deg,
        var(--hue-red), var(--hue-yellow), var(--hue-green),
        var(--hue-cyan), var(--hue-blue), var(--hue-violet));
    opacity: 0; transition: opacity 0.18s;
}
.card:hover, .card:focus-within {
    border-color: color-mix(in srgb, var(--brand) 45%, var(--line));
    box-shadow: 0 10px 30px -14px rgb(var(--shadow) / 26%);
    transform: translateY(-2px);
}
.card:hover::before, .card:focus-within::before { opacity: var(--edge); }

.card h3 { margin: 0; font-size: var(--t-card); font-weight: 650; letter-spacing: -0.015em; }
.card p { margin: 0; color: var(--muted); }
/*
 * Only the outer margin. This rule beats .open on specificity (0,1,1 against 0,1,0), so a padding-top
 * here landed on the button itself: 12px on top, 0 underneath, and the label sat six pixels below the
 * centre of its own control. Breathing room above an action is margin or grid gap, never the control's
 * own padding. The card's `gap: var(--s2)` keeps the spacing.
 */
.card > :last-child { margin-top: auto; }

.open {
    display: inline-flex; align-items: center; justify-content: center; align-self: flex-start;
    min-height: 3rem; padding: 0 1.375rem; border-radius: var(--r-md);
    background: var(--brand); color: var(--on-brand); font-size: 1rem; font-weight: 600; text-decoration: none;
    transition: background-color 0.15s;
}
.open:hover { background: var(--brand-strong); }

/* The old rule pulled this up with a negative top margin to cancel the grid's bottom margin — a
   number that only worked because of another number set elsewhere. Both now come off the scale. */
/* --- footer ------------------------------------------------------------- */
/*
 * Two rows, not two edges. The old footer pushed one paragraph to each side with space-between, and
 * because flex items of unequal height start on the same top line, "© 2026 LogiAURA" ended up level
 * with the company name: a lower register aligned with a higher one, which is why it read as debris
 * rather than as a placed element.
 *
 * Row one is the colophon — the signature on the left, the operator on the right, on the same grid as
 * the product cards above it. Row two is the baseline, and it is a bottom bar because it has two ends.
 *
 * One rule and one only, on <footer> itself, running the full width: the division between the page
 * and the notice that closes it. Everything inside the footer is separated by space.
 */
/*
 * No rule. The footer is a different surface instead — a few per cent of ink mixed into the page
 * colour, which reads in both themes and separates the two blocks by material rather than by drawing
 * a line between them. A hairline at 1.31:1 was doing very little work for how much attention it drew.
 */
footer {
    background: color-mix(in srgb, var(--ink) 3.5%, var(--surface));
    padding: var(--flow) 0 var(--s6);
    font-size: var(--t-small);
    line-height: 1.7;
    color: var(--muted);
}
/*
 * A footer link is the same colour as the prose around it, so its underline is the only thing that
 * says it is a link — it cannot be decorative. It was currentColor at 40 % alpha, which measures
 * 1.90:1 against the light surface. Full strength at one pixel, with a little offset, is both legible
 * and quiet.
 */
footer a {
    color: inherit;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.18em;
}
footer a:hover { color: var(--ink); }

.colophon { row-gap: var(--s5); }

.signature { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s2); }
/* Sentence case, not the hero eyebrow's uppercase: the eyebrow opens the page and this closes it, and
   copying the eyebrow here would make the footer read as a reprise of the hero rather than a signature. */
.positioning { margin: 0; max-width: 26ch; text-wrap: pretty; }

/*
 * <address> is the contact information for the page's owner, which is exactly this and not the common
 * misuse. The copyright sits outside it on purpose: a year is not contact information. The user agent
 * sets italic and nothing else, so one reset is the whole cost.
 *
 * The company name is a span, not <strong>. <strong> means strong importance, and the name is not more
 * important than the address beneath it; it was bold text wearing a semantic element.
 *
 * The address and the site are on their own lines rather than separated by a middot: two 15px links
 * ten pixels apart is a tap-target collision, and stacked at line-height 1.7 each line box clears the
 * 24px minimum.
 */
.operator { font-style: normal; }
/*
 * The control that puts the address on the page. It sits inside the operator block, so it borrows the
 * look of the links around it rather than announcing itself as a button — same colour, same
 * underline, same focus ring. Only the cursor and the pointer target say it is pressable.
 */
.mail-show {
    padding: 0; border: 0; background: none;
    font: inherit; color: inherit; cursor: pointer;
    text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.18em;
}
.mail-show:hover { color: var(--ink); }
/* Both controls carry their own display, which would beat the user agent's [hidden] rule. */
.mail-show[hidden], [data-mail-fallback][hidden] { display: none; }

/* shown only when the script has not run, so a human can still read the address and type it */
[data-mail-fallback] { white-space: nowrap; }
.operator .who { color: var(--ink); font-weight: 600; }

/*
 * The copyright, separated by space alone.
 *
 * It briefly had a second rule above it, inset to .wrap while the footer's own rule runs full width.
 * Two lines of different lengths a few dozen pixels apart read as carelessness, not as a system, and
 * over one line of text a rule is over-articulation anyway. The footer needs exactly one division:
 * the one that separates it from the page.
 *
 * It also briefly had the language switch on the other end of the row. The header carries that control
 * on every page and never scrolls out of reach, and this site has two addresses — repeating it here
 * was the same redundancy that cost the hero its second button.
 */
.baseline { margin-top: var(--s6); }
.baseline p { margin: 0; }

/* --- motion ------------------------------------------------------------- */
/*
 * Opt-in, so a reader who asked for less motion gets none at all, and `backwards` means the content is
 * visible even if the animation never runs. The stagger is addressed by class rather than :nth-child:
 * .actions is the fourth child of the hero wrap, so the old :nth-child(2)/(3) pair gave it no delay and
 * the sequence ended one element early.
 */
@media (prefers-reduced-motion: no-preference) {
    .reveal { animation: rise 0.5s cubic-bezier(0.16, 1, 0.3, 1) backwards; }
    .r2 { animation-delay: 0.06s; }
    .r3 { animation-delay: 0.12s; }
    /*
     * The animation is on the card, the delay is what :nth-child moves. Written the other way round —
     * one whole rule per index, which is how this stood — a third card matched nothing and simply
     * appeared while the two behind it rose in, which reads as a fault rather than a shorter
     * sequence. This way a fourth card still animates; it only shares the last step.
     */
    .products .card { animation: rise 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.24s backwards; }
    .products .card:nth-child(2) { animation-delay: 0.3s; }
    .products .card:nth-child(3) { animation-delay: 0.36s; }
    .theme::before, .card, .card::before { transition-duration: 0.18s; }
}
@keyframes rise {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: none; }
}

/* --- narrow ------------------------------------------------------------- */
/*
 * Below 26rem the two beats of the headline are close enough in width that balancing them can strand a
 * single word; let them set naturally instead, and drop the label on the theme button so the header
 * never wraps to two rows on a 320 px screen.
 */
@media (max-width: 26rem) {
    .beat { text-wrap: pretty; max-width: none; }
    /* The label goes off-screen rather than to display:none: hiding it outright would strip it from the
       accessible name and leave the button called "Theme:" with no state after it. */
    :root:not([data-theme]) .theme .t-system,
    :root[data-theme="light"] .theme .t-light,
    :root[data-theme="dark"] .theme .t-dark {
        position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
        overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
    }
    .theme { padding: 0 0.75rem; }
    .actions .cta { flex: 1 1 100%; }
}
