:root { --page-max: 1920px; }   /* mirrors shell.css — this page loads without it */

/* ═══════════════════════════════════════════════════════════════════════
   LOGIN — page composition

   Separate file, not a <style> block. Every rule here is layout; not one
   introduces a colour grimoire.css did not already define. That is the
   test for whether a page stylesheet is allowed to exist.

   ⚠ Written first at laptop width, which stranded the text block in the
   left eighth of a 2400px monitor at a fixed 22px title. Everything
   structural below is now fluid: the block moves inboard and the title
   grows with the viewport. A fixed px title on a full-bleed page is the
   bug, not the particular value.
   ═══════════════════════════════════════════════════════════════════════ */

.login-page {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr;

  /* ⚠ Capped and centred, not full-bleed. Left-aligning a 44ch column in a
     2370px viewport left the running-head rule running two-thirds of the
     way across empty ground, which reads as content that failed to load
     rather than as deliberate space. A book page has a width; the desk it
     sits on does not change it. */
  /* Anchored left, not centred. Centring made the margins symmetric and
     the page duller for it — the empty right is the composition, not a gap
     to be balanced away. The binding edge stays welded to the viewport's
     left margin, which is where a binding actually is. */
  /* ⚠ NO CAP. The cap was there to stop a 44ch column stranding itself in
     a wide viewport — but the leaf is left-aligned inside a fixed 460px
     device column, so its position never depended on the page width. All
     the cap actually did was end the running head's rule two-thirds of the
     way across, which reads as a page that failed to load. */
  /* Anchored left up to the ceiling, then centred with the rest of the
     application — past 1920 the leaf would otherwise sit in the far corner
     of an ultrawide with the device stranded beside it. */
  max-width: var(--page-max);
  margin-inline: 0 auto;
  position: relative;
  /* ⚠ Left padding is FIXED, not fluid. A vw-based left margin compounds
     with the binding's own offset, and on a wide monitor the whole page
     drifts toward the middle — which is what centring was, arrived at by
     accident. The right margin may breathe; the left is the binding. */
  padding: var(--s5) clamp(var(--s4), 5vw, var(--s7)) var(--s6) 44px;
}

/* Two columns above 1100px: the device holds the left, the leaf the right.
   The empty half was the real fault in the last pass — capping the page made
   the margins symmetric but left half the composition doing nothing. A title
   page carries a device in exactly that position. */
@media (min-width: 1025px) {
  .login-page {
    /* Fixed device column, not 1fr/1fr. Splitting the page in half tied
       the form's position to the viewport width — on a wider monitor the
       form drifted right and the composition changed. The offset is a
       decision; it should not depend on the display. */
    grid-template-columns: 460px minmax(0, 1fr);
    grid-template-areas:
      "head   head"
      "device leaf";
    column-gap: 40px;
  }
  .running-head { grid-area: head; }
  .device       { grid-area: device; }
  .leaf         { grid-area: leaf; }
}

/* The binding edge — a leaf of a larger book. Structural: it fixes the
   outer margin the running head hangs from, and it will carry
   the index once the other pages exist. */
.binding {
  /* Absolute within the page, not fixed to the viewport — it is this
     leaf's binding edge, so it must travel with the page when centred.

     ⚠ 14px, the same as every page inside the session. At 44 it sat exactly
     where the page's left padding puts the running head, so the rule and
     the word GRIMOIRE were drawn on top of each other — the head looked
     mispositioned when what was wrong was the line behind it. */
  position: absolute;
  left: 14px;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--rule-strong);
}

/* ⚠ THE RULE IS THE PAGE'S WIDTH, NOT THE CONTENT'S. The border sits on
   this element, so it stops wherever this element's padding starts — and
   the page's right padding is fluid, so the shortfall grew with the
   monitor. It reaches the edge now by cancelling that padding with a
   negative margin and re-applying it inside, which keeps the text off the
   edge while the rule runs the whole way. */
.running-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  margin-right: calc(-1 * clamp(var(--s4), 5vw, var(--s7)));
  padding-right: clamp(var(--s4), 5vw, var(--s7));
  padding-bottom: var(--s4);
  border-bottom: 1px solid var(--rule);
}
.running-head-right {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
}
.running-head-right .num {
  font-size: var(--t-xs);
  color: var(--text-faint);
  letter-spacing: .04em;
}

/* Left and vertically centred — a title page, not a dialogue box.
   Centring horizontally would make it a modal. */
.leaf {
  align-self: center;
  width: 100%;
  max-width: 44ch;
  padding-bottom: var(--s8);
}


/* The one element allowed to grow with the page. At a fixed 22px the title
   had no more presence than the field labels beneath it. */
/* The only line on the page. Lowercase with the full stop, set large and
   tight — it is a nameplate, not a heading, so it wants weight rather than
   the letterspacing a label would take. */
.leaf .title {
  font-size: clamp(28px, 3.4vw, 52px);
  font-weight: 700;
  letter-spacing: -.035em;
  line-height: 1;
  margin-bottom: clamp(var(--s6), 5vh, var(--s8));
}

.notice {
  font-size: var(--t-sm);
  line-height: 1.45;
  margin-bottom: var(--s5);
}
.notice[hidden] { display: none; }

.form {
  display: flex;
  flex-direction: column;
  gap: clamp(var(--s4), 3vh, var(--s6));
}

/* Set at reading size, not label size — you type into these, and a
   password field at 12px is a squint. */
.field-input { font-size: clamp(var(--t-base), 1.1vw, 17px); }

.hint {
  font-family: var(--sans);
  font-size: var(--t-xs);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ochre);
}
.hint[hidden] { display: none; }

.actions {
  display: flex;
  align-items: center;
  gap: var(--s4);
  margin-top: var(--s2);
}
.actions .btn { min-width: 132px; padding-block: var(--s3); }

.status {
  font-size: var(--t-xs);
  color: var(--text-faint);
  letter-spacing: .08em;
}

/* Phone: the binding edge would eat a tenth of a 380px viewport, so it
   goes. The left-aligned block stays — it reads correctly at any width. */
/* Between 900 and 1100 the fixed device column has no room; the device is
   already hidden there, so the leaf simply takes the full width. */
@media (max-width: 1024px) {
  .login-page { padding-left: var(--s5); }
}

@media (max-width: 640px) {
  .login-page { padding: var(--s4) var(--s4) var(--s6) var(--s4); }
  .binding { display: none; }
  .leaf { align-self: start; margin-top: var(--s7); }
}


/* ═══ The device ══════════════════════════════════════════════════════════
   Ornament, and the only ornament in the application. Permitted here and
   nowhere else: this is the one page with no readings on it, so there is
   nothing for it to compete with. The moment it appears next to a
   temperature it is noise.
   ═══════════════════════════════════════════════════════════════════════ */
.device {
  display: none;
  place-self: center;
  color: var(--text);
  pointer-events: none;
}

@media (min-width: 1025px) {
  .device { display: grid; place-items: center; width: 100%; }
}

.device svg {
  width: min(100%, 370px);
  height: auto;
  /* Was 5%, now 10%. The lower figure was set against a page that has since
     lost its colophon and its hostname — with less on the leaf the device
     had less to compete with, and at 5% it read as a smudge rather than as
     a mark. */
  opacity: .10;
}

/* Each ring turns at its own rate, none of them trackable. The point is
   that no single element appears to move — only the figure as a whole
   changes, the way a real instrument does when you are not watching it. */
.ring { transform-origin: 200px 200px; }
.ring-limb   { animation: device-turn 240s linear infinite; }
.ring-degree { animation: device-turn 160s linear infinite reverse; }
.ring-rete   { animation: device-turn 110s linear infinite; }
.ring-core   { animation: device-turn  90s linear infinite reverse; }

@keyframes device-turn { to { transform: rotate(360deg); } }

/* The global reduced-motion block already stops the animation. Nothing else
   is needed — the device is legible static, which is the test for whether
   motion was decorative or load-bearing. */

/* ═══ Small screens ═══════════════════════════════════════════════════
   The page is already fluid; these are the two cases fluidity does not
   cover on its own. */

/* Landscape on a phone, and short laptop windows. The title's bottom margin
   is 5vh of a 380px viewport — nothing — while its own size is driven by
   width, so it stayed large above a form that no longer fit. */
@media (max-height: 560px) {
  .login-page { padding-block: var(--s4) var(--s5); }
  .leaf { align-self: start; padding-bottom: var(--s5); }
  .leaf .title { font-size: clamp(24px, 2.4vw, 34px); margin-bottom: var(--s5); }
  .form { gap: var(--s4); }
  .device { display: none; }
}

@media (max-width: 640px) {
  /* Thumb-sized. The button is the only control on the page and it sits at
     the bottom of a phone screen, which is the hardest place to hit. */
  .actions .btn { min-width: 0; width: 100%; padding-block: var(--s3); }
  .actions { flex-direction: column; align-items: stretch; gap: var(--s2); }
  .status { text-align: center; }
  /* iOS zooms any field under 16px on focus, which resizes the whole page
     mid-login. */
  .field-input { font-size: 16px; }
}
