/* ═══════════════════════════════════════════════════════════════════════
   careers.css — MyGymDay site · THE FORM LAYER  ·  careers.html only
   Loaded FOURTH, after tokens.css, base.css and site.css, and linked by
   exactly one page. Before this file the whole project's form CSS was six
   declarations in base.css — `input, button, textarea, select { font:
   inherit; color: inherit }` — and that was right, because the other seven
   pages carry no input at all.

   site.css's five rules hold here unchanged: depth from a hairline, an
   image edge or a step in the surface ramp; no box-shadow, no text-shadow,
   no radial-gradient, no backdrop-filter, no opacity used as a tint, no
   animation that repeats; no second gradient; lime flat and rare. A grep of
   this file for shadow, gradient, filter or blur should find this paragraph
   and nothing else.

   ── THE LIME BUDGET, AND HOW A FORM SPENDS IT ───────────────────────────
   Two lime elements per viewport. This page paints two: the fill inside
   .wiz__bar, and the primary button in .wiz__nav. Every other piece of lime
   a visitor sees here is one they put there themselves — accent-color on a
   radio they checked, the wash behind a row they chose. The budget counts
   what the PAGE paints, not what the ANSWERS paint; site.css's .checks
   block already settled the principle when it ruled that six identical
   hairlines are texture rather than an accent. A fourteen-checkbox question
   cannot be built under any other reading. Do not grey the checked states.

   ── FOUR TRAPS THIS FILE EXISTS TO DISARM ───────────────────────────────
   1 · base.css's :focus-visible sets `border-radius: var(--mgd-radius-sm)`
       on whatever holds focus. Across seven pages of rectangles that never
       showed. On a form it would: a control resting at any other radius
       visibly changes shape the instant it is focused. So every control
       below rests at var(--mgd-radius-sm), and the trap becomes a no-op.
       Give a new control that same radius rather than a squarer one.
   2 · The site's disabled convention is [aria-disabled='true'] and
       .is-disabled — never :disabled, which appears in no other stylesheet
       in this project. A real form cannot follow it: a disabled <fieldset>
       disables its controls through the DOM, and question 05's seven
       checkboxes are disabled by property the moment "No experience" is
       ticked. So this file adds a :disabled rung, scoped to .wiz, carrying
       the two site selectors alongside it. A deliberate, contained
       deviation — not an oversight, and not a licence to use :disabled
       anywhere else.
   3 · tokens.css's prefers-reduced-motion block names its hand-maintained
       exceptions by selector and states that there are three. THERE ARE NOW
       FOUR. The step transition moves 12px, and a 1ms translate still snaps
       exactly the way [data-settle]'s scale does, so the offset is dropped
       rather than shortened — in the last block of this file. This sentence
       is the amendment to that comment.
   4 · Every inactive <fieldset> carries `hidden` AND `disabled`. The UA rule
       `[hidden] { display: none }` sits at UA specificity, so any class in
       this file that gives .wiz__step a display would silently beat it and
       render all eight steps at once. base.css's .hidden helper is a class
       and cannot help. [hidden] is therefore re-asserted once, below, and
       that one line is load-bearing. The same collision one level up — hiding
       the whole wizard from a visitor without JavaScript — is solved in the
       same block by `html:not(.js-wiz) .wiz`, which needs no !important
       because it is a rule about an ancestor rather than a rule fighting one.

   ── WHAT THIS FILE DELIBERATELY DOES NOT DEFINE ─────────────────────────
   No textarea rung: the eight questions ask for short answers, dates and
   choices, and the form has no long-text control. No .wiz__err either — a
   form-level failure renders site.css's existing `.notice .notice--danger`,
   because a submit error is a notice and the site already has one. And no
   .wiz__live: the polite announcement region is base.css's .sr-only carrying
   a data-live hook, which needs no geometry of its own.
   ═══════════════════════════════════════════════════════════════════════ */

/* ══ 0 · [hidden] WINS ══════════════════════════════════════════════════
   Trap 4. One rule, one !important, and the only !important in the file. */
.wiz [hidden] { display: none !important; }

/* The same trap one level up, solved without a second !important. The whole
   wizard is useless without JavaScript — a `hidden` attribute on .wiz would
   lose to `.wiz { display: grid }` for exactly the reason above, so the
   no-JS hide is a class on <html> that careers.js adds from inside <head>,
   before <body> is parsed. No flash, no attribute to forget to remove, and
   the <noscript> notice beside it is what a visitor without JS actually
   reads. */
html:not(.js-wiz) .wiz { display: none; }

/* ══ 1 · THE WIZARD SHELL ═══════════════════════════════════════════════
   .wiz is a grid, not a flex column, so the progress control, the current
   step and the nav each own a row and nothing collapses when a step is
   short. It is NOT sticky: the site header already is, and on a 640px-tall
   phone a second pinned strip costs more room than it returns in
   information. careers.js scrolls the progress control into view on every
   step change instead — no z-index, no header-height arithmetic, no second
   thing to keep in sync with the 640px header breakpoint. */
.wiz {
  --wiz-pct: 12.5%;          /* step 1 of 8. careers.js writes the rest. */
  display: grid;
  gap: var(--mgd-space-xl-2);
  align-content: start;
}

/* The <form> is a child of .wiz rather than .wiz itself, so that the progress
   control sits outside it. A progress bar is not a form control, and keeping
   it out means the success state hides two elements — the progress and the
   form — and leaves the panel it swaps in as a sibling rather than something
   nested inside a form that has already been sent. */
.wiz__form {
  display: grid;
  gap: var(--mgd-space-xl-2);
  min-width: 0;
}

/* Tabular figures on the wrapper, so they reach BOTH children that carry a
   numeral. On .wiz__dots it is the visible win: eight proportional digits in
   a flex row are eight different widths, and 1 is much narrower than 8, so
   the rail reads slightly ragged and the gaps look uneven when they are not.
   Equal advance widths make it a rail. On .wiz__count it stops the strong
   numeral from nudging the words after it as the step advances. */
.wiz__progress {
  display: grid;
  gap: var(--mgd-space-md);
  font-variant-numeric: tabular-nums;
}

/* ON THE SCALE, not near it. This was 3px, chosen only because 2px read as a
   hairline rather than as a measurement — true, but 3px is a magic number and
   every other length on this page comes from the space scale. --mgd-space-xs
   is 4px: still a bar and not a band, one pixel more legible at arm's length,
   and this is the one element that has to be readable at a glance while
   thumbing. The radius is a pill either way, so nothing else moves. */
.wiz__bar {
  height: var(--mgd-space-xs);
  border-radius: var(--mgd-radius-pill);
  background-color: var(--mgd-color-divider);
  overflow: hidden;
}

.wiz__bar-fill {
  display: block;
  width: var(--wiz-pct);
  height: 100%;
  border-radius: inherit;
  background-color: var(--mgd-color-primary);
  transition: width var(--mgd-site-dur-base) var(--mgd-site-ease);
}

/* ── The eight numerals, and the sentence that replaces them ─────────────
   .wiz__dots is the bar's companion rather than a second progress control:
   the bar says how far, the numerals say which of the eight. It carries no
   lime whatsoever, which is what holds the budget at two. Below 720px there
   is no room for eight numerals next to a 44px tap target, so the row is
   simply not painted and .wiz__count carries the entire message. That is
   what mobile-first means here: the small screen keeps the sentence, the
   wide screen gets the extra glance.

   Every colour below is a PASSING rung. --mgd-color-text-tertiary is not
   used for any of them: tokens.css measures it at 4.24 : 1 and these are
   12px words, not lines or dots. */
.wiz__dots { display: none; }

.wiz__count {
  font: var(--mgd-site-caption);
  color: var(--mgd-site-text-quiet);
}

.wiz__count strong {
  font-weight: 700;
  color: var(--mgd-color-text);
}

@media (min-width: 720px) {
  .wiz__dots {
    display: flex;
    gap: var(--mgd-space-lg);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .wiz__dot {
    padding-bottom: var(--mgd-space-xs);
    border-bottom: var(--mgd-layout-hairline) solid transparent;
    font: var(--mgd-type-caption);
    font-variant-numeric: tabular-nums;
    color: var(--mgd-site-text-quiet);
    transition: color var(--mgd-site-dur-fast) linear,
                border-color var(--mgd-site-dur-fast) linear;
  }

  .wiz__dot.is-done { color: var(--mgd-color-text-secondary); }

  /* Emphasis by hairline, exactly as the rest of the site does it. */
  .wiz__dot.is-current {
    border-bottom-color: currentcolor;
    font-weight: 700;
    color: var(--mgd-color-text);
  }
}

/* ══ 2 · A STEP ═════════════════════════════════════════════════════════
   <fieldset> needs four resets, and `min-width: 0` is the one that is easy
   to miss: the UA gives a fieldset `min-width: min-content`, which stops any
   grid or flex child inside it from shrinking below its content and hangs a
   horizontal scrollbar off a 320px phone the first time a long option label
   appears. */
.wiz__step {
  display: grid;
  gap: var(--mgd-space-xl-2);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

/* A conditional group inside a step — the five that step 02 shows or hides
   depending on the answer to "what is your current status?". It has to be a
   <fieldset> and not a <div>, because `disabled` on a fieldset is what takes
   its controls out of validation and out of the submitted payload; a div
   cannot be disabled and would leave a hidden required field failing
   silently. Same four resets as .wiz__step, and the same min-width: 0 for
   the same reason. */
.branch {
  display: grid;
  gap: var(--mgd-space-xl);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

/* <legend> is the step's heading and its focus target: careers.js moves
   focus here on every step change, which is why it carries tabindex="-1" in
   the markup and why base.css's focus outline needs somewhere to sit.
   display: block frees it from the UA's border-notch positioning. The font
   is declared here rather than borrowed from .t-title so that the markup
   does not depend on a second class for its shape. */
.wiz__legend {
  display: block;
  width: 100%;
  padding: 0;
  font: var(--mgd-site-title);
  line-height: 1;
  letter-spacing: var(--mgd-site-track-title);
  text-transform: uppercase;
  color: var(--mgd-color-text);
}

/* WHY line-height: 1 IS ON THE LINE ABOVE, AND WHY THE MARGIN BELOW IS
   POSITIVE. Both halves of one bug: at desktop width the step heading sat
   *through* its own paragraph.

   --mgd-site-title carries Anton at 0.96 leading, and Anton's ink is taller
   than any line box tighter than about 1.0, so at the clamp's 44px ceiling
   roughly 51px of letter had 42px of box to live in and the overflow landed
   on the next line. line-height: 1 is the site's own compensation for this
   exact font — .uploader__plus below and .faq__q::after in site.css both
   pair it with the same shorthand.

   The margin then made it worse by pulling the paragraph up another 12px,
   to close a 24px grid gap between the legend and the lede that does not
   exist: a rendered <legend> is NOT one of its fieldset's grid items in any
   current engine — display: block does not change that — so the gap was
   never generated and there was nothing to cancel. The 12px is now stated
   the ordinary way, as the paragraph's own top margin. Do not restore the
   negative form. */
.wiz__lede {
  margin: var(--mgd-space-md) 0 0;
  max-width: var(--mgd-site-measure-lead);
  font: var(--mgd-site-body);
  color: var(--mgd-color-text-secondary);
}

/* ── The step transition ─────────────────────────────────────────────────
   careers.js writes .js-wiz onto <html> only when it is definitely going to
   drive the wizard, mirroring site.js's .js-reveal handshake exactly: if the
   script never runs, or throws before it takes over, no step is ever left
   invisible by a stylesheet. The sequence is — add --pending while the step
   is still hidden, unhide it, drop --pending on the next frame. */
.js-wiz .wiz__step {
  transition: opacity var(--mgd-site-dur-base) var(--mgd-site-ease),
              transform var(--mgd-site-dur-base) var(--mgd-site-ease);
}

.js-wiz .wiz__step--pending {
  opacity: 0;
  transform: translate3d(0, 12px, 0);
}

/* ══ 3 · A FIELD ════════════════════════════════════════════════════════
   .fields is the question list inside a step, .field is one question.
   .fields--2 pairs two short questions on a wide screen and stacks them on a
   phone with no media query at all — repeat() plus minmax is the same
   mechanism base.css's .grid--2 already runs on. 260px is chosen so two
   columns need 540px of content box; .shell--narrow gives 732px at a desktop
   gutter (860 − 2 × 64), so two tracks form and a third never does, and the
   breakpoint where the second column appears works out at a ~596px viewport —
   wider than any phone in portrait, so a phone always gets one column.

   AUTO-FILL, NOT AUTO-FIT, and the difference is three real fields. auto-fit
   collapses a track nothing is sitting in, so a .fields--2 holding a single
   question stretched that question across the whole 732px. Three of the
   branch steps do exactly that — "Year of graduation", "Highest
   qualification" and "Work experience" (careers.html:299, :317, :337) — and
   each one is a <select> whose longest option is "Bachelor's degree". A
   700px-wide select for a four-character answer, sitting under a pair of
   fields that are half that width, is the misalignment you notice before you
   notice the question. auto-fill keeps the empty track, so the lone select is
   one column wide and its left and right edges line up with the column above
   it. Nothing else moves: where the item count fills the tracks the two
   keywords are identical, so the four-up and two-up steps are untouched. */
.fields {
  display: grid;
  gap: var(--mgd-space-xl);
}

/* min(100%, 260px), not a bare 260px, and the difference only shows on the
   narrowest screen anyone still hands you. A track floor is a floor: below it
   the track refuses to shrink and the row spills instead. At a 280px viewport
   — the Fold's cover display — the gutter is still 20px a side, so the shell
   content box is 240px against a 260px floor and the fields overhang by 20px
   with no scrollbar to show it, because base.css clips horizontal overflow.
   min(100%, …) lets the floor collapse to the container, so the row falls to
   one column instead of overflowing. Identical behaviour at every width where
   260px actually fits, and the same guard base.css:398 puts on .grid--2. */
.fields--2 {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
  gap: var(--mgd-space-xl) var(--mgd-space-lg);
}

.field {
  display: grid;
  gap: var(--mgd-space-sm);
  min-width: 0;
}

.field__label {
  font: var(--mgd-type-label);
  color: var(--mgd-color-text);
}

/* The asterisk is decoration — `required` on the control is what a screen
   reader announces, so the span is aria-hidden in the markup. It is not lime
   (fourteen lime asterisks in one step would overspend a budget of two seven
   times over) and it is not tertiary grey either, because at 13px that rung
   measures 4.24 : 1. */
.field__req { color: var(--mgd-site-text-quiet); }

.field__hint,
.field__err {
  font: var(--mgd-site-caption);
}

/* A HINT ON THIS FORM IS AN INSTRUCTION, NOT AN ASIDE, so it gets the site's
   ordinary secondary rung (8.4 : 1) rather than the quiet one (5.16 : 1).
   Read what they actually say: "Choose at least one", "PDF, DOC or DOCX, up to
   5 MB", "Include the country code if you are outside India". Those are the
   rules the control will reject you for breaking, and a rule you have to lean
   in to read is a rule that produces an error message instead. The step under
   .field__label is still unmistakable — 18.3 against 8.4 — and .field__req
   above keeps the quiet rung, because an asterisk really is decoration. */
.field__hint { color: var(--mgd-color-text-secondary); }

/* 6.3 : 1 on the page ground. The leading glyph lives in the markup rather
   than in a ::before, so it survives a copy-paste into a bug report. */
.field__err { color: var(--mgd-color-danger); }

/* AN OPTION GROUP'S ERROR IS A STRIP, NOT A SENTENCE. Everywhere else on this
   form the message sits immediately under the control it judges, where red text
   is signal enough. On an option group it now sits ABOVE the list (careers.html
   puts every .field__err before its <ul>, so a rejection cannot land below
   fourteen rows), which drops a 13px line into the 8px gap between a question
   and its answers — findable, but easy to read past on the way to the options.

   So it borrows .notice's exact idiom at caption scale: a 2px leading edge,
   square on that side and 6px on the other three, over the danger wash. Same
   shape the rest of the site already uses to mean "read this bit first".

   #E66B70 on #241315 is 5.69 : 1, down from 6.3 : 1 on the page ground and
   still clear of AA for body text. Half a point of contrast buys the one thing
   red text cannot do on its own: a shape you notice before you read it.

   :has() is safe to lean on here in a way it would not be in base.css. This
   file already carries it for :checked, for the exclusive-choice disabled rung
   and for the row focus ring, and the fallback is not a defect — the strip
   reverts to the plain red sentence it was before, in the same place. */
.field:has(> .choices) > .field__err {
  padding: var(--mgd-space-sm) var(--mgd-space-md);
  border-inline-start: 2px solid var(--mgd-color-danger);
  border-radius: 0 var(--mgd-site-radius-card) var(--mgd-site-radius-card) 0;
  background-color: var(--mgd-color-danger-wash);
}

/* ── The control ─────────────────────────────────────────────────────────
   Trap 1 in one declaration: var(--mgd-radius-sm) at rest, because that is
   the radius base.css imposes the instant the control takes focus.

   The 16px font is not a taste decision. iOS Safari zooms the viewport when
   a focused input's text is smaller than 16px, and there is no way to zoom
   back out with one hand. --mgd-site-body is exactly 16px; do not step it
   down to --mgd-type-body (15px) to tighten a layout. */
.field__ctl {
  width: 100%;
  min-height: var(--mgd-layout-control-md);
  padding: var(--mgd-space-md) var(--mgd-space-lg);
  border: var(--mgd-layout-hairline) solid var(--mgd-color-border);
  border-radius: var(--mgd-radius-sm);
  background-color: var(--mgd-color-surface);
  color: var(--mgd-color-text);
  font: var(--mgd-site-body);
  transition: border-color var(--mgd-site-dur-fast) linear;
}

/* 4.69 : 1 on --mgd-color-surface, per tokens.css's measured table. Firefox
   ships a placeholder opacity below 1, which would drop it under the bar. */
.field__ctl::placeholder {
  color: var(--mgd-site-text-quiet);
  opacity: 1;
}

.field__ctl:hover { border-color: var(--mgd-color-text-tertiary); }

.field__ctl[aria-invalid='true'] { border-color: var(--mgd-color-danger); }

/* Insurance for the one engine that has painted a dropdown list white on a
   dark page despite color-scheme: dark being set in tokens.css. */
.field__ctl option {
  background-color: var(--mgd-color-surface);
  color: var(--mgd-color-text);
}

/* ── The select, and its caret ───────────────────────────────────────────
   appearance: none, then a chevron drawn from two borders on the wrapper's
   ::after. Not an SVG, not a background-image, not an icon font: this site
   ships no icon pack, and a data-URI chevron needs one copy per colour it is
   ever asked to be. Two borders and a rotation need none. */
.field__sel {
  position: relative;
  display: grid;
  min-width: 0;
}

.field__sel > .field__ctl {
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--mgd-space-xl-4);
  cursor: pointer;
}

/* A 7px box rotated 45° reads as a ~10px wide chevron, which the 40px of
   right padding above clears at every width. Tertiary grey is correct here
   and nowhere in this file's text: this is a line, not a word. */
.field__sel::after {
  content: '';
  position: absolute;
  top: 50%;
  right: var(--mgd-space-xl);
  width: 7px;
  height: 7px;
  margin-top: -5px;
  border-right: 2px solid var(--mgd-color-text-tertiary);
  border-bottom: 2px solid var(--mgd-color-text-tertiary);
  transform: rotate(45deg);
  pointer-events: none;
}

/* ══ 4 · A CHOICE ═══════════════════════════════════════════════════════
   A radio on a phone is a 13px target sitting in a 48px row, so the ROW is
   the target: <label class="choice"> wraps the input, which makes the whole
   rectangle tappable natively — no JavaScript, no aria-labelledby, no
   div-pretending-to-be-a-radio. The control stays a real input with a real
   accent-color, which is also what keeps Windows High Contrast working. */
.choices {
  display: grid;
  gap: var(--mgd-space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Fourteen options do not want fourteen rows on a 1280px screen. auto-fit
   again: one column on a phone, two from about 560px, three past about
   880px, and no breakpoint for anyone to keep in sync. min(100%, 240px) for
   the same reason .fields--2 has it: a bare floor cannot shrink, so a 240px
   track inside a 240px content box has nothing left for the border and spills
   the row. Below 280px of viewport that is what would happen. */
.choices--wrap {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}

.choice {
  display: flex;
  align-items: center;
  gap: var(--mgd-space-md);
  min-height: var(--mgd-layout-control-md);
  padding: var(--mgd-space-md) var(--mgd-space-lg);
  border: var(--mgd-layout-hairline) solid var(--mgd-color-border);
  border-radius: var(--mgd-radius-sm);
  background-color: var(--mgd-color-bg-alt);
  font: var(--mgd-site-body);
  color: var(--mgd-color-text-secondary);      /* 8.0 : 1 on bg-alt */
  cursor: pointer;
  transition: border-color var(--mgd-site-dur-fast) linear,
              background-color var(--mgd-site-dur-fast) linear,
              color var(--mgd-site-dur-fast) linear;
}

.choice:hover { border-color: var(--mgd-color-text-tertiary); }

.choice__input {
  flex: none;
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--mgd-color-primary);
}

.choice__text { min-width: 0; }

/* THE ROW IS THE TARGET, SO THE ROW IS WHAT THE RING GOES ROUND. base.css puts
   a 2px lime ring on every :focus-visible, which on a real 18px radio is a
   24px ring floating inside a 44px row — correct, tokenised, and easy to lose
   when you are tabbing through fourteen of them. Ringing the label instead
   makes the keyboard indicator the same rectangle the finger already gets.

   Both halves carry :has() ON PURPOSE, so they stand or fall together. A
   browser without :has() drops the whole of an unparseable selector, so it
   drops the suppression alongside the row ring and the input keeps base.css's
   ring — a small indicator rather than no indicator. Suppressing the input in
   a rule of its own would have been the one :has() in this file that degrades
   into an accessibility bug. */
.choice:has(> .choice__input:focus-visible) {
  outline: 2px solid var(--mgd-color-primary);
  outline-offset: 3px;
}

.choice:has(> .choice__input:focus-visible) > .choice__input {
  outline: none;
}

/* Progressive enhancement, and it degrades honestly. Where :has() is missing
   the native control still shows checked in lime through accent-color; all
   that is lost is the row's own border, so nothing becomes ambiguous and
   there is no fallback worth writing. Warm white on the wash measures
   12.3 : 1. The fill is a flat token colour, never opacity over the ground. */
.choice:has(> .choice__input:checked) {
  border-color: var(--mgd-color-primary);
  background-color: var(--mgd-color-primary-wash);
  color: var(--mgd-color-text);
}

/* ══ 5 · DISABLED — the deviation, spelled out ══════════════════════════
   Trap 2. The site styles disabled with [aria-disabled='true'] and
   .is-disabled, and has no :disabled selector anywhere. A form cannot work
   that way: a disabled <fieldset> disables every descendant through the DOM
   without setting an attribute on any of them, and question 05 disables
   seven checkboxes by property the moment "No experience" is ticked. So all
   three selectors appear together here, scoped to .wiz so the deviation
   cannot leak onto the other seven pages.

   Colour, not opacity. opacity-as-a-tint is forbidden by site.css rule 2 and
   it is the wrong instrument anyway: 0.5 of warm white over near-black lands
   at a ratio nobody has measured. --mgd-color-text-tertiary is used for
   words exactly once in this file, right here, because WCAG 1.4.3 exempts
   disabled controls from the contrast minimum and greying is the entire
   message. */
.wiz :disabled,
.wiz [aria-disabled='true'],
.wiz .is-disabled {
  cursor: default;
  color: var(--mgd-color-text-tertiary);
}

.wiz .choice:has(> .choice__input:disabled),
.wiz .choice:has(> .choice__input:disabled):hover {
  cursor: default;
  border-color: var(--mgd-color-divider);
  background-color: var(--mgd-color-bg);
  color: var(--mgd-color-text-tertiary);
}

/* ══ 6 · THE UPLOADER ═══════════════════════════════════════════════════
   The input is real and it is CLIPPED, never display: none — a hidden input
   is unfocusable, which takes the field off the keyboard entirely and out of
   the tab order a screen-reader user depends on. It is clipped with the same
   declarations base.css's .sr-only uses, and it sits BEFORE its label in the
   DOM so `:focus-visible ~` can draw the ring on the visible box. That
   sibling order is the whole trick: reverse it and the focus ring silently
   disappears with no other symptom. */
.uploader {
  display: grid;
  gap: var(--mgd-space-md);
  min-width: 0;
}

.uploader__input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* A dashed hairline is still a hairline: site.css rule 1 governs where depth
   comes from, not which border-style is allowed to say "drop it here". */
.uploader__drop {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: var(--mgd-space-xs);
  min-height: 132px;
  padding: var(--mgd-space-xl);
  border: var(--mgd-layout-hairline) dashed var(--mgd-color-border);
  border-radius: var(--mgd-radius-sm);
  background-color: var(--mgd-color-bg-alt);
  text-align: center;
  cursor: pointer;
  transition: border-color var(--mgd-site-dur-fast) linear,
              background-color var(--mgd-site-dur-fast) linear;
}

.uploader__drop:hover { border-color: var(--mgd-color-text-tertiary); }

/* THE REJECTION IS PAINTED ON THE DROP ZONE, NOT ONLY WRITTEN ABOVE IT. The
   input is clipped to 1px, so its own [aria-invalid] has no surface to show and
   the dashed panel is the only thing on screen big enough to change state. Same
   forward-only sibling combinator as the focus ring below, and the same trap
   with it: swap the input and the label in careers.html and this stops working
   with no other symptom.

   It is also why the resume field's sentence is left as plain red text while an
   option group's is boxed — a 132px panel turning red IS the box.

   DECLARED BEFORE .is-dragging ON PURPOSE. Both selectors carry three classes,
   so the later one wins, and the later one should be the drag: while a file is
   over the page the only question that matters is whether it will land, and the
   answer to a rejected earlier file can wait ten more pixels of travel. Hover
   is one class lighter, so an invalid uploader stays red under the pointer. */
.uploader__input[aria-invalid='true'] ~ .uploader__drop {
  border-color: var(--mgd-color-danger);
  background-color: var(--mgd-color-danger-wash);
}

/* The one transient lime in this file besides a focus ring. A file being
   dragged over the page is the only moment where the answer to "will this
   land?" has to be unmistakable, and it cannot coexist with reading anything
   else, so it does not compete for the budget. */
.uploader.is-dragging .uploader__drop {
  border-color: var(--mgd-color-primary);
  background-color: var(--mgd-color-surface);
}

.uploader__input:focus-visible ~ .uploader__drop {
  outline: 2px solid var(--mgd-color-primary);
  outline-offset: 3px;
}

/* Warm white, not lime: the bar fill and the primary button have already
   spent the viewport's two. */
.uploader__plus {
  font: var(--mgd-site-title);
  line-height: 1;
  color: var(--mgd-color-text);
}

.uploader__cta {
  font: var(--mgd-type-subheading);
  color: var(--mgd-color-text);
}

.uploader__meta {
  font: var(--mgd-site-caption);
  color: var(--mgd-site-text-quiet);        /* 4.96 : 1 on bg-alt */
}

/* The chosen file, once there is one: a hairline row rather than a second
   panel, because it is a fact about the field and not a new region. */
.uploader__file {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--mgd-space-sm) var(--mgd-space-md);
  margin: 0;
  padding: var(--mgd-space-md) var(--mgd-space-lg);
  border: var(--mgd-layout-hairline) solid var(--mgd-color-border);
  border-radius: var(--mgd-radius-sm);
  background-color: var(--mgd-color-surface);
  font: var(--mgd-site-caption);
  color: var(--mgd-color-text-secondary);
}

/* overflow-wrap: anywhere, because a resume filename is user input and
   "Prasanna_Kumar_Resume_Final_v3_updated_September.pdf" has no space in it
   to break at. Without this one line a 320px phone scrolls sideways. */
.uploader__name {
  min-width: 0;
  overflow-wrap: anywhere;
  font-weight: 700;
  color: var(--mgd-color-text);
}

.uploader__file .btn { margin-inline-start: auto; }

/* ══ 7 · THE NAV, AND THE FOOT OF THE LAST STEP ═════════════════════════
   Back is `0 0 auto` and Continue is `1 1 auto`, so from 360px up the row is
   one line: a ~90px ghost button, a 12px gap, and a primary that fills the
   rest and is still the widest thing on the screen.

   IT WRAPS AT 320px, AND IT HAS TO. This comment used to budget 92 + 12 + 216
   = 320px, which is the whole viewport with the two 20px gutters spent twice.
   The real content box at 320px is 280px, and on step 8 careers.js relabels
   the primary "Send application" — ~190px of nowrap ink that cannot shrink,
   so the row came to ~292px and pushed the primary's right edge through the
   gutter. flex-wrap costs nothing above 320px (the line still fits, so it
   never wraps) and at 320px drops the primary onto its own full-width line,
   which is the layout that phone would have wanted anyway.

   The hairline above is the step's bottom edge — it is what says the
   questions have ended. */
.wiz__nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--mgd-space-md);
  padding-top: var(--mgd-space-xl-2);
  border-top: var(--mgd-layout-hairline) solid var(--mgd-color-divider);
}

.wiz__back { flex: 0 0 auto; }
.wiz__next { flex: 1 1 auto; }

/* "SENDING…" HAS TO BE THE MOST READABLE WORD ON THE PAGE, AND IT WAS THE
   LEAST. setBusy() writes aria-disabled alongside aria-busy — correctly, since
   it is what stops a second submit — and site.css's .btn--primary
   [aria-disabled='true'] then repaints the button surface-strong on tertiary
   grey: #70766F on #202721 is 3.28 : 1. So the single moment the applicant most
   needs to know what the button is doing, after eight steps and a file upload,
   was the moment its label fell below AA.

   Busy is not unavailable, so it should not borrow the unavailable rung.
   [aria-busy] carries three classes-worth of specificity against that rule's
   two, so it wins wherever it is declared, and it lands on the pressed lime the
   button already uses for :active with the ink label on top — 13 : 1, and a
   button that looks held down while it works is the honest metaphor. The
   pointer stays default, because .btn[aria-disabled='true'] is untouched. */
.wiz .btn--primary[aria-busy='true'] {
  background-color: var(--mgd-color-primary-pressed);
  color: var(--mgd-color-text-inverse);
}

/* Turnstile renders a 300 × 65 iframe into .wiz__captcha. The min-height is
   reserved for the same reason every image on this site declares its box: so
   the submit button below does not jump 65px down the page the moment the
   widget arrives.

   THE 300px IS FIXED, NOT FLUID. The comment here used to say Cloudflare
   scales its box below 300px; that is true of `data-size="compact"` only,
   and neither the markup (careers.html, no data-size) nor the render options
   (careers.js, no size key) asks for compact. So on a 320px phone the iframe
   is 20px wider than the 280px column, and `body { overflow-x: clip }` in
   base.css does not scroll it — it silently cuts the right edge off, taking
   the page's 20px gutter with it. The box only reaches 300px at a 340px
   viewport. max-width keeps the frame honest and overflow-x gives the widget
   somewhere to go instead of somewhere to hide.

   The widget is wrapped in a plain .field in the markup — label, hint, control,
   and a hidden .notice--warn under it for the case where the check cannot load
   at all. That is why there is no bespoke container class here and no rule
   below beyond the reserved height: a security check is a question like the
   twenty-two above it, and it inherits the same rhythm by being built the same
   way. Do not add a .wiz__captcha frame; put the widget in a .field. */
.wiz__foot {
  display: grid;
  gap: var(--mgd-space-lg);
}

.wiz__captcha {
  min-height: 65px;
  max-width: 100%;
  overflow-x: auto;
}

.wiz__fineprint {
  margin: 0;
  max-width: var(--mgd-site-measure-body);
  font: var(--mgd-site-caption);
  color: var(--mgd-site-text-quiet);
}

/* ══ 8 · THE TWO END PANELS ═════════════════════════════════════════════
   .wiz__done replaces the form after a successful submit; .wiz__closed
   replaces it when the posting is closed. One shape, one differing accent.
   They are deliberately NOT .notice: a notice sits inside a page that carries
   on around it, and from the moment either of these appears it IS the page.

   The accent is a 2px top edge rather than .notice's leading edge, because
   these are full-width panels and a vertical rule on a 780px block reads as a
   stray mark. Lime on done, warning amber on closed — and in both cases the
   form is gone, so the viewport's lime budget is untouched. */
.wiz__done,
.wiz__closed {
  display: grid;
  gap: var(--mgd-space-lg);
  padding: var(--mgd-space-xl-2);
  border: var(--mgd-layout-hairline) solid var(--mgd-color-border);
  border-top: 2px solid var(--panel-accent);
  border-radius: var(--mgd-site-radius-card);
  background-color: var(--mgd-color-surface);
}

.wiz__done   { --panel-accent: var(--mgd-color-primary); }
.wiz__closed { --panel-accent: var(--mgd-color-warning); }

.wiz__panel-title {
  margin: 0;
  font: var(--mgd-site-heading);
  letter-spacing: var(--mgd-site-track-heading);
  color: var(--mgd-color-text);
}

.wiz__done p,
.wiz__closed p {
  margin: 0;
  max-width: var(--mgd-site-measure-lead);
  font: var(--mgd-site-body);
  color: var(--mgd-color-text-secondary);
}

/* ══ 9 · THE FOURTH REDUCED-MOTION EXCEPTION ════════════════════════════
   tokens.css takes every duration token to 1ms under reduce, which stills the
   bar fill and every colour transition in this file from one place and needs
   no help at all. It cannot help the step transition: a 12px translate
   completed in 1ms is not a shortened animation, it is a jump — the identical
   reason site.css's [data-settle] holds its scale at 1 rather than shortening
   it. So the offset is dropped and the fade is left to finish in its 1ms.

   THIS IS THE FOURTH ENTRY IN A LIST tokens.css SAYS HAS THREE. The other
   three, named by selector because line numbers here have drifted before:
     base.css  html                      scroll-behavior back to auto
     base.css  .js-reveal [data-reveal]   the 24px rise and the stagger
     site.css  [data-settle]              settle's scale held at 1

   One last thing this file does not define, for the same reason it defines no
   .wiz__err: the wizard's aria-live region is base.css's .sr-only carrying a
   data-live hook, not a .wiz__live of its own. It is CLIPPED and never
   hidden, because an aria-live region inside display: none announces
   nothing. */
@media (prefers-reduced-motion: reduce) {
  .js-wiz .wiz__step--pending { transform: none; }
}
