/* ══════════════════════════════════════════════════════════════════════
   F04 — Founding a practice.

   Colours and geometry come from the extracted Miro frames in
   docs/designs/: login.json, invite-flow-3/4/5/6.json, practice-name.json
   and practice-already-exists.json. Those files carry DEAD frame ids — the
   board was re-created on 2026-08-29 and every item id changed — but the
   geometry and the copy are still what was drawn, so that is what this
   stylesheet is built from.

   Where a drawn value could not be used as drawn, the rule says so:
     - The frames are 1549 x 871 mockups whose reported font sizes (10, 12,
       14, 18, 24) are mockup-scale, not screen-scale: a 14px value inside an
       81px-tall field is unreadable. The GEOMETRY is kept literally — 576px
       column, 81px fields, 207x39 buttons, an 8px progress bar, 70px code
       boxes — and the TYPE is scaled up to the smallest sizes that read,
       preserving the drawn ratio between title, value, label and helper.
     - The drawn fields are a 20%-opacity grey fill inside a 3px WHITE border,
       which on the drawn grey artboard reads as an inset well and on a white
       page is invisible. They get a 1px --edge-firm edge here so the field
       has one at WCAG 1.4.11's 3:1 — the first pass used #e6e6e6, which is
       1.20:1 on the fill and left every field reading as a ghost.
   ══════════════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* MEASURED, not defensive. The `hidden` attribute is styled by the UA
   stylesheet as `[hidden] { display: none }`, and any author rule that sets
   `display` beats it — so `.btn { display: inline-flex }` made every
   `hidden` button visible anyway. That shipped as the early refusal screen
   showing a "Try again" button meant only for the provisioning variant,
   which was found by screenshotting it. Anything this app hides, it hides
   through this attribute, so the override belongs here once. */
[hidden] { display: none !important; }

:root {
  /* Drawn literally. */
  --ink:        #1a1a1a;  /* titles, values, primary button fill      */
  --field:      #fafafa;  /* practice-name.json's field fill          */
  --track:      #e7e7e7;  /* progress bar track                       */

  /* MEASURED AND DARKENED, 2026-09-03. These three arrived "drawn
     literally" out of frames whose grey artboard is not this white page, and
     the arithmetic says so:

       --helper  #b0b0b0 on #ffffff = 2.17:1   (WCAG AA body text wants 4.5)
       --label   #808080 on #ffffff = 3.95:1, and #808080 on the #f2f2f2
                 secondary button = 3.53:1
       --fill    #659df2 on the #e7e7e7 track = 2.22:1 (1.4.11 wants 3 for a
                 non-text graphical object, and the bar IS the object)

     --helper carries the line under every heading, the six unmet password
     rules, the reason both federated blocks are dead, and the paragraph that
     explains an irreversible dissolution. It was the least readable text in
     the app and it was carrying the most.

     Each replacement is the value checked against the background it actually
     sits on rather than against white:
       --helper #6e6e6e = 5.10 on --page, 4.89 on --field (.field-note, .panel)
       --label  #666666 = 5.74 on --page, 5.13 on --secondary, 4.73 on the
                #e9e9e9 hover, 4.68 on --chip (the unselected radio ring)
       --fill   #3a7ae0 = 3.37 on --track, and still the drawn hue */
  --helper:     #6e6e6e;  /* the line under every title               */
  --label:      #666666;  /* field labels, secondary button text      */
  --fill:       #3a7ae0;  /* progress bar fill                        */
  --danger:     #bd0a0a;  /* the refusal dot, shared with F01's       */
  --chip:       #e3e9ec;  /* unselected expertise chip                */
  --chip-on:    #86bde8;  /* selected expertise chip                  */
  /* #305bab as drawn is 3.26:1 on --chip-on, which clears WCAG 1.4.11's 3:1
     for the dot and fails 1.4.3's 4.5:1 for the chip's own 16px label — and
     the label is the same two colours. #1f3d75 is 5.27:1 on --chip-on and
     10.59:1 on the page, in the same hue. */
  --chip-on-fg: #1f3d75;  /* selected expertise chip's label          */
  --secondary:  #f2f2f2;  /* "Use a different email"                  */
  --apple:      #424242;  /* the Apple button's drawn fill            */

  /* Not drawn — a white page needs an edge on a #fafafa field.

     --field on --page is 1.04:1 and the old --edge on it was 1.20:1, so a
     field was a ghost: WCAG 1.4.11 wants 3:1 for the boundary of a control.
     --edge-firm is now that boundary (#8f8f8f = 3.23:1 on --page, 3.10:1 on
     --field) and every CONTROL edge uses it — the fields, the six code
     boxes, the reveal button, the unmet rule dot. --edge stays light because
     what is left using it is decoration: the panel, the harness fact list
     and the progress track's own container. */
  --edge:       #e6e6e6;
  --edge-firm:  #8f8f8f;
  --page:       #ffffff;
  --ok:         #34571d;  /* satisfied rule line; from site/styles.css */

  /* Drawn geometry, in real pixels. */
  --col:        576px;
  --field-h:     81px;
  --btn-w:      207px;
  --btn-h:       39px;
  --bar-w:      571px;
  --bar-h:        8px;
  --code-w:      70px;

  --font-body: 'Roboto Slab', Georgia, 'Times New Roman', serif;
  --font-mono: 'Roboto Mono', ui-monospace, 'SF Mono', Menlo, monospace;
}

html { -webkit-text-size-adjust: 100%; }

/* moveFocusIn() puts the caret on the new screen's heading when the screen has
   no [data-autofocus], so that focus never lands on <body>. Chrome does not
   match :focus-visible for programmatic focus, so no ring is painted anyway —
   this says so out loud rather than leaving it to the engine. Only elements
   app.js has made programmatically focusable are matched; nothing here is a
   tab stop, so no real focus indicator is being removed. */
[tabindex='-1']:focus { outline: none; }

body {
  font-family: var(--font-body);
  background: var(--page);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

/* THE UNRELEASED-BUILD BANNER IS GONE. Nolan asked for it, walking the flow
   on 2026-09-03, and he owns the product. Its own comment argued it was "not
   decoration" because this app creates real accounts in a real pool — that
   fact has not changed, so the environment it is pointed at is now a row in
   the harness's "What this browser is talking to" list, beside the pool, the
   client and the API. One place, where an operator is already looking, rather
   than a strip above every screen a founder sees. */

/* ── Stage and column ────────────────────────────────────────────────── */
/* BLOCK, NOT A CENTRING FLEX CONTAINER, and that is the fix for a measured
   bug rather than a preference.

   `display: flex; justify-content: center` centres the overflow of a child
   it cannot shrink, which puts half of it at NEGATIVE x — and negative x is
   not merely off-screen, it is unreachable, because `window.scrollX` cannot
   go below 0. Measured at 375x812 on f04-confirm-email: the section sat at
   left: -40px, `document.documentElement.scrollWidth` was 415 against a
   maxScrollX of 0, the heading read "ve sent you a confirmation e-mail", and
   `document.elementFromPoint` at digit box 1's own centre returned nothing.

   A block box with `margin-inline: auto` centres identically and cannot do
   that: an auto margin with no free space resolves to 0, so anything too
   wide overflows to the RIGHT, where the page can be scrolled. */
.stage {
  padding: 56px 24px 96px;
}

.screen {
  width: 100%;
  max-width: var(--col);
  margin-inline: auto;
  /* Insurance, for the day somebody makes .stage a flex container again:
     a flex item's automatic minimum size is its min-content width, which is
     what refused to shrink in the bug above. */
  min-width: 0;
}

/* login is the one split screen: a panel on the left and the form on the
   right, per login.json's 701-wide #e6e6e6 rectangle at x 0..701 of a
   1549-wide frame. Every other frame is a single centred column. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 701fr) minmax(0, 848fr);
  gap: 0;
  width: 100%;
  max-width: 1549px;
  margin-inline: auto;
  min-width: 0;
  align-items: stretch;
}
.split-panel {
  background: #f0efec;
  border-radius: 18px;
  min-height: 520px;
  display: flex;
  align-items: flex-end;
  padding: 32px;
}
.split-panel p {
  font-size: 15px;
  line-height: 1.5;
  color: #6b6860;
  max-width: 26em;
}
.split-form {
  display: flex;
  justify-content: center;
  padding: 0 24px;
}
.split-form > * { width: 100%; max-width: var(--col); }

/* ── Progress bar ────────────────────────────────────────────────────── */
/* Drawn as a 571x8 #e7e7e7 track with a #659df2 fill sharing its left edge
   (both at x 500 on the frame). The drawn FILL WIDTHS are not reused: they
   are the invited flow's progress values (invite-flow-3 is 165/571 and
   invite-flow-5 is 499/571), and F04 reorders those screens, so the fraction
   is computed from F04's own step index instead — see PROGRESS in app.js. */
.bar {
  width: 100%;
  max-width: var(--bar-w);
  height: var(--bar-h);
  border-radius: calc(var(--bar-h) / 2);
  background: var(--track);
  overflow: hidden;
  margin-bottom: 34px;
}
.bar-fill {
  height: 100%;
  /* The first bar screen is at 0% because nothing has been finished yet, and
     an empty track reads as a broken bar. One bar-height of fill reads as
     "at the start", which is what it is. */
  min-width: var(--bar-h);
  background: var(--fill);
  border-radius: calc(var(--bar-h) / 2);
  transition: width 220ms ease;
}

/* "Step 2 of 5" ON THE PAGE and not only in an aria-label. A bar that
   appears from nowhere on the second screen, unlabelled, tells a person
   neither where they are nor how much is left. */
.bar-step {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--label);
  margin-bottom: 10px;
}

/* ── Type ────────────────────────────────────────────────────────────── */
.wordmark {
  font-size: 30px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 40px;
}

.title {
  font-size: clamp(24px, 3.2vw, 30px);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: -0.01em;
}

/* practice-already-exists.json draws an 8px #bd0a0a dot at the column's left
   edge with the title starting 16px after it. F01 marks
   connection-unsuccessful the same way, which is why it is that colour. */
.title-refused {
  display: flex;
  align-items: baseline;
  gap: 16px;
}
.title-refused::before {
  content: '';
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--danger);
  transform: translateY(-4px);
}

.helper {
  font-size: 15px;
  line-height: 1.5;
  color: var(--helper);
  margin-top: 12px;
}
.helper-ink { color: #5b5854; }

.panel {
  background: var(--field);
  border: 1px solid var(--edge);
  border-radius: 14px;
  padding: 22px 24px;
  margin-top: 28px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink);
}
.panel p + p { margin-top: 12px; }

/* ── Fields ──────────────────────────────────────────────────────────── */
.fields { margin-top: 40px; display: grid; gap: 16px; }

.field { display: block; }
.field-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--label);
  margin-bottom: 8px;
}
.field-input {
  width: 100%;
  height: var(--field-h);
  background: var(--field);
  border: 1px solid var(--edge-firm);
  border-radius: 14px;
  padding: 0 22px;
  font-family: var(--font-body);
  font-size: 18px;
  color: var(--ink);
}
.field-input::placeholder { color: #737373; }  /* 4.54:1 on --field */
.field-input:focus {
  outline: none;
  border-color: var(--ink);
  box-shadow: 0 0 0 3px rgba(26, 26, 26, 0.08);
}
.field-input[aria-invalid='true'] { border-color: var(--danger); }
.field-note {
  font-size: 13px;
  line-height: 1.5;
  color: var(--helper);
  margin-top: 10px;
}
/* ALWAYS IN THE DOM, hidden by being empty rather than by the `hidden`
   attribute. A live region inside `display: none` announces nothing when it
   is revealed and filled in the same tick, and `role="alert"` on these spans
   is the only thing that makes a validation failure audible — see fieldError
   in app.js. `:empty` matches only a node with no children at all, and
   fieldError writes `textContent = ''`, so it really is empty. */
.field-error {
  display: block;
  font-size: 13px;
  line-height: 1.5;
  color: var(--danger);
  margin-top: 8px;
}
.field-error:empty { display: none; }

/* Named state for a screen reader where the page shows it in colour and
   shape. Used by the password rule lines, which had `aria-checked` on a
   plain <li> — an attribute that role-less element ignores. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── The password reveal toggle ──────────────────────────────────────── */
/* Not drawn anywhere in the fifty-two frames, and added anyway: Nolan,
   walking the flow, "there is no function to view the password you enter - I
   think this is bog standard feature for this."

   A REAL BUTTON WITH A WORD ON IT, not a bare eye glyph. The word is what
   makes the control keyboard-reachable with a name a screen reader can say
   and a person can search for; an unlabelled icon is neither. It is styled to
   the field's own scale (--btn-h tall, the field-label's mono cap) so it
   reads as part of the field rather than as an action on the form.

   The input's right padding is widened by the button's width plus its inset,
   so a long password scrolls behind the field's edge and never under the
   button. */
.field-reveal { position: relative; display: block; }
.field-reveal .field-input { padding-right: 104px; }
.reveal {
  position: absolute;
  top: 50%;
  right: 14px;
  transform: translateY(-50%);
  height: var(--btn-h);
  min-width: 72px;
  padding: 0 14px;
  border: 1px solid var(--edge-firm);
  border-radius: 10px;
  background: var(--page);
  color: var(--label);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}
.reveal:hover { color: var(--ink); border-color: var(--ink); }
.reveal:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
/* Pressed means the password is on screen. Saying so on the face of the
   control, and not only in aria-pressed, is what makes the state visible to
   somebody who can see the button but not the difference between a row of
   dots and a word they typed. */
.reveal[aria-pressed='true'] {
  color: var(--ink);
  border-color: var(--ink);
  background: var(--secondary);
}

/* ── Password rules ──────────────────────────────────────────────────── */
/* SIX lines, not the four drawn. Five are Cognito's default password policy
   — minimum eight, an uppercase letter, a lowercase letter, a number, a
   symbol (REQ-IAM-002, expressed in lib/constructs/cognito.ts as an OMITTED
   passwordPolicy so the pool takes the service default) — and the sixth is
   the drawn "Matches" line. The frame draws three policy lines plus Matches,
   and one of its three, "No Consecutive numbers or letters", was deleted as
   unenforceable. docs/designs/invite-flow-5.json is deliberately left as
   drawn rather than edited to agree.

   These lines are ADVISORY. Cognito's SignUp is the authority, and an
   InvalidPasswordException renders on the field. */
.rules {
  list-style: none;
  margin-top: 18px;
  display: grid;
  gap: 7px;
}
.rule {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--helper);
}
.rule::before {
  content: '';
  flex: 0 0 auto;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 1.5px solid var(--edge-firm);
  background: transparent;
}
.rule-met { color: var(--ok); }
.rule-met::before {
  border-color: var(--ok);
  background: var(--ok);
  box-shadow: inset 0 0 0 2px var(--page);
}

/* ── Code boxes ──────────────────────────────────────────────────────── */
/* invite-flow-6.json draws SEVEN 70x81 boxes. Cognito's confirmation code is
   six digits, so six are rendered — recorded as a divergence rather than
   drawing a box no code can fill. */
/* A GRID, NOT A FLEX ROW, and six boxes fit a 375px phone because of it.

   MEASURED. `.code-digit` was `flex: 1 1 70px` with `max-width: 70px`, and a
   flex item's min-content contribution is its flex base size clamped by
   max-width — 70px each, so the row's min-content width was 6x70 + 5x7 =
   455px in a 343px column. Nothing downstream could shrink it: `.screen` was
   a flex item with the automatic minimum size, and `.stage` centred the
   overflow into negative x. The heading lost "We'" off the left edge and
   digit box 1's centre sat at x = -5, where no scroll reaches.

   `repeat(6, minmax(0, var(--code-w)))` fixes the arithmetic at the source.
   A track's base size is its minmax MINIMUM, so the grid's min-content width
   is the gaps alone; the tracks then grow equally toward the 70px maximum
   and stop there. 70px each at 1400px, exactly as drawn; 51px each at 375px,
   where 70px does not fit. */
.code {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, var(--code-w)));
  gap: 12px;
  margin-top: 36px;
}
.code-digit {
  width: 100%;
  min-width: 0;
  height: var(--field-h);
  background: var(--field);
  border: 1px solid var(--edge-firm);
  border-radius: 14px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 26px;
  color: var(--ink);
}
.code-digit:focus {
  outline: none;
  border-color: var(--ink);
  box-shadow: 0 0 0 3px rgba(26, 26, 26, 0.08);
}
.code[aria-invalid='true'] .code-digit { border-color: var(--danger); }

/* ── Chips ───────────────────────────────────────────────────────────── */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 36px;
}
/* SEVEN REAL RADIO BUTTONS IN A LABEL EACH, not seven toggle buttons in a
   group. The drawn control is a radio group and it now is one, which buys
   from the browser everything that had to be hand-built or was simply
   missing: ONE tab stop for the whole group instead of seven, arrow keys
   that move and select, Enter that submits the form instead of re-selecting
   the chip under the caret, and "radio button, Surveyor, selected, 3 of 7"
   where a screen reader used to hear "toggle button, pressed".

   The input is the control and stays focusable; `.chip-face` is everything
   you can see. Sibling selectors rather than `:has()`, because the state is
   on the element right before the face. */
.chip {
  display: inline-flex;
  position: relative;
  cursor: pointer;
}
.chip-input {
  position: absolute;
  top: 50%;
  left: 22px;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}
.chip-face {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  height: 55px;
  padding: 0 22px 0 14px;
  border: 1px solid transparent;
  border-radius: 14px;
  background: var(--chip);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
}
/* MEASURED, and it is the same bug that shipped on the SELECTED dot. The
   unselected dot was `background: #e6e6e6` on a `--chip` #e3e9ec chip —
   1.02:1, a disc painted in the surface it sits on — so until you clicked
   something the group did not read as a radio group at all. An unselected
   radio is an empty ring, so it is drawn as one: the page colour inside, and
   a --label ring at 4.68:1 on the chip carrying the shape. */
.chip-face::before {
  content: '';
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--page);
  border: 2px solid var(--label);
}
.chip-input:checked + .chip-face {
  background: var(--chip-on);
  color: var(--chip-on-fg);
}

/* MEASURED, and it shipped. The selected dot was `background: var(--chip-on)`
   — the SAME token as the selected chip's own background one rule up — so the
   fill was the colour of the surface behind it and only the 2px inset ring
   survived. Nolan, picking Architect: "the radio button doesn't fill in -
   looks slightly weird." It was an empty outline because it was painted in
   the background colour.

   Filled with --chip-on-fg, the same ink the chip's label uses, in the same
   ring-gap-dot shape .rule-met::before already uses for a satisfied rule
   line: a 2px outer ring, a 4px gap in the page colour, and an 18px disc.
   Multiple inset shadows stack FIRST ON TOP, so the 2px ring paints over the
   6px gap and leaves the gap showing between 2px and 6px.

   Contrast: #305bab on #86bde8 is 3.26:1, over WCAG 1.4.11's 3:1 for a
   non-text graphical object, so the ring is what carries the shape and the
   white gap is only separation. The focus ring is on the CHIP, not on this
   pseudo-element, so it is unaffected by the new fill. */
.chip-input:checked + .chip-face::before {
  background: var(--chip-on-fg);
  border-color: var(--chip-on-fg);
  box-shadow: inset 0 0 0 2px var(--chip-on-fg),
              inset 0 0 0 6px var(--page);
}
.chip-input:focus-visible + .chip-face {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* "See more" WAS A DISABLED BUTTON STYLED AS A LINK — grey, underlined,
   opacity 1, because `.btn:disabled` never matched it — sitting next to
   "Client" and doing nothing. Its only explanation was a `title` on a
   disabled element: not shown reliably, skipped in the tab order, invisible
   on touch. A control that lies, and the cheapest honest version of it is
   not a control. The divergence it recorded is now a sentence. */
.chip-note {
  font-size: 13px;
  line-height: 1.5;
  color: var(--helper);
  margin-top: 14px;
}

/* ── Buttons ─────────────────────────────────────────────────────────── */
.actions {
  display: flex;
  justify-content: flex-end;
  gap: 24px;
  margin-top: 44px;
  flex-wrap: wrap;
}
.actions-split { justify-content: space-between; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-width: var(--btn-w);
  height: var(--btn-h);
  padding: 0 22px;
  border: 1px solid transparent;
  border-radius: 10px;
  font-family: var(--font-body);
  font-size: 15px;
  cursor: pointer;
  text-decoration: none;
}
.btn-primary { background: var(--ink); color: #ffffff; }
.btn-primary:hover:not(:disabled) { background: #000000; }
.btn-secondary { background: var(--secondary); color: var(--label); }
.btn-secondary:hover:not(:disabled) { background: #e9e9e9; }
.btn-danger { background: #ffffff; color: var(--danger); border-color: var(--danger); }
.btn-danger:hover:not(:disabled) { background: #fdf2f2; }
.btn-wide { min-width: 100%; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

.btn-busy { position: relative; color: transparent; }
.btn-busy::after {
  content: 'Working…';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  font-size: 14px;
}
.btn-secondary.btn-busy::after, .btn-danger.btn-busy::after { color: var(--label); }

.linkish {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--ink);
  text-decoration: underline;
  cursor: pointer;
}
/* Without this, "Create one" and "Sign in" were the only two controls in the
   app wearing Chrome's default `outline: auto 1px rgb(0, 95, 204)` while
   every input, button and chip wore the app's own ring. */
.linkish:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

.aside {
  font-size: 15px;
  color: #5b5854;
  margin-top: 28px;
}

/* ── Federated buttons, disabled with a visible reason ───────────────── */
/* Drawn on login.json ("Sign In with Google" / "Sign Up In Apple") and
   invite-flow-5.json ("Sign Up with Google" / "Sign Up with Apple"). Neither
   is wired: REQ-IAM-007 specifies federation attaching to the same pool, and
   nothing has been attached. They render DISABLED with the reason on the
   face, because drawing them enabled and having them fail is worse than not
   drawing them. */
.federated {
  display: flex;
  gap: 16px;
  margin-top: 28px;
  flex-wrap: wrap;
}
.federated-btn {
  flex: 1 1 220px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  height: 40px;
  padding: 0 16px;
  border-radius: 10px;
  border: 1px solid var(--edge);
  background: var(--chip);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 14px;
  cursor: not-allowed;
}

/* THERE WAS NO `:disabled` RULE HERE AT ALL, and all four of these buttons
   are disabled. So "Sign in with Apple" rendered at full-strength #424242
   with white text: at 375px the second most prominent thing on the screen,
   and on f04-credentials a stronger call to action than the real primary
   (a 45%-opacity grey Next). Tap it and nothing happens — `cursor:
   not-allowed` is a mouse-hover cue and says nothing to a thumb. A control
   that looks like the most important thing on the screen and does nothing is
   worse than one that is absent.

   Muted, dashed, and struck through the word "with", so it reads as
   unavailable at a glance and at any zoom. `.federated-reason` under it says
   why, and is now legible enough to be read (see --helper). */
.federated-btn:disabled {
  background: var(--page);
  color: var(--label);
  border: 1px dashed var(--edge-firm);
  opacity: 1;
}
.federated-btn:disabled::after {
  content: 'Soon';
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--label);
  border: 1px solid var(--edge-firm);
  border-radius: 6px;
  padding: 1px 6px;
}

/* The drawn Apple fill, kept for the day federation is wired. It deliberately
   does not apply while the button is disabled: the whole defect above was a
   dead control drawn as the strongest one on the screen. */
.federated-apple:not(:disabled) {
  background: var(--apple);
  color: #ffffff;
  border-color: var(--apple);
}
.federated-reason {
  font-size: 13px;
  line-height: 1.5;
  color: var(--helper);
  margin-top: 12px;
}

/* ── Banners ─────────────────────────────────────────────────────────── */
.banner {
  border-radius: 12px;
  padding: 16px 18px;
  font-size: 14px;
  line-height: 1.55;
  margin-top: 28px;
}
.banner-error { background: #fdf2f2; border: 1px solid #f0c9c9; color: #7d1414; }
.banner-note  { background: #f4f7fb; border: 1px solid #d6e2f2; color: #2c4a76; }
.banner-good  { background: #f2f6ef; border: 1px solid #d3e0c9; color: #34571d; }
.banner-scaffold {
  background: #fff7d6;
  border: 1px solid #eadfa8;
  color: #6b5b18;
  margin-top: 0;
  margin-bottom: 32px;
}

/* ── Harness ─────────────────────────────────────────────────────────── */
.harness { max-width: 760px; }

.facts {
  margin-top: 28px;
  border: 1px solid var(--edge);
  border-radius: 14px;
  overflow: hidden;
}
.fact {
  display: grid;
  grid-template-columns: minmax(140px, 30%) 1fr;
  gap: 16px;
  padding: 13px 20px;
  font-size: 14px;
  border-top: 1px solid var(--edge);
}
.fact:first-child { border-top: none; }
.fact dt {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--label);
}
.fact dd {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.facts-head {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--label);
  margin: 32px 0 10px;
}

.danger-zone {
  margin-top: 40px;
  border: 1px solid #f0c9c9;
  border-radius: 14px;
  padding: 22px 24px;
}
.danger-zone .title { font-size: 20px; color: var(--danger); }

/* ── Small screens ───────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; }
  /* NOT `display: none`. Hiding the panel took the only sentence saying what
     Theode is off the phone entirely, on the app's first screen. It stays,
     as the plain paragraph it always was, above the form. */
  .split-panel {
    display: block;
    background: none;
    border-radius: 0;
    min-height: 0;
    padding: 0 0 28px;
  }
  .split-panel p { color: #5b5854; max-width: none; }
  .split-form { padding: 0; }
}
@media (max-width: 560px) {
  .stage { padding: 32px 16px 72px; }
  :root { --field-h: 66px; --btn-w: 100%; }
  .code { gap: 7px; }
  .code-digit { font-size: 20px; }
  /* `column-reverse` REVERSED THE PICTURE AND NOT THE TAB ORDER. Measured on
     f04-refused?when=provisioning: the DOM ran "Try again, Back to log in"
     and the screen showed "Back to log in" at y 406 above "Try again" at
     y 469 — so focus moved bottom to top (WCAG 2.4.3), and the first button
     a thumb reached on the one screen whose whole promise is "Nothing is
     lost. Try again" was the one that abandons the recovery. The DOM order
     is now the order to read, on every screen, and this stacks it. */
  .actions { flex-direction: column; }
  .btn { width: 100%; }
}
