/* ============================================================================
   Kizuna Logic — the sign-in experience  (auth.css)
   Rebuilt 2026-09-16 on the owner's brief: match the sign-in and sign-up pages of
   the best SaaS products, and drop the decorative icons.

   WHAT CHANGED, AND WHY — read this before "improving" it back:
   · The split screen is gone. Half the page was a cartoon dashboard drawn in 90
     lines of hand-written SVG, repeated in five templates with five variations.
     It said nothing true about the product and it is the thing the owner called
     childish. The product's OWN screenshots are the wallpaper now: blurred,
     dimmed and masked so they read as depth rather than as content.
   · The card is the only thing in focus, and it holds exactly one job. Brand
     lockup above it, legal line below it, nothing beside it.
   · Field icons are gone. A label already names the field; a glyph inside the box
     costs 44px of a 352px input and tells the reader nothing they did not know.
     The only icons left on these pages are the two that DO something — the theme
     toggle and the password eye.

   Scoped under `.kz-auth` so nothing here can reach the app (kizuna-ui §9ag: a
   page stylesheet loaded after app.css inherits every generic class name it has
   ever defined — `.kz-field` collided exactly that way and broke every login).
   Every class in this file is prefixed for that reason.
   ========================================================================== */

.kz-auth{
  /* ================= THE STAGE — fixed in BOTH themes, never flips =================
     The ground, the product windows behind it, the brand lockup and the policy line.
     🔴 Do not add these to the dark block below. The whole point is that they do not
     move: the screenshots are captures of a dark application, and on a pale ground they
     read as two different worlds stacked on one page (owner-reported 2026-09-16). */
  /* 🔴 NAVY-INDIGO, not black. A neutral near-black is the colour of a switched-off
     screen; the eye reads it as absence. Two points of blue in the ground is the whole
     difference between "dark theme" and "unlit". */
  --kz-stage:#080B1A;
  --kz-stage-2:#1A2145;         /* the brand-lit pool the card sits in */
  --kz-stage-ink:#F3F6FD;       /* the wordmark, on the stage */
  --kz-stage-muted:#9AA6C2;     /* the policy line and the alt line, on the stage */
  --kz-stage-brd:rgba(165,180,252,.22);
  /* the brand's own three stops, used for the aurora and for every accent, so nothing
     coloured on this page is a one-off */
  --kz-indigo:#4F46E5;
  --kz-blue:#2563EB;
  --kz-cyan:#06B6D4;

  /* ================= THE CARD — light first, complete; dark redefines ONLY these = */
  /* ⚠ NOT pure #FFF. Against a very dark stage a pure-white card is a light box, and the
     eye fights it; a hair of blue in the white sits in the same family as the stage and
     reads as paper rather than as a lamp. The gradient is 1.5% from top to foot — enough to
     say "lit from above", not enough to look grey. */
  /* 🔴 THE GRADIENT IS ON THE CARD (owner ask 2026-09-16). White at the head, where the
     title and the first labels are, into a light brand tint at the foot — the same indigo
     the button is built from, at a twentieth of its strength. A form surface may graduate,
     but it may not cost the fields their definition, which is why the wells below were
     darkened a step to read against BOTH ends of this range. */
  --kz-card-bg:#FFFFFF;
  --kz-card-mid:#F7F9FF;
  --kz-card-bg-2:#EAEFFC;       /* the card's own gradient foot */
  --kz-card-brd:rgba(15,23,42,.10);
  --kz-card-hi:rgba(255,255,255,.9);
  --kz-ink:#0B0F1C;
  --kz-muted:#57617A;
  --kz-faint:#6B7484;           /* placeholder ink — AA on the card, not a whisper */
  --kz-brand-ink:#4338CA;       /* brand as TEXT. The button fill is too light to read
                                   as a link on white, and too dark on the dark card —
                                   a fill and an ink are two different colours. */
  /* 🔴 A FIELD IS A WELL, NOT THE CARD WITH A LINE ROUND IT. On white-on-white the only
     thing saying "type here" was a hairline. */
  --kz-field:#EEF2FB;
  --kz-field-focus:#FFFFFF;
  --kz-field-brd:#D2DAEE;
  --kz-field-brd-hover:#B6C1DC;
  --kz-brand:#4F46E5;
  --kz-brand-2:#4338CA;
  --kz-ring:rgba(79,70,229,.25);
  --kz-shadow:0 1px 0 var(--kz-card-hi) inset, 0 2px 8px rgba(0,0,0,.26),
              0 44px 88px -30px rgba(0,0,0,.6);
  --kz-shot-op:.72;             /* how present the product shots are         */
  --kz-shot-brd:rgba(148,163,184,.18);
  --kz-shot-shadow:0 34px 70px -28px rgba(0,0,0,.72);
  --logo:40px;                  /* the brand mark above the card — ONE number */

  font-family:'Geist','Inter',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  color:var(--kz-stage-ink);
  background:var(--kz-stage);
  /* 🔴 NOT `height:100dvh; overflow:hidden`. A card taller than the viewport must be
     able to scroll or its bottom is unreachable — kizuna-ui §9e, which cost a whole
     sign-up form. `clip` on one axis only; `hidden` would force the other to scroll. */
  min-height:100dvh;
  position:relative;
  overflow-x:clip;
  -webkit-font-smoothing:antialiased;
  display:flex;
}

/* Only the CARD flips. Every --kz-stage-* token above is deliberately absent here: the
   ground is one dark in both themes, because the screenshots behind it are captures of a
   dark application and a pale ground puts two worlds on one page. */
html[data-theme="dark"] .kz-auth{
  --kz-card-bg:#1B2136;
  --kz-card-mid:#181D30;
  --kz-card-bg-2:#141829;
  --kz-card-brd:rgba(165,180,252,.16);
  --kz-card-hi:rgba(255,255,255,.055);
  --kz-ink:#E9EDF6;
  --kz-muted:#98A2B8;
  --kz-faint:#8A94A8;
  --kz-brand-ink:#A5B4FC;
  --kz-field:#101731;
  --kz-field-focus:#131B3A;
  --kz-field-brd:#2C3658;
  --kz-field-brd-hover:#3E4A74;
  --kz-brand:#6366F1;
  --kz-brand-2:#4F46E5;
  --kz-ring:rgba(99,102,241,.3);
  --kz-shadow:0 1px 0 var(--kz-card-hi) inset, 0 2px 8px rgba(0,0,0,.42),
              0 44px 88px -30px rgba(0,0,0,.78);
  --kz-shot-op:.56;
}

/* ============================ the ground ============================ */
/* The aurora lives on the STAGE element, so it paints behind the product windows (which
   are its children) and under the veil (which is its last child). Three very low-alpha
   blobs at the corners: colour where the eye is not working, nothing where it is. */
.kz-auth__stage{
  position:fixed; inset:0; z-index:0; overflow:hidden; pointer-events:none;
  background:
    /* ⚠ PUSH THE CENTRES OFF-CANVAS AND TAKE THE ALPHA DOWN. The first tuning put three
       bright centres INSIDE the frame, so instead of one lit field the page showed three
       distinguishable patches — a teal one bottom-centre that read as a stain. Only the
       soft falloff should ever be on screen. */
    radial-gradient(76% 66% at -4% -10%,  color-mix(in srgb, var(--kz-indigo) 34%, transparent) 0%, transparent 72%),
    radial-gradient(70% 62% at 106% 110%, color-mix(in srgb, var(--kz-cyan)   19%, transparent) 0%, transparent 70%),
    radial-gradient(92% 58% at 50% 124%,  color-mix(in srgb, var(--kz-blue)   22%, transparent) 0%, transparent 74%),
    var(--kz-stage);
}

/* 🔴 SHOW THE PRODUCT, OR SHOW NOTHING. Owner-reported 2026-09-16 with a screenshot:
   *"the bg images are so hiding"*. Blurring a screenshot to 7px and dimming it to a third
   leaves no recognisable shape at all — what the eye gets is grey cloud, which reads as
   dirt on the display, not as depth. A background nobody can identify is worse than no
   background. These are REAL application windows: framed, angled, and moving.

   The frame is load-bearing. A radius, a hairline and a shadow make a hard edge read as a
   WINDOW rather than as a torn image, which is exactly why the blurred version had to
   dissolve its edges and this one does not. */
/* 🔴 THE DIMMING AND ANY FILTER LIVE ON THE RAIL, NOT ON EACH IMAGE. Measured, not
   guessed: with `opacity`, `filter` and `box-shadow` on each of the twelve screenshots the
   page ran at a median 50ms per frame (20fps) while the rails moved. Pausing the animation
   returned 16.7ms; so did removing the rails; so did keeping the animation and stripping
   only those three properties. **The motion was free and the per-image effects were the
   whole cost** — twelve filter/opacity layers re-composited every frame. One opacity on one
   element per side does the same job for nothing. `filter:saturate()` is gone entirely; at
   .92 it was invisible and it is not worth a compositing pass. */
.kz-rail{
  position:absolute; top:50%;
  /* ⚠ The rail runs PAST the point where the veil has gone fully opaque, so its own
     clip edge is painted over rather than fading into view. Sized the other way round
     — rail ending before the veil closes — it left a faint vertical seam down each
     side, which is the exact hard edge this whole treatment exists to avoid. */
  width:min(42vw,620px); height:150vh;
  overflow:hidden;
  opacity:var(--kz-shot-op);
}
/* 🔴 THE RAILS RUN UPRIGHT, AND THAT IS A PERFORMANCE DECISION, NOT A STYLE ONE.
   Measured on the finished page, median frame time while the rails move:

       rotated rail, plain translate inside      50.0ms   (20fps)
       rotation moved into the animated transform 33.4ms  (30fps)
       no rotation at all                        16.7ms   (60fps)

   A rotated ancestor forces the moving track to be re-rasterised every frame instead of
   composited, and no amount of `will-change` or layer promotion recovers it — both were
   tried and measured at 50.0ms unchanged. The tilt was decoration; half the frame rate on
   the page where someone types a password is not a price worth paying for it. An upright
   column of product screens is also the shape most premium marquees actually use.
   ⚠ Isolate before optimising: the first hypothesis was the per-image filters, and a
   sloppy probe appeared to confirm it. Toggling ONE property at a time found the real one. */
/* 🔴 NO MASK ON THE RAIL. A mask is an effect on the ANCESTOR of a moving layer, exactly
   like the rotation, and it costs exactly the same: median 50.0ms. The inner edge is
   dissolved by the VEIL instead — a static element that never animates, so fading it there
   is free. ⚠ AND THE MEASUREMENT THAT NEARLY HID THIS: adding the mask with
   `add_style_tag` AFTER load measured 16.7ms, because Chrome kept the layer it had already
   composited. **A property injected after load is not the same as a property in the
   stylesheet.** Restart, reload, then measure. */
.kz-rail--l{ left:-8vw;  transform:translateY(-50%); }
.kz-rail--r{ right:-8vw; transform:translateY(-50%); }

/* 🔴 ONE animated element per rail, and the track holds the set TWICE so -50% lands on the
   duplicate. `will-change` promotes it once rather than every frame. */
.kz-rail__t{
  display:block;
  will-change:transform;
  animation:kz-rail-up 96s linear infinite;
}
.kz-rail--r .kz-rail__t{ animation-duration:124s; animation-direction:reverse; }
@keyframes kz-rail-up{ from{ transform:translateY(0); } to{ transform:translateY(-50%); } }

.kz-shot{
  display:block; width:100%; height:auto;
  /* 🔴 MARGIN, NOT GAP. Every item including the last carries the same margin, so the track
     is two identical halves and the loop is exact. A `gap` leaves the halves one gap apart
     and the rail jumps once per cycle — invisible while you watch for it, obvious later. */
  margin:0 0 26px;
  border-radius:12px;
  border:1px solid var(--kz-shot-brd);
}

@media (prefers-reduced-motion:reduce){
  .kz-rail__t{ animation:none; }
}

/* 🔴 THE VEIL IS WHAT MAKES CRISP SCREENSHOTS SAFE. It paints the page's own ground
   solidly across the middle and releases toward the edges, so the shots are only ever seen
   in peripheral vision and the card sits on a clean surface. Without it a legible
   screenshot behind a form is noise; with it, the product is visible where the eye is not
   working and absent where it is. Two stops, not one: a hard inner disc and a long fade,
   or the ground bleeds all the way out and hides the shots again. */
.kz-auth__veil{
  position:absolute; inset:0;
  background:
    /* the brand lighting the card sits in — a pool, not a spotlight: it has to lift the
       middle away from the ground without competing with the card's own edge */
    radial-gradient(56% 48% at 50% 40%, color-mix(in srgb, var(--kz-stage-2) 78%, transparent) 0%, transparent 76%),
    /* the ground itself, solid across the middle and releasing at the edges, so the
       windows are only ever seen in peripheral vision */
    radial-gradient(40% 50% at 50% 48%, var(--kz-stage) 0%, var(--kz-stage) 38%,
                    color-mix(in srgb, var(--kz-stage) 55%, transparent) 70%, transparent 100%),
    /* the horizontal band that dissolves each rail's inner edge — the rail itself carries no
       mask, for the reason above, so this static layer does the fading */
    /* ⚠ the opaque band is what the card sits on; widening the CLEAR band on either side
       of it is how the product is allowed forward without the form ever sitting on a
       moving picture. The rails are sized to end INSIDE this band (§ the rail rule). */
    linear-gradient(to right, transparent 0%, transparent 3%, var(--kz-stage) 28%,
                    var(--kz-stage) 72%, transparent 97%, transparent 100%),
    linear-gradient(to bottom, var(--kz-stage) 0%, transparent 16%, transparent 84%, var(--kz-stage) 100%);
}

/* On a phone the shots would be a smear behind the form. Take them away entirely. */
/* On a phone the windows would be two slivers behind the form. Take them away; the
   brand-lit stage alone still reads as designed. */
@media (max-width:820px){ .kz-rail{ display:none; } }

/* ============================ the column ============================ */
/* 🔴 `margin:auto` on the ITEM, never `align-items:center` on the scroller. Centring a
   possibly-tall item with align-items overflows in BOTH directions and the part above
   the start edge cannot be scrolled to (kizuna-ui §9e). */
.kz-auth__center{
  position:relative; z-index:1;
  margin:auto;
  /* Owner ask 2026-09-16: *"try get the card grid width larger"*. A 452px card on a
     1440px screen reads as a dialog; the premium sign-in pages all sit nearer 500, with
     the padding grown to match so the extra width becomes AIR rather than longer lines. */
  width:100%; max-width:516px;
  /* 🔴 THE FOLD IS MEASURED ON THE POLICY LINE, NOT ON THE LAST FIELD. Owner-reported
     2026-09-16: *"its again and again asking to scroll the screen to view and click terms
     and condition"*. My own fold check measured the last CONTROL inside the card, so a card
     whose form fitted perfectly could still push the line underneath it off the screen —
     and that line is the one thing on the page a person may need to click before they sign
     in. The vertical padding is tighter, and the check now measures the column. */
  padding:clamp(18px,3.4vh,38px) 20px clamp(18px,3vh,32px);
  display:flex; flex-direction:column; align-items:stretch;
}
/* The sign-up card is wider still, because three columns need ~250px each for a label
   to stay on one line. Below 1180px it falls back to two columns and the old width. */
.kz-auth--wide .kz-auth__center{ max-width:764px; }
@media (min-width:1180px){ .kz-auth--wide .kz-auth__center{ max-width:892px; } }

/* ---- brand lockup, above the card ---- */
/* 🔴 THE LOCKUP IS THE APP'S, NOT A ONE-OFF. The product's brand treatment is a mark that
   reads as an app icon — hairline ring, soft drop — beside a BOLD, UPPERCASE, single-colour
   wordmark with open tracking (kizuna-ui §2, and the sidebar lockup). The auth pages had
   drifted to sentence case at a tighter track, which is why it read as "normal": it was not
   the brand, it was type. Uppercase with .085em of tracking also gives the two words a
   width that balances the card beneath them instead of huddling in the middle. */
.kz-mark{
  display:flex; align-items:center; justify-content:center; gap:13px;
  margin:0 0 18px; text-decoration:none; color:var(--kz-stage-ink);
  font-family:var(--font-display),'Switzer',system-ui,sans-serif;
  font-weight:700; font-size:1.02rem;
  text-transform:uppercase; letter-spacing:.085em;
}
.kz-mark img{
  width:var(--logo); height:var(--logo); border-radius:12px;
  /* the ring and the drop are what make a flat tile read as an application icon */
  box-shadow:0 0 0 1px rgba(165,180,252,.24), 0 10px 24px -10px rgba(79,70,229,.8);
  transition:transform .2s var(--kz-ease, cubic-bezier(.2,.7,.3,1)), box-shadow .2s ease;
}
.kz-mark:hover{ color:var(--kz-stage-ink); }
.kz-mark:hover img{
  transform:translateY(-2px);
  box-shadow:0 0 0 1px rgba(165,180,252,.4), 0 14px 30px -10px rgba(79,70,229,.95);
}

/* ---- the card ---- */
.kz-card{
  /* A lit object: a gradient down its face and a hairline of light along its top edge,
     which is what separates a card from a hole cut in the page. Three stops, not two — with
     two, the tint starts too early and the heading sits on colour. */
  background:linear-gradient(168deg, var(--kz-card-bg) 0%, var(--kz-card-mid) 46%,
                             var(--kz-card-bg-2) 100%);
  position:relative; isolation:isolate;
  border:1px solid var(--kz-card-brd);
  border-radius:18px;
  /* Lifted off a dark stage in both themes, so the shadow is a real one and the top edge
     carries a hairline of light — that is what makes a white card read as an object
     resting on the page rather than a hole cut out of it. */
  box-shadow:var(--kz-shadow);
  padding:clamp(28px,4vw,44px) clamp(24px,4.4vw,46px);
}
.kz-card__t{
  font-family:var(--font-display),'Switzer',system-ui,sans-serif;
  font-weight:700; font-size:1.72rem; line-height:1.18; letter-spacing:-.028em;
  text-align:center; text-wrap:balance; margin:0;
  color:var(--kz-ink);
}
.kz-card__sub{
  text-align:center; color:var(--kz-muted);
  font-size:.9rem; line-height:1.55; margin:10px auto 0;
  /* the card is wider now; the SENTENCE is not. A measure past ~48 characters reads
     as a paragraph and the eye stops treating it as a caption. */
  max-width:46ch; text-wrap:balance;
}
.kz-card > form, .kz-card > .kz-after{ margin-top:22px; }

/* A reassurance under the heading, e.g. "No credit card required". It is a PILL and not a
   sentence because it answers the first objection a visitor has, and an objection is
   answered by being SEEN rather than read. Deliberately quiet — green would make it a
   status, and it is not one. */
.kz-card__pill{ margin:11px 0 0; text-align:center; }
.kz-card__pill span{
  display:inline-block; padding:4px 12px; border-radius:999px;
  font-size:.75rem; font-weight:650; letter-spacing:-.005em;
  color:var(--kz-muted);
  background:color-mix(in srgb, var(--kz-ink) 6%, transparent);
  border:1px solid var(--kz-card-brd);
}

/* A quiet pill naming the portal — the one place an eyebrow earns its space, because
   four of these pages are on four different addresses and look otherwise identical. */
.kz-eyebrow{
  display:flex; justify-content:center; margin:0 0 14px;
}
.kz-eyebrow span{
  display:inline-block; padding:4px 11px; border-radius:999px;
  font-size:.688rem; font-weight:700; letter-spacing:.07em; text-transform:uppercase;
  color:var(--kz-brand-ink);
  /* the pill borrows the same two stops as the button, at a twentieth of the strength */
  background:linear-gradient(118deg, color-mix(in srgb, var(--kz-indigo) 15%, transparent),
                                     color-mix(in srgb, var(--kz-cyan) 13%, transparent));
}

/* ============================ fields ============================ */
/* `.kz-f` and not `.kz-field`: that name belongs to the credential row in app.css and
   the collision broke every login page once already (kizuna-ui §9ag). */
.kz-f{ margin-bottom:15px; }
/* The label row: the name on the reading edge, its hint on the far one. Baseline-aligned
   so the two sit on one line however different their sizes are, and the hint is allowed to
   shrink rather than push the label. */
/* ⚠ `space-between` threw the required asterisk to the FAR RIGHT of the row, a lone red
   star with nothing near it — it belongs against the word it qualifies. Only the hint is
   pushed out, with its own auto margin. */
.kz-f > label, .kz-card .form-label{
  display:flex; align-items:baseline; gap:10px;
  margin:0 0 7px;
  font-size:.785rem; font-weight:660; color:var(--kz-ink); letter-spacing:.003em;
}
.kz-f > label > i, .kz-card .form-label > i{
  margin-inline-start:auto;
  font-style:normal; font-weight:500; font-size:.7rem; color:var(--kz-muted);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0;
  text-align:end;
}
/* an empty hint (the phone one before a country is picked) must not reserve a gap */
.kz-f > label > i:empty{ display:none; }
.kz-inwrap{ position:relative; display:block; }

.kz-in,
.kz-card .form-control,
.kz-card .form-select{
  width:100%; height:48px;
  padding:0 15px;
  font-size:.95rem; font-family:inherit; color:var(--kz-ink);
  background:var(--kz-field);
  border:1px solid var(--kz-field-brd);
  border-radius:11px;
  outline:none;
  box-shadow:0 1px 2px rgba(15,23,42,.03) inset;
  transition:border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.kz-card textarea.form-control{ height:auto; padding:11px 14px; line-height:1.5; }
/* ⚠ `text-transform:uppercase` on the business-code input also uppercases its
   PLACEHOLDER — it rendered "E.G. SHUBH2", which reads as shouting rather than as an
   example. The hint is not the value; exempt it. */
.kz-in::placeholder, .kz-card .form-control::placeholder{ color:var(--kz-faint); opacity:1; text-transform:none; }
.kz-in:hover, .kz-card .form-control:hover, .kz-card .form-select:hover{ border-color:var(--kz-field-brd-hover); }
/* 🔴 Focus is a change of MATERIAL, not just a ring: the well brightens to the card's own
   white, so the field you are typing in is visibly the live one even at a glance. */
.kz-in:focus, .kz-card .form-control:focus, .kz-card .form-select:focus{
  background:var(--kz-field-focus);
  border-color:var(--kz-brand);
  box-shadow:0 0 0 3px var(--kz-ring);
}
.kz-card .form-control[readonly]{ background:color-mix(in srgb, var(--kz-ink) 5%, var(--kz-field)); color:var(--kz-muted); }
/* Room for the eye button, and only where there is one. */
.kz-inwrap .kz-in[type="password"], .kz-inwrap .kz-in[data-eyed]{ padding-right:46px; }

.kz-eye{
  position:absolute; top:50%; right:6px; transform:translateY(-50%);
  width:34px; height:34px; border:0; background:transparent; cursor:pointer;
  border-radius:8px; color:var(--kz-faint);
  display:flex; align-items:center; justify-content:center;
}
.kz-eye .ki, .kz-eye .bi{ width:17px; height:17px; font-size:17px; }
.kz-eye:hover{ color:var(--kz-ink); background:color-mix(in srgb, var(--kz-ink) 6%, transparent); }
.kz-eye:focus-visible{ outline:none; box-shadow:0 0 0 3px var(--kz-ring); }

/* the hint line under a field (password rules, phone length) */
.kz-hint, .kz-card .form-text{
  display:block; margin-top:6px; font-size:.75rem; color:var(--kz-muted); line-height:1.45;
}

/* the required marker and its legend */
.kz-card .req{ color:#DC2626; font-weight:700; margin-left:-6px; }
html[data-theme="dark"] .kz-card .req{ color:#F87171; }
.kz-reqnote{ white-space:nowrap; color:var(--kz-faint); }

/* the honeypot — off-screen, NOT display:none, which some bots skip (kizuna-ui §9c) */
.kz-hp{ position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden; }

/* ---- the remember / forgot row ---- */
.kz-row{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  margin:2px 0 20px; font-size:.82rem; flex-wrap:wrap;
}
.kz-check{ display:flex; align-items:center; gap:7px; color:var(--kz-muted); cursor:pointer; margin:0; }
.kz-check input{ width:15px; height:15px; accent-color:var(--kz-brand); margin:0; cursor:pointer; }
.kz-link{ color:var(--kz-brand-ink); font-weight:600; text-decoration:none; }
.kz-link:hover{ color:var(--kz-brand-ink); text-decoration:underline; }

/* ---- the primary button. One per card. ---- */
.kz-btn,
.kz-card .btn-kz{
  display:flex; align-items:center; justify-content:center; gap:8px;
  width:100%; height:48px; padding:0 18px;
  font-family:inherit; font-size:.94rem; font-weight:650; letter-spacing:-.005em;
  color:#fff; cursor:pointer;
  /* 🔴 The page's one loud element takes the BRAND GRADIENT, not a flat fill. Indigo into
     blue is the product's own mark; a single flat purple was the whole reason the palette
     read as arbitrary. Diagonal so the two stops are both visible across a wide button. */
  background:linear-gradient(118deg, var(--kz-indigo) 0%, #4338CA 46%, var(--kz-blue) 100%);
  border:1px solid rgba(37,99,235,.55);
  border-radius:11px;
  box-shadow:0 1px 0 rgba(255,255,255,.2) inset, 0 10px 22px -10px rgba(59,80,220,.75);
  transition:transform .12s ease, box-shadow .18s ease, filter .18s ease;
}
.kz-btn:hover, .kz-card .btn-kz:hover{ color:#fff; filter:brightness(1.07) saturate(1.05);
  box-shadow:0 1px 0 rgba(255,255,255,.2) inset, 0 14px 30px -10px rgba(59,80,220,.85); }
.kz-btn:active, .kz-card .btn-kz:active{ transform:translateY(1px); }
.kz-btn:focus-visible, .kz-card .btn-kz:focus-visible{ outline:none; box-shadow:0 0 0 3px var(--kz-ring), 0 8px 18px -10px rgba(79,70,229,.7); }
.kz-btn[disabled], .kz-card .btn-kz[disabled]{ opacity:.65; cursor:not-allowed; }

/* a secondary, quiet button (used by the two-step reset) */
.kz-btn--ghost{
  background:transparent; color:var(--kz-ink);
  border:1px solid var(--kz-field-brd); box-shadow:none;
}
.kz-btn--ghost:hover{ color:var(--kz-ink); background:color-mix(in srgb, var(--kz-ink) 4%, transparent); filter:none; box-shadow:none; }

/* ---- lines under the card ---- */
.kz-alt{
  margin:15px 0 0; text-align:center;
  font-size:.875rem; color:var(--kz-stage-muted);
}
/* ⚠ On the stage, not on the card — so it takes the stage's brand ink. The card's
   `--kz-brand-ink` is a dark indigo in the light theme and would be unreadable here. */
.kz-alt a{ color:#B4BEFF; font-weight:650; text-decoration:none; }
.kz-alt a:hover{ text-decoration:underline; }

/* the fine print INSIDE the card (recovery links) */
.kz-fine{
  margin:18px 0 0; padding-top:16px;
  /* a hairline, not a rule: it separates without drawing a line across the card */
  border-top:1px solid color-mix(in srgb, var(--kz-card-brd) 60%, transparent);
  /* ⚠ `--kz-faint` is the PLACEHOLDER ink and measured 3.09:1 here. Small print is
     still print: it takes the readable colour. */
  text-align:center; font-size:.79rem; color:var(--kz-muted);
}
.kz-fine a{ color:var(--kz-muted); text-decoration:none; }
.kz-fine a:hover{ color:var(--kz-brand-ink); text-decoration:underline; }

/* 🔴 The legal line — on every auth page (owner ask 2026-09-16). */
.kz-legal{
  margin:13px 0 0; text-align:center;
  /* 🔴 This line is a legal notice. It measured 2.56:1 against the light ground on the
     first cut — small AND dim is how a notice becomes decorative. It is the smallest
     thing on the page and it is still fully readable. */
  font-size:.75rem; line-height:1.6; color:var(--kz-stage-muted);
}
.kz-legal a{ color:var(--kz-stage-ink); text-decoration:underline; text-underline-offset:2px; }
.kz-legal a:hover{ color:#B4BEFF; }

/* ---- the theme toggle, the one piece of chrome ---- */
.kz-theme-toggle{
  position:fixed; top:18px; right:18px; z-index:5;
  width:38px; height:38px; border-radius:10px; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  color:var(--kz-stage-muted);
  background:color-mix(in srgb, var(--kz-stage-ink) 8%, transparent);
  border:1px solid var(--kz-stage-brd);
  backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
  transition:color .15s ease, transform .15s ease, background .15s ease;
}
.kz-theme-toggle .ki, .kz-theme-toggle .bi{ width:18px; height:18px; font-size:18px; }
.kz-theme-toggle:hover{ color:var(--kz-stage-ink); transform:translateY(-1px);
  background:color-mix(in srgb, var(--kz-stage-ink) 14%, transparent); }
.kz-theme-toggle:focus-visible{ outline:none; box-shadow:0 0 0 3px var(--kz-ring); }

/* ---- a flash / notice inside the card (sign-up confirmation) ---- */
.kz-note{
  margin:18px 0 0; padding:14px 16px;
  border-radius:11px; font-size:.85rem; line-height:1.55;
  color:var(--kz-ink);
  background:color-mix(in srgb, var(--kz-brand) 8%, transparent);
  border:1px solid color-mix(in srgb, var(--kz-brand) 20%, transparent);
}
.kz-note--ok{
  background:color-mix(in srgb, #10B981 11%, transparent);
  border-color:color-mix(in srgb, #10B981 26%, transparent);
}

/* ---- the sign-up form's two columns ---- */
/* ONE seam down the form: every paired row is an even split, so the left column's right
   edge is a single x for the whole form (kizuna-ui §9e1 — mixed 7/5, 6/6 and 5/7 splits
   made that edge jump 100px between rows). */
/* 🔴 `1fr` IS NOT SAFE — this is §9q, and I failed to apply it to this grid. A `1fr`
   track keeps `min-width:auto` and refuses to shrink below its content's min-content width,
   so on a 320-375px phone the label rows pushed their columns past the screen. `minmax(0,
   1fr)` plus `min-width:0` on the item AND on the flex label is what lets them give way. */
.kz-grid{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:0 16px; }
.kz-grid > .kz-f{ min-width:0; }
.kz-f > label, .kz-card .form-label{ min-width:0; }
.kz-grid > .kz-span{ grid-column:1 / -1; }
/* ⚠ On a phone the SHORT pairs stay side by side — name/email, country/phone,
   password/confirm hold short values and read fine at ~160px each. Collapsing every row
   costs ~216px of scrolling on a nine-field form for no gain. Only the fields that need
   a real line (a business name, an industry label) go full width, via `.kz-stack-sm`. */
/* three columns only where they fit; two below that; one on a phone except the short
   pairs, which read fine side by side and save ~200px of scrolling */
/* 🔴 THE THREE-COLUMN BREAKPOINT IS 980px, NOT 1180. At 1180 a landscape tablet
   (1024x690) fell back to two columns — six rows — and the form ran 135px past the fold on
   a device that has plenty of WIDTH and very little HEIGHT, which is exactly the case three
   columns exist for. At 1024 each column is ~237px, which every label on this form fits. */
@media (min-width:980px){ .kz-grid--3{ grid-template-columns:repeat(3, minmax(0,1fr)); } }
/* the label's own text may wrap on a narrow phone rather than widen its column */
.kz-f > label{ overflow-wrap:anywhere; }
@media (max-width:520px){
  .kz-grid{ gap:0 10px; }
  .kz-grid > .kz-stack-sm{ grid-column:1 / -1; }
}

/* ============================ responsive ============================ */
/* On a short laptop the CARD gives up its air before the FORM does — nobody came to this
   page to read the heading (kizuna-ui §9e1). Input heights never shrink. */
/* On a short laptop the CARD gives up its air before the FORM does — nobody came to this
   page to read the heading (kizuna-ui §9e1). Input heights never shrink. */
@media (max-height:860px){
  .kz-auth__center{ padding-top:18px; padding-bottom:18px; }
  .kz-mark{ margin-bottom:12px; font-size:1.06rem; --logo:34px; }
  .kz-card{ padding-top:24px; padding-bottom:24px; }
  .kz-card__t{ font-size:1.42rem; }
  .kz-f{ margin-bottom:13px; }
}
@media (max-height:660px){
  .kz-auth__center{ padding-top:12px; padding-bottom:12px; }
  .kz-mark{ margin-bottom:9px; font-size:.9rem; --logo:28px; }
  .kz-card__sub{ display:none; }          /* the heading already says it */
  .kz-alt{ margin-top:10px; }
  .kz-legal{ margin-top:9px; }
  .kz-f{ margin-bottom:10px; }
  .kz-card{ padding-top:18px; padding-bottom:18px; }
  .kz-fine{ margin-top:13px; padding-top:12px; }
}
/* ⚠ The last of the chrome, for a 1280x560 window — a real 1366x768 laptop with the tab
   strip, the address bar and a bookmarks bar. The portal name leaves the pill because the
   BUTTON already says it ("Sign in to Finance"); nothing is lost and 34px is gained. */
/* A LANDSCAPE PHONE has ~390px of height and no sign-in card can fit that, so the goal
   there is a short, comfortable scroll rather than the fold: the stage stops trying to be
   a full screen and the column simply flows. */
@media (max-height:440px){
  .kz-auth{ min-height:100%; }
  .kz-auth__center{ padding-top:14px; padding-bottom:16px; }
  .kz-mark{ margin-bottom:10px; }
}
@media (max-height:600px){
  .kz-eyebrow{ display:none; }
  .kz-card__t{ font-size:1.24rem; }
  .kz-in, .kz-card .form-control, .kz-card .form-select{ height:43px; }
  .kz-btn, .kz-card .btn-kz{ height:43px; }
  .kz-f{ margin-bottom:9px; }
}
@media (max-height:760px){
  .kz-card{ padding-top:20px; padding-bottom:20px; }
  .kz-card__t{ font-size:1.32rem; }
  .kz-card__sub{ font-size:.85rem; margin-top:7px; }
  .kz-f{ margin-bottom:11px; }
  .kz-in, .kz-card .form-control, .kz-card .form-select{ height:44px; }
  .kz-btn, .kz-card .btn-kz{ height:44px; }
}
/* On a phone reclaim the CHROME, never the controls — a cramped tap target costs far
   more on touch than a short scroll does. */
@media (max-width:575.98px){
  /* the column's own air and the lockup are the last chrome to give back */
  .kz-auth__center{ padding:20px 16px 22px; }
  .kz-mark{ margin-bottom:14px; font-size:1.02rem; --logo:32px; }
  .kz-card{ padding:22px 18px; border-radius:14px; }
  .kz-card__t{ font-size:1.3rem; }
  .kz-card__sub{ font-size:.85rem; margin-top:8px; }
  .kz-card__pill{ margin-top:9px; }
  .kz-card__pill span{ font-size:.7rem; padding:3px 10px; }
  .kz-theme-toggle{ top:12px; right:12px; }
  /* ⚠ The ROW GAP is chrome; the CONTROL is not. A nine-field sign-up form grew past a
     390x844 screen when the card widened, and the honest place to find 70px back is the
     space between fields — never the field height, which is a tap target on the one
     device where a tap target matters (§9e1, §9ab). */
  .kz-f{ margin-bottom:11px; }
  .kz-grid{ gap:0 10px; }
}

/* ============================ touch ============================ */
/* 🔴 A STANDALONE LINK IS A TAP TARGET. `Forgot password?` measured 116x20 on a phone,
   under WCAG 2.5.8's 24px — and it is not covered by the standard's "inside a sentence"
   exemption, because it stands on its own beside a checkbox. The app-wide coarse-pointer
   layer in `app.css` (§9ab) covers `.btn` and form controls; these auth-only classes are
   not in it, so they need their own floor. Set `min-height`, never `height`: a component's
   own rule may be defined later and simply win (§9ab). */
@media (pointer:coarse){
  .kz-row{ margin-bottom:18px; }
  .kz-row .kz-link,
  .kz-fine a,
  .kz-alt a,
  .kz-legal a{
    display:inline-flex; align-items:center; min-height:24px;
  }
  .kz-check{ min-height:24px; }
  .kz-check input{ width:18px; height:18px; }
  .kz-eye{ min-width:34px; min-height:34px; }
  /* the two policy links sit in one sentence and would otherwise stack awkwardly once
     they are inline-flex; keep the line reading as a line */
  .kz-legal{ line-height:1.9; }
}

@media (prefers-reduced-motion:reduce){
  .kz-mark img, .kz-btn, .kz-card .btn-kz, .kz-theme-toggle{ transition:none; }
}

/* ============================ the 80% desktop zoom ============================ */
/* 🔴 `vh`/`dvh` and a fixed `inset:0` IGNORE `zoom` (kizuna-ui §5). `/register` opts into
   the app's 80% laptop zoom, so its `min-height:100dvh` shell and its `inset:0` stage each
   covered only 80% of the screen and left a bright undressed band across the bottom of the
   page — the same defect that once left the modal backdrop failing to dim a page edge.
   Scale both terms by the same factor the zoom applies. The media query must MATCH the one
   in app.css exactly, or this compensates on a device that never zoomed. */
@media screen and (min-width:1024px) and (hover:hover) and (pointer:fine){
  .kz-auth--zoom{ min-height:calc(100dvh / 0.8); }
  .kz-auth--zoom .kz-auth__stage{
    width:calc(100vw / 0.8); height:calc(100vh / 0.8);
    right:auto; bottom:auto;
  }
}

/* the way back to the site (2026-10-01): a small hairline pill, top-left, the toggle's twin —
   the toggle's own colours, so it reads on both themes */
.kz-back{
  position:fixed; top:18px; left:18px; z-index:5;
  display:inline-flex; align-items:center; gap:8px; min-height:38px; padding:0 14px;
  border:1px solid var(--kz-stage-brd); border-radius:10px;
  background:color-mix(in srgb, var(--kz-stage-ink) 8%, transparent);
  color:var(--kz-stage-ink); font-weight:600; font-size:.9rem; text-decoration:none;
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
  transition:background .15s ease, border-color .15s ease;
}
.kz-back:hover{ background:color-mix(in srgb, var(--kz-stage-ink) 14%, transparent); color:var(--kz-stage-ink); }
@media (max-width:640px){ .kz-back{ top:12px; left:12px; min-height:36px; padding:0 11px; font-size:.84rem; } }
