/*
 * web/css/auth-funnel.css — the pre-auth funnel, and only the pre-auth funnel.
 *
 * WHY THIS FILE EXISTS. vtpgo.ro went live in IBM Plex, #10264A and a 10px
 * radius, and web/inregistrare.html was rebuilt to match it. login.html,
 * totp.html and enroll.html were not: they still wear the app's 2024 chrome
 * (Segoe UI, #1a3c8f, a 42px white circle on a navy bar). The seam that used to
 * sit between the site and signup now sits at the login screen, which every
 * existing customer crosses on EVERY sign-in. This sheet closes it by giving
 * the three screens the same clothes inregistrare.css gave the fourth.
 *
 * WHAT IT REPLACES. css/index.css (misnamed — it styles login.html, not
 * index.html) and css/enroll.css, both deleted, and css/totp.css FOR THIS PAGE
 * ONLY. ⚠ totp.css itself must stay byte-identical: confirma-email.html,
 * invitatie.html, parola-uitata.html and reset-parola.html still load it, and
 * its `[role="alert"]` is the only styling invitatie.html's #inv-error has.
 *
 * ONE SHEET, NOT THREE. .sr-only, the .topbar block, the a.skip-link override,
 * .form-group*, [role=alert], .btn-login, .btn-spinner, @keyframes spin, footer,
 * the four @font-face blocks and the [hidden] guard would otherwise be written
 * out three times and drift apart three ways. Six pages load this file: the
 * three above plus their -en siblings (PR 2).
 *
 * ⚠ THIS IS THE PR-1 (ROMANIAN-ONLY) VERSION. The RO|EN switcher's .lang and
 * .topbar-actions rules belong to PR 2 and are appended ABOVE the [hidden]
 * guard at the bottom, never after it.
 *
 * ⚠ LINK IT AS href="css/auth-funnel.css" — no leading "/" and no "./".
 * page-elements.test.mjs:194 collects sheets with /<link[^>]+href="(css\/[^"]+)"/g;
 * any other spelling makes this file invisible to the guard and every class in
 * it reads as undefined.
 *
 * ⚠ THE [hidden] GUARD AT THE BOTTOM IS LOAD-BEARING — read its own comment
 * before touching anything that sets `display`. Four classes redefined here
 * (.card, .toast, .btn-label, .countdown) are toggled by el.hidden in script and
 * are protected by nothing else. .countdown is already broken this way today.
 *
 * ⚠ THE FONTS MUST BE FIRST-PARTY. app.vtpgo.ro sends
 * `Content-Security-Policy: default-src 'self'` with NO font-src, so font-src
 * falls back to 'self'. A @font-face pointing at https://vtpgo.ro/assets/fonts/
 * is blocked by CSP, and vtpgo.ro sends no Access-Control-Allow-Origin, so it
 * would fail the CORS check even if the CSP allowed it — blocked twice. The four
 * .woff2 already sit in web/fonts/ (64 KB, byte-identical to site/assets/fonts/).
 * NO SERVER CHANGE IS NEEDED — do not add font-src to the host Caddy config.
 *
 * ⚠ HOW THIS FILE RELATES TO css/responsive.css, WHICH IS LINKED AFTER IT AND
 * CANNOT BE EDITED (26 pages). Every rule of its that reaches these three pages,
 * and what this sheet does about it:
 *
 *   §0a   *:focus-visible{outline:2px solid #1a3c8f}   (0,1,0)
 *         → OUT-SPECIFIED, deliberately, at (0,1,1)/(0,2,1). See "focus" below.
 *   §0a-bis .skip-link{…background:#1a3c8f…}           (0,1,0)
 *         → colour OUT-SPECIFIED by a.skip-link (0,1,1). Its off-screen
 *           left:-9999px and :focus{left:8px} mechanics are LEFT ALONE: they are
 *           the entire behaviour of the control and nothing here touches `left`.
 *   §1    *,*::before,*::after{box-sizing:border-box}
 *         → same declaration, restated here so the sheet is correct standalone.
 *   §1    img,svg,video,canvas{max-width:100%;height:auto}  (0,0,1)
 *         → OUT-SPECIFIED twice, on purpose: .qr-wrap img (0,1,1) for the
 *           200×200 data-URL PNG, and .countdown-ring (0,1,0), whose height would
 *           otherwise compute to `auto` and collapse the ring.
 *   §1    .content,.main{min-width:0}                  (0,1,0)
 *         → LEFT ALONE. Wanted: it keeps a wide child from stretching the page.
 *   §2    the 44px touch floor, ≤767px                 (0,3,1)
 *         → LEFT ALONE, deliberately. It beats .form-group input.code-input
 *           (0,2,1), so the 6-digit fields are 44px tall on phones, not 56.
 *           That is the correct outcome and this sheet does not fight it.
 *   §5    ≤767px .content,.main{margin-left:0}         (0,1,0)
 *         → LEFT ALONE. No margin is set on .main here.
 *   §7    <1280px .topbar{flex-wrap:wrap;row-gap:8px}  (0,1,0)
 *         → LEFT ALONE, deliberately. It lets „Înapoi la vtpgo.ro" drop to a
 *           second line instead of clipping off the right edge.
 *   §8    ≤767px input[type=text]{min-width:0!important;max-width:100%}
 *         → LEFT ALONE. .code-input is width:100% already; no effect.
 *   §6    prefers-reduced-motion blanket, with !important durations
 *         → LEFT ALONE — and it is the reason the two `animation:none`
 *           carry-overs at the bottom of this file exist. See them.
 *   §3, §3b, §4, §9 (.responsive-table, .dnna-option, dialog[open], .sidebar-menu)
 *         → none of those elements or classes exist on these pages.
 *
 * ⚠ CLASS NAMES HERE ARE JS SELECTORS. totp.js:56 queries `p.subtitle`
 * (tag-qualified — it must stay a <p>) and totp.js:57/enroll.js:66 query
 * `.card h2` (so .card must stay on #totp-form/#enroll-form and the heading must
 * stay an h2 descendant). index.js queries '#submit-btn .btn-label' and
 * '#submit-btn .btn-spinner'. Rename one and init() throws on a null, leaving a
 * textless page — and on the login success path index.js:98 throws AFTER the
 * sessionStorage write at :70-79, stranding the user on a frozen login page
 * holding valid auth state.
 *
 * NOT ADOPTED, ON PURPOSE: card-header, card-body, card-subtitle, header-icon,
 * form-footer. Those five are page-elements.test.mjs:171's CHROME_CLASSES; a
 * redesigned header reaching for .header-icon is the likeliest way to trip it.
 */

/* ---------- type: first-party IBM Plex, four weights (see header) --------- */
@font-face{font-family:'Plex';src:url('../fonts/plex-regular.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Plex';src:url('../fonts/plex-medium.woff2') format('woff2');font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:'Plex';src:url('../fonts/plex-semibold.woff2') format('woff2');font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:'Plex';src:url('../fonts/plex-bold.woff2') format('woff2');font-weight:700;font-style:normal;font-display:swap}

/* The marketing site's palette and scale, copied verbatim from
   site/assets/css/style.css. Namespaced --mk-* so they cannot collide with the
   generated --vtp-* tokens in tokens.css, which these pages do not use. */
:root{
  --mk-navy:#10264A;
  --mk-navy-2:#1B3A6B;
  --mk-ground:#F5F7FB;
  --mk-ink:#16202F;
  --mk-slate:#647287;
  --mk-line:#DDE3EC;
  --mk-accent:#D9541E;
  --mk-err:#B4321A;

  /* ⚠ DELIBERATELY NOT the site's --line for a field border. #DDE3EC is 1.29:1
     against white and fails WCAG 1.4.11's 3:1 rule for a control the visitor
     must actually find and fill. #8794A9 measures 3.07:1 at the same blue-grey
     temperature. (The app's own #9ca3af is 2.54:1 — totp.css:154's "3.06:1"
     comment is simply wrong. The site's contact form has the 1.29:1 problem;
     this page does not inherit it.) */
  --mk-field-border:#8794A9;

  --mk-step--1:clamp(.82rem,.79rem + .14vw,.9rem);
  --mk-step-0:clamp(1rem,.96rem + .2vw,1.08rem);
  --mk-step-2:clamp(1.45rem,1.3rem + .7vw,1.85rem);
  --mk-gut:clamp(1.15rem,4vw,2.5rem);
  --mk-r:10px;
}

/* Three things the funnel needs that the marketing site has no component for:
   it has no toast, no monospace face and nothing a user copies off the screen.
   Kept in a separate block so the palette above stays a verbatim copy. */
:root{
  /* „Copiat ✓". #0F5132 on #E4F3EA measures 8.16:1. */
  --mk-ok-bg:#E4F3EA;
  --mk-ok-ink:#0F5132;
  /* ⚠ No 'IBM Plex Mono' in the stack, on purpose: web/fonts/ ships the four
     SANS weights and nothing else, so naming it would resolve only on machines
     that happen to have it installed — i.e. the recovery codes would be read off
     one shape of glyph here and another there. System monospace everywhere. */
  --mk-mono:ui-monospace,'Cascadia Mono','SF Mono',Menlo,Consolas,monospace;
}

/* ---------- base ---------------------------------------------------------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}

/* ⚠ This sheet does NOT carry the `*{margin:0;padding:0}` reset the three
   sheets it replaces opened with. That reset also flattens <ol>'s
   padding-inline-start and <figure>'s auto margins, both of which this page
   then has to put back by hand. Every element on these three pages that needs
   zeroing is known, so it is zeroed by name. */
h1,h2,p,ol,figure,button,input{margin:0}

body{
  margin:0;
  background:var(--mk-ground);
  color:var(--mk-ink);
  font-family:'Plex',ui-sans-serif,system-ui,'Segoe UI',sans-serif;
  font-size:var(--mk-step-0);
  line-height:1.6;
  font-feature-settings:"kern" 1;
  -webkit-font-smoothing:antialiased;
  /* min-height + column flex + .main{flex:1} is what pins the footer to the
     bottom of a short page (the login card is ~420px tall on a 1080px screen). */
  min-height:100vh;
  display:flex;
  flex-direction:column;
}

/* ---------- header -------------------------------------------------------- */
/* The markup keeps .logo-circle / .app-name / .app-subtitle — other pages and
   the nav builder use those names, and renaming them here would be a markup
   change for no gain. They are simply restyled into the site's header: white
   with a hairline, a rounded-square mark (the favicon's rx:14/64 ≈ 22%, NOT a
   circle — the site has no circle anywhere), and a baseline-aligned wordmark. */
.topbar{
  /* 2026-10-04 — a white bar: the navy VTP GO mark (brand.css §1 reads this). */
  --vtp-mark: url("../icons/vtpgo-mark-dark-256.png");
  background:#fff;
  border-bottom:1px solid var(--mk-line);
  padding:.85rem var(--mk-gut);
  display:flex;align-items:center;gap:1.5rem;
}
.topbar-brand{
  display:flex;align-items:center;gap:.6rem;
  text-decoration:none;color:var(--mk-ink);
}
/* Two shapes of the same lockup. login.html and totp.html wrap it in
   <a class="topbar-brand">, so the first child IS .logo-circle and `> div + div`
   is the text block. enroll.html has NO .topbar-brand and no back link — its
   .logo-circle and text block are direct children of .topbar — so the second
   selector picks up the same text block there. Both must exist; matching only
   one leaves the enrolment header stacked and unaligned. */
.topbar-brand > div + div,
.topbar > div + div{
  display:flex;align-items:baseline;gap:.5rem;flex-wrap:wrap;
}
.topbar .logo-circle{
  width:30px;height:30px;flex:none;
  border-radius:7px;
  background:var(--mk-navy);color:#fff;
  display:flex;align-items:center;justify-content:center;
  font-size:11px;font-weight:700;letter-spacing:-.02em;
}
.topbar .app-name{
  font-size:1.12rem;font-weight:700;letter-spacing:-.02em;color:inherit;
}
.topbar .app-subtitle{
  font-size:var(--mk-step--1);font-weight:400;color:inherit;opacity:.72;
}

/* #back-to-marketplace — „Înapoi la vtpgo.ro", pointing at the public site.
   margin-left:auto is what puts it on the right: .topbar is a plain flex row
   here, not space-between, because enroll.html's header has no second child and
   space-between would leave its lockup alone in the middle of the bar. */
.btn-back-marketplace{
  margin-left:auto;
  padding:.42rem .8rem;
  border:1px solid var(--mk-line);
  border-radius:var(--mk-r);
  color:var(--mk-navy-2);            /* 11.27:1 on white */
  font-size:var(--mk-step--1);font-weight:600;
  text-decoration:none;
  /* The label is three words with a domain in it; wrapping it mid-domain reads
     as a broken link. Below 1280px responsive.css §7 lets the whole bar wrap
     instead, which is the intended escape hatch. */
  white-space:nowrap;
  transition:background-color .15s ease,border-color .15s ease;
}
.btn-back-marketplace:hover{background:var(--mk-ground);border-color:var(--mk-field-border)}
/* The hairline is decoration: this control is identified by its text (11.27:1),
   not by its box, so WCAG 1.4.11's 3:1 does not attach to the border. Contrast
   the FIELD borders below, where the empty box is the only thing to find. */

/* ---------- the centred card --------------------------------------------- */
/* Composition matches inregistrare.html exactly, so signup and sign-in read as
   one product: one card, horizontally centred, top-aligned. flex-start and not
   `center` — the enrolment card is tall (QR + key + field) and vertical
   centring makes it jump up the page as content is added. */
.main{
  flex:1 1 auto;
  display:flex;align-items:flex-start;justify-content:center;
  padding:clamp(2rem,7vh,4rem) var(--mk-gut);
}

/* ⚠ NO `display` HERE. See the guard at the bottom: enroll.js:202-203 swaps
   #enroll-form for #codes-card with el.hidden, and .main above is a flex ROW —
   so a display on .card would not merely show both, it would sit the enrolment
   form and the one-time recovery codes SIDE BY SIDE. */
.card{
  width:100%;max-width:26rem;
  background:#fff;
  border:1px solid var(--mk-line);
  border-radius:var(--mk-r);
  padding:clamp(1.6rem,4vw,2.6rem);
  box-shadow:0 10px 34px -22px rgba(16,38,74,.5);
  /* ⚠ LEFT, not centred. A form people have to fill in reads left; only the
     three trailing one-liners under the login form are centred, by name. */
  text-align:left;
}
/* The card ground must stay #fff: --mk-field-border is 3.07:1 against white but
   only 2.86:1 against --mk-ground, so tinting the card fails 1.4.11 on every
   field in the funnel. */

/* Enrolment is wider: a fixed 200px QR, then a row of label + 19-character key +
   copy button, then a 2-column code grid. 26rem cramps all three. */
.card-wide{max-width:30rem}
/* #codes-card carries plain .card in the markup, so without this it would snap
   from 30rem to 26rem at the exact moment it replaces the form — a 64px width
   jump that reads as a page change rather than a step. An id rule instead of a
   markup edit, because the markup is frozen (see §4.6 of the spec). */
#codes-card{max-width:30rem}

.card h2{                     /* ⚠ totp.js:57 / enroll.js:66 query `.card h2` */
  margin:0 0 .55rem;
  font-size:var(--mk-step-2);font-weight:600;
  line-height:1.12;letter-spacing:-.018em;color:var(--mk-ink);
}
.card p.subtitle{             /* ⚠ totp.js:56 queries `p.subtitle` — keep the <p> */
  margin:0 0 1.5rem;
  color:var(--mk-slate);      /* 4.89:1 on the white card */
  font-size:var(--mk-step--1);line-height:1.5;
}

/* „Cont: user@example.com" — the line that tells you which account you are
   about to prove you own. */
.account-line{margin:0 0 .35rem;font-size:var(--mk-step--1);line-height:1.5}
.account-prefix{color:var(--mk-slate)}
.account-email{
  color:var(--mk-ink);font-weight:500;
  /* A 60-character address must wrap inside the card rather than widen it. */
  overflow-wrap:anywhere;
}

/* ---------- screen-reader utility ---------------------------------------- */
/* ⚠ BEHAVIOUR, NOT DECORATION. This is the only thing keeping three <h1>s
   („Autentificare", „Autentificare în doi pași", „Înrolare TOTP") and the two
   <label for="…-code"> out of the visual layout. Delete it and every page grows
   a duplicate heading above its real one. */
.sr-only{
  position:absolute;
  width:1px;height:1px;
  padding:0;margin:-1px;
  overflow:hidden;
  clip:rect(0,0,0,0);
  white-space:nowrap;
  border:0;
}

/* ---------- fields -------------------------------------------------------- */
.form-group{margin-bottom:1.05rem}
.form-group label{
  display:block;margin-bottom:.34rem;
  font-size:var(--mk-step--1);font-weight:500;color:var(--mk-ink);
}
.form-group input{
  width:100%;
  font:inherit;font-size:var(--mk-step-0);
  padding:.66rem .8rem;
  border:1px solid var(--mk-field-border);   /* 3.07:1 — see the token comment */
  border-radius:var(--mk-r);
  background:#fff;color:var(--mk-ink);
}
.form-group input:focus{border-color:var(--mk-navy)}
/* ⚠ NO `outline:none` anywhere in this file. The three sheets it replaces all
   set it on inputs and buttons, which is why responsive.css §0a had to exist. */

/* enroll.js:136 and totp.js disable every input in the form while the request is
   in flight; the field has to read as unavailable rather than broken. */
.form-group input:disabled{
  background:var(--mk-ground);color:var(--mk-slate);cursor:not-allowed;
}

/* The 6-digit field's wrapper. Its only <label> is .sr-only, so the group
   carries no label gap and sits tighter to the paragraph above it than a
   labelled .form-group does. */
.code-group{margin-bottom:.85rem}

/* ⚠ SPECIFICITY TRAP, AND A LIVE BUG IN THE SHEETS THIS ONE REPLACES.
   `.form-group input` is (0,1,1) and `.code-input` is (0,1,0), so in totp.css
   and enroll.css the plain-field `font-size:14px` BEATS `.code-input`'s 28px —
   the 2FA code box renders at 14px today, bold and letter-spaced inside a 56px
   box, which is why it looks empty. `.form-group input.code-input` is (0,2,1)
   and settles it by specificity rather than by hoping about source order. */
.form-group input.code-input{
  min-height:3.5rem;                          /* 56px — see §2 note in the header */
  font-family:var(--mk-mono);
  /* Fluid, and NOT 28px flat: #totp-code doubles as the recovery-code box
     (totp.js:158 swaps maxlength 6 → 13). Thirteen characters at 28px with 8px
     of tracking is ~315px of text in a ~206px field on a 320px phone, i.e. the
     code scrolls out of sight in the box the user is checking it against.
     The clamps keep 13 characters inside the field at every width. */
  font-size:clamp(1.05rem,5.6vw,1.55rem);
  letter-spacing:clamp(.1em,1.1vw,.32em);     /* em, so tracking scales with size */
  /* Tracking adds a trailing space after the last glyph, and text-align:center
     counts it, so the digits sit half a letter-space to the LEFT of centre.
     ~half the tracking, added back on the left, re-centres them optically. */
  text-indent:.16em;
  font-weight:700;
  text-align:center;
  font-variant-numeric:tabular-nums;
}
.form-group input.code-input:focus{border-color:var(--mk-navy)}

/* ---------- countdown ----------------------------------------------------- */
/* ⚠ NO `display` ON .countdown — THIS IS DEFECT §3.2, LIVE IN PRODUCTION.
   totp.js:156 does `countdownEl.hidden = on` when the user switches to a
   recovery code, with the comment "The 30-second ring is about TOTP and would be
   a lie next to a code that does not expire." totp.css:198's `.countdown{display:flex}`
   is author-origin and beats the UA's [hidden]{display:none}, so that line is a
   no-op and the lie is on screen today.
   The row is laid out WITHOUT a display: .countdown is a <div>, so its two
   children (an inline <svg> and an inline <span>) already form one line box.
   vertical-align does the rest. */
.countdown{
  margin:.5rem 0 1.15rem;
  font-size:var(--mk-step--1);
  color:var(--mk-slate);
}
.countdown-ring{
  /* ⚠ (0,1,0) — this is what beats responsive.css §1's
     `img,svg,video,canvas{max-width:100%;height:auto}` (0,0,1). Without it the
     svg's height computes to `auto` and the ring collapses. */
  width:24px;height:24px;
  flex:none;
  vertical-align:middle;
  margin-right:.55rem;
  /* ⚠ BEHAVIOUR, NOT DECORATION. SVG circles start at 3 o'clock; without this
     the ring drains from the right instead of from the top. */
  transform:rotate(-90deg);
}
.countdown-track{
  fill:none;
  stroke:var(--mk-line);
  stroke-width:4;
}
.countdown-progress{
  fill:none;
  stroke:var(--mk-navy);
  stroke-width:4;                 /* r=18 + half of 4 = 20 = half the 40-unit
                                     viewBox: the stroke exactly meets the edge.
                                     Widen it and the ring clips. */
  stroke-linecap:round;
  /* ⚠ A THREE-WAY NUMERIC CONTRACT, CARRIED OVER UNCHANGED.
     113.1 = 2π·18. It must equal CIRC at totp.js:96 and match r="18" in
     viewBox="0 0 40 40" at totp.html:79-80. totp.js:107 writes
     strokeDashoffset as a fraction of that constant every rAF tick; change the
     radius in any one of the three places and the ring drains to the wrong
     length in the other two. */
  stroke-dasharray:113.1;
  stroke-dashoffset:0;            /* rewritten each tick by totp.js:107 */
}
.countdown-text{
  vertical-align:middle;
  /* #countdown-seconds is rewritten every second. Proportional digits make the
     whole line jitter sideways as 30 → 9; tabular figures hold the width. */
  font-variant-numeric:tabular-nums;
}
/* The ring is aria-hidden and duplicated by the numeric label beside it, so its
   navy-on-hairline contrast is exempt from 1.4.11 — it is decoration, and the
   seconds are the accessible copy. */

/* ---------- the inline error region --------------------------------------- */
/* #form-error, one per form (D-170). ⚠ No `display` here on purpose: the region
   ships `hidden` on all three pages and is revealed only when a request fails.
   #B4321A on #FBEAE5 measures 5.28:1. */
[role="alert"]{
  margin:0 0 1rem;padding:.7rem .9rem;
  background:#FBEAE5;color:var(--mk-err);
  border-left:3px solid var(--mk-err);
  border-radius:0 6px 6px 0;
  font-size:var(--mk-step--1);
}

/* ---------- buttons ------------------------------------------------------- */
/* The site's .btn.btn-primary, full width. The flex row here is what centres
   the label and the spinner together — and it is also the reason .btn-label
   needs no display of its own: as a flex item its display is blockified anyway,
   so width/height/margins already apply to it. */
.btn-login{
  display:flex;align-items:center;justify-content:center;gap:.5rem;
  width:100%;min-height:2.75rem;
  border:0;border-radius:var(--mk-r);cursor:pointer;
  font:inherit;font-weight:600;font-size:var(--mk-step--1);
  padding:.72rem 1.15rem;
  background:var(--mk-navy);color:#fff;       /* 15.02:1 */
  text-align:center;
  transition:background-color .15s ease;
}
.btn-login:hover{background:var(--mk-navy-2)}
/* Disabled during the request, and on #codes-continue until the box is ticked.
   A disabled control is exempt from 1.4.3/1.4.11, which is what makes a flat
   opacity acceptable here. Values match #codes-continue:disabled below so the
   two rules can never disagree. */
.btn-login:disabled{opacity:.5;cursor:not-allowed}

/* ⚠ .btn-label MUST BE DEFINED HERE, BUT WITH NO PROPERTIES.

   The class is used in three pages' HTML but has zero intentional styling. The
   empty ruleset exists to satisfy web/js/auth-funnel.test.mjs's per-class
   coverage guard, which verifies every class used in a page's markup is defined
   in its stylesheet. The guard strips comments before testing, so it is this
   ruleset — not a comment — that keeps the test green.

   Deleting it breaks the test with no visual symptom: the page still renders
   correctly because the class does nothing. A future reader tidying "dead CSS"
   will find the test failure cryptic and may not trace it back here.

   It must never gain a `display` (an author-origin display beats [hidden] and
   pins the label open during the loading state) or a width/height (it is a
   flex item of .btn-login and sizing would break the flex layout). See the
   [hidden] guard below. */
.btn-label{}

/* The 16px ring that replaces the label during a request. No `display` either:
   it is only ever a flex item of .btn-login, where width/height already apply,
   and one less author-origin display near a script-toggled element is one less
   way to pin it open. */
.btn-spinner{
  width:16px;height:16px;
  border:2px solid rgba(255,255,255,.4);
  border-top-color:#fff;
  border-radius:50%;
  animation:spin 1s linear infinite;
  flex-shrink:0;
}
@keyframes spin{to{transform:rotate(360deg)}}

/* #recovery-toggle — „Nu ai acces la aplicație?" under the submit button.
   A link, not a second field: the recovery path is the rare one, and a visible
   second code box invites burning a single-use code needlessly.
   It stays type="button" in the markup (it sits inside a form).
   On phones responsive.css §2 grows it to 44×44, which is why the margin above
   it is modest — the touch floor supplies most of the separation. */
.link-button{
  display:block;
  margin:.9rem auto 0;
  background:none;border:0;padding:0;
  font:inherit;font-size:var(--mk-step--1);font-weight:600;
  color:var(--mk-navy-2);
  text-decoration:underline;text-underline-offset:3px;
  cursor:pointer;
}
.link-button:hover{color:var(--mk-navy)}

/* #copy-btn, #codes-copy, #codes-download. Outlined rather than filled so the
   card has exactly one filled button — the one that moves you forward. Navy
   border reads 15.02:1 on the white card and 14.00:1 on the ground-tinted key
   row, so 1.4.11 holds in both places it is used. */
.btn-copy{
  padding:.44rem .8rem;
  background:#fff;color:var(--mk-navy);
  border:1px solid var(--mk-navy);
  border-radius:var(--mk-r);
  font:inherit;font-size:var(--mk-step--1);font-weight:600;
  cursor:pointer;flex-shrink:0;
  transition:background-color .15s ease,color .15s ease;
}
.btn-copy:hover{background:var(--mk-navy);color:#fff}

/* ---------- login: the three lines under the form ------------------------- */
/* ⚠ DEFECT §3.3. .forgot-line is defined in exactly one place today —
   totp.css:330-332 — and login.html does not load totp.css. „Am uitat parola"
   currently renders as a default blue underlined anchor with margin:0, flush
   against the divider beneath it. */
.forgot-line{margin:.9rem 0 0;text-align:center;font-size:var(--mk-step--1)}
.forgot-line a{
  color:var(--mk-navy-2);font-weight:600;
  text-decoration:underline;text-underline-offset:3px;
}

/* „sau". The rules are flex tracks, not the old pair of absolutely positioned
   42%-wide pseudo-elements: that version reserved a fixed 16% slot for the word
   and assumed a three-letter one. PR 2 puts „or" in the same slot. */
.divider{
  display:flex;align-items:center;gap:.75rem;
  margin:1.35rem 0 1rem;
  color:var(--mk-slate);font-size:var(--mk-step--1);
}
.divider::before,.divider::after{
  content:"";flex:1 1 auto;height:1px;background:var(--mk-line);
}

/* „Nu ai cont? Începe gratuit 3 luni" → inregistrare.html. */
.contact-text{text-align:center;font-size:var(--mk-step--1);color:var(--mk-slate)}
.contact-text a{
  color:var(--mk-navy-2);font-weight:600;
  /* Underlined permanently, not on hover: this link sits INSIDE a sentence, so
     WCAG 1.4.1 forbids colour as its only cue. */
  text-decoration:underline;text-underline-offset:3px;
}

/* ---------- enrolment: QR + manual key ------------------------------------ */
/* A fixed 200×200 box because #qr-image is a 200×200 data-URL PNG (D-167): at
   exactly 1:1 it is never resampled, and a resampled QR is a QR some phones
   will not read. */
.qr-wrap{
  margin:1.4rem auto;
  width:200px;height:200px;
  display:flex;align-items:center;justify-content:center;
}
/* ⚠ (0,1,1), and that is the whole point: it beats responsive.css §1's
   `img,svg,video,canvas{max-width:100%;height:auto}` (0,0,1), which would
   otherwise let the height float. */
.qr-wrap img{width:100%;height:100%;display:block}

.manual-key-row{
  display:flex;align-items:center;gap:.75rem;flex-wrap:wrap;
  padding:.7rem .9rem;
  margin-bottom:1.4rem;
  background:var(--mk-ground);
  border-radius:var(--mk-r);
  /* ⚠ BEHAVIOUR, NOT DECORATION. This is the ONLY reason #copy-toast is on
     screen: .toast is position:absolute and this is its containing block.
     Remove it and „Copiat ✓" renders 36px above the top of the document. */
  position:relative;
}
.manual-key-label{font-size:var(--mk-step--1);font-weight:500;color:var(--mk-slate);flex-shrink:0}
.manual-key{
  flex:1 1 auto;
  font-family:var(--mk-mono);
  font-size:.95rem;font-weight:600;
  letter-spacing:.08em;
  color:var(--mk-navy);              /* 14.00:1 on the ground-tinted row */
  word-break:break-all;
  /* ⚠ BEHAVIOUR, NOT DECORATION. Backs the clipboard fallback at
     enroll.js:152-164: when navigator.clipboard is missing or rejects, the
     handler selects this node with createRange()/getSelection() and the user
     copies by hand. `all` is what makes one click take the whole key rather
     than one word of it. -webkit- prefix because the repo's floor is Safari
     15.4 (web/CROSSBROWSER.md + D-234) and unprefixed user-select is newer. */
  -webkit-user-select:all;
  user-select:all;
}

/* #copy-toast and #codes-toast. ⚠ NO `display` — both ship `hidden` and are
   flashed for 2s by enroll.js:168-169 and :208-209. */
.toast{
  position:absolute;
  top:-36px;right:12px;
  padding:.3rem .6rem;
  background:var(--mk-ok-bg);color:var(--mk-ok-ink);   /* 8.16:1 */
  border-radius:6px;
  font-size:var(--mk-step--1);font-weight:600;
  animation:toast-in 150ms ease-out,toast-out 150ms ease-out 1850ms forwards;
}
@keyframes toast-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
@keyframes toast-out{to{opacity:0}}

.confirm-instructions{
  margin:0 0 .9rem;
  color:var(--mk-slate);font-size:var(--mk-step--1);line-height:1.5;
}

/* ---------- enrolment: the recovery codes, shown once and only here -------- */
/* Deliberately plain: monospace, generous spacing, high contrast. These get read
   off a screen and copied onto paper, and every ambiguity between characters is
   a support call months from now. The alphabet already drops I/L/O/0/1; the type
   has to do the rest. */
.recovery-codes{
  list-style:none;
  margin:1.1rem 0;
  padding:1rem;
  display:grid;
  grid-template-columns:repeat(2,1fr);
  gap:.6rem 1.2rem;
  background:var(--mk-ground);
  border:1px solid var(--mk-line);
  border-radius:var(--mk-r);
  font-family:var(--mk-mono);
  font-size:.95rem;
  letter-spacing:.06em;
  color:var(--mk-ink);               /* 15.28:1 on the ground */
  /* ⚠ BEHAVIOUR, NOT DECORATION — same contract as .manual-key above; it backs
     the createRange()/getSelection() fallback at enroll.js:219-223. These codes
     are the one thing on this page a user MUST be able to select. */
  -webkit-user-select:all;
  user-select:all;
}
.recovery-codes li{padding:.1rem 0;text-align:center}

.codes-actions{
  display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;
  /* ⚠ THE FIX FOR THE SECOND HALF OF THE #copy-toast CONTRACT. .manual-key-row
     has position:relative and #copy-toast works; .codes-actions never had it,
     so #codes-toast resolves its `top:-36px` against the initial containing
     block and „Copiat ✓" renders 36px ABOVE the document — off screen, on the
     one action whose whole purpose is telling you the codes were saved. */
  position:relative;
}

.codes-warning{
  margin:1rem 0 .3rem;
  padding:.7rem .9rem;
  background:var(--mk-ground);
  border-left:3px solid var(--mk-accent);
  border-radius:0 6px 6px 0;
  font-size:var(--mk-step--1);line-height:1.5;
  color:var(--mk-ink);               /* 15.28:1 */
}

/* „Am salvat codurile" — the gate in front of #codes-continue. Defined nowhere
   the page can see it today: the only .toggle-row rules in the repo are
   dialog-scoped (shared.css:565, technicians.css:154) and enroll.html loads
   neither sheet. */
.toggle-row{
  display:flex;align-items:flex-start;gap:.6rem;
  margin:1rem 0 1.15rem;
  padding:.35rem 0;
  font-size:var(--mk-step--1);line-height:1.5;
  cursor:pointer;
}
.toggle-row input{
  width:1.05rem;height:1.05rem;
  margin:.2rem 0 0;
  flex:none;
  accent-color:var(--mk-navy);       /* Safari 15.4+, i.e. exactly the floor;
                                        older engines get the default checkbox */
  cursor:pointer;
}
/* The 17px box is not the tap target — the <label for="codes-saved"> wraps both
   halves, so the whole row is clickable. responsive.css §2's 44px floor
   explicitly excludes [type=checkbox], which is why the box keeps its size. */

/* ⚠ AN ID-SPECIFICITY RULE, CARRIED OVER UNCHANGED. (1,1,0) — a class-based
   restyle cannot replace it, so it stays as the sheet it came from wrote it.
   The continue button is disabled until the checkbox is ticked and must read as
   unavailable rather than broken. .btn-login:disabled above carries the same
   values so the two can never paint differently. */
#codes-continue:disabled{opacity:0.5;cursor:not-allowed}

/* ---------- footer -------------------------------------------------------- */
/* --mk-slate on --mk-ground is 4.56:1 — over the 4.5:1 floor for this size, but
   only just. Do not lighten it; on white it would be 4.89:1, but this line sits
   on the page ground, not on the card. */
footer{
  text-align:center;
  padding:1.15rem var(--mk-gut);
  font-size:var(--mk-step--1);
  color:var(--mk-slate);
}

/* ---------- focus: the site's orange ring, not the app's navy one ---------- */
/* ⚠ SPECIFICITY, NOT SOURCE ORDER. responsive.css carries a global
   `*:focus-visible{outline:2px solid #1a3c8f}` (0,1,0) at :25-28 and is linked
   AFTER this file by the repo's documented convention — it cannot be edited and
   the <link> order must not be "fixed", because responsive.css's own header
   declares that order load-bearing. These selectors are (0,2,1) and (0,1,1), so
   they win regardless of load order.
   #D9541E is 4.01:1 against the white card and 3.74:1 against the page ground —
   both over 1.4.11's 3:1 for a focus indicator. */
.card a:focus-visible,
.card button:focus-visible,
.card input:focus-visible,
.topbar a:focus-visible,
a.skip-link:focus-visible{
  outline:3px solid var(--mk-accent);
  outline-offset:3px;
  border-radius:4px;
}
/* (0,1,1) beats responsive.css's .skip-link (0,1,0) for the colour only. Its
   left:-9999px / :focus{left:8px} mechanics are left entirely alone — they are
   the control. */
a.skip-link{background:var(--mk-navy)}

/* ---------- responsive adjustments ---------------------------------------- */
@media (max-width:480px){
  /* Two columns of 13-character monospace do not fit a 320px card; a code split
     across a line break is a code someone types wrong. */
  .recovery-codes{grid-template-columns:1fr}
}

@media (prefers-reduced-motion:reduce){
  /* ⚠ THE CSS HALF OF A CONTRACT WHOSE JS HALF IS totp.js:99 and :110-113.
     Under reduce, totp.js drops the rAF loop and ticks once a second — so the
     ring would jump in whole-second steps, which is worse than the animation it
     replaced. The JS branch only makes sense because this rule hides the ring;
     the numeric label carries the countdown alone. The seconds keep ticking.
     ⚠ NOTE FOR web/js/auth-funnel.test.mjs: this sets `display` on
     .countdown-RING, which is a different class from .countdown. The guard that
     asserts .countdown has no display must anchor the token — /\.countdown(?![\w-])/,
     as page-elements.test.mjs:251 does — or it will trip on this line. */
  .countdown-ring{display:none}

  /* responsive.css §6 forces animation-duration to 0.01ms with !important on
     everything. For the spinner that leaves a white arc frozen at a random
     angle; `animation:none` plus a transparent top edge turns it into a plain
     static ring instead. */
  .btn-spinner{animation:none;border-top-color:transparent}

  /* Same blanket, worse consequence: `toast-out … forwards` would land
     instantly, so „Copiat ✓" would be pinned at opacity 0 for the whole two
     seconds it is un-hidden — the copy confirmation would simply never appear
     for a reduce-motion visitor. Killing the animation lets `hidden` do the
     showing and hiding on its own. */
  .toast{animation:none}
}

/* ---------- the header's right-hand side, and the RO|EN pill ------------- */
/* PR 2. Used by login.html and login-en.html and by no other funnel page —
   §5.1 of the design: language is chosen at the door, never mid-flow. The two
   2FA pages keep a bare back link as their only header action.

   ⚠ .topbar above is `display:flex` with a gap and NO justify-content. This
   wrapper is pushed right by `margin-left:auto`, not by putting
   `justify-content:space-between` on .topbar — with three header children
   space-between would strand the pill in the middle of the bar, and the bar has
   three children here and two on the other four pages. */
.topbar-actions{
  margin-left:auto;
  display:flex;align-items:center;gap:.75rem;
  /* responsive.css:192 hands .topbar `flex-wrap:wrap` under 1280px and cannot
     be edited. Keeping this wrapper unwrappable means the bar breaks BETWEEN
     the brand and the actions, never between the pill and the back link. */
  flex-wrap:nowrap;
}

/* The pill itself, lifted from site/assets/css/style.css:136-155. On the site it
   sits over a photograph and draws its border and ground from `currentColor`;
   this header is white with a hairline, so both are spelled out in the --mk-*
   palette instead. The site's base `opacity:.85` is deliberately NOT carried
   over: on white it lifts navy text off the 4.5:1 floor for no visual gain. */
.lang{
  display:inline-flex;
  border:1px solid var(--mk-line);
  border-radius:100px;
  font-size:var(--mk-step--1);
  color:var(--mk-navy);
}
.lang .lang-opt{
  display:block;
  padding:.24rem .68rem;
  color:inherit;text-decoration:none;font-weight:500;
  white-space:nowrap;   /* "RO"/"EN" must never break; the bar is tight at 360px */
}
/* the hairline that separates the two halves */
.lang .lang-opt + .lang-opt{border-left:1px solid var(--mk-line)}
/* ⚠ ROUNDED ON THE CHILDREN, not `overflow:hidden` on the pill — the site does
   it this way for a reason. `overflow:hidden` would clip the focus ring off the
   interactive half, and that ring is the only thing telling a keyboard user
   where they are. */
.lang .lang-opt:first-child{border-radius:100px 0 0 100px}
.lang .lang-opt:last-child{border-radius:0 100px 100px 0}
/* ⚠ TWO CUES, NOT COLOUR. The current language is marked by a heavier weight
   AND a filled ground, so the state survives greyscale, a monochrome display
   and WCAG 1.4.1. Do not "simplify" this into a colour swap. */
.lang .lang-opt[aria-current]{font-weight:700;background:rgba(16,38,74,.10)}
/* Only the OTHER language is interactive — the current one ships as a <span>,
   so there is no such thing as a link to the page you are already on. */
.lang a.lang-opt:hover{
  background:rgba(16,38,74,.06);
  text-decoration:underline;text-underline-offset:3px;
}
/* (0,3,1). responsive.css carries `*:focus-visible{outline:2px solid #1a3c8f}`
   at (0,1,0) and is linked AFTER this sheet; specificity is what wins here, not
   source order. z-index lifts the ring over the adjacent half's border. */
.lang a.lang-opt:focus-visible{
  position:relative;z-index:1;
  outline:3px solid var(--mk-accent);
  outline-offset:2px;
  border-radius:4px;
}

/* ---------- phones: the header on ONE row (2026-10-03) ------------------
   Andrei, on his phone: the RO|EN pill and „Înapoi la vtpgo.ro" looked
   misplaced. Below 1280px responsive.css §7 lets .topbar wrap, so at 390px they
   fell onto a second, right-aligned row under the lockup (header 103px tall).
   On phones the subtitle goes (the public site hides it too), and the back
   link shortens — back-link.js renders all three lengths; its accessible name
   is always the full label.
   ⚠ header.topbar, not .topbar: responsive.css is linked AFTER this sheet and
   sets `.topbar{flex-wrap:wrap}` (0,1,0) below 1280px — equal specificity here
   would lose on order. Measured in WebKit: one row, 65px, at 390 and 360px. */
.btn-back-marketplace .back-short{display:none}
@media (max-width:600px){
  header.topbar{flex-wrap:nowrap;gap:.75rem}
  .topbar .app-subtitle{display:none}
  .topbar-actions{gap:.5rem}
  .btn-back-marketplace .back-long{display:none}
  .btn-back-marketplace .back-short{display:inline}
}
/* The narrowest phones (320px): „VTP GO", the pill and „← vtpgo.ro" do not fit
   on one row, so the link keeps only its arrow. */
@media (max-width:340px){
  .btn-back-marketplace .back-domain{display:none}
}

/* ---------- ⚠ THE GUARD — read the file header before touching ------------
 *
 * Four classes defined in this file are toggled by `el.hidden` in script, and
 * NOTHING protects them except the absence of an author-origin `display`. The
 * UA's [hidden]{display:none} is the weakest rule in the cascade; any author
 * `display` on these elements beats it and pins them open:
 *
 *   .card       — enroll.js:202-203 sets form.hidden=true / card.hidden=false to
 *                 swap the enrolment form for the recovery-codes panel. With a
 *                 display, the swap silently stops happening: the user sees the
 *                 enrolment form AND their one-time recovery codes at once — and
 *                 because .main is a flex row, side by side rather than stacked.
 *   .btn-label  — index.js:98, totp.js and enroll.js:134 set submitLabel.hidden
 *                 while the request is in flight. With a display, every sign-in
 *                 shows „Autentificare" and the spinner simultaneously.
 *   .toast      — enroll.js:168 and :208 flash #copy-toast / #codes-toast for two
 *                 seconds. With a display, both read „Copiat ✓" from first paint,
 *                 before anything has been copied, and never stop.
 *   .countdown  — totp.js:156 sets countdownEl.hidden when the user switches to a
 *                 recovery code. THIS ONE IS ALREADY BROKEN IN PRODUCTION:
 *                 totp.css:198 gives it `display:flex`, so the 30-second TOTP
 *                 ring keeps ticking beside a recovery code that does not expire.
 *                 That is the bug this sheet is here to end.
 *
 * Two more ride along on the same rule: #form-error ([role=alert]) would show an
 * empty red box on every page load, and .btn-spinner would spin permanently.
 *
 * `!important` is not decoration either — responsive.css is linked AFTER this
 * file, so an equal-specificity rule there would out-order this one. An
 * important author declaration wins on origin, whatever the order.
 * ------------------------------------------------------------------------- */
[hidden]{display:none !important}
