/**
 * resources/css/forms.css
 * ApplyRole — form controls and inline messages, shared by auth and account.
 *
 * EXTRACTED BECAUSE THE ACCOUNT PAGES WERE RENDERING UNSTYLED FIELDS. These
 * primitives were originally written in auth.css, which the account pages do not
 * load — so every input on the profile, settings, notifications and alerts pages
 * fell back to browser defaults while the surrounding cards looked designed. It is
 * the kind of gap that is invisible in the markup and obvious in a screenshot.
 *
 * One file linked by both, rather than a copy in each: two copies of a form field
 * is two definitions of what a focus ring looks like, and they drift.
 *
 * NO HEX VALUES. Everything from tokens.css — the rule the whole stylesheet layer
 * is built on.
 */

/* ─── Fields ───────────────────────────────────────────────────────────────── */

/* ─── Form layout ──────────────────────────────────────────────────────────────
 *
 * THE SPACING LIVED IN account.css AND THE AUTH PAGES NEVER LOADED IT.
 *
 * `.field` is `display: block` with no margin, on purpose: the gap between fields
 * belongs to the form, not to the field, so a form can choose its own rhythm. The
 * only rule that supplied one was `.acctform` in account.css — and auth/shell.php
 * loads tokens, app, forms and auth, never account.
 *
 * So every sign-in, registration, password-reset and consent screen rendered its
 * fields flush against each other: the "Password" label sat on the bottom edge of
 * the email input, and the checkbox touched the field above it. Six screens, since
 * the first folder, and it read exactly as badly as it sounds.
 *
 * Both class names are defined HERE, in the sheet every form-bearing page loads.
 * account.css keeps its own identical `.acctform` rule; a duplicate that agrees is
 * harmless, and removing it would be a change to a file this fix does not need to
 * touch.
 */
.authform,
.acctform { display: grid; gap: 1rem; }

/* Inside the narrow auth card the form is the card's width; elsewhere it is capped
   so a label does not run the width of a desktop console. */
.acctform { max-width: 26rem; }
.authform { max-width: none; }

.field { display: block; }

.field__label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: .5rem;
  margin-bottom: .375rem;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--ink-2);
}

.field__aside { font-weight: 500; font-size: .75rem; }

.field input[type="text"],
.field input[type="email"],
.field input[type="password"],
.field input[type="number"],
.field input[type="url"],
.field select,
.field textarea {
  width: 100%;
  box-sizing: border-box;
  padding: .6875rem .875rem;
  font: inherit;
  color: var(--ink);
  background: var(--surface-2);
  border: 1.5px solid var(--line-2);
  border-radius: 10px;
}

.field textarea { resize: vertical; min-height: 5rem; }

.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft);
}

/* A readonly field is for copying, not editing. It says so by not looking
   editable, while staying selectable. */
.field input[readonly] {
  background: var(--surface);
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .8125rem;
}

.field--bad input,
.field--bad select,
.field--bad textarea { border-color: var(--late); }

.field__hint {
  display: block;
  margin-top: .375rem;
  font-size: .75rem;
  line-height: 1.5;
  color: var(--muted);
}

.fieldset { border: 0; margin: 0; padding: 0; display: grid; gap: .625rem; }

/* ─── Choices ──────────────────────────────────────────────────────────────── */

.check, .radio {
  display: flex;
  gap: .625rem;
  align-items: flex-start;
  font-size: .875rem;
  line-height: 1.5;
  color: var(--ink-2);
}

.check input[type="checkbox"],
.radio input[type="radio"] {
  margin-top: .1875rem;
  width: 1.0625rem;
  height: 1.0625rem;
  accent-color: var(--brand);
  flex-shrink: 0;
}

.radio {
  padding: .75rem;
  border: 1.5px solid var(--line-2);
  border-radius: 10px;
  cursor: pointer;
}

/* :has() is progressive enhancement. Where it is unsupported the radio still works,
   it just does not tint — nothing depends on it. */
.radio:has(input:checked) { border-color: var(--brand); background: var(--brand-soft); }

.check--bad { color: var(--late); }

/* ─── Buttons ──────────────────────────────────────────────────────────────── */
/* app.css defines .btn and .btn--primary; these are the variants auth and account
   add on top of them. */

.btn--block { display: block; width: 100%; text-align: center; }
.btn--block + .btn--block { margin-top: .625rem; }
.btn--small { padding: .3125rem .625rem; font-size: .75rem; }

.btn--danger {
  background: var(--late);
  color: var(--surface);
  border-color: var(--late);
}

/* ─── Inline messages ──────────────────────────────────────────────────────── */
/* role="alert"/"status" is on the element in the template; this is only the visual
   half. Colour never carries the meaning on its own — each has its own words. */

.autherror,
.authnotice,
.authflash {
  margin: 0 0 1.25rem;
  padding: .75rem 1rem;
  border-radius: 10px;
  font-size: .875rem;
  line-height: 1.55;
}

.autherror  { background: var(--late-bg); color: var(--late); }
.authnotice { background: var(--info-bg); color: var(--info); }

.authflash          { background: var(--info-bg); color: var(--info); }
.authflash--success { background: var(--ok-bg);   color: var(--ok); }
.authflash--error   { background: var(--late-bg); color: var(--late); }

/* ─── Pills ────────────────────────────────────────────────────────────────── */
/* width: fit-content and justify-self matter: a pill inside a grid cell stretches
   to the full column otherwise, which turns a small "Always on" badge into a
   full-width bordered box. */

.pill {
  display: inline-block;
  justify-self: start;
  width: fit-content;
  padding: .0625rem .5rem;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  font-size: .6875rem;
  font-weight: 700;
  color: var(--muted);
}

/* The text is --due-ink, not --due. --due is the amber of the *state* — it works as
   a border, a bar, a dot, anything the eye reads as a marker. As TEXT it measures
   2.00:1 on --due-bg, which is not text anybody can read; --due-ink exists for
   exactly this and clears 5.52:1. Fourteen rules across the product already got this
   right and seven, including this one, did not. */
.pill--warn { background: var(--due-bg); border-color: var(--due); color: var(--due-ink); }

/* A rare, consequential action folded away until it is wanted — handing an
   organisation to somebody else, and anything else with the same shape. <details>
   rather than a modal: it is keyboard-operable and announced with no JavaScript,
   and a modal that cannot open without a script is a control that disappears. */
.acct__reveal > summary {
  cursor: pointer;
  font-size: .875rem;
  font-weight: 600;
  color: var(--brand);
  padding: .25rem 0;
  list-style: none;
}
.acct__reveal > summary::-webkit-details-marker { display: none; }
.acct__reveal > summary:hover { text-decoration: underline; }
.acct__reveal[open] > summary { margin-bottom: .75rem; }

/* Attachments on an application. A row per slot rather than one multi-file input:
   each file gets its own "what is this" picker, and a multi-select input cannot
   carry a per-file answer. */
.field--files { border: 0; padding: 0; margin: 0 0 1.25rem; }
.field--files legend { padding: 0; }

.filerow {
  display: flex; gap: .5rem; align-items: center; flex-wrap: wrap;
  margin-top: .5rem;
}
.filerow input[type="file"] { flex: 1 1 16rem; min-width: 0; font-size: .8125rem; }
.filerow select {
  flex: 0 0 auto; padding: .375rem;
  font-size: .8125rem; border: 1px solid var(--line-2);
  border-radius: 6px; background: var(--surface); color: var(--ink);
}

/* The API reference on employer/api.php. */
.apitable { width: 100%; border-collapse: collapse; font-size: .8125rem; margin-top: .5rem; }
.apitable th, .apitable td { text-align: left; padding: .4375rem .5rem; vertical-align: top; }
.apitable thead th { font-size: .6875rem; text-transform: uppercase; letter-spacing: .04em;
                     color: var(--muted); border-bottom: 1px solid var(--line); }
.apitable tbody tr + tr td { border-top: 1px solid var(--line); }
.apitable code { font-size: .75rem; white-space: nowrap; }

.apilist { margin: .5rem 0 0; padding-left: 1.125rem; font-size: .8125rem; line-height: 1.6; color: var(--ink-2); }
.apilist li + li { margin-top: .5rem; }

/* ─── The document picker ──────────────────────────────────────────────────────
 *
 * A rail of cards, one per CV or cover letter, scrolling sideways. It replaced a
 * `<select>` full of titles — see templates/partials/docpicker.php for why a list of
 * strings was the wrong control for a collection the product already knows a lot
 * about.
 *
 * THE CONTROL UNDERNEATH IS A NATIVE RADIO GROUP and everything here is written to
 * stay out of its way. The radio is not `display: none` — a hidden input cannot be
 * focused, which would delete keyboard access to the whole thing. It is clipped to a
 * pixel, still focusable, and its focus ring is drawn on the card via `:focus-visible`
 * on the sibling so the ring appears where the eye is.
 */

.picker { border: 0; padding: 0; margin: 0 0 1.5rem; min-width: 0; }

.picker__legend {
  padding: 0;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--ink-2);
}

.picker__hint  { margin: .25rem 0 .75rem; font-size: .8125rem; line-height: 1.6; color: var(--muted); }
.picker__count { margin: .625rem 0 0; font-size: .75rem; color: var(--faint); }

/* ── The frame, and the arrows that sit over it ─────────────────────────────── */

.picker__frame { position: relative; min-width: 0; }

/*
 * NEGATIVE MARGIN AND MATCHING PADDING. The rail bleeds to the edges of a narrow
 * screen so a card is not stranded mid-gutter, and the padding puts the first card
 * back where the rest of the form starts. Without the padding the focus ring on the
 * first card is clipped by the scroll container.
 */
.picker__rail {
  display: flex;
  gap: .625rem;
  margin: 0 -.25rem;
  padding: .25rem;
  list-style: none;

  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
}

.picker__rail::-webkit-scrollbar { height: 6px; }
.picker__rail::-webkit-scrollbar-thumb { background: var(--scroll-thumb); border-radius: 999px; }

.picker__slide {
  flex: 0 0 auto;
  width: 14.5rem;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  gap: .25rem;
  min-width: 0;
}

/* The upload card holds a file input and a second line of small print. */
.picker__slide:has(.doccard--upload) { width: 17rem; }

.picker__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;

  width: 2rem; height: 2rem;
  display: grid; place-items: center;
  padding: 0;

  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface);
  color: var(--ink-2);
  box-shadow: var(--sh-2);
  cursor: pointer;
  font-size: 1.125rem;
  line-height: 1;
}
.picker__arrow:hover { border-color: var(--brand); color: var(--brand); }
.picker__arrow[hidden] { display: none; }
.picker__arrow--prev { left: -.75rem; }
.picker__arrow--next { right: -.75rem; }

/* ── A card ────────────────────────────────────────────────────────────────── */

.doccard {
  position: relative;
  display: block;
  height: 100%;
  padding: .75rem .8125rem .8125rem;

  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  cursor: pointer;
  text-decoration: none;
  color: inherit;

  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.doccard:hover { border-color: var(--brand-line); }

/*
 * The document's own accent, as a stripe down the leading edge. It is the same colour
 * the CV renders in, so the card and the document are recognisably the same object —
 * which is the whole reason for showing cards instead of titles. Set inline from the
 * document's stored accent; no colour is invented here.
 */
.doccard::before {
  content: "";
  position: absolute;
  left: 0; top: .75rem; bottom: .75rem;
  width: 3px;
  border-radius: 999px;
  background: var(--doc-accent, var(--line-2));
}
.doccard--none::before,
.doccard--upload::before,
.doccard--make::before { background: var(--line-2); }

/*
 * CLIPPED, NOT HIDDEN. `display:none` or `visibility:hidden` on a radio removes it
 * from the tab order, which would take the keyboard route away from the one control
 * this component is built on.
 */
.doccard__radio {
  position: absolute;
  width: 1px; height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.doccard__body { display: block; padding-left: .5rem; }

.doccard__kind {
  display: block;
  font-size: .625rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--muted);
}

.doccard__title {
  margin-top: .1875rem;
  font-size: .875rem;
  font-weight: 700;
  line-height: 1.35;
  color: var(--ink);

  /* Two lines, then ellipsis. A card that grows to fit a long title makes every card
     in the rail that tall, and the rail is mostly short titles. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.doccard__meta { display: block; margin-top: .375rem; font-size: .75rem; color: var(--ink-2); }
.doccard__when { display: block; margin-top: .125rem; font-size: .6875rem; color: var(--faint); }
.doccard__note { display: block; margin-top: .3125rem; font-size: .75rem; line-height: 1.5; color: var(--muted); }
.doccard__note--small { font-size: .6875rem; color: var(--faint); }

/* An empty document is the worst thing this control can send, so it says so. */
.doccard__warn { color: var(--due-ink); font-weight: 600; }
.doccard--thin { border-style: dashed; }

/* ── The miniature ────────────────────────────────────────────────────────────
 *
 * A real render of the document (documents/thumb.php), scaled with a transform the
 * way the template gallery scales its previews — a preview that reflowed at card
 * width would be a preview of a document that does not exist. The window shows the
 * top of the page, which is where a CV keeps its face.
 */

.doccard--doc { padding: 0; }
.doccard--doc .doccard__body { padding: .5625rem .8125rem .75rem 1.0625rem; display: block; }

/* The accent stripe would sit on top of the real page, which already shows its own
   accent. The body keeps it, beside the words. */
.doccard--doc::before { top: auto; height: 2.375rem; bottom: .75rem; }

.doccard__preview {
  display: block;
  height: 8.5rem;
  overflow: hidden;
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  border-radius: 11px 11px 0 0;
}

.doccard__frame {
  /* A4 at 96dpi, same as the gallery's frames. Scaled from the card's width. */
  display: block;
  width: 794px;
  height: 1123px;
  border: 0;
  transform-origin: 0 0;
  transform: scale(.29);
  /* The card is the click target; a click that landed inside the iframe would do
     nothing and feel broken. */
  pointer-events: none;
}

/* ── Chosen ─────────────────────────────────────────────────────────────────── */

.doccard__radio:checked ~ .doccard__body .doccard__title { color: var(--brand); }

.doccard:has(.doccard__radio:checked) {
  border-color: var(--brand);
  box-shadow: 0 0 0 1px var(--brand), var(--sh-1);
}

/*
 * The focus ring is drawn on the card because the input it belongs to is a pixel
 * wide. `:focus-visible`, so a pointer click does not leave a ring behind.
 */
.doccard:has(.doccard__radio:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }

/*
 * NO :has() — every rule above has a fallback, because the selected card must be
 * distinguishable in a browser that does not support it. `:checked ~` on the body is
 * the fallback and it is what draws the tick.
 */
.doccard__radio:checked ~ .doccard__body::after {
  content: "✓";
  position: absolute;
  top: .625rem; right: .6875rem;
  width: 1.125rem; height: 1.125rem;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--brand);
  color: var(--surface);
  font-size: .6875rem;
  font-weight: 700;
}

/* ── The two cards that are not documents ──────────────────────────────────── */

.doccard--make { border-style: dashed; }
.doccard--make:hover { border-color: var(--brand); }

.doccard--upload .doccard__file {
  display: block;
  width: 100%;
  margin-top: .5rem;
  font-size: .6875rem;
}

/* ── Read it ──────────────────────────────────────────────────────────────── */

.doccard__peek {
  align-self: flex-start;
  padding: .125rem .25rem;
  font-size: .6875rem;
  font-weight: 600;
  color: var(--muted);
}
.doccard__peek:hover { color: var(--brand); }

@media (max-width: 30rem) {
  .picker__slide { width: 12.5rem; }
  .picker__slide:has(.doccard--upload) { width: 15rem; }
  .picker__arrow { display: none; }
  .doccard__frame { transform: scale(.25); }
  .doccard__preview { height: 7.25rem; }
}

/* ── Nothing to pick ───────────────────────────────────────────────────────── */

.picker__empty {
  padding: 1rem;
  border: 1px dashed var(--line-2);
  border-radius: 12px;
  background: var(--surface-2);
}
.picker__emptytitle { margin: 0; font-size: .875rem; font-weight: 700; }
.picker__emptynote  { margin: .3125rem 0 .75rem; font-size: .8125rem; line-height: 1.6; color: var(--muted); }
