/* Login. Deliberately a different world from the console: the app proper is
   a light, clinical instrument, and this is the brutalist poster the brand
   leads with. Tokens are lifted from the Anatomic Labs landing page so the
   two are the same design, not a lookalike. */

.auth {
  --a-bg: #0a0a0a;
  --a-surface: #161616;
  --a-primary: #ff3131;
  --a-text: #f5f3f1;
  --a-muted: #8ca0ac;
  --a-border: #2a2a2a;
  /* The hero's white frame. The unlocked bar borrows the same weight, so the
     two are set from one place. */
  --a-frame-border: 2px;
  --a-font: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, sans-serif;
}

/* ------------------------------------------------------------------ login */
.auth {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  grid-template-columns: 1fr;
  /* `safe` so a short viewport scrolls from the top instead of clipping the
     poster off the top edge the way plain centring would. */
  align-content: safe center;
  justify-items: center;
  padding: clamp(24px, 5vh, 56px) 24px;
  background: var(--a-bg);
  color: var(--a-text);
  font-family: var(--a-font);
  overflow-y: auto;
}

/* Poster and form share one measure, so the form reads as the next line of
   the composition rather than as a second thing on the page. */
.auth__column { width: min(100%, 560px); }

.auth__poster { display: block; }
.auth__frame { width: 100%; }

/* The poster's exit once the bar has been slid. Delayed so the bar's pop is
   seen before the whole hero starts to go; the form arrives once it has. */
.auth__poster.is-leaving {
  animation: authFade 0.26s ease 0.32s both;
  pointer-events: none;
}
@keyframes authFade {
  to { opacity: 0; transform: translateY(-6px); }
}

.auth__panel {
  position: relative;
  width: 100%;
  aspect-ratio: 21 / 10;
  background: var(--a-primary);
  border: var(--a-frame-border) solid #fff;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
}
.auth__panel-figure {
  width: auto;
  height: 82%;
  max-width: 82%;
  object-fit: contain;
  display: block;
}
.grain-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  background-repeat: repeat;
  pointer-events: none;
}

/* Brand on the left, location flush with the panel's right edge: the caption
   spans the frame, so space-between lines the location up with the hero. */
.auth__caption {
  margin-top: 12px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
}
.auth__brand {
  margin: 0;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 1;
  font-size: clamp(1.1rem, 1rem + 0.5vw, 1.5rem);
}
.auth__location {
  margin: 0;
  line-height: 1.2;
  font-size: 13px;
  font-weight: 500;
  text-align: right;
  white-space: nowrap;
}

/* ------------------------------------------------------------ unlock bar */
/* Same width as the hero so it reads as part of the poster, not a widget
   dropped beneath it. Square corners and the poster's border, per the brand. */
.unlock {
  position: relative;
  margin-top: 18px;
  height: 58px;
  /* border-box so the frame thickening on unlock doesn't grow the bar. */
  box-sizing: border-box;
  background: var(--a-surface);
  border: 1px solid var(--a-border);
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  transition: border-color 0.18s ease, border-width 0.18s ease;
}
.unlock__fill {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  background: var(--a-primary);
  opacity: 0.22;
  pointer-events: none;
  transition: width 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.unlock.is-dragging .unlock__fill { transition: none; }

.unlock__handle {
  position: absolute;
  top: 4px; bottom: 4px; left: 4px;
  width: 58px;
  /* border-box so the added border sits inside the 58px square instead of
     growing it. */
  box-sizing: border-box;
  display: grid;
  place-items: center;
  background: var(--a-primary);
  color: #fff;
  /* Same weight as unlock__arrow's strokeWidth, same color as the arrow
     itself (currentColor -> #fff here), the handle's outline reads as an
     extension of the icon's own line rather than a separate decoration. */
  border: 2.4px solid #fff;
  cursor: grab;
  touch-action: none;
  will-change: transform;
  /* Springs back (or snaps to the end) when released; tracks the finger with
     no lag while it is actually being dragged. */
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
  /* A barely-there rightward nudge that repeats every few seconds, not a
     wave or a wobble, just a suggestion of the direction to drag. Idle for
     most of the cycle so it reads as an occasional hint, not motion that
     never stops. It's the handle itself that nudges, the same square that
     tracks the finger while dragging, not just the icon inside it. */
  animation: unlockHint 2.8s ease-in-out infinite;
}
.unlock.is-dragging .unlock__handle { cursor: grabbing; transition: none; }
/* The flight back to the start after letting go short of the end: a real
   overshoot-and-settle rather than the crisp ease the snap-to-end success
   case keeps, so a false start reads as the handle springing home, not
   snapping there. Only this one return gets it, .is-springing is added
   for exactly that flight and dropped the moment it is grabbed again. */
.unlock.is-springing .unlock__handle {
  transition: transform 0.65s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Stopped while actually being dragged or once unlocked, so the hint never
   competes with the finger or fights the inline transform driving position. */
.unlock.is-dragging .unlock__handle,
.unlock.is-unlocked .unlock__handle {
  animation: none;
}
.unlock__handle:focus-visible { outline: 2px solid #fff; outline-offset: -4px; }
@keyframes unlockHint {
  0%, 70%, 100% { transform: translateX(0); }
  80%           { transform: translateX(2px); }
  90%           { transform: translateX(0); }
}

.unlock__arrow,
.unlock__check {
  grid-area: 1 / 1;
  transition: opacity 0.18s ease, transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
.unlock__check { opacity: 0; transform: scale(0.6); }

/* Reaching the end: the fill goes solid, the arrow flips to a check, the bar
   takes the hero's white frame, and the whole thing gives one short pop, the
   "click" of the lock opening. Solid red in a white frame is the poster's own
   panel in miniature, which is the point. */
.unlock.is-unlocked {
  border: var(--a-frame-border) solid #fff;
  animation: unlockPop 0.42s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.unlock.is-unlocked .unlock__fill { opacity: 1; width: 100% !important; }
/* The grain fades up with the pop. Only a `from` is given, so it settles on
   the opacity GrainOverlay sets inline, the hero's, rather than a copy of
   it kept here. Animating the overlay itself, not a wrapper: a wrapper at
   partial opacity would isolate the multiply blend and show raw grey noise. */
.unlock.is-unlocked .grain-overlay { animation: unlockGrainIn 0.3s ease both; }
@keyframes unlockGrainIn {
  from { opacity: 0; }
}
.unlock.is-unlocked .unlock__handle { background: #fff; color: var(--a-primary); }
.unlock.is-unlocked .unlock__arrow { opacity: 0; transform: translateX(6px); }
.unlock.is-unlocked .unlock__check { opacity: 1; transform: scale(1); }
@keyframes unlockPop {
  0%   { transform: scale(1);     box-shadow: 0 0 0 0 rgba(255, 49, 49, 0.55); }
  35%  { transform: scale(1.025); box-shadow: 0 0 0 10px rgba(255, 49, 49, 0); }
  100% { transform: scale(1);     box-shadow: 0 0 0 10px rgba(255, 49, 49, 0); }
}

/* The sign-in page proper. The poster has gone by the time this mounts, so
   the form is the only thing on the page: centred in the column, no panel and
   no hairline, and its entrance is the page turning over. */
.auth__form-side {
  display: flex;
  justify-content: center;
  animation: authRise 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes authRise {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
.auth__form {
  width: min(100%, 400px);
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.auth__form-head { display: flex; flex-direction: column; gap: 6px; }
.auth__title {
  margin: 0;
  font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.05;
}
.auth__sub { margin: 0; font-size: 13.5px; color: var(--a-muted); line-height: 1.5; }

.auth__field { display: flex; flex-direction: column; gap: 7px; }
.auth__label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--a-muted);
}
.auth__input {
  appearance: none;
  width: 100%;
  padding: 12px 14px;
  background: var(--a-surface);
  border: 1px solid var(--a-border);
  border-radius: 0;
  color: var(--a-text);
  font: 400 15px var(--a-font);
  transition: border-color 0.15s ease;
}
.auth__input:focus { outline: none; border-color: var(--a-primary); }
.auth__input::placeholder { color: #5a5a5a; }

.auth__submit {
  appearance: none;
  border: 0;
  padding: 13px 18px;
  background: var(--a-primary);
  color: #fff;
  font: 600 14px var(--a-font);
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: filter 0.15s ease, opacity 0.15s ease;
}
.auth__submit:hover { filter: brightness(1.08); }
.auth__submit:disabled { opacity: 0.5; cursor: default; }

.auth__error { margin: 0; font-size: 13px; color: #ff7d6b; line-height: 1.45; }
.auth__notice { margin: 0; font-size: 13px; color: var(--a-muted); line-height: 1.45; }
.auth__switch { margin: 0; font-size: 13px; color: var(--a-muted); }
.auth__link {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  color: var(--a-text);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

@media (max-width: 860px) {
  .auth { padding: 24px 20px 32px; --a-frame-border: 1.5px; }
  .auth__panel { aspect-ratio: 4 / 3; }
}

@media (prefers-reduced-motion: reduce) {
  .unlock.is-unlocked, .unlock.is-unlocked .grain-overlay, .auth__form-side, .auth__poster.is-leaving { animation: none; }
  .auth__poster.is-leaving { opacity: 0; }
  .unlock__handle, .unlock__fill { transition: none; }
  .unlock__handle { animation: none; }
  /* Higher specificity than the blanket rule above, so it needs its own
     override to actually lose the bouncy return under reduced motion. */
  .unlock.is-springing .unlock__handle { transition: none; }
}
