/* Sign-in surface on Design System v6 -- direction B, "marketing-first split".
 *
 * Rewritten 2026-09-21 from the owner-approved mock (login-b.html in the
 * login-review folder; its <style> block is this file, its markup is
 * accounts/templates/sign_in.html). Until that day this file was a
 * byte-for-byte port of upstream's v5 sheet; that sheet now lives with its
 * last consumer, the MCP consent shell, as
 * integrations/static/integrations/signin_v5.css, and signin_local.css (the
 * loader's show/hide rule) folded in here.
 *
 * Rules of the file:
 *   - tokens only. Every colour, size, radius and duration is a --pk-* value
 *     from static/design-system/css/tokens.css; the two literal numbers are
 *     geometry (the OTP cell, the calendar column) and a third party's widget
 *     size (Turnstile, 300x65), each marked where it sits.
 *   - everything is scoped under .login-b, the class the page's <body> wears
 *     (with pk-page, the kit's opt-in). Class prefix login- for page-specific
 *     parts; never a pk- class the kit does not define.
 *   - the kit sheets are linked BEFORE this one, so a rule here beats a kit
 *     rule of equal specificity by order alone. Nothing here needs !important.
 *   - the Plek loader is the only animation. The v5 shake keyframes are gone:
 *     a state change is a border-colour transition, nothing loops, nothing
 *     moves.
 */

/* ==========================================================================
   The marketing shell, neutralised
   --------------------------------------------------------------------------
   landing/templates/base.html is the shell this page extends, and its inline
   block carries `main { padding: 8rem 0 2rem; min-height: 100vh; }` for the
   auth surfaces that are still on v5 (choose_workspace, workspace_changed,
   the MCP consent shell). landing/site.css takes both off with
   `body.pk-page main`; restated here so this page does not stand on a sheet
   the marketing branch owns and may rewrite. core.css's `body { min-height:
   100vh; position: relative }` is harmless under a 100vh grid. Everything
   else the shell sets on body (v5 colour, weight, font-size from core.css and
   design/tokens.css) is already overridden by the kit's .pk-page rule, which
   is a class and loads later.
   ========================================================================== */

.login-b main {
  padding: 0;
  margin: 0;
  width: 100%;
  min-height: 0;
}

/* ==========================================================================
   The split
   --------------------------------------------------------------------------
   A 5fr/7fr grid the height of the viewport. .pk-panel was the obvious base
   but it is a bordered block with a fixed 40% copy cell and a 320px stage
   floor, built for a section inside a page, not for two full-height halves
   of the viewport -- so the page writes its own two-column grid and borrows
   .pk-panel's stacking rule (one column below 1200px, brand first).
   ========================================================================== */

.login-b__split {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  min-height: 100vh;
}

/* the brand side: the hero's navy weave (the CTA band's ground), the on-dark
   lockup at its one documented size, the promise as a two-line display
   headline. Both lines white: the owner dropped the .pk-accent second line
   on 2026-09-21. */
.login-b__brand {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--pk-sp-10);
  padding: var(--pk-sp-9);
}
.login-b__brand-link {
  display: block;
  width: max-content;
  line-height: 0;
  border-radius: var(--pk-radius-s);
}
.login-b__brand-link img { width: 88px; height: 34px; }   /* the lockup's minimum and only documented size */
.login-b__hero { display: grid; gap: var(--pk-sp-7); }
/* Keep the two authored lines. Size against the hero column, with a smaller
   French scale for its longer first line. Allow wrapping as a fallback for
   substituted fonts or other translations instead of clipping the text. */
.login-b__hero { container-type: inline-size; }
.login-b .login-b__title { margin: 0; text-wrap: wrap; font-size: min(var(--pk-fs-display), 10cqw); }
.login-b .login-b__title:lang(fr) { font-size: min(var(--pk-fs-display), 7cqw); }
.login-b__line { display: block; white-space: normal; overflow-wrap: anywhere; }
.login-b .pk-lead { max-width: 40ch; }   /* ch in the lead's own 18px Inter */

/* the form side: page ground, one narrow column, centred in the half */
.login-b__form {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--pk-sp-9) var(--pk-sp-5);
  background: var(--pk-ground);
}
/* geometry: the form's measure. There is no width token at this size
   (--pk-measure-text is 68ch), so it is declared as component geometry. */
.login-b__col {
  --login-measure: 400px;
  width: 100%;
  max-width: var(--login-measure);
}
/* the no-access outcome carries an inline calendar. It keeps the form's
   measure: at 400px the month view's day cells come out at the code row's
   48px, so the calendar reads at the same scale as the field and the cells
   above it -- the 560px opening the mock tried made the grid the biggest
   thing on the page (owner, 2026-09-21). otp_flow.js adds is-demo to <body>
   with the panel; the class is kept for the focus and caption rules. */
.login-b .login-b__intro { display: grid; gap: var(--pk-sp-4); }

/* below 1200 the halves stack, brand on top, the way .pk-panel does */
@media (max-width: 1199.98px) {
  .login-b__split { grid-template-columns: minmax(0, 1fr); }
  .login-b__brand { gap: var(--pk-sp-9); padding: var(--pk-sp-8) var(--pk-sp-6); }
  .login-b__form { align-items: flex-start; padding: var(--pk-sp-9) var(--pk-sp-6) var(--pk-sp-11); }
}
/* a phone: 16px gutters, the help line moves under the form */
@media (max-width: 809.98px) {
  .login-b__brand { gap: var(--pk-sp-8); padding: var(--pk-sp-6) var(--pk-sp-5) var(--pk-sp-8); }
  .login-b__help { display: none; }
  .login-b__form { padding: var(--pk-sp-8) var(--pk-sp-5) var(--pk-sp-10); }
}
.login-b__help-mobile { display: none; }
@media (max-width: 809.98px) {
  .login-b__help-mobile { display: block; margin-top: var(--pk-sp-8); }
}

/* ==========================================================================
   The flow: what otp_flow.js toggles
   --------------------------------------------------------------------------
   Four panels swap in place inside the column: #emailFormPanel, #otpPanel,
   #profilePanel (first-time readers only), #demoPanel (the one outcome that
   stays on the page). The script adds/removes .visible on each and, on the
   email panel only, writes display:none / display:flex inline -- so the
   email panel's base display is flex and the other three start hidden.
   #emailInput.invalid, #emailError.visible, #otpSpinner.visible,
   .otp-box.error / .success / [disabled], #resendCodeBtn.disabled and the
   profile fields' .invalid are the states the script writes; each has a rule
   here. #otpBoxes has its className rewritten to "otp-boxes otp-boxes--6|8"
   and is filled with <input class="otp-box"> + one <span class="otp-sep">.
   ========================================================================== */

.login-b { --login-otp-cell: 48px; }   /* geometry: the largest an OTP cell grows; the row shrinks below it */

.login-b .email-form-panel,
.login-b .otp-panel,
.login-b .profile-panel,
.login-b .demo-panel {
  flex-direction: column;
  gap: var(--pk-sp-6);
  opacity: 0;
  transition: opacity var(--pk-dur-slow) var(--pk-ease);   /* a reveal: 600ms, once */
}
.login-b .email-form-panel { display: flex; }
.login-b .otp-panel,
.login-b .profile-panel,
.login-b .demo-panel { display: none; }
.login-b .email-form-panel.visible,
.login-b .otp-panel.visible,
.login-b .profile-panel.visible,
.login-b .demo-panel.visible { display: flex; opacity: 1; }

.login-b form.pk-form { display: grid; gap: var(--pk-sp-5); }
.login-b .login-submit { width: 100%; }

/* the invalid state, as the script writes it (a class, not aria-invalid).
   Mirrors app.css's own [aria-invalid="true"] rule. */
.login-b .pk-input.invalid { border-color: var(--pk-negative); }
/* the email error replaces the help text; never beside it. The profile and
   OTP errors are authored empty, so :empty hides them. */
.login-b .email-error:not(.visible) { display: none; }
.login-b .profile-error:empty { display: none; }
.login-b .email-error.visible ~ .pk-field__help { display: none; }

/* Turnstile slot: 300x65 is Cloudflare's "normal" widget, not a design
   value. The height is reserved so nothing jumps when it appears, and the
   widget -- narrower than the field and the button -- sits on the column's
   axis (owner, 2026-09-21). The inline script in the template hides the slot
   with style.display once a token is minted. */
.login-b .turnstile-group {
  min-height: 65px;
  display: flex;
  justify-content: center;
}
.login-b .turnstile-group .cf-turnstile { display: block; }

/* the code row: N square cells in a grid, split by one presentational dash.
   Half the digits, the separator, the other half -- the separator is a real
   grid column, auto-width because the dash is narrower than a cell. Cells
   are minmax(0, cell) so the row shrinks with the column and never scrolls;
   aspect-ratio keeps them square as they shrink. Only these two grids exist,
   which is why otp_flow.js refuses any other code_length rather than drawing
   a row with no columns to sit in. */
.login-b .otp-boxes {
  display: grid;
  gap: var(--pk-sp-1h);
  align-items: center;
  justify-content: start;
}
.login-b .otp-boxes--6 {
  grid-template-columns: repeat(3, minmax(0, var(--login-otp-cell))) auto repeat(3, minmax(0, var(--login-otp-cell)));
}
.login-b .otp-boxes--8 {
  grid-template-columns: repeat(4, minmax(0, var(--login-otp-cell))) auto repeat(4, minmax(0, var(--login-otp-cell)));
}
.login-b .otp-box {
  /* .pk-input, one digit wide: same border, radius, ground and focus ring */
  width: 100%;
  min-width: 0;
  aspect-ratio: 1 / 1;
  min-height: 24px;
  padding: 0;
  border: var(--pk-line) solid var(--pk-control-border);
  border-radius: var(--pk-radius-s);
  background: var(--pk-surface);
  text-align: center;
  caret-color: transparent;
  font-family: var(--pk-font-mono);   /* digits compared along a row are set in Space Mono */
  font-size: var(--pk-fs-h3);
  line-height: var(--pk-lh-tight);
  letter-spacing: var(--pk-ls-body);
  color: var(--pk-ink);
  transition: border-color var(--pk-dur) var(--pk-ease),
              background-color var(--pk-dur) var(--pk-ease);   /* a state change; no shake, nothing loops */
}
.login-b .otp-box:hover { border-color: var(--pk-ink-2); }
.login-b .otp-box:focus-visible {
  border-color: var(--pk-ink);
  outline: 2px solid var(--pk-ink);
  outline-offset: 1px;
}
.login-b .otp-box.error { border-color: var(--pk-negative); }
.login-b .otp-box.success { border-color: var(--pk-positive); }
.login-b .otp-box:disabled {
  background: var(--pk-card);
  border-color: var(--pk-rule);
  color: var(--pk-muted);
  cursor: not-allowed;
}
.login-b .otp-sep {
  padding: 0 var(--pk-sp-1);
  font-family: var(--pk-font-mono);
  font-size: var(--pk-fs-h3);
  line-height: var(--pk-lh-tight);
  letter-spacing: var(--pk-ls-body);
  color: var(--pk-muted);
  user-select: none;
}

/* verifying and the error share one line, so the row above never moves.
   24px is .plek-loader--sm's mark. The loader's show/hide rule is the one
   signin_local.css used to hold: hidden by default, inline while a code
   verifies -- otp_flow.js's setLoading() only toggles "visible". */
.login-b .otp-status {
  display: flex;
  align-items: center;
  gap: var(--pk-sp-3);
  min-height: 24px;
}
.login-b #otpSpinner { display: none; }
.login-b #otpSpinner.visible { display: inline-flex; }
.login-b .otp-error { margin: 0; }

/* the resend cooldown, written by the script as a class rather than
   [disabled]; mirrors components.css's .pk-btn[disabled]. */
.login-b .pk-btn.disabled {
  background: var(--pk-card);
  color: var(--pk-muted);
  cursor: not-allowed;
  pointer-events: none;
}
/* quiet buttons carry 12px side padding; pull the row back onto the text edge */
.login-b .otp-links { margin-inline: calc(-1 * var(--pk-sp-4)); }

/* ==========================================================================
   The no-access outcome: cal.com inline
   --------------------------------------------------------------------------
   The embed sizes its own iframe; the column's width (above) is what gives
   the month view room. The dashed frame shows only while the container is
   empty, i.e. between the panel appearing and embed.js drawing into it.
   ========================================================================== */

.login-b .demo-panel__cal { width: 100%; min-height: 440px; }   /* geometry: the month view's height at the form measure, reserved so the caption does not jump */
.login-b .demo-panel__cal:empty {
  border: var(--pk-line) dashed var(--pk-deco);
  border-radius: var(--pk-radius-s);
}
/* no booking link configured: otp_flow.js sets `hidden` on the mount point
   and the panel is its caption alone. The `hidden` attribute has to win over
   the width and min-height above, which would otherwise keep the empty room
   reserved for a calendar that never comes. */
.login-b .demo-panel__cal[hidden] { display: none; }
/* the panel takes focus by script (tabindex="-1"); it is a region, not a
   control, so no ring on it */
.login-b .demo-panel:focus { outline: none; }
