/* ============================================================================
   SPECTER — core.css
   Design system imported 1:1 from the Claude Design project
   "Specter woodworking app website" (Specter.dc.html).
   Warm paper surface · Archivo + IBM Plex Mono · engineering blue #1F5FD0.
   ========================================================================== */

@import url("https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap");

:root{
  /* ── surfaces ─────────────────────────────────────────────── */
  --paper:      #FBF9F5;   /* page background                    */
  --surface:    #ffffff;   /* cards, panels                      */
  --surface-2:  #FCFBF8;   /* inset / subtle fill                */
  --surface-3:  #F4F1EA;   /* workspace background, hover fill   */
  --surface-4:  #F1EEE6;   /* pressed / segmented track          */
  --surface-5:  #EFEBE2;   /* machine bed, deep inset            */

  /* ── ink ──────────────────────────────────────────────────── */
  --ink:        #1A2430;   /* headings, primary buttons          */
  --ink-2:      #3C4855;   /* body strong                        */
  --ink-3:      #46525F;   /* body                               */
  --mut:        #7C8794;   /* secondary                          */
  --mut-2:      #5A6672;   /* tertiary on light                  */
  /* ── §PREGLED-2 §7 — tri tona koja su padala ispod WCAG praga ──────────
     Mereno WCAG formulom na obe podloge na kojima stvarno stoje: karta
     `--surface` #ffffff i radna površina `--surface-3` #F4F1EA.
       --mut-3  2,79:1 / 2,47:1  →  5,13:1 / 4,54:1
       --mut-4  2,28:1 / 2,02:1  →  3,39:1 / 3,00:1
       --mut-5  2,56:1 / 2,27:1  →  3,41:1 / 3,02:1
     `--mut-3` je najozbiljniji: to nije dekor nego BOJA TEKSTA svakog
     naslova panela u programu (`.label`, `.panel-hd .t`, `.tbl-hd`,
     `.lang-menu .cur-hd`) — na 2,47:1 je bio ispod čak i najblažeg praga
     od 3:1, pa sada traži 4,5:1 kao tekst. `--mut-4` i `--mut-5` su
     dekor/ikonica u mirovanju i traže 3:1, koje sada prelaze.
     Nijedan ton nije promenjen — ista nijansa, samo tamnija. Tamna tema
     (red 236) je merenje prošla bez izmene, pa se ne dira. */
  --mut-3:      #736D63;   /* mono labels, warm grey             */
  --mut-4:      #918B7E;   /* faintest                           */
  --mut-5:      #848C94;   /* icon rest                          */
  --on-ink:     #FFFFFF;   /* text ON an --ink surface                */
  --on-dark:    #A9B4C0;   /* body text on --ink                 */
  --on-dark-2:  #8A97A5;   /* muted on --ink                     */
  --on-dark-3:  #7E8B99;   /* mono label on --ink                */

  /* ── §18.6 — the two whites that are NOT `--on-ink` ────────────────────
     There were twenty bare `#fff` in the stylesheets and they were not twenty
     decisions, they were two, repeated:

       · text or an icon sitting on a SATURATED or permanently dark fill —
         the accent button, a danger chip, the mark on the sign-in art panel.
         White is right there in both themes, because the fill underneath does
         not turn with the theme either.
       · an object that is actually white — a switch knob, a slider thumb, the
         sheet of paper in the print preview, the halo around a drill dot.

     Both are deliberately fixed. `--on-ink` is the one that DOES turn, because
     `--ink` turns with it (that was bug §21.18), and mixing the three up is
     how a button ends up white on white. */
  --on-solid:   #FFFFFF;   /* on an accent / danger / always-dark fill */
  --white:      #FFFFFF;   /* a white OBJECT: knob, thumb, printed page */

  /* ── hairlines ────────────────────────────────────────────── */
  --line:       #E6E2D9;   /* section divider                    */
  --line-2:     #EDE9E0;   /* card inner divider                 */
  --line-3:     #F4F1EA;   /* table row                          */
  --line-4:     #F6F3EC;   /* faintest row                       */
  --line-5:     #F0EDE5;   /* panel header                       */
  --border:     #E0DCD2;   /* input + control border             */
  --border-2:   #DCD7CC;   /* strong control border              */
  --border-3:   #DFDAD0;   /* framed viewport border             */
  --border-4:   #EFEBE2;   /* stat column divider                */

  /* ── brand + semantic ─────────────────────────────────────── */
  --acc:        #1F5FD0;
  --acc-ink:    #16408f;
  --acc-2:      #5D8DE8;
  --acc-3:      #6E9BEC;
  --acc-4:      #5A8BE0;
  --acc-soft:   rgba(31,95,208,.07);
  --acc-soft-2: rgba(31,95,208,.11);
  --acc-line:   rgba(31,95,208,.18);
  --acc-line-2: rgba(31,95,208,.25);

  --ok:         #1F7A4A;
  --ok-soft:    rgba(31,122,74,.08);
  --ok-line:    rgba(31,122,74,.20);
  --warn:       #C2703A;
  --warn-soft:  rgba(194,112,58,.05);
  --warn-line:  rgba(194,112,58,.35);
  --danger:     #B03A2E;
  --danger-2:   #D6453C;
  --danger-soft:rgba(176,58,46,.07);
  --danger-line:rgba(176,58,46,.22);
  --violet:     #7A3FA6;
  /* §55.14 — narandžasta ZNAKA. Namerno NIJE `--warn`: `--warn` znači
     „pazi", a znak ne sme da nosi boju stanja — inače bi svaka strana
     izgledala kao da nešto upozorava. Mereno prema STVARNOJ iscrtanoj podlozi
     (`node tools/znak.js`, ne prema onome što piše u fajlu): `--brand-wood`
     na zaglavlju #FBF9F5 **4,92:1**, `--brand-wood-dark` na tamnom #0A0E14
     **8,13:1**. Jedna ista boja ne prolazi na obe podloge — najbolja od devet
     probanih pada na 3,03:1 na tamnom — pa ih je dve.

     IME nosi `brand-` NAMERNO. Prvi pokušaj se zvao samo `--wood` i **tiho ga
     je pregazio `--wood: #D8BE96` iz reda 109 — boja DASKE u crtežu.** Natpis
     je ispao svetlo-peščan na skoro beloj podlozi: kontrast 1,7:1, čita se
     jedva. Očima se to ne vidi kao kvar nego kao „nežna nijansa"; `znak.js`
     ga je našao iz prvog pokušaja. Dva različita pojma ne smeju da dele ime. */
  --brand-wood:      #C2410C;
  --brand-wood-dark: #F0913F;

  /* material / CNC legend */
  --drill-dowel:  #D6453C;
  --drill-sys32:  #1F7A4A;
  --drill-hinge:  #7A3FA6;
  --wood:         #D8BE96;

  /* ── radii ────────────────────────────────────────────────── */
  /* --r-1 je najmanji korak: predmet visok 12 px (nacrtana zastavica) sa
     radijusom 4 px više nije zaobljen nego zaobljast — trećina svoje visine. */
  --r-1:2px;
  --r-2:4px; --r-3:5px; --r-4:6px; --r-5:7px; --r-6:8px; --r-7:9px;
  --r-8:10px; --r-9:11px; --r-10:12px; --r-12:14px; --r-14:16px;
  --r-16:18px; --r-pill:99px;

  /* ── elevation ────────────────────────────────────────────── */
  --sh-1: 0 8px 20px -14px rgba(26,36,48,.50);
  --sh-2: 0 14px 30px -22px rgba(26,36,48,.70);
  --sh-3: 0 24px 44px -30px rgba(26,36,48,.55);
  --sh-4: 0 40px 80px -50px rgba(26,36,48,.50), 0 2px 0 rgba(26,36,48,.03);
  --sh-5: 0 50px 90px -40px rgba(0,0,0,.55);
  --sh-btn: 0 10px 26px -14px rgba(26,36,48,.90);
  --sh-btn-h: 0 18px 34px -16px rgba(26,36,48,.90);
  --sh-acc: 0 20px 40px -34px rgba(31,95,208,.80);

  /* ── §31.6 — the two shadows that are NOT elevation ──────────────────────
     `--sh-1…5` answer one question: how far off the page is this box. A focus
     ring and a knob's contact shadow answer different questions — „the
     keyboard is here" and „this object is lying on that track" — so forcing
     them onto the elevation scale would be filing them under the wrong thing.
     They were written out by hand instead, which is how the same ring ended up
     4 px in `core.css` and 3 px in `app.css`.

     TWO ring sizes, deliberately, and both are read off the design source
     (`docs/design/Specter.dc.html`): the sign-in inputs there, 13 px of
     padding, carry `0 0 0 4px`; the inspector fields, 9 px of padding, carry
     `0 0 0 3px`. The same ring around a smaller control stops reading as a
     ring and starts reading as a glow. `--sh-focus-sm` has no user in this
     file — its users are the compact fields in `app.css`, and a token that
     lives anywhere but here is not a token.

     The colours are literal rather than `color-mix(var(--acc))` on purpose:
     that would also change the ring in the dark theme, and this pass is a
     tidy-up, not a redesign. */
  --sh-focus:        0 0 0 4px rgba(31,95,208,.12);
  --sh-focus-sm:     0 0 0 3px rgba(31,95,208,.12);
  --sh-focus-danger: 0 0 0 4px rgba(176,58,46,.12);
  /* one white object resting on a track: switch knob, slider thumb */
  --sh-knob:         0 1px 3px rgba(0,0,0,.25);
  /* a marker that must stay readable on top of ANY drawing under it — the zero
     point on the machine bed. Not elevation: a hairline of paper around a dot. */
  --sh-halo:         0 0 0 2px var(--white);
  /* the one surface whose shadow falls UPWARD, because it comes up from the
     bottom edge: the client sheet (§30.10). Same weight as `--sh-3`, mirrored. */
  --sh-up:           0 -20px 50px -30px rgba(26,36,48,.6);

  /* ── motion ───────────────────────────────────────────────── */
  --ease:    cubic-bezier(.2,.8,.2,1);
  --ease-io: cubic-bezier(.65,0,.35,1);
  /* ── §18.6 — ONE motion scale ────────────────────────────────────────────
     Measured before this was written: **117 raw durations** in the
     stylesheets across 19 distinct values — .1, .12, .13, .14, .16, .18, .2,
     .22, .25, .28, .3, .35, .38, .4, .44, .5, .6, .7, .8 — which is not a
     scale, it is whatever each rule felt like on the day. The same fault the
     type scale had, and the same fix.

     `--t-1`, `--t-2` and `--t-3` keep their exact values because 46, 32 and 15
     rules already ride them and a scale is worthless if adopting it changes
     what is on screen. `--t-4` and `--t-5` had **no uses at all**, so they are
     re-pitched to the steps the data actually asks for, and two longer steps
     are added for entrances and scene moves.

     A duration outside this scale is allowed only where the number is part of
     a sequence tuned against something else. §24.13's opening was excused on
     that ground and the excuse did not survive being checked: `opening.js`
     writes the paced numbers INLINE, which beats any stylesheet, so the CSS
     values were never the ones running. They are on the scale now — see the
     note over `.fo-reveal .stage3d` in `site.css`.

     What genuinely stays off the scale is a STAGGER: `.fo-file`'s `.065s` step
     is the gap between one tile and the next, not how long anything takes. */
  --t-0: .12s;   /* micro: a tint, a border, a cursor press               */
  --t-1: .16s;   /* control state                                         */
  --t-2: .18s;   /* small move                                            */
  --t-3: .25s;   /* panel, fade                                           */
  --t-4: .34s;   /* larger move                                           */
  --t-5: .45s;   /* the big one                                           */
  --t-6: .62s;   /* an entrance                                           */
  --t-7: .9s;    /* a scene changing                                      */

  /* ── §31.6 — a loop is not a step on that scale ──────────────────────────
     Everything above answers „how long does this take to get from A to B".
     An animation that runs for ever has no A and no B; what it has is a
     RHYTHM, and a rhythm picked off a transition scale is either a strobe or
     a stall. So they are a category of their own, and they are named after
     what they are doing, not after how long they take.

     Measured before this block existed: six raw values across the three
     stylesheets — 1.4, 1.6, 1.8, 2, 3.6, 5.5 s. Three of the six were the
     same thing at three speeds (something breathing to say „this is live":
     the live chip, the hero dot, the sign-in caption), 1.6 / 1.8 / 2 s. One
     rhythm cannot be three numbers, so it is one token; the two that moved
     went ±.2 s, and two pulses have to differ by more than that before an eye
     watching one of them can tell.

     The sweep and the scan stay apart: 3.6 against 5.5 s is 34 %, they cross
     different distances, and one of them is meant to look like a measurement
     being taken. */
  --t-loop-pulse:   1.8s;  /* an indicator breathing: live chip, hero dot   */
  --t-loop-shimmer: 1.4s;  /* a skeleton waiting for its content            */
  --t-loop-sweep:   5.5s;  /* the slow band across a marketing picture      */
  --t-loop-scan:    3.6s;  /* the LiDAR pass down the phone — a scan reads
                              as work being done, so it beats the sweep      */
  --t-loop-float:   6s;    /* the mark on an EMPTY viewport, rising and
                              settling. Slower than the sweep on purpose:
                              nothing is happening, and the motion says so   */
  /* „off", for prefers-reduced-motion. Not `0`: a zero-length animation
     fires no `animationend`, and anything waiting on one would wait for
     ever. This is the smallest duration that still ends. */
  --t-off: .01ms;
  /* ── §18.6 — ONE type scale ──────────────────────────────────────────────
     Measured before this existed: **282 raw `font-size` declarations across
     30 distinct sizes**, half of them half-pixel steps (8.5, 9.5, 10.5, 11.5,
     12.5, 13.5, 14.5, 15.5, 16.5, 18.5). That is not a scale, it is a list,
     and it is most of what "a mixture of a hundred styles" means. These
     fourteen steps are taken FROM that data — the sizes that were actually
     carrying the design are kept exactly, and the half-steps snap to the
     nearest one. Nothing may set a raw px font-size again. */
  --fs-9:9px;   --fs-10:10px; --fs-11:11px; --fs-12:12px; --fs-13:13px;
  --fs-14:14px; --fs-15:15px; --fs-16:16px; --fs-17:17px; --fs-19:19px;
  --fs-22:22px; --fs-27:27px; --fs-34:34px; --fs-42:42px;


  /* ── type ─────────────────────────────────────────────────── */
  --font: Archivo, Helvetica, Arial, system-ui, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Consolas, monospace;

  /* ── layout ───────────────────────────────────────────────── */
  --wrap: 1240px;
  --pad: 28px;
  --head-h: 66px;
  --tap: 44px;
  --safe-t: env(safe-area-inset-top,0px);
  --safe-b: env(safe-area-inset-bottom,0px);
  --safe-l: env(safe-area-inset-left,0px);
  --safe-r: env(safe-area-inset-right,0px);
}

/* ── dark theme ─────────────────────────────────────────────── */
html[data-theme="dark"]{
  --paper:#10161F; --surface:#161E29; --surface-2:#131A24; --surface-3:#0D131B;
  --surface-4:#1B2431; --surface-5:#1E2836;
  --ink:#F1F4F8; --ink-2:#D3DBE4; --ink-3:#B7C1CC; --mut:#8896A5; --mut-2:#9AA7B4;
  --mut-3:#77828F; --mut-4:#5E6975; --mut-5:#79848F;
  --line:#232E3C; --line-2:#1F2937; --line-3:#1C2531; --line-4:#1A222D; --line-5:#202A37;
  --border:#2A3542; --border-2:#35414F; --border-3:#2A3542; --border-4:#1F2937;
  --acc:#4E86EE; --acc-ink:#7FA9F0;
  --acc-soft:rgba(78,134,238,.12); --acc-soft-2:rgba(78,134,238,.18);
  --acc-line:rgba(78,134,238,.28); --acc-line-2:rgba(78,134,238,.38);
  --on-ink:#10161F;        /* dark theme: --ink is light, so ink-backed
                              surfaces need DARK text or the button reads blank */
  --on-dark:#A9B4C0; --wood:#B79A72;
  --sh-4: 0 40px 80px -50px rgba(0,0,0,.9), 0 2px 0 rgba(255,255,255,.02);
  --sh-5: 0 50px 90px -40px rgba(0,0,0,.85);
}

/* ============================ reset ====================================== */
*,*::before,*::after{ box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
html{ scroll-behavior:smooth; -webkit-text-size-adjust:100%; }
html,body{ margin:0; padding:0; }
body{
  background:var(--paper); color:var(--ink); font-family:var(--font);
  font-size:var(--fs-15); line-height:1.5; -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale; overscroll-behavior-y:none;
}
[hidden]{ display:none !important; }
img,svg,canvas,video{ display:block; max-width:100%; }
canvas{ touch-action:none; }
input,button,select,textarea{ font-family:inherit; color:inherit; }
button{ background:none; border:0; padding:0; cursor:pointer; }
a{ color:var(--acc); text-decoration:none; }
a:hover{ color:var(--acc-ink); }
h1,h2,h3,h4,h5,p,figure{ margin:0; }
ul,ol{ margin:0; padding:0; list-style:none; }
table{ border-collapse:collapse; width:100%; }
:focus{ outline:none; }
:focus-visible{ outline:2px solid var(--acc); outline-offset:2px; border-radius:var(--r-4); }
::selection{ background:var(--acc); color:var(--on-solid); }
::-webkit-scrollbar{ width:10px; height:10px; }
::-webkit-scrollbar-track{ background:transparent; }
::-webkit-scrollbar-thumb{ background:#D6DDE4; border-radius:var(--r-8); }
html[data-theme=dark] ::-webkit-scrollbar-thumb{ background:#2C3745; }
.sr-only{ position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap; border:0; }

/* ============================ keyframes ================================== */
@keyframes spFloat { 0%,100%{ transform:translateY(0) } 50%{ transform:translateY(-12px) } }
@keyframes spDash  { to { stroke-dashoffset:0 } }
@keyframes spPulse { 0%,100%{ opacity:.35; transform:scale(1) } 50%{ opacity:1; transform:scale(1.35) } }
@keyframes spSweep { 0%{ transform:translateY(-100%) } 100%{ transform:translateY(400%) } }
@keyframes spSpin  { to { transform:rotate(360deg) } }
@keyframes spIn    { from{ opacity:0; transform:translateY(18px) } to{ opacity:1; transform:none } }
@keyframes spBlink { 0%,100%{ opacity:1 } 50%{ opacity:.15 } }
@keyframes spSlide { from{ opacity:0; transform:translateY(10px) scale(.99) } to{ opacity:1; transform:none } }
@keyframes spShake { 10%,90%{transform:translateX(-1px)} 20%,80%{transform:translateX(2px)}
                     30%,50%,70%{transform:translateX(-4px)} 40%,60%{transform:translateX(4px)} }
@keyframes spShimmer{ 0%{ background-position:100% 0 } 100%{ background-position:0 0 } }

/* ============================ typography ================================= */
.mono{ font-family:var(--mono); font-variant-numeric:tabular-nums; }
.kicker{ font-family:var(--mono); font-size:var(--fs-11); letter-spacing:.15em; color:var(--mut);
  text-transform:uppercase; }
.kicker.acc{ color:var(--acc); }
.label{ font-family:var(--mono); font-size:var(--fs-10); letter-spacing:.13em; color:var(--mut-3);
  text-transform:uppercase; }
.h1{ font-size:clamp(38px,4.4vw,64px); line-height:1.03; letter-spacing:-.035em; font-weight:700;
  text-wrap:balance; }
.h2{ font-size:clamp(30px,3vw,42px); line-height:1.08; letter-spacing:-.035em; font-weight:700;
  text-wrap:balance; }
.h3{ font-size:clamp(28px,2.8vw,40px); line-height:1.08; letter-spacing:-.035em; font-weight:700; }
.h4{ font-size:var(--fs-22); letter-spacing:-.02em; font-weight:600; }
.h5{ font-size:var(--fs-17); letter-spacing:-.02em; font-weight:600; }
.lead{ font-size:var(--fs-19); line-height:1.62; color:var(--ink-3); text-wrap:pretty; }
.body{ font-size:var(--fs-16); line-height:1.75; color:var(--ink-2); text-wrap:pretty; }
.small{ font-size:var(--fs-14); line-height:1.6; color:var(--ink-3); }
.tiny{ font-size:var(--fs-12); line-height:1.5; color:var(--mut); }
.mut{ color:var(--mut); }

/* ============================ layout ===================================== */
.wrap{ max-width:var(--wrap); margin:0 auto; padding-inline:var(--pad); }
.row{ display:flex; align-items:center; gap:10px; }
.col{ display:flex; flex-direction:column; }
.stack{ display:flex; flex-direction:column; gap:12px; }
.spacer{ flex:1 1 auto; min-width:0; }
.wrapline{ flex-wrap:wrap; }
.sec{ border-bottom:1px solid var(--line); }
.sec-pad{ padding-block:88px; }
.grid-bg{
  background-image:linear-gradient(rgba(31,95,208,.055) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(31,95,208,.055) 1px,transparent 1px);
  background-size:34px 34px;
}
.grid-bg-sm{
  background-image:linear-gradient(rgba(31,95,208,.06) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(31,95,208,.06) 1px,transparent 1px);
  background-size:26px 26px;
}
.grid-bg-dark{
  background-image:linear-gradient(rgba(255,255,255,.05) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(255,255,255,.05) 1px,transparent 1px);
  background-size:34px 34px;
}

/* ============================ header ===================================== */
.hdr{
  position:sticky; top:0; z-index:60;
  background:color-mix(in srgb, var(--paper) 86%, transparent);
  -webkit-backdrop-filter:blur(14px); backdrop-filter:blur(14px);
  border-bottom:1px solid var(--line);
}
@supports not (background: color-mix(in srgb, red 50%, blue)){ .hdr{ background:var(--paper); } }
/* §55.16 — zaglavlje ide OD IVICE DO IVICE, ne po širini teksta ispod.
   Ranije je bilo ograničeno na `--wrap` (1240 px) i centrirano, pa je na
   ekranu od 1440 px znak počinjao na 128 px od ivice — poravnat sa telom
   strane, ali sa velikom prazninom levo od sebe. Sada počinje na `--pad`.
   Posledica koju treba znati: znak više NIJE u istoj koloni sa tekstom
   ispod. To je namerna zamena — traka uz ivicu, sadržaj po sredini. */
.hdr-in{ max-width:none; margin:0; padding:0 var(--pad); height:var(--head-h);
  display:flex; align-items:center; gap:34px; }
.hdr-prog{ height:2px; background:var(--border-4); position:relative; overflow:hidden; }
.hdr-prog > i{ position:absolute; inset:0 auto 0 0; width:0;
  background:linear-gradient(90deg,var(--acc),var(--acc-2)); transition:width var(--t-0) linear; }

/* ── the Specter mark — assets/js/brand.js draws it, this only places it ── */
/* Razmak od simbola do natpisa je .30em NATPISA, ne zakucanih 10 px. Dva
   razloga: znak se pojavljuje u dve veličine (19 i 16 px) i sa zakucanom
   vrednošću manji izgleda razmaknutije nego veći; i sam natpis je sada viši
   (dva reda), pa je stari razmak od 10 px odvajao simbol od imena umesto da
   ih drži zajedno. Mereno: 10 px → 5,7 px na velikom, 4,8 px na malom. */
.brand{ display:flex; align-items:center; gap:.20em; cursor:pointer; user-select:none;
  color:var(--ink); font-size:var(--fs-22); }
.brand.sm{ font-size:var(--fs-19); }
/* §17.5 — the mark is one colour, grey, lines only. It never takes the
   accent and it never fills. */
.brand .mark{ color:var(--mut-2,#5A636E); transition:color var(--t-2); }
.brand:hover .mark{ color:var(--ink); }
.brand .mark{ width:32px; height:32px; flex:0 0 auto; display:grid; place-items:center; }
.sp-mark{ display:block; overflow:visible; }
.brand .mark .sp-mark{ width:100%; height:100%; }
/* §55.14 — natpis je dvoredan, i OBA reda su pravi elementi.
   Prvi red je isprva bio go tekst, pa se merio opsegom preko čvora — a Chrome
   taj pravougaonik zaokružuje na cele piksele (mereno: 21 px na slovima od
   19 px, ali 17 px na slovima od 16 px, dakle odnos 1,105 prema 1,063). Zbog
   toga je JEDAN ISTI razmak u `em`-ima ispadao različit u merenju i alatka je
   prijavljivala kvar koga nema. Sa `w1` oba reda imaju svoju kutiju. */
.brand .name{ font-size:1em; font-weight:700; letter-spacing:-.03em; align-self:baseline;
  display:flex; flex-direction:column; align-items:center; line-height:1; }
.brand .name .w1{ line-height:1; }
/* `WOOD` je CENTRIRAN pod `Specter`, ne poravnat levo.
   Prva verzija ga je razvlačila razmakom slova da bude iste širine kao red
   iznad, i merenje je tvrdilo da JESTE — 67,26 prema 67,27 px. Merenje je
   bilo pogrešno: merilo je KUTIJU, a fleks-stavka u koloni se podrazumevano
   razvuče na širinu roditelja. Broj je bio tačan za kutiju i besmislen za
   slova, koja su na slici očigledno kraća. Slika je pobedila broj.
   `margin-right` skida razmak koji `letter-spacing` dodaje i IZA poslednjeg
   slova; bez toga centriranje pomeri slova ulevo za pola razmaka. */
.brand .name .wood{ font-size:.50em; font-weight:700; letter-spacing:.24em; line-height:1;
  margin-top:-.06em; margin-right:-.24em; color:var(--brand-wood); }
/* Oznaka `BETA`: CEO OKVIR je visok koliko malo slovo `Specter`-a, sedi na
   istoj osnovnoj liniji, a slova u njemu su još manja od okvira.

   · visina okvira — `.545 × veličina slova`. Odnos nije pogođen: mereno
     `tools/znak.js`, x-visina je 12 px na slovima od 22 px i 10 px na 19 px.
     `ex` jedinica bi bila prirodnija, ali se u CSS-u računa prema pismu SAME
     oznake (ovde tanko, mono), a treba nam pismo `Specter`-a — zato broj.
   · osnovna linija — `align-self:baseline` + `overflow:hidden`. Drugi deo nije
     ukras: kutija sa sakrivenim prelivom uzima DONJU IVICU za svoju osnovnu
     liniju, pa okvir sedne tačno na liniju `Specter`-a i gornjom ivicom dohvati
     visinu malih slova. Bez toga bi se poravnavao po tekstu u sebi.
   · boja — plava, i slova i okvir. */
.brand{ --brand-fs:var(--fs-22); }
.brand.sm{ --brand-fs:var(--fs-19); }
.brand .tag{ font-family:var(--mono); font-size:calc(.30 * var(--brand-fs));
  letter-spacing:.10em; line-height:1;
  color:var(--acc); border:1px solid var(--acc-line-2,rgba(31,95,208,.42));
  border-radius:var(--r-2); padding:0 .40em; box-sizing:border-box;
  height:calc(.545 * var(--brand-fs));
  align-self:baseline; overflow:hidden;
  /* Poravnanje po osnovnoj liniji spusti TEKST oznake na liniju „Specter"-a,
     a traženo je da na njoj sedi DONJA IVICA OKVIRA. Razlika je izmerena, ne
     procenjena: 3,98 px na slovima od 22 px i 3,34 px na 19 px — dakle .178
     veličine slova, isto na obe. Merena je osnovna linija koju prijavljuje
     sam pregledač (prazan element poravnat po njoj), ne izračunata iz metrika
     pisma; račun i merenje su se poklopili u stotinku, pa je broj pouzdan. */
  position:relative; top:calc(-.178 * var(--brand-fs));
  /* `inline-block`, NE `inline-flex`: pravilo „kutija sa sakrivenim prelivom
     uzima donju ivicu za osnovnu liniju" vazi za `inline-block`. Sa
     `inline-flex` je Chrome uzimao osnovnu liniju TEKSTA u kutiji i okvir je
     sedao 4,3 px prenisko. Slova se centriraju razmakom redova. */
  line-height:calc(.545 * var(--brand-fs) - 2px);
  display:inline-block; text-align:center; }
.brand.sm .mark{ width:27px; height:27px; }
.brand.on-dark{ color:var(--on-solid); }
.brand.on-dark .mark{ color:rgba(255,255,255,.72); }
/* Oznaka na tamnom: `--mut` i `--border` su smišljeni za svetlu podlogu i na
   tamnoj se ne vide. Vrednosti su birane merenjem kontrasta, ne po oku. */
/* Na tamnoj podlozi ista plava pada nisko; `--acc-3` je isti ton podignut. */
.brand.on-dark .tag{ color:var(--acc-3); border-color:rgba(110,155,236,.42); }
/* Na tamnoj podlozi ista narandžasta pada na 3,03:1 i više se ne čita. Zato
   postoji drugi ton — nije druga boja, isti je ton podignut da bi prošao. */
.brand.on-dark .name .wood{ color:var(--brand-wood-dark); }

/* mark + wordmark as one unit, for splash screens and print */
.sp-lockup{ display:inline-flex; align-items:center; gap:.20em; color:var(--ink);
  font-size:var(--sp-mark,28px); line-height:1; }
.sp-lockup .sp-word{ font-size:.78em; font-weight:700; letter-spacing:-.035em;
  display:flex; flex-direction:column; align-items:center; line-height:1; }
.sp-lockup .sp-word .w1{ line-height:1; }
/* Isti odnosi kao u zaglavlju — inače bi znak na uvodnom ekranu i na štampi
   bio drugi znak, a ne isti u drugoj veličini. */
.sp-lockup .sp-word .sp-wood{ font-size:.50em; letter-spacing:.24em; line-height:1;
  margin-top:-.06em; margin-right:-.24em; color:var(--brand-wood); }
.sp-lockup.on-dark .sp-word .sp-wood{ color:var(--brand-wood-dark); }
.sp-lockup .sp-sub{ font-family:var(--mono); font-size:.3em; letter-spacing:.16em;
  color:var(--mut); align-self:flex-end; padding-bottom:.35em; }

.nav{ display:flex; gap:4px; margin-left:6px; }
/* ══ §38.5 — navigacija na uskom ekranu ══════════════════════════════════
   Dugme postoji samo tamo gde je traka pretesna za spisak; ispod 1100 px
   navigacija se seli u panel ispod zaglavlja. Panel se zatvara na `Esc`,
   na klik van sebe i na drugi pritisak (§27.2), i **ne pravi masku** —
   maska bi napravila novi kontekst isecanja i pojela jezički meni. */
/* `.btn` je opisan POSLE ovoga i takođe je jedna klasa, pa bi golo
   `.hdr-burger{display:none}` izgubilo od njega — otuda roditelj u selektoru. */
.hdr-in .hdr-burger{ display:none; }
.hdr-burger-i, .hdr-burger-i::before, .hdr-burger-i::after{
  display:block; width:17px; height:1.6px; border-radius:var(--r-1);
  background:currentColor; transition:transform var(--t-2) var(--ease),
    opacity var(--t-1) var(--ease); }
.hdr-burger-i::before, .hdr-burger-i::after{ content:""; position:absolute; }
.hdr-burger-i::before{ transform:translateY(-5.5px); }
.hdr-burger-i::after{ transform:translateY(5.5px); }
.hdr-burger[aria-expanded="true"] .hdr-burger-i{ background:transparent; }
.hdr-burger[aria-expanded="true"] .hdr-burger-i::before{ transform:rotate(45deg); }
.hdr-burger[aria-expanded="true"] .hdr-burger-i::after{ transform:rotate(-45deg); }
.nav a,.nav button{ padding:7px 13px; border-radius:var(--r-6); font-size:var(--fs-14); font-weight:500;
  color:var(--ink-3); cursor:pointer; transition:background var(--t-2),color var(--t-2); }
.nav a:hover,.nav button:hover{ background:var(--surface-4); color:var(--ink); }
.nav a.on,.nav button.on{ background:var(--surface-4); color:var(--ink); font-weight:600; }

/* ============================ buttons ==================================== */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  padding:9px 17px; min-height:38px; border-radius:var(--r-7);
  font-size:var(--fs-14); font-weight:600; letter-spacing:-.005em; white-space:nowrap;
  background:var(--surface); color:var(--ink); border:1.5px solid var(--border);
  cursor:pointer; user-select:none; touch-action:manipulation;
  transition:transform var(--t-2), box-shadow var(--t-2), border-color var(--t-2),
             background var(--t-2), color var(--t-2), opacity var(--t-2);
}
.btn:hover{ border-color:var(--ink); }
.btn:active{ transform:translateY(0) scale(.985); }
.btn[disabled],.btn.is-off{ opacity:.45; pointer-events:none; }

.btn-primary{ background:var(--ink); color:var(--on-ink); border-color:transparent; box-shadow:var(--sh-btn); }
.btn-primary:hover{ transform:translateY(-1px); box-shadow:var(--sh-btn-h); border-color:transparent; }
.btn-acc{ background:var(--acc); color:var(--on-solid); border-color:transparent; }
.btn-acc:hover{ transform:translateY(-1px); background:var(--acc-ink); border-color:transparent; }
.btn-ghost{ background:transparent; border-color:transparent; font-weight:500; color:var(--ink); }
.btn-ghost:hover{ background:var(--surface-4); border-color:transparent; }
.btn-danger{ color:var(--danger); border-color:var(--danger-line); background:var(--danger-soft); }
.btn-danger:hover{ border-color:var(--danger); }
.btn-lg{ padding:14px 24px; min-height:50px; font-size:var(--fs-15); border-radius:var(--r-9); }
.btn-sm{ padding:6px 11px; min-height:30px; font-size:var(--fs-12); border-radius:var(--r-5); }
.btn-block{ width:100%; }
.btn-icon{ width:38px; height:38px; padding:0; flex:0 0 auto; }
.btn-icon.sm{ width:34px; height:34px; border-radius:var(--r-6); }
.btn .spin{ width:14px; height:14px; border:2px solid rgba(255,255,255,.3); border-top-color:var(--on-solid);
  border-radius:var(--r-pill); animation:spSpin var(--t-6) linear infinite; display:inline-block; }
.btn-primary.is-loading{ pointer-events:none; }

/* rail / tool buttons */
.tool{ width:34px; height:34px; border-radius:var(--r-6); display:flex; align-items:center;
  justify-content:center; cursor:pointer; font-size:var(--fs-14); color:var(--mut-2);
  background:transparent; transition:background var(--t-1), color var(--t-1); }
.tool:hover{ background:var(--surface-4); color:var(--ink); }
.tool.on{ background:var(--ink); color:var(--on-ink); }
.tool.lg{ width:38px; height:38px; border-radius:var(--r-8); font-size:var(--fs-15); }
.tool.mini{ width:22px; height:22px; border-radius:var(--r-4); font-size:var(--fs-10); color:var(--mut-5); }
.tool.mini:hover{ background:var(--surface-3); color:var(--ink); }

/* ============================ cards & panels ============================= */
.card{ border:1px solid var(--line); border-radius:var(--r-12); background:var(--surface);
  transition:transform var(--t-3), box-shadow var(--t-3), border-color var(--t-3); }
.card.hoverable:hover{ transform:translateY(-4px); box-shadow:var(--sh-3); border-color:var(--border-2); }
.card-pad{ padding:26px; }

.panel{ border:1px solid var(--border); border-radius:var(--r-10); background:var(--surface);
  display:flex; flex-direction:column; min-height:0; overflow:hidden; }
.panel-hd{ padding:8px 8px 8px 12px; border-bottom:1px solid var(--line-5); display:flex;
  align-items:center; gap:6px; flex:0 0 auto; }
.panel-hd .t{ font-family:var(--mono); font-size:var(--fs-9); letter-spacing:.13em; color:var(--mut-3);
  text-transform:uppercase; }
.panel-hd .s{ font-size:var(--fs-11); color:var(--mut); margin-top:2px; white-space:nowrap;
  overflow:hidden; text-overflow:ellipsis; }
.panel-bd{ flex:1; min-height:0; overflow:auto; }
.panel-hd-lg{ padding:12px 17px; border-bottom:1px solid var(--line-5); display:flex;
  align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap; flex:0 0 auto; }

.frame{ border:1px solid var(--border-3); border-radius:var(--r-14); background:var(--surface);
  box-shadow:var(--sh-4); overflow:hidden; }
.frame-bar{ height:38px; border-bottom:1px solid var(--line-2); display:flex; align-items:center;
  gap:8px; padding:0 14px; background:var(--surface-2); }
.frame-bar .dot{ width:9px; height:9px; border-radius:var(--r-pill); background:#E4DFD4; }
.frame-bar .path{ font-family:var(--mono); font-size:var(--fs-11); color:var(--mut); margin-left:8px; }

.inverted{ background:var(--ink); color:var(--on-ink); border-color:var(--ink); }
.inverted .mut,.inverted .small{ color:var(--on-dark); }
.inverted .label{ color:var(--on-dark-3); }

/* ============================ form controls ============================== */
.field{ display:flex; flex-direction:column; gap:7px; min-width:0; }
.field > label,.field .lbl{ font-family:var(--mono); font-size:var(--fs-11); letter-spacing:.12em;
  color:var(--mut); text-transform:uppercase; }
.inp,input[type=text],input[type=email],input[type=password],input[type=number],
input[type=search],select,textarea{
  width:100%; padding:13px 14px; border:1.5px solid var(--border); border-radius:var(--r-8);
  font-size:var(--fs-15); background:var(--surface); color:var(--ink); outline:none;
  transition:border-color var(--t-2), box-shadow var(--t-2);
}
.inp:focus,input:focus,select:focus,textarea:focus{
  border-color:var(--acc); box-shadow:var(--sh-focus); }
.inp::placeholder,input::placeholder,textarea::placeholder{ color:var(--mut-4); }
.inp.sm{ padding:9px 11px; font-size:var(--fs-13); border-radius:var(--r-6); background:var(--surface-2);
  border-color:var(--line); }
.inp.mono{ font-family:var(--mono); font-size:var(--fs-13); }
.field.err .inp,.field.err input{ border-color:var(--danger); box-shadow:var(--sh-focus-danger); }
textarea.inp{ min-height:92px; resize:vertical; line-height:1.55; }

input[type=range]{ -webkit-appearance:none; appearance:none; width:100%; height:22px;
  background:transparent; accent-color:var(--acc); cursor:pointer; }
input[type=range]::-webkit-slider-runnable-track{ height:5px; border-radius:var(--r-pill);
  background:linear-gradient(90deg,var(--acc) var(--pct,0%),var(--line-3) var(--pct,0%)); }
input[type=range]::-webkit-slider-thumb{ -webkit-appearance:none; width:16px; height:16px;
  margin-top:-5.5px; border-radius:var(--r-pill); background:var(--white); border:1.5px solid var(--acc);
  box-shadow:var(--sh-knob); }
input[type=range]::-moz-range-track{ height:5px; border-radius:var(--r-pill); background:var(--line-3); }
input[type=range]::-moz-range-thumb{ width:16px; height:16px; border-radius:var(--r-pill);
  background:var(--white); border:1.5px solid var(--acc); }

/* switch (design: 42×24 track, 18px knob) */
.sw{ width:42px; height:24px; border-radius:var(--r-pill); background:var(--border-2);
  position:relative; flex:0 0 auto; cursor:pointer; transition:background var(--t-3);
  border:0; padding:0; }
.sw > i{ position:absolute; top:3px; left:3px; width:18px; height:18px; border-radius:var(--r-pill);
  background:var(--white); box-shadow:var(--sh-knob); transition:left var(--t-3) var(--ease); }
.sw[aria-checked="true"]{ background:var(--acc); }
.sw[aria-checked="true"] > i{ left:21px; }
.sw-row{ display:flex; align-items:center; gap:16px; }

/* segmented control */
.seg{ display:inline-flex; padding:4px; background:var(--surface-4); border:1px solid var(--line);
  border-radius:var(--r-9); gap:0; }
.seg > button{ padding:8px 16px; border-radius:var(--r-6); font-size:var(--fs-14); font-weight:600;
  color:var(--ink-3); cursor:pointer; transition:background var(--t-3), color var(--t-3);
  white-space:nowrap; }
.seg > button.on{ background:var(--ink); color:var(--on-ink); }

/* chips / pills */
.chip{ display:inline-flex; align-items:center; gap:6px; font-family:var(--mono); font-size:var(--fs-10);
  letter-spacing:.1em; padding:5px 9px; border-radius:var(--r-pill); text-transform:uppercase;
  background:var(--surface-3); color:var(--mut-2); border:1px solid transparent; white-space:nowrap; }
.chip.acc{ background:var(--acc-soft); color:var(--acc); border-color:var(--acc-line); }
.chip.ok{ background:var(--ok-soft); color:var(--ok); border-color:var(--ok-line); }
.chip.warn{ background:rgba(194,112,58,.09); color:var(--warn); border-color:var(--warn-line); }
.chip.danger{ background:var(--danger-soft); color:var(--danger); border-color:var(--danger-line); }
.chip.live::before{ content:""; width:6px; height:6px; border-radius:var(--r-pill);
  background:currentColor; animation:spBlink var(--t-loop-pulse) ease-in-out infinite; }

.badge{ font-family:var(--mono); font-size:var(--fs-10); padding:6px 9px; border-radius:var(--r-5);
  background:var(--surface); color:var(--ink); border:1px solid var(--line); }
.badge.on{ background:var(--ink); color:var(--on-ink); border-color:var(--ink); }

/* ── HUD readouts floating over the viewport ── */
.hud{ font-family:var(--mono); font-size:var(--fs-10); color:var(--mut-2);
  background:color-mix(in srgb, var(--surface) 94%, transparent);
  border:1px solid var(--line); padding:6px 9px; border-radius:var(--r-5); white-space:nowrap; }
@supports not (background: color-mix(in srgb, red 50%, blue)){ .hud{ background:var(--surface); } }
.hud-group{ display:flex; background:color-mix(in srgb, var(--surface) 94%, transparent);
  border:1px solid var(--line); border-radius:var(--r-5); overflow:hidden; }
.hud-group > *{ padding:6px 9px; font-family:var(--mono); font-size:var(--fs-10); cursor:pointer; }
.hud-group > .v{ color:var(--mut); border-left:1px solid var(--line-2);
  border-right:1px solid var(--line-2); cursor:default; }
.hud-stack{ display:flex; flex-direction:column; gap:3px; background:var(--surface);
  border:1px solid var(--line); border-radius:var(--r-9); padding:5px; box-shadow:var(--sh-1); }

/* ============================ tables ===================================== */
.tbl{ width:100%; }
.tbl-hd{ display:grid; padding:12px 16px; border-bottom:1px solid var(--line-2);
  background:var(--surface-2); font-family:var(--mono); font-size:var(--fs-10); letter-spacing:.1em;
  color:var(--mut-3); text-transform:uppercase; }
.tbl-row{ display:grid; padding:11px 16px; border-bottom:1px solid var(--line-3); font-size:var(--fs-13);
  align-items:center; transition:background var(--t-1); }
.tbl-row:hover{ background:var(--surface-2); }
.tbl-row .n{ font-family:var(--mono); font-size:var(--fs-11); color:var(--mut-4); }
.tbl-row .dim{ font-family:var(--mono); font-size:var(--fs-12); color:var(--ink-3); }
.tbl-row .nm{ font-weight:500; }
.tbl-row.on{ background:var(--acc-soft); }

/* progress bars */
.bar{ height:6px; background:var(--line-3); border-radius:var(--r-pill); overflow:hidden; }
.bar > i{ display:block; height:100%; border-radius:var(--r-pill); background:var(--acc);
  transition:width var(--t-7) var(--ease); }
.bar.lg{ height:7px; }
.bar.grad > i{ background:linear-gradient(90deg,var(--acc),var(--acc-3)); }

/* ============================ tabs ======================================= */
.tabs{ display:flex; gap:2px; overflow-x:auto; overflow-y:hidden; scrollbar-width:none; }
.tabs::-webkit-scrollbar{ display:none; }
.tabs > button{ padding:11px 12px; font-size:var(--fs-13); font-weight:500; color:var(--mut);
  cursor:pointer; border-bottom:2px solid transparent; white-space:nowrap;
  transition:color var(--t-2), border-color var(--t-2); }
.tabs > button:hover{ color:var(--ink-3); }
.tabs > button.on{ color:var(--ink); font-weight:600; border-bottom-color:var(--ink); }

/* ============================ modal ====================================== */
.modal{ position:fixed; inset:0; z-index:var(--z-modal); background:rgba(20,26,34,.45);
  -webkit-backdrop-filter:blur(4px); backdrop-filter:blur(4px); display:none;
  align-items:flex-start; justify-content:center; padding:24px; overflow:auto; }
.modal.open{ display:flex; animation:spIn var(--t-2) ease both; }
.modal-card{ background:var(--surface); border-radius:var(--r-16); max-width:560px; width:100%;
  margin:auto; max-height:calc(100vh - 48px); display:flex; flex-direction:column;
  box-shadow:var(--sh-5); overflow:hidden; animation:spSlide var(--t-3) var(--ease) both; }
.modal-card.wide{ max-width:920px; }
.modal-hd{ padding:22px 26px; border-bottom:1px solid var(--line-2); display:flex;
  justify-content:space-between; align-items:center; gap:12px; flex:0 0 auto; }
.modal-hd .t{ font-size:var(--fs-19); font-weight:700; letter-spacing:-.025em; margin-top:5px; }
.modal-bd{ padding:24px 26px; overflow:auto; flex:1; min-height:0; }
.modal-ft{ padding:16px 26px 22px; display:flex; gap:10px; justify-content:flex-end;
  flex:0 0 auto; border-top:1px solid var(--line-3); background:var(--surface); }
.modal-x{ width:32px; height:32px; border-radius:var(--r-6); display:flex; align-items:center;
  justify-content:center; cursor:pointer; color:var(--mut); font-size:var(--fs-17);
  transition:background var(--t-1); }
.modal-x:hover{ background:var(--surface-3); }

/* option rows inside modals
   §41.12 — `<button>` po pravilu pregledača centrira svoj tekst, pa su naslov i
   opis u svakom redu stajali po sredini umesto uz levu ivicu: spisak formata je
   izgledao kao da se svaki red sam poravnava. Poravnanje se kaže ovde, jednom. */
.opt{ display:flex; justify-content:space-between; align-items:center; gap:12px;
  text-align:left; width:100%;
  padding:12px 14px; border:1.5px solid var(--line-2); border-radius:var(--r-8);
  cursor:pointer; transition:border-color var(--t-1), background var(--t-1); }
.opt > div{ min-width:0; }
.opt .go{ flex:0 0 auto; }
/* podmeni jednog reda: uvučen, sa linijom koja pokazuje kome pripada */
.opt-sub{ margin:6px 0 2px 14px; padding-left:12px; border-left:2px solid var(--line);
  display:flex; flex-direction:column; gap:6px; }
.opt-sub .opt{ padding:9px 12px; }
.opt-sub .opt .t{ font-size:var(--fs-13); }
.opt:hover{ border-color:var(--acc); }
.opt.on{ border-color:var(--acc); background:var(--acc-soft); }
.opt .t{ font-size:var(--fs-14); font-weight:600; }
.opt .d{ font-size:var(--fs-12); color:var(--mut); margin-top:3px; }
.opt .go{ font-family:var(--mono); font-size:var(--fs-10); color:var(--acc); }
.dropzone{ border:1.5px dashed var(--border-2); border-radius:var(--r-10); padding:26px;
  text-align:center; background:var(--surface-2); transition:border-color var(--t-2), background var(--t-2); }
.dropzone.hot{ border-color:var(--acc); background:var(--acc-soft); }

/* ============================ toast ====================================== */
#toasts{ position:fixed; right:22px; bottom:calc(22px + var(--safe-b)); z-index:var(--z-toast);
  display:flex; flex-direction:column-reverse; gap:10px; align-items:flex-end; pointer-events:none; }
/* §31.6 — the toast was carrying its own `0 24px 48px -24px rgba(0,0,0,.6)`,
   which is `--sh-3` with the blur 4 px wider and the spread 6 px tighter.
   A toast is a card floating over the page; that IS `--sh-3`. */
/* §53.6 — poruka NE SME da pojede potez. Ovde je stajalo `pointer-events:auto`,
   a poruka nema nijedno dugme: `U.toast()` je pravi od tačkice i teksta i
   ništa se u njoj ne klikće. Jedino što je to `auto` radilo bilo je da
   presretne prst. Mereno na položenom telefonu, 844 × 390: `elementFromPoint`
   na SREDINI 3D platna vraćao je `.toast`, pa prevlačenje po sceni nije
   okretalo kameru uopšte — 0,0000° na potez od 110 px. Domaćin `#toasts` je
   i pre bio proziran za dodir; sada je i sama poruka.
   Ako poruka ikad dobije dugme, njemu se dodir vraća — ali samo njemu. */
.toast{ background:var(--ink); color:var(--on-ink); padding:14px 18px; border-radius:var(--r-10);
  font-size:var(--fs-14); font-weight:500; box-shadow:var(--sh-3);
  animation:spSlide var(--t-4) var(--ease) both; display:flex; align-items:center; gap:10px;
  max-width:min(92vw,420px); pointer-events:none; }
.toast button,.toast a,.toast [data-act]{ pointer-events:auto; }
.toast .dot{ width:7px; height:7px; border-radius:var(--r-pill); background:#5FD08A; flex:0 0 auto; }
.toast.err .dot{ background:#F2705F; } .toast.warn .dot{ background:#EBAE5A; }
.toast.out{ animation:spSlide var(--t-3) var(--ease-io) reverse both; }

/* ============================ tooltip (never clipped) ==================== */
#tt-layer{ position:fixed; inset:0; pointer-events:none; z-index:9000; }
.tt-pop{ position:absolute; padding:6px 10px; border-radius:var(--r-4); font-size:var(--fs-12);
  font-weight:500; background:var(--ink); color:var(--on-ink); white-space:nowrap; opacity:0;
  transform:translateY(3px); transition:opacity var(--t-0) ease, transform var(--t-1) var(--ease);
  /* §31.6 — was `0 8px 22px -10px rgba(0,0,0,.55)`: the same 8 px lift as
     `--sh-1`, 2 px more blur, 4 px less spread. One step, one shadow. */
  box-shadow:var(--sh-1); }
.tt-pop.show{ opacity:1; transform:none; }
.tt-pop kbd{ margin-left:7px; padding:1px 5px; border-radius:var(--r-2); font-family:var(--mono);
  font-size:var(--fs-10); background:rgba(255,255,255,.16); }

/* ============================ reveal ===================================== */
[data-reveal]{ opacity:0; transform:translateY(20px);
  transition:opacity var(--t-6) var(--ease), transform var(--t-6) var(--ease); }
[data-reveal].in{ opacity:1; transform:none; }

.skel{ background:linear-gradient(90deg,var(--line-3) 25%,var(--line-2) 37%,var(--line-3) 63%);
  background-size:400% 100%; animation:spShimmer var(--t-loop-shimmer) ease infinite;
  border-radius:var(--r-4); }

/* ============================ 3D (CSS panels, hero/marketing) ============ */
.stage3d{ perspective:1500px; }
.scene3d{ width:0; height:0; position:relative; transform-style:preserve-3d; }
.p3d{ position:absolute; left:50%; top:50%; transform-style:preserve-3d; }
.p3d > i{ position:absolute; display:block; border:1px solid var(--ink); box-sizing:border-box; }
.sweep{ position:absolute; left:0; right:0; height:110px; pointer-events:none;
  background:linear-gradient(180deg,rgba(31,95,208,0),rgba(31,95,208,.07),rgba(31,95,208,0));
  animation:spSweep var(--t-loop-sweep) ease-in-out infinite; }

/* ============================ misc ======================================= */
.divider{ height:1px; background:var(--line); border:0; margin:0; }
.dot-sep{ width:7px; height:7px; border-radius:var(--r-pill); background:var(--acc); }
.tick{ color:var(--acc); font-weight:700; }
.checkline{ display:flex; gap:11px; font-size:var(--fs-15); line-height:1.55; color:var(--ink-2); }
.note{ border:1px solid var(--acc-line); background:var(--acc-soft); border-radius:var(--r-9);
  padding:14px 16px; font-size:var(--fs-15); line-height:1.62; color:#25405f; }
html[data-theme=dark] .note{ color:var(--ink-2); }
/* §0! N9 — u kutiji `.note` PRVO dete `<b>` je NASLOV kutije („SAVET", „ZAŠTO",
   „OBAVEZNO"), i on jeste zaseban red. Svaki sledeći `<b>` je naglašena reč
   usred rečenice i mora da ostane u redu sa tekstom oko sebe. Pravilo je pre
   ovoga glasilo `.note b` — dakle SVAKI `<b>`, ma gde bio — pa se rečenica
   lomila na tri reda sa jednom rečju u sredini kao zasebnim redom. Mereno na
   uzorku iz dokumentacije: 148,5 px i dva deteta sa `display:block`, umesto
   101,4 px i jednog. `> b:first-child` je zato jedini pravi izbor: naslov je
   uvek prvo dete kutije, a naglašena reč nikad nije. */
.note > b:first-child{ font-family:var(--mono); font-size:var(--fs-11); letter-spacing:.12em;
  display:block; margin-bottom:5px; color:var(--acc); text-transform:uppercase; }
.code{ background:var(--ink); border-radius:var(--r-9); padding:16px 18px; font-family:var(--mono);
  font-size:var(--fs-13); line-height:1.75; color:#C7D3E0; overflow:auto; }
.code .on{ background:rgba(31,95,208,.35); color:var(--on-solid); border-radius:var(--r-2); padding:0 5px; }

/* language menu */
.lang{ position:relative; }
.lang-menu{ position:absolute; top:calc(100% + 8px); right:0; min-width:186px; padding:6px;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-10);
  box-shadow:var(--sh-2); z-index:200; display:none; }
.lang-menu.open{ display:block; animation:spSlide var(--t-2) var(--ease) both; }
.lang-menu button{ display:flex; align-items:center; gap:10px; width:100%; min-height:38px;
  padding:0 12px; border-radius:var(--r-6); font-size:var(--fs-14); color:var(--ink-3);
  transition:background var(--t-1); }
.lang-menu button:hover{ background:var(--surface-3); }
.lang-menu button.on{ color:var(--acc); font-weight:600; }

/* §24.7 — the currency, in the same menu and in the same clothes as the
   language: identical row height, identical radius, identical hover and
   active colour. Only the separator and the small caption say it is a second
   list, and the symbol sits where the flag sits so the two columns line up. */
.lang-menu .cur-block{ margin-top:6px; padding-top:6px; border-top:1px solid var(--line); }
.lang-menu .cur-hd{ font-family:var(--mono); font-size:var(--fs-10); letter-spacing:.14em;
  text-transform:uppercase; color:var(--mut-3); padding:4px 12px 6px; }
.lang-menu .cur-opt .sym{ display:inline-flex; align-items:center; justify-content:center;
  min-width:22px; font-family:var(--mono); font-size:var(--fs-13); color:var(--mut); }
.lang-menu .cur-opt.on .sym{ color:var(--acc); }

/* cookie bar */
#cookie-bar{ position:fixed; left:50%; bottom:calc(18px + var(--safe-b));
  transform:translateX(-50%) translateY(180%); width:min(720px,calc(100% - 28px));
  padding:18px 20px; border-radius:var(--r-12); z-index:250; display:flex; gap:16px;
  align-items:center; flex-wrap:wrap; background:var(--surface); border:1px solid var(--border);
  box-shadow:var(--sh-5); transition:transform var(--t-6) var(--ease); }
#cookie-bar.show{ transform:translateX(-50%) translateY(0); }
#cookie-bar p{ flex:1 1 260px; font-size:var(--fs-13); color:var(--ink-3); line-height:1.55; }

/* ============================ responsive ================================= */
@media (max-width:1080px){
  :root{ --pad:20px; }
  .sec-pad{ padding-block:64px; }
  /* §38.5 — navigacija se ne GASI, nego se seli u panel iza dugmeta */
  .hdr-in .hdr-burger{ display:inline-flex; align-items:center; justify-content:center;
    position:relative; order:-1; }
  .nav{
    position:absolute; top:var(--head-h); left:0; right:0;
    display:none; flex-direction:column; gap:2px; margin:0; padding:10px var(--pad) 14px;
    background:var(--surface); border-bottom:1px solid var(--line);
    box-shadow:var(--sh-2); z-index:var(--z-pop);
  }
  .nav.open{ display:flex; animation:spSlide var(--t-2) var(--ease) both; }
  .nav a{ padding:12px 14px; font-size:var(--fs-15); border-radius:var(--r-7); }
}
@media (max-width:820px){
  .hdr-in{ gap:14px; }

  .sec-pad{ padding-block:52px; }
  .lead{ font-size:var(--fs-17); }
  .card-pad{ padding:20px; }
  /* §31.6 — modal je kartica, na svakoj širini. Ispod 820 px je postajao
     „sheet" zalepljen za dno: **gornje ivice zaobljene, donje oštre** — isti
     prozor, dva različita oblika, zavisno od širine. To nije pravac nego dva
     pravca. Ostaje kartica, samo se stisne uz ivice ekrana. */
  .modal{ padding:12px; align-items:center; }
  .modal-card{ border-radius:var(--r-16); max-height:92dvh; }
}
@media (max-width:520px){
  :root{ --pad:16px; }
  .btn-lg{ padding:13px 18px; font-size:var(--fs-15); }
  .modal-bd,.modal-hd,.modal-ft{ padding-inline:18px; }
}

/* ══ §24.1 — the header, on every page, at every width ═══════════════════
   The header was the widest thing on the site: at a 375 px viewport it laid
   out 487 px wide and pushed the whole document sideways, so every section
   under it appeared clipped. Nothing was overflowing on its own — the row
   simply had no instruction to give way, and the „Radni prostor" button and
   the account chip ran past the edge (measured: 276…399 and 407…487 in a
   375 px window).

   The order things give way in is deliberate: first the spacing, then the
   customer's NAME beside the avatar (the avatar is the control, the name is
   a courtesy), then the wordmark beside the mark. The account and the one
   primary action are the last things standing, because they are what the
   header is for. */
.hdr-in{ min-width:0; }
.hdr-in > .row{ min-width:0; flex-wrap:nowrap; }
.brand{ flex:0 0 auto; min-width:0; }
@media (max-width:720px){
  .hdr-in{ gap:10px; padding-inline:14px; }
  #hdr-actions{ gap:6px; }
  .hdr-in .btn{ padding-inline:12px; font-size:var(--fs-13); }
  .acct-chip{ padding:3px 6px 3px 3px; gap:6px; }
  .acct-chip .nm{ display:none; }          /* the avatar IS the control */
}
@media (max-width:560px){
  /* §55.14 — natpis i oznaka odlaze ZAJEDNO. Ranije je `BETA` nestajala na
     820 px a `Specter` tek na 560 px, pa je izmedju te dve sirine postojao
     ceo raspon u kome je znak imao ime bez oznake — jedan znak koji na tri
     sirine izgleda na tri nacina. */
  .brand .name,
  .brand .tag{ display:none; }             /* the mark alone is the logo */
  .hdr-prefs{ gap:2px; padding-right:2px; margin-right:0; }
  .hdr-in .btn{ padding-inline:10px; font-size:var(--fs-12); }
}
/* the narrowest phone still sold. Measured at 320 px: „Radni prostor" (98 px)
   plus the avatar plus the two preference controls came to 336 — 16 px over.
   The language keeps its flag and loses its code; a flag says which language
   it is, which is the whole reason §2.2 put a flag there. */
@media (max-width:400px){
  .hdr-in{ gap:6px; padding-inline:10px; }
  .hdr-prefs .lang .cap,
  .hdr-prefs .lang-cap{ display:none; }
  .hdr-in .btn{ padding-inline:9px; }
}

/* ══ §27.4 — JEDAN REDOSLED SLOJEVA ZA CEO PROGRAM ═════════════════════════
   Zapisan ovde da se ne pogađa po fajlovima. Broj se bira po pitanju „šta sme
   da stoji preko čega", ne po tome koliko je nekome trebalo u tom trenutku.

     10   sadržaj u toku (splitteri, ručke, oznake u prikazu)
     100  trake koje uvek stoje (traka projekta, kartice, alatnica)
     180  iskačući meni ili panel zakačen za svoje dugme
     190  radna površina preko celog ekrana (dizajner izveštaja)
     200  modal — mora da stoji preko radne površine, jer ga ona otvara
     300  poruka (toast) — kratka, i vidi se i preko modala
     500  zastor za prevlačenje fajla — sistemsko stanje, iznad svega

   **Mereno `node tools/press.js`: dizajner izveštaja je stajao na 260, dakle
   IZNAD modala (200) — prozor „Uvoz" otvoren iz njega bio je iza njega.** */
:root{
  --z-content: 10; --z-bars: 100; --z-pop: 180; --z-work: 190;
  --z-modal: 200; --z-toast: 300; --z-drop: 500;
}

/* ══ §2.2 / §29.11 — nacrtana zastavica ════════════════════════════════════
   Emodži zastave na Windows-u ne postoje u fontu: pregledač ispiše dva slova
   oznake države, pa je dugme za jezik pisalo „GB EN". `I18N.flag(code)` sada
   vraća SVG, a ovde dobija veličinu i oblik — mala kartica sa zaobljenim
   uglovima, kakva i stoji u ostatku programa. */
.flag{ display:inline-flex; align-items:center; line-height:0; }
.flag-svg{ width:18px; height:12px; border-radius:var(--r-1); display:block;
  box-shadow:var(--sh-knob); }
.lang-menu button .flag{ flex:0 0 auto; }
#lang-btn [data-lang-label]{ display:inline-flex; align-items:center; gap:6px; }
#lang-btn .cap{ font-family:var(--mono); font-size:var(--fs-11); letter-spacing:.06em; }
@media (max-width:400px){ #lang-btn .cap{ display:none; } }

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{ animation-duration:var(--t-off) !important; animation-iteration-count:1 !important;
    transition-duration:var(--t-off) !important; scroll-behavior:auto !important; }
  [data-reveal]{ opacity:1 !important; transform:none !important; }
}

@media print{
  .hdr,.nav,#toasts,#cookie-bar,#tt-layer,.modal{ display:none !important; }
  body{ background:var(--white); }
}

/* ═══════════ §26.3 — the page's own pointer: OFF ══════════════════════════
   These rules used to make the whole site wear the physics cursor. They are
   gone, and the reason is worth keeping: `.spc-layer`'s `pointer-events:none`
   lives in `site.css`, which `login.html` and `app.html` do not load — so on
   those two pages the layer covered everything and swallowed every click. The
   cursor now exists only inside the landing page's opening animation
   (§24.13), where `opening.js` builds it in a host it owns and `site.css` is
   loaded anyway.

   If it is ever brought back site-wide, the base `.spc-*` rules move HERE
   first — every page loads this file, and that is the whole lesson. */
.spc-layer{ pointer-events:none; }

/* ═══════════ §53.3 — PRST. Svaka meta bar 44 × 44 px ══════════════════════
   Mereno alatkom `tools/dodir.js`, pravim dodirom kroz pregledač, na 390×844
   i 844×390: **693 mete, 276 ispod 44 px**. Ništa od toga nije prelivalo,
   ništa nije bilo isečeno — zato `fit.js` sve vreme kaže „čisto". Dugme od
   30 px stoji uredno u ekranu i promašuje se prstom, i to je druga mera.

   Dva pravila, jer 44 px nije uvek besplatno:

   1. GDE JE RAST JEFTIN — raste kutija (`min-height`). Sadržaj je već
      centriran, pa dugme izgleda isto a prima prst.
   2. GDE BI RAST POJEO EKRAN — položen telefon ima 390 px visine i traka
      projekta plus traka kartica već uzimaju petinu — raste SAMO osetljiva
      površina, kroz nevidljiv `::after`. Piksel ostaje gde je bio, prst
      dobija svojih 44 px. Zato ova pravila NE menjaju nijedno merenje na
      velikom ekranu: cela sekcija je pod `pointer: coarse`.

   Zašto baš `pointer: coarse` a ne širina ekrana: uzak prozor na stolnom
   računaru se i dalje klika mišem, a tablet od 1024 px se dodiruje prstom.
   Mera je čime se pokazuje, ne koliko ima piksela. */
@media (pointer: coarse){

  /* ── 1 · raste kutija ─────────────────────────────────────────────────── */
  .btn{ min-height:44px; }
  .btn-sm{ min-height:44px; padding-top:0; padding-bottom:0; }
  .tabs > button{ min-height:44px; padding-top:0; padding-bottom:0;
    display:inline-flex; align-items:center; }
  .inp,.inp.sm,input[type=text],input[type=email],input[type=password],
  input[type=number],input[type=search],input[type=tel],select,textarea{ min-height:44px; }

  /* Čip koji nosi RADNJU je meta; čip koji ispisuje broj („Otpad 30,7 %") je
     rečenica u kutiji i ostaje kakav jeste — rastezanje reda brojeva na
     44 px ne dobija ništa, a red se prestaje čitati. */
  button.chip,a.chip,.chip[data-act],.chip[data-view],.chip[role=button],
  .chip[tabindex]{ min-height:44px; }

  /* Segmentirani prekidač — filtri magacina i slično. Bio je 31 px. */
  .seg > button{ min-height:44px; padding-top:0; padding-bottom:0; }

  /* Znak programa je i dugme — vodi na naslovnu. Bio je 28 px visok. */
  .brand{ min-height:44px; }

  /* ── 2 · raste samo osetljiva površina ────────────────────────────────── */
  /* Zajedničko pravilo: dete koje se ne vidi, ne boji i ne pomera ništa, a
     `elementFromPoint` na njemu vraća roditelja. Roditelj mora da bude
     pozicioniran da bi ovo imalo od čega da se meri. */
  .hit44{ position:relative; }
  .hit44::after{ content:""; position:absolute; left:50%; top:50%;
    width:max(100%,44px); height:max(100%,44px);
    transform:translate(-50%,-50%); }

  /* Veza u rečenici („Obriši sve", „Zaboravljena lozinka") ne sme da poraste
     — porasla bi i linija teksta oko nje. Raste samo ono što hvata prst, i
     samo po visini: levo i desno od nje stoji ista rečenica. */
  .linky{ position:relative; }
  .linky::after{ content:""; position:absolute; left:0; right:0;
    top:50%; height:44px; transform:translateY(-50%); }
}
