/* =========================================================================
   Digital Symbio — design system

   Brand SSoT: Corporate Identity/paleta farieb_digitalsymbio_CMYK.pdf
     orange #da8a26 · turquoise #69b3b3 · green #498878 · red #9f3634 · ink #2a2a2a

   THE ONE RULE, measured not guessed (WCAG 2.x, computed 2026-07-17):
     orange on white = 2.75  -> FAILS AA for text, and fails 3:1 for UI edges
     orange on #2a2a2a = 5.22 -> passes AA
     #2a2a2a on orange = 5.22 -> passes AA
   So orange is a SURFACE carrying dark ink, never ink on a light surface.
   Same for turquoise (2.41 on white / 5.95 on dark). Brand red is unusable on
   dark (2.09), hence a lifted --danger there.

   Every ratio in this file was recomputed from the hexes on 2026-07-17 (sRGB
   relative luminance, WCAG 2.x). Four of the :root comments disagreed with their
   own tokens and have been corrected: --text-muted 6.90 -> 6.69, --muted-on-deep
   6.13 -> 6.04, --cta-edge 3.06 -> 4.51, --data 5.12 -> 5.79. No token value
   changed — every one of the four understated the real ratio, so nothing was
   failing, and the .motto block had three of them right all along. If you change
   a hex here, recompute rather than adjusting the comment to match your memory.
   ========================================================================= */

:root {
    /* brand — for graphics and fills; never assume these are legible as text */
    --brand-orange: #da8a26;
    --brand-turq:   #69b3b3;
    --brand-ink:    #2a2a2a;

    /* light surface (default) */
    --surface:      #ffffff;
    --surface-2:    #f7f5f2;   /* warm, not grey — the tree/organic half of "symbio" */
    --surface-deep: #2a2a2a;   /* deliberate dark bands inside the light page */
    --text:         #2a2a2a;   /* 14.35 on white */
    --text-muted:   #5c5c5c;   /* 6.69 on white */
    --text-on-deep: #e8e8e8;   /* 11.71 on #2a2a2a */
    --muted-on-deep:#a8a8a8;   /*  6.04 on #2a2a2a */
    --rule:         #e3ded7;
    --rule-deep:    #454545;

    --cta-fill:     #da8a26;
    --cta-ink:      #2a2a2a;   /* 5.22 on orange */
    --cta-edge:     #a8681a;   /* 4.51 on white — satisfies WCAG 1.4.11 for the button edge */
    --accent-text:  #9c6010;   /* darkened orange, 5.13 on white — for orange-ish TEXT */
    --data:         #2f6f6f;   /* darkened turquoise, 5.79 on white — for stat numerals */
    --danger:       #9f3634;   /* 6.88 on white */
    --focus:        #9c6010;

    --maxw: 68rem;
    --pad: clamp(1.25rem, 4vw, 2.5rem);
    --radius: 3px;
    --font: "Nunito", ui-rounded, "Segoe UI", system-ui, -apple-system, sans-serif;
}

/* Dark is OPT-IN, not automatic.
   The OS colour preference is deliberately not honoured here: the light surface
   is the brand (warm paper, orange, the tree), and following the system setting
   would hand roughly half of all visitors a dark first impression nobody chose.
   The toggle writes data-theme + localStorage; absent that, everyone gets light. */
:root[data-theme="dark"] {
    --surface:      #2a2a2a;
    --surface-2:    #343434;
    --surface-deep: #1e1e1e;
    --text:         #e8e8e8;
    --text-muted:   #a8a8a8;
    --text-on-deep: #e8e8e8;
    --muted-on-deep:#a8a8a8;
    --rule:         #454545;
    --rule-deep:    #3a3a3a;
    --cta-fill:     #da8a26;
    --cta-ink:      #2a2a2a;
    --cta-edge:     #da8a26;
    --accent-text:  #da8a26;
    --data:         #69b3b3;
    --danger:       #e08a87;
    --focus:        #da8a26;
}

/* Body/heading face: Nunito, variable, self-hosted, subsetted to Latin +
   Latin Extended-A. ~43 KB, one file, one request. OFL — see LICENSE-Nunito.txt.

   Why not Rollcage, the brand wordmark face? It has 98 glyphs and ZERO Slovak
   diacritics — verified 2026-07-17, all 34 of ľĽďĎťŤňŇšŠčČžŽýÝáÁíÍéÉúÚäÄôÔŕŔĺĹóÓ
   are missing, and so is €. Licensing was never the issue; the font physically
   cannot set Slovak. It stays where it works: the wordmark, already outlined in
   logo.svg. Nunito was chosen for its round bowls and soft terminals, which is
   what the wordmark reads as.

   Latin-Ext is not optional: ľ ď ť ĺ ŕ ô live there and Google's "latin" subset
   omits them. Candidates were rendered before adoption — Outfit was rejected
   because it sets ľuďom as l'ud'om, detaching the caron into a spaced
   apostrophe. That only shows up in Slovak, never in an English specimen. */
@font-face {
    font-family: "Nunito";
    src: url("/assets/fonts/nunito-subset.woff2") format("woff2");
    font-weight: 400 700;
    font-style: normal;
    font-display: swap;   /* never FOIT: invisible text is an LCP penalty */
}

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

/* The header is sticky, so an anchor target would otherwise land underneath it.
   The cards on the home page link straight to services#agent, so this is a real
   path, not a hypothetical one. Matches the 3.75rem bar plus a little air. */
html { scroll-behavior: smooth; scroll-padding-top: 4.75rem; }
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
    }
}

body {
    margin: 0;
    background: var(--surface);
    color: var(--text);
    font-family: var(--font);
    font-size: clamp(1rem, .96rem + .2vw, 1.125rem);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

h1, h2, h3 { line-height: 1.15; letter-spacing: -0.02em; margin: 0 0 .5em; font-weight: 700; }
h1 { font-size: clamp(2rem, 1.4rem + 2.8vw, 3.4rem); }
h2 { font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2.2rem); }
h3 { font-size: clamp(1.1rem, 1rem + .5vw, 1.35rem); }
p  { margin: 0 0 1em; }
p, li { max-width: 62ch; }

a { color: var(--accent-text); text-underline-offset: .18em; text-decoration-thickness: 1px; }
a:hover { text-decoration-thickness: 2px; }

:where(a, button, input, textarea, summary, [tabindex]):focus-visible {
    outline: 3px solid var(--focus);
    outline-offset: 2px;
    border-radius: 2px;
}

img, svg { max-width: 100%; height: auto; }

/* ------------------------------------------------------------- structure */
.wrap { max-width: var(--maxw); margin-inline: auto; padding-inline: var(--pad); }
.section { padding-block: clamp(2.5rem, 6vw, 4.5rem); }
.section--alt  { background: var(--surface-2); }
.section--deep { background: var(--surface-deep); color: var(--text-on-deep); }
.section--deep h2, .section--deep h3 { color: var(--text-on-deep); }
.section--deep a { color: var(--brand-orange); }        /* 5.22 on #2a2a2a */
.section--deep .muted { color: var(--muted-on-deep); }
.muted { color: var(--text-muted); }
.lede { font-size: 1.15em; }

.skip {
    position: absolute; left: -9999px; top: 0; z-index: 100;
    background: var(--cta-fill); color: var(--cta-ink);
    padding: .75rem 1.25rem; font-weight: 700;
}
.skip:focus { left: 0; }

/* ------------------------------------------------------------- header */
.site-header {
    border-bottom: 1px solid var(--rule);
    position: sticky; top: 0; z-index: 20;
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    backdrop-filter: blur(8px);
}
/* One row, never wrapping. The previous rule let the bar wrap onto three lines
   (logo / links / tools), which measured 182px at 320px — a sticky header eating
   28% of the viewport before a word of content. The links now live in the
   disclosure below, so the bar itself is a fixed, small object. */
.site-header .wrap {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; min-height: 3.75rem;
}
.brand { display: inline-flex; align-items: center; color: var(--text); text-decoration: none; }
.brand svg, .brand img { width: clamp(140px, 18vw, 190px); display: block; }
.nav { display: flex; align-items: center; }

/* ---- the disclosure ------------------------------------------------------
   Closed by default at phone widths; forced open from 48em up. See the
   @supports note at the desktop end of this block for the one browser
   assumption being made here, and what happens when it does not hold. */
.nav-disc { position: static; }

/* --cta-edge, not --rule, for the edge: this is the only way to the rest of the
   site on a phone, so the box has to read as a control on its own. 4.51 on white
   and 5.22 on #2a2a2a, both clear of the 3:1 that 1.4.11 wants for a component
   boundary; --rule would have been 1.34 and is fine only for the secondary
   controls inside the panel, which sit under a heading and next to each other. */
.nav-burger {
    display: flex; align-items: center; gap: .5rem;
    min-height: 44px; padding: .5rem .8rem;      /* 2.5.8: target >= 44x44 */
    border: 1px solid var(--cta-edge); border-radius: var(--radius);
    color: var(--text); font-weight: 700; font-size: .95rem;
    cursor: pointer; user-select: none;
    list-style: none;                             /* kill the disclosure triangle */
}
.nav-burger::-webkit-details-marker { display: none; }
.nav-burger::marker { content: ""; }
.nav-burger:hover { background: var(--surface-2); }   /* the edge is already --cta-edge */
.nav-burger__ico { font-size: 1.35rem; }
.nav-burger__ico--close { display: none; }
.nav-disc[open] .nav-burger__ico--open  { display: none; }
.nav-disc[open] .nav-burger__ico--close { display: block; }

/* The panel hangs off the header rather than sitting in its flow, so opening the
   menu never reflows the page underneath it. .site-header is position:sticky,
   which is a positioned element, so it is the containing block here — .nav and
   .nav-disc are deliberately left unpositioned so this anchors to the full bar
   width and not to the burger.

   max-height + overflow so the menu can never grow past the viewport and trap
   whatever is at the bottom of it. */
.nav-panel {
    position: absolute; top: 100%; left: 0; right: 0;
    background: var(--surface);
    border-bottom: 1px solid var(--rule);
    box-shadow: 0 12px 28px rgb(0 0 0 / .16);
    padding: .5rem var(--pad) 1rem;
    max-height: calc(100dvh - 3.75rem);
    overflow-y: auto;
}
.nav-links { list-style: none; margin: 0; padding: 0; display: grid; gap: .15rem; }
.nav-links li { max-width: none; }
.nav-links a {
    display: flex; align-items: center;
    min-height: 48px; padding: .6rem .75rem;
    color: var(--text); text-decoration: none; font-weight: 600;
    border-radius: var(--radius);
    border-left: 3px solid transparent;
}
.nav-links a:hover { background: var(--surface-2); }
.nav-links a[aria-current="page"] {
    border-left-color: var(--cta-fill);
    background: var(--surface-2);
    color: var(--accent-text);                    /* 5.13 on white, 4.85 on --surface-2 */
}

.tools { display: flex; align-items: center; gap: .5rem; margin-top: .75rem; padding-top: .75rem; border-top: 1px solid var(--rule); }
.lang-switch, .theme-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px;           /* touch target */
    border: 1px solid var(--rule); border-radius: var(--radius);
    background: transparent; color: var(--text);
    font: inherit; font-size: .85rem; font-weight: 700; text-decoration: none;
    cursor: pointer; padding: 0 .6rem;
}
.lang-switch:hover, .theme-toggle:hover { border-color: var(--cta-edge); }

/* ---- desktop: put the bar back exactly as it was ------------------------
   A closed <details> hides its content through the ::details-content
   pseudo-element (content-visibility: hidden). Nothing an author can say about
   the *children* reaches inside that — display:block on the panel and
   display:contents on the <details> were both tried and both leave the nav
   collapsed. Forcing ::details-content visible is the only thing that works, so
   the whole desktop bar is gated on the browser having that selector.

   The @supports is not decoration. Without the guard, a browser that does not
   know ::details-content would apply `.nav-burger { display: none }` and then
   fail to open the panel — an unreachable nav, which is the worst outcome on
   offer. With it, such a browser simply keeps the labelled "Menu" disclosure at
   desktop width: not the intended design, but every link still one tap away.
   ::details-content is Chrome 131+ / Safari 18.4+ / Firefox 139+. */
@media (min-width: 48em) {
    @supports selector(::details-content) {
        .nav-burger { display: none; }
        .nav-disc::details-content { content-visibility: visible; block-size: auto; }

        .nav-panel {
            position: static; max-height: none; overflow: visible;
            background: none; border: 0; box-shadow: none; padding: 0;
            display: flex; align-items: center; gap: clamp(.6rem, 2vw, 1.5rem);
        }
        .nav-links { display: flex; align-items: center; gap: clamp(.6rem, 2vw, 1.5rem); }
        .nav-links a {
            min-height: 0; padding: .5rem .1rem; font-size: .95rem;
            border-left: 0; border-radius: 0; background: none;
            border-bottom: 2px solid transparent;
        }
        .nav-links a:hover { background: none; border-bottom-color: var(--cta-fill); }
        .nav-links a[aria-current="page"] {
            background: none; color: var(--text);
            border-bottom-color: var(--cta-fill);
        }
        .tools { margin-top: 0; padding-top: 0; border-top: 0; gap: .25rem; }
        .site-header .wrap { min-height: 4.5rem; }
    }
}
.theme-toggle svg { width: 20px; height: 20px; fill: currentColor; }
.theme-toggle .icon-dark { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-dark { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-light { display: none; }

/* ------------------------------------------------------------- language hint */
.lang-hint {
    background: var(--surface-2);
    border-bottom: 1px solid var(--rule);
    font-size: .9rem;
}
.lang-hint .wrap { display: flex; gap: .75rem 1rem; align-items: center; justify-content: center; flex-wrap: wrap; padding-block: .6rem; }
/* Without nowrap the button is a flex item that can be squeezed down to its
   longest word, and at 320 it set "Slovensky" on one line with the arrow alone
   on the next. This banner only renders for a Slovak speaker who landed on the
   English page, so a phone is exactly where it gets seen. */
.lang-hint .lang-switch { white-space: nowrap; }

/* ------------------------------------------------------------- buttons */
.btn {
    display: inline-flex; align-items: center; justify-content: center;
    gap: .5rem;
    min-height: 48px; padding: .7rem 1.4rem;
    background: var(--cta-fill);
    color: var(--cta-ink);                        /* 5.22 on orange */
    border: 2px solid var(--cta-edge);            /* edge contrast for 1.4.11 */
    border-radius: var(--radius);
    font: inherit; font-weight: 700; text-decoration: none;
    cursor: pointer;
    transition: transform .12s ease, filter .12s ease;
}
.btn:hover { filter: brightness(1.06); transform: translateY(-1px); }
.btn:active { transform: none; }
.btn--ghost {
    background: transparent; color: var(--text);
    border-color: var(--rule);
}
.btn--ghost:hover { border-color: var(--cta-edge); filter: none; }
.section--deep .btn--ghost { color: var(--text-on-deep); border-color: var(--rule-deep); }

/* ------------------------------------------------------------- hero */
.hero { padding-block: clamp(3rem, 8vw, 6rem) clamp(2.5rem, 5vw, 4rem); }
.hero__claim { font-size: clamp(1.05rem, .95rem + .6vw, 1.35rem); max-width: 54ch; margin-bottom: 1rem; }

/* ------------------------------------------------------------- motto lockup

   The official lockup (brand/motto.png), redrawn as live type. Not the PNG:
   that file is English-only, raster, and black-on-white — in the dark theme it
   would be a black rectangle. This is the page's <h1>, so it has to stay real
   selectable text anyway: an image of a sentence is invisible to search, to a
   screen reader, and to anyone trying to copy it.

   CONTRAST, measured not guessed. The original artwork sets ALREADY in brand
   turquoise #69b3b3, which is 2.41 on white — it fails AA outright. Fine for a
   press kit, not for the largest text on the page. So the accent word uses
   --data (#2f6f6f, 5.79 on white), which flips back to the true brand #69b3b3
   in the dark theme (5.95 on #2a2a2a). Both pass; both still read turquoise.

   WHY SLOVAK GETS A SHAPE OF ITS OWN. The lockup's force is that the rows form
   one block. English gets that for free — measured in Nunito 700 at 100px:
   FUTURE 376, ALREADY 445, HERE 253, and once HERE is set larger the three rows
   land within a few percent of each other.

   Slovak cannot have it. BUDÚCNOSŤ measures 611 at that size and TU measures
   130, so equal rows would need TU set 4.7x the lead — the punchline as a wall
   of two letters. Tracking cannot rescue it either: TU has exactly one gap
   between its letters, so filling a 470px row means ~260px of air between T and
   U. That is not tracking, that is two letters at opposite ends of the page.

   So Slovak takes the shape Slovak actually has: two beats, BUDÚCNOSŤ JE / UŽ
   TU, which is where a Slovak speaker breaks the sentence out loud. Four
   letters and a word space give tracking something to bite on, so a modest
   letter-spacing and a wide word gap close row two to the width of row one —
   no stretched glyphs, and TU still lands as the largest thing on the page.
   Same markup, same reading order; only the geometry differs, which is the
   honest way round.

   Sizes are per-language custom properties keyed off html[lang] and expressed
   in vw, so the ratio between the rows holds at every width. Not inline styles
   (the CSP blocks style attributes outright) and not JS. The numbers are tuned
   against measured glyph widths, not guessed. */
.motto {
    --m-lead:       clamp(1.9rem, 10.5vw, 5.2rem);
    --m-accent:     clamp(1.9rem, 10.5vw, 5.2rem);
    --m-huge:       clamp(3rem, 19vw, 9rem);
    --m-rot:        clamp(1.05rem, 3vw, 1.9rem);
    --m-row2-gap:   0rem;
    --m-row2-track: 0em;

    display: grid;
    justify-items: start;
    margin: 0;
    font-weight: 700;
    line-height: .88;
    letter-spacing: -0.03em;
    /* Uppercased here rather than stored shouting: SHOUTED source text follows
       you into copy-paste, and some screen readers spell all-caps letter by
       letter. */
    text-transform: uppercase;
}

/* Slovak: the lead comes down (it is the long word, not the point) and TU goes
   up (it is the point). line-height opens up because at this size the caron on
   Ž and the acute on Ú would otherwise climb into the row above. */
html[lang="sk"] .motto {
    --m-lead:       clamp(1.75rem, 12vw, 4.25rem);
    --m-accent:     clamp(2.8rem, 22.5vw, 7.5rem);
    --m-huge:       clamp(3.8rem, 31vw, 10.3rem);
    --m-row2-gap:   clamp(.5rem, 5vw, 2.8rem);
    --m-row2-track: .07em;
    line-height: .95;
}

.motto__row1 { display: flex; align-items: flex-end; gap: .1em; }
/* English stacks ALREADY over HERE; Slovak sets UŽ TU on one line, tracked out
   to meet the row above and sharing a baseline. */
.motto__row2 { display: flex; flex-direction: column; align-items: flex-start; }
html[lang="sk"] .motto__row2 {
    flex-direction: row;
    align-items: baseline;
    gap: var(--m-row2-gap);
    letter-spacing: var(--m-row2-track);
}
/* The rotated function words, as in the original artwork. Vertical writing plus
   a half turn reads bottom-to-top, which is the direction THE runs in the PNG. */
/* The floor is 1.05rem, not .8rem: at 320px the vw term bottoms out, and "je"
   was landing at 12.8px against a 99px TU — the function word disappeared and
   the lockup stopped reading as a sentence. The vw term and the ceiling are
   unchanged, so nothing moves above ~360px. */
.motto__rot {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font-size: var(--m-rot);
    line-height: 1;
    letter-spacing: 0;
    margin-bottom: .12em;
}
.motto__lead   { font-size: var(--m-lead); }
.motto__accent { font-size: var(--m-accent); color: var(--data); }
.motto__huge   { font-size: var(--m-huge); }
/* Floor raised .72rem -> .875rem. The vw term bottoms out on a phone, so the
   second half of the quotation — the half that carries the actual meaning — was
   being set at 11.5px under a 99px TU. The ceiling is untouched: from ~800px up
   this is the same 1.05rem it always was. */
.motto__tail {
    font-size: clamp(.875rem, 2.1vw, 1.05rem);
    letter-spacing: .01em; line-height: 1.35;
    margin-top: .6em; max-width: 44ch;
}
/* Gibson stays legible. This attribution is the entire defence for quoting a
   living author we do not license, and it costs one line — so it is set at
   readable size in --text-muted (6.69 on white, 6.04 on the dark surface),
   never as 6px grey, and never with a trademark symbol next to it. */
.motto__attrib {
    font-size: clamp(.8125rem, 2vw, .95rem);   /* floor raised for the same reason as the tail */
    font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-muted);
    margin: .5rem 0 1.75rem;
}
.hero__cta { display: flex; gap: .75rem; flex-wrap: wrap; margin-top: 1.75rem; }

/* ------------------------------------------------------------- icons

   Material Design Icons, inlined by icon() from the map in src/icons.php.
   Inlined rather than <img>: only a document-context SVG can see currentColor,
   and everything here is coloured by the theme. Sized in em so an icon tracks
   whatever it sits beside. */
.icon { width: 1em; height: 1em; fill: currentColor; flex: none; }

/* An icon next to a heading is decoration — the heading already says it. Orange
   is never ink on a light surface (2.75), so a standalone icon takes the
   darkened --accent-text (5.13 on white / brand orange back again on dark),
   which clears 1.4.11 even though, being aria-hidden, it need not. */
.h-ico { display: flex; align-items: center; gap: .6rem; }
.h-ico--top { align-items: start; }
.h-ico--top .icon { margin-top: .25em; }
.h-ico .icon { color: var(--accent-text); font-size: 1.15em; }
/* On the dark bands, --accent-text is the wrong end of the ramp: #9c6010 on
   #2a2a2a is 2.32, worse than the orange it was darkened from. Dark surfaces get
   the real brand orange, which is 5.22 there. */
.section--deep .h-ico .icon,
.site-footer .h-ico .icon { color: var(--brand-orange); }

/* ------------------------------------------------------------- hexagon motif */
.hex {
    width: 2.25rem; height: 2.25rem; flex: none;
    display: grid; place-items: center;
    background: var(--cta-fill); color: var(--cta-ink);
    clip-path: polygon(25% 4%, 75% 4%, 100% 50%, 75% 96%, 25% 96%, 0% 50%);
}
.hex svg { width: 1.15rem; height: 1.15rem; fill: currentColor; }
.hex--sm { width: 1.5rem; height: 1.5rem; }
.hex--sm svg { width: .8rem; height: .8rem; }

/* ------------------------------------------------------------- illustrations

   Line drawings for the sections they sit beside, inlined so they follow the
   theme. Every colour is a token — a hard-coded hex here is how the last set of
   graphics turned into black shapes on the dark background.

   No lazy loading and no CLS risk: inlined SVG is part of the document, so
   there is no second request to defer and nothing arrives late. aspect-ratio
   reserves the box before layout either way.

   Decorative throughout (aria-hidden at the call site): each one restates the
   prose next to it, and a <title> baked into a static file could only be in one
   of two languages. Nothing here is the sole carrier of anything. */
.illus {
    display: block; width: 100%; max-width: 26rem; height: auto;
    aspect-ratio: 400 / 250;
    margin-block: 1.5rem;
    color: var(--text);
}
.illus--wide { max-width: 34rem; }
.section--deep .illus { color: var(--text-on-deep); }

.illus .i-ink, .illus .i-ink-s, .illus .i-dash,
.illus .i-turq-s, .illus .i-gate-line, .illus .i-no {
    fill: none; stroke-linecap: round; stroke-linejoin: round;
}
.illus .i-ink     { stroke: currentColor; stroke-width: 2; opacity: .45; }
.illus .i-ink-s   { stroke: currentColor; stroke-width: 2.5; }
.illus .i-dash    { stroke: currentColor; stroke-width: 2; stroke-dasharray: 5 5; opacity: .6; }
.illus .i-orange  { fill: var(--brand-orange); }
.illus .i-turq    { fill: var(--data); }
.illus .i-turq-s  { stroke: var(--data); stroke-width: 2.5; }
/* Marks that sit inside an orange hexagon: dark ink on the orange plane, which
   is the only direction that combination is ever allowed to run. */
.illus .i-gate-ink  { fill: var(--cta-ink); }
.illus .i-gate-line { stroke: var(--cta-ink); stroke-width: 2; }
.illus .i-no        { stroke: var(--danger); stroke-width: 2.5; }

/* ------------------------------------------------------------- cards */
.cards { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); }
.card {
    border: 1px solid var(--rule); border-radius: var(--radius);
    padding: 1.25rem; background: var(--surface);
    display: flex; flex-direction: column; gap: .75rem;
}
.card h3 { margin: 0; }
.card p { margin: 0; }
/* The card's only action, so it is a target and not just an underlined phrase.
   inline-flex keeps the underline hugging the words — a block link would drag a
   280px rule across the card — while the 44px box is what the thumb gets. The
   negative margin keeps the text optically aligned with the paragraph above
   despite the taller box. */
.card__link {
    margin-top: auto; font-weight: 700;
    display: inline-flex; align-items: center; align-self: start;
    min-height: 44px; margin-bottom: -.5rem;
}
@media (min-width: 48em) {
    .card { padding: 1.5rem; }
}

/* ------------------------------------------------------------- proof strip */
/* Stays one column on a phone, deliberately. A 2x2 block was tried here to halve
   the strip's 886px, and it was wrong: proof.*.label is a sentence, not a
   stat caption ("48 / 48 sledovaných targetov je zelených. Dvadsaťjeden alert
   rules..."), so two 130px columns set Slovak prose four words to the line. It
   bought 73px at 320 and cost the paragraph. Length here is content, not waste.
   Only the padding is tightened, which is real dead space at this width. */
.proof { display: grid; gap: 1px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); background: var(--rule-deep); border: 1px solid var(--rule-deep); }
.proof__item { background: var(--surface-deep); padding: 1.25rem 1.1rem; }
@media (min-width: 40em) {
    .proof__item { padding: 1.5rem; }
}
.proof__num {
    font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2rem); font-weight: 700;
    color: var(--brand-turq); line-height: 1.1; display: block; margin-bottom: .35rem;
}
.proof__label { color: var(--text-on-deep); font-size: .95rem; margin: 0; }
.proof__note { color: var(--muted-on-deep); font-size: .9rem; margin: 1.5rem 0 0; max-width: 60ch; }

/* ------------------------------------------------------------- case studies */
.case { border-top: 1px solid var(--rule); padding-block: clamp(2rem, 4vw, 3rem); }
.case:first-of-type { border-top: 0; }
.case__kicker { font-size: .8rem; letter-spacing: .1em; text-transform: uppercase; color: var(--accent-text); font-weight: 700; margin: 0 0 .5rem; }
.case__grid { display: grid; gap: 1.5rem 2.5rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); margin-top: 1.25rem; }
.case__block h4 { margin: 0 0 .4rem; font-size: .8rem; letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted); font-weight: 700; }
.case__block p { margin: 0; }
.case__at {
    border-left: 3px solid var(--cta-fill);
    padding: .1rem 0 .1rem 1rem; margin-top: 1.5rem; background: var(--surface-2);
    padding-block: 1rem; padding-right: 1rem;
}
.case__at h4 { color: var(--accent-text); }

/* ------------------------------------------------------------- pricing */
.price-table { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
/* Phones stack name / price / description. Side by side, the price column is
   white-space:nowrap and "1 500 € / deň" is 190px of a 280px row, which left
   "Deň školenia" wrapping to two lines beside a price that would not wrap — the
   name column was being starved by the widest price in the table. Stacked, no
   name can ever collide with a price, and the price becomes the thing the eye
   lands on, which is what a price list is for. */
.price-row { background: var(--surface); padding: 1.1rem 1.25rem; display: grid; gap: .2rem; grid-template-columns: 1fr; align-items: start; }
.price-row__name { font-weight: 700; }
.price-row__price { font-weight: 700; color: var(--accent-text); white-space: nowrap; font-size: 1.25em; }
@media (min-width: 40em) {
    .price-row { padding: 1.25rem 1.5rem; gap: .35rem 1.5rem; grid-template-columns: 1fr auto; }
    .price-row__price { font-size: 1.1em; }
}
.price-row__desc { grid-column: 1 / -1; color: var(--text-muted); margin: 0; font-size: .95rem; }

/* ------------------------------------------------------------- lists */
.ticks { list-style: none; padding: 0; margin: 0; display: grid; gap: .6rem; }
.ticks li { display: flex; gap: .7rem; align-items: start; }
.ticks li::before {
    content: ""; flex: none; width: .85rem; height: .85rem; margin-top: .4em;
    background: var(--cta-fill);
    clip-path: polygon(25% 4%, 75% 4%, 100% 50%, 75% 96%, 25% 96%, 0% 50%);
}
.ticks--no li::before { background: var(--text-muted); }

/* A list that carries its own icons drops the generic bullet. The hexagon is the
   brand's affirmative shape, so "what we do not take" gets a different mark
   rather than the same hexagon in a duller fill — the two lists say opposite
   things and should not have to be read to tell them apart. */
.ticks--icons li::before { content: none; }
.ticks--icons .hex { margin-top: .15em; }
.ticks__no {
    font-size: 1.5rem; flex: none; margin-top: .1em;
    color: var(--text-muted);
}
.section--deep .ticks__no { color: var(--muted-on-deep); }   /* 6.13 on #2a2a2a */

/* ------------------------------------------------------------- proof strip icons */
.proof__ico { display: block; margin-bottom: .55rem; }
.proof__ico .icon { font-size: 1.6rem; color: var(--brand-orange); }   /* 5.22 on #2a2a2a */

/* ------------------------------------------------------------- form */
.form { display: grid; gap: 1.1rem; max-width: 34rem; }
.field { display: grid; gap: .35rem; }
.field label { font-weight: 700; font-size: .95rem; }
.field input, .field textarea {
    font: inherit; color: var(--text); background: var(--surface);
    border: 1px solid var(--rule); border-radius: var(--radius);
    padding: .7rem .8rem; min-height: 48px; width: 100%;
}
.field textarea { min-height: 9rem; resize: vertical; }
.field input[aria-invalid="true"], .field textarea[aria-invalid="true"] { border-color: var(--danger); border-width: 2px; }
.field__err { color: var(--danger); font-size: .9rem; margin: 0; font-weight: 600; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.alert { border-left: 4px solid; padding: 1rem 1.25rem; border-radius: var(--radius); background: var(--surface-2); margin-bottom: 1.5rem; }
.alert--err { border-color: var(--danger); }
.alert--ok  { border-color: var(--data); }
.alert p:last-child { margin: 0; }
.form-note { font-size: .9rem; color: var(--text-muted); max-width: 34rem; }

/* ------------------------------------------------------------- footer */
.site-footer { background: var(--surface-deep); color: var(--text-on-deep); padding-block: clamp(2.5rem, 5vw, 3.5rem); font-size: .92rem; }
.site-footer a { color: var(--brand-orange); }
.footer__grid { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); }
.footer__grid h3 { font-size: .8rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted-on-deep); margin-bottom: .8rem; font-weight: 700; }
.footer__grid p, .footer__grid li { color: var(--text-on-deep); max-width: none; }
.footer__list { list-style: none; padding: 0; margin: 0; display: grid; gap: .4rem; }
/* These are standalone links in a list, not links inside a sentence, so the
   2.5.8 "inline" exception does not cover them — they were ~24px of thumb at a
   28px pitch. inline-flex rather than block so the underline stays the width of
   the word. The list gets taller; that is the correct trade on a phone.
   Reverted to the compact pitch at 48em, where the pointer is a mouse. */
.footer__list a, .presskit { display: inline-flex; align-items: center; min-height: 44px; }
.footer__list .h-ico { align-items: center; }
/* Two-up on phones, which pays for the taller rows above: five short labels in
   half-width columns come out shorter than five full-width ones, and read as a
   block of links rather than a ladder. Only ever applied to a list whose labels
   have been checked to fit — see the note at the call site in footer.php. */
.footer__list--cols { grid-template-columns: repeat(2, 1fr); column-gap: 1rem; }
@media (min-width: 48em) {
    .footer__list a, .presskit { min-height: 0; }
    .footer__list--cols { grid-template-columns: 1fr; }
}
.footer__bottom { border-top: 1px solid var(--rule-deep); margin-top: 2.5rem; padding-top: 1.5rem; display: flex; gap: 1rem; justify-content: space-between; flex-wrap: wrap; color: var(--muted-on-deep); }
.presskit { display: inline-flex; align-items: center; gap: .5rem; font-weight: 700; }

/* ------------------------------------------------------------- prose */
.prose h2 { margin-top: 2em; }
.prose h3 { margin-top: 1.5em; }
.prose ul { padding-left: 1.1rem; }
.prose li { margin-bottom: .4rem; }
.prose table { border-collapse: collapse; width: 100%; margin: 1.5rem 0; font-size: .95rem; }
.prose th, .prose td { border: 1px solid var(--rule); padding: .6rem .8rem; text-align: left; vertical-align: top; }
.prose th { background: var(--surface-2); font-weight: 700; }
.table-scroll { overflow-x: auto; }

@media print {
    .site-header, .lang-hint, .hero__cta, .theme-toggle { display: none; }
    body { background: #fff; color: #000; }
}

/* ------------------------------------------------------------- spacing utilities
   These exist because CSP forbids style="" attributes (style-src 'self'), and the
   templates previously carried two dozen of them — every one silently blocked, so
   the spacing they described never applied. Weakening the policy to 'unsafe-inline'
   to win back a margin would have been a bad trade: it is the same directive that
   stops an injected style from repainting the page over a phishing overlay.
   Deliberately a small, closed set — not a utility framework. If you need a value
   that is not here, the element wants a real class. */
.m-0    { margin: 0; }
.mt-s   { margin-top: 1rem; }
.mt-m   { margin-top: 1.25rem; }
.mt-l   { margin-top: 1.5rem; }
.mt-xl  { margin-top: 1.75rem; }
.mt-2xl { margin-top: 2rem; }
.mt-3xl { margin-top: 3rem; }
.my-l   { margin-block: 1.5rem; }
.fs-s   { font-size: .9rem; }
.gap-s  { gap: .6rem; }
.span-all { grid-column: 1 / -1; }
