/* Enrollment payment gate — modal + branded method buttons. */

/* ---------------------------------------------------------------------------
   ONE body size, ONE body colour (client, 2026-08-11)
   ---------------------------------------------------------------------------
   The client annotated a screenshot carrying BOTH gate steps — the guest auth
   step and the payment step — with four notes that all say the same thing:
   «все тексты одинакового цвета» (all texts one colour), «Уменьшить шрифт на
   0,5 шага» (half a step smaller), «Привести в прямоугольниках шрифт к
   единому» (the three boxed texts — disclaimer body, consent label, guest auth
   lead — to one size), «У Email размер шрифта как тут» (the Email field to
   match).

   He was describing something real. This file had accumulated TEN different
   body sizes (0.72 / 0.75 / 0.78 / 0.8 / 0.85 / 0.9 / 0.95 / 1rem) and SIX
   colours (#4a4a55, #272639, #6b6b76, #9a9aa3, #3c4043, #d9534f) across a
   420px dialog — every round of feedback tuned one line in isolation until
   nothing matched anything.

   So the fix is not ten edited numbers, it is ONE declaration point. Every
   body-text node in the gate reads the two custom properties below; the only
   way to make them drift again is to deliberately opt a rule out, which is
   visible in review. Individual rules keep their weight, line-height and
   spacing — size and colour are no longer theirs to set.

   15px = half a step down from the 1rem baseline. This client's "step" has
   meant 2px consistently across the last week's cards, so half a step is 1px.
   Pinned in PIXELS, not rem, on purpose: main.css drops the root to 80% below
   800px, so the "1rem" texts here were rendering at 12.8px on a phone — the
   phone was the worst offender in the very screenshot he annotated. A px value
   lands 15px on every viewport, which RAISES the phone render rather than
   shrinking it further.

   15px is below the site-wide 16px type floor. That is an explicit,
   client-approved exception for this dialog, recorded the same way the course
   rail's label exception was — not an oversight to "fix" later.

   #4a4a55 was already the dominant colour in the file and is the readable dark
   grey he approved on 2026-08-11 («Цвет текста, темно серый читабельный»); it
   measures 8.74:1 on the modal's white, well over the 4.5:1 body minimum.

   NOT flattened into this group, deliberately:
   - The title (1.5rem) and the course + price line (1.25rem) keep their
     display sizes and bold weights. He drew lines to them, but he asked about
     COLOUR and about the boxed body texts; dropping a heading to 15px grey
     would delete the modal's hierarchy in a dialog whose entire job is "here
     is the thing you are buying and what it costs".
   - `.enroll-gate__auth-field-error` keeps #d9534f, and the two in-modal alert
     notices keep Bootstrap's danger palette. An error message in body grey
     stops reading as an error. Sizes ARE unified — colour is what carries the
     warning, not caliber.
   - `.enroll-gate__auth-google` keeps #3c4043 and its own weight. Google's
     branding terms fix the LABEL of their sign-in button — its colour, its
     weight and the logo mark — so those three are not ours to sweep into the
     body-copy group. Do not "finish the job" on the text here.
     Scope note added 2026-08-15: that carve-out was never about the button's
     FACE. The client asked for a grey face on 2026-08-14 and Google publish a
     neutral variant of the button themselves, so the greying done that day is
     inside their guidelines. See the rule near the bottom of this file.
   - The payment method tiles (`.pay-method__*`) are white-on-brand button
     labels, not body copy, and this card explicitly did not touch buttons.

   One partial reversal to record honestly: on 2026-08-11 (commit 44d6af45)
   `.enroll-gate__consent-text` was raised 0.72rem → 1rem on this same client's
   instruction («тут крупнее на 1 шаг»). Today's half-step takes it to 15px —
   still far above where it started, but a step back down one day later. Both
   instructions are his; if a third arrives, read this block before guessing
   which way he meant. */
.enroll-gate {
  --enroll-gate-body-size: 15px;
  --enroll-gate-body-color: #4a4a55;
}

/* The single declaration point. Anything in the gate that is body copy — on
   either step — belongs in this list and nowhere else. */
.enroll-gate__disclaimer-title,
.enroll-gate__disclaimer-body,
.enroll-gate__disclaimer-access,
.enroll-gate__consent-text,
.enroll-gate__note,
.enroll-gate__unavailable,
.enroll-gate__auth-lead,
.enroll-gate__auth-label,
.enroll-gate__auth-input,
.enroll-gate__auth-terms-text,
.enroll-gate__auth-or {
  font-size: var(--enroll-gate-body-size);
  color: var(--enroll-gate-body-color);
}

/* Size only — see the carve-out note above: these three stay in their own reds
   so an error still looks like an error. Listed separately rather than dropped
   from the sweep, because "the fine print is unreadably small" was the
   complaint that started this whole thread, and a 0.72rem field error was the
   smallest text in the dialog. */
.enroll-gate__error,
.enroll-gate__auth-error,
.enroll-gate__auth-field-error {
  font-size: var(--enroll-gate-body-size);
}

/* Mid-screen, and never unreachable. Bootstrap 3 top-aligns every dialog, so
   the gate hung under the viewport's top edge with a lake of backdrop below
   it. The dialog becomes a flex box whose min-height is the viewport minus
   Bootstrap's own dialog margins (10px below 768px, 30px above it): a short
   modal — the auth step — is centred inside that full-height box, and a modal
   taller than the viewport — the payment step on a phone — simply outgrows the
   min-height, at which point the centring is a no-op and `.modal`'s scroll
   takes over with the heading still reachable at the top. Centring by
   `align-items` rather than `margin: auto` for exactly that reason: the box
   grows, it is never a fixed-height container that could push its overflow off
   the top. */
.enroll-gate .modal-dialog {
  display: flex;
  align-items: center;
  min-height: calc(100% - 20px);
  max-width: 420px;
}

@media (min-width: 768px) {
  .enroll-gate .modal-dialog {
    min-height: calc(100% - 60px);
  }
}

.enroll-gate__content {
  /* A lone flex item shrink-wraps to its content; the panel has to keep the
     dialog's full (capped) width instead. */
  width: 100%;
  border: 0;
  border-radius: 12px;
}

.enroll-gate__header {
  border-bottom: 0;
  /* Air under the heading — the client asked for a clear break between the
     title and the course line, which the shared 15px of .modal-body padding
     alone did not give. Kept at the reduced heading sizes: shrinking the type
     narrowed the gap the padding was added to open, so the break needs it more
     now, not less. */
  padding-bottom: 16px;
}

/* The cross keeps Bootstrap's own seat: floated right, at the header's top
   padding edge, out of normal flow. Nothing here moves it — the client asked
   for the heading to move, not the cross. Bootstrap's `.modal-header .close`
   also carries a -2px optical lift; left alone for the same reason. */

/* Deliberately BELOW the site's heading scale (main.css h1 2rem / h2 1.625rem),
   which is what the title and the course + price line used to borrow. The
   client saw the result and called it too large: a 420px dialog is not a page,
   and page-sized headings turned a short confirmation into a poster — the
   heading shouted over the thing the reader is actually there to check, the
   course and its price. Both steps render this same header node, so the quieter
   caliber lands on the auth and the payment step alike. Sizes are still one
   scale step apart (1.5 / 1.25rem) so the hierarchy survives the shrink. */
.enroll-gate__title {
  font-size: 1.5rem;
  font-weight: 700;
  /* Bootstrap's .modal-title forces 1.42857143; the site's headings run 1.2. */
  line-height: 1.2;
  /* Dropped a clear step below the dismiss "×" (client, 2026-08-07: "почему-то
     на уровне крестика, а я просил ниже гораздо, а не выше сделать"). Two
     earlier rounds missed this: 2026-07 gave the heading 6px, which still read
     as one row with the cross, and 2026-08-07 answered "сдвинуть чуть-чуть
     относительно крестика" by putting the two on ONE midline — the opposite of
     what he wanted. 24px is what actually breaks the tie: the cross is a 21px
     box at the header's padding edge, so the heading now starts below its
     bottom edge instead of beside it, and the two can no longer read as a
     single line. Applied to the heading rather than lifting the cross, which
     would only push it into the panel's rounded corner. The float takes the
     cross out of flow, so this margin moves the heading alone — and because the
     heading's line boxes now clear the float entirely, a long title wraps
     across the header's full width instead of around the cross. */
  margin: 24px 0 0;
}

.enroll-gate__course {
  display: flex;
  /* LAST baseline, not the default first one. A course name long enough to wrap
     — "Programa máster de trenzado profesional", and the English title on a
     phone — is two line boxes tall, and plain `baseline` binds every item to the
     FIRST of them: the price then floated beside line one with the name's second
     line hanging below it, which is what the client drew a line through. Asking
     for the last baseline pins the price to whichever line box actually ends the
     name, so it reads as the tail of the sentence at any length. A one-line name
     has one baseline that is both its first and its last, so the single-line
     render — the one the client already signed off — is untouched, offset below
     included (measured: identical to the pixel).

     Declared twice on purpose: a browser too old for the two-value keyword drops
     the second line as invalid and keeps the plain `baseline` above it, which is
     exactly today's behaviour rather than a collapsed default. */
  align-items: baseline;
  align-items: last baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 18px;
}

.enroll-gate__course-name {
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.2;
}

/* Same size as the name — the two are one line of information, not a label and
   a value, and any step between them re-inflates the row the client asked to
   quiet down. */
.enroll-gate__price {
  font-size: 1.25rem;
  font-weight: 700;
  white-space: nowrap;
}

/* …and the price is dropped back onto the name's baseline. `.money` is an
   inline-flex, so the row's `align-items: baseline` reads its FIRST flex item —
   the half-size "$" hung at the top of the block — not the big digits, which
   sit lower again because they run at line-height 0.85 from the same flex-start
   top. The digits' baseline therefore lands about a third of an em below the
   block's: the two were the same size but visibly stepped, which is what the
   client saw.

   Retuned from 0.325em when the row above dropped to 1.25rem. Being em-relative
   the old value did rescale by itself, but the gap it cancels is NOT a fixed
   fraction of the type: the engine rounds the font's ascent and descent to half
   pixels, so measured on this dialog the gap runs 9.5px at the old 26px
   (0.365em) and 7px at today's 20px (0.350em). Carried down unchanged, 0.325em
   left the digits half a pixel low; 0.35em seats them exactly on the name's
   baseline at 1.25rem, and measures the same at 1x and 2x. Re-measure if this
   row is ever resized again — the em figure does not travel. Same idiom
   money.css already uses to seat the "$": a relative offset moves paint only,
   so the row's baseline maths — and the widget everywhere else — are
   untouched.

   Re-measured when the row above switched to `last baseline`, and deliberately
   left at 0.35em. The switch does not touch this figure: the price is a single
   line, so its first and last baselines are the same line, and the gap being
   cancelled here is internal to .money. Re-measured across device scales with
   the offset removed, that gap runs 6px at 1x, 7.5px at 2x and 7px at 3x on the
   desktop dialog, and 7.5px-equivalent (6px of 16px type) on the phone: the
   engine's half-pixel rounding of ascent and descent moves it, so no single em
   value is exact everywhere. 0.35em is the value that stays within a pixel of
   the name's baseline at every one of them — 0.30em would be exact at 1x only
   and 1.5px low at 2x, where most people actually read this. */
.enroll-gate__price .money {
  position: relative;
  top: -0.35em;
}

/* All available methods ride ONE row (client, 2026-07). How many there are is
   geo-dependent — the gate renders between one and three gateways per the
   country whitelists — so the columns are equal fractions rather than a fixed
   split, and a single available method still fills the width exactly as before. */
.enroll-gate__methods {
  display: flex;
  flex-direction: row;
  gap: 12px;
}

.enroll-gate__form {
  margin: 0;
  /* flex-basis 0 (not auto) so the columns are equal regardless of how long each
     label is; min-width:0 lets them shrink below their content on narrow
     viewports instead of overflowing the modal. */
  flex: 1 1 0;
  min-width: 0;
}

.pay-method {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  /* The forms are equal-width flex items that stretch to the row height; the
     button must fill its form or the three tiles end up ragged whenever one
     label wraps to a second line ("Local payment methods"). */
  height: 100%;
  min-height: 56px;
  padding: 8px 16px;
  border: 1px solid transparent;
  border-radius: 10px;
  line-height: 1.25;
  text-align: center;
  cursor: pointer;
  transition: filter 0.15s ease-in-out, transform 0.05s ease-in-out;
}

.pay-method:hover {
  filter: brightness(0.96);
}

.pay-method:active {
  transform: translateY(1px);
}

.pay-method:focus-visible {
  outline: 2px solid #272639;
  outline-offset: 2px;
}

/* Until the consent box is ticked, the methods are visibly locked. */
.pay-method:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  filter: none;
  transform: none;
}

.pay-method__label {
  font-size: 1rem;
  font-weight: 700;
}

.pay-method__caption {
  margin-top: 2px;
  font-size: 0.75rem;
  font-weight: 400;
  opacity: 0.85;
}

/* Brand colors. */
.pay-method--stripe {
  background: #635bff;
  color: #fff;
}

.pay-method--paypal {
  background: #003087;
  color: #fff;
}

.pay-method--dlocal {
  background: #11b886;
  color: #fff;
}

/* Both of these carried #6b6b76 — a second, lighter grey that existed for no
   reason other than "this line is less important". Size and colour come from
   the shared tokens now; centring is what marks them as asides. */
.enroll-gate__note {
  margin-top: 16px;
  margin-bottom: 0;
  text-align: center;
}

.enroll-gate__unavailable {
  margin: 0;
  text-align: center;
}

/* In-modal error notice, revealed by enroll-gate.js when a checkout can't open. */
.enroll-gate__error {
  margin-bottom: 16px;
  text-align: center;
}

.enroll-gate__error[hidden] {
  display: none;
}

/* While a payment attempt is in flight, dim + lock the buttons. */
.enroll-gate.is-busy .enroll-gate__methods {
  opacity: 0.6;
  pointer-events: none;
}

/* Legal disclaimer: digital product, immediate access, final sale. Sits at the
   top of the payment step, above the buttons; a bottom margin sets it off from
   the methods below. */
.enroll-gate__disclaimer {
  margin-bottom: 20px;
}

/* The three disclaimer texts were once fine print at 0.72/0.78rem and read as
   unreadable legalese (client), which is what first pulled them up to the body
   size. Size and colour now come from the shared tokens at the top of the file;
   only weight and spacing are set here — the title and the access line stay
   bold, the paragraph stays regular. */
.enroll-gate__disclaimer-title {
  display: block;
  margin-bottom: 6px;
  font-weight: 700;
}

.enroll-gate__disclaimer-body {
  margin: 0;
  line-height: 1.5;
}

/* ---- "Terms of Sale" collapsible ----------------------------------------
   2026-08-13, client: the legal paragraph and the bold access line under it
   («вот эту кучку убрать в подкат») fold behind a trigger titled "Terms of
   Sale", drawn «стиль подката» like the course rail's "Module Description"
   link. Treatment is therefore COPIED from .course-menu__desc-btn rather than
   re-invented — 14px / 400 / #e106a4 / dashed crimson underline at a 4px
   offset — so the two triggers read as one component in two places. If that
   rail rule is ever retuned, retune this with it.

   The one deliberate divergence is padding. The rail's trigger carries
   `0 12px 8px 20px` to sit on the module name's 20px indent; the modal has no
   such rail, so the horizontal padding is zero and the label lines up with the
   title above it. Vertical spacing is margin here for the same reason.

   14px is under the site's 16px type floor — inherited from the rail's own
   documented, client-asked-for exception, not a new one.

   2026-08-14, client: the copy had drifted apart once touched. He put the two
   triggers side by side and marked the rail's dashes «тут пунктиры ок» against
   this one's «а тут не ок, поправить» — this one wore a dark box and a heavier
   underline. Both artifacts were local to this file and both are gone; see the
   hover/focus rule below and the note where the focus rule used to be. */

/* The rail's `.course-menu__desc` wrapper, restated: a column flex box is what
   makes `align-self: flex-start` on the trigger shrink-wrap it to its label —
   and what blockifies the button's `inline-flex` to `flex`, so the two triggers
   agree on every computed value, not merely on the ones that show. */
.enroll-gate__terms {
  display: flex;
  width: 100%;
  flex-flow: column nowrap;
}

.enroll-gate__terms-btn {
  display: inline-flex;
  align-self: flex-start;
  align-items: center;
  margin: 0;
  padding: 0;
  border: none;
  background: none;
  font-size: 14px;
  line-height: 22px;
  font-weight: 400;
  color: #e106a4;
  /* Written as a text-decoration, not a border, so the dashes hug the glyphs
     and follow the label if it ever wraps. currentColor keeps the crimson in
     one place. */
  text-decoration: underline dashed currentColor;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
  text-align: left;
  cursor: pointer;
}

/* Restates the dashed style: a bare `underline` on hover repaints it solid.
   The thickness has to be restated WITH it. `text-decoration` is a shorthand,
   so naming it here resets every longhand it covers back to initial — including
   the `1px` pinned above, which falls back to `auto`. `auto` is font-derived,
   and at this 14px it lands around 1.5px, so the line thickened by half again
   the moment the trigger was hovered or focused; on a higher-density screen it
   reads heavier still, which is how it reached the client as the coarser
   underline he marked. Keep the two declarations together: the shorthand cannot
   be dropped (a bare `underline` loses the dashes) and the thickness cannot be
   dropped (the shorthand undoes it), so this pair is not a tidy-able duplicate. */
.enroll-gate__terms-btn:hover,
.enroll-gate__terms-btn:focus {
  color: #e106a4;
  text-decoration: underline dashed currentColor;
  text-decoration-thickness: 1px;
}

/* There is deliberately NO :focus-visible rule here. This trigger used to carry
   `outline: 2px solid #272639; outline-offset: 2px`, which is the dark box the
   client crossed out — and it was the exact thing he had already had removed
   site-wide twice over ("почему двойные границы при нажатии? исправить", then
   "нет, у меня все еще есть"; the rationale sits at main.css:4012). The rail's
   .course-menu__desc-btn has no focus rule either, so the site's global
   `button:focus { outline: 0 }` reset is what both now fall through to, which
   is what makes them identical pressed as well as at rest. Do not reintroduce
   a ring on this one alone — indication for this pair of triggers is a
   site-wide decision, taken with the client, not a local one. */

/* The panel ships OPEN and enroll-gate.js closes it on init, so a JS-less
   visitor keeps the whole legal text in front of him. `[hidden]` is what the
   script toggles; the content stays in the DOM either way. */
.enroll-gate__terms-panel {
  margin-top: 8px;
}

.enroll-gate__terms-panel[hidden] {
  display: none;
}

/* Access period — glued to the bottom of the disclaimer paragraph, in bold. The
   translatable string carries a <strong> around the day count; the whole line
   reads bold. Was #272639, a shade nobody else in the block used; folded into
   the shared body colour on 2026-08-11 («все тексты одинакового цвета»). The
   bold weight is what sets this line apart now, which is enough — it is the
   last line of a paragraph, not a heading. */
.enroll-gate__disclaimer-access {
  margin: 10px 0 0;
  font-weight: 700;
  line-height: 1.5;
}

.enroll-gate__disclaimer-access strong {
  font-weight: 700;
}

/* Mandatory consent: one box for the whole gate, right under the terms. */
.enroll-gate__consent {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 12px 0 0;
  font-weight: 400;
  cursor: pointer;
}

.enroll-gate__consent-box {
  flex-shrink: 0;
  margin: 2px 0 0;
}

/* History worth keeping, because it reverses within one day. 2026-08-11: raised
   0.72rem → 1rem, the client asking for the consent label one step up («тут
   крупнее на 1 шаг») and matching the disclaimer above it («Это таким же фонтом
   как тут будет»). 2026-08-11, later card: the whole modal comes down half a
   step to 15px, this label with it. Net over two days: 11.5px → 15px, so his
   original complaint (legally significant text set as fine print) still stands
   answered — the half step back is a trim, not a reversal. It is bound to the
   shared token now, so it can only ever move together with the disclaimer it
   was asked to match. */
.enroll-gate__consent-text {
  line-height: 1.5;
}

/* ---- Sub-steps: guests see auth first, then payment is revealed in place ---- */
.enroll-gate__step[hidden] {
  display: none;
}

.enroll-gate__auth-lead {
  margin: 0 0 16px;
}

.enroll-gate__auth-error {
  margin-bottom: 14px;
}

.enroll-gate__auth-error[hidden] {
  display: none;
}

.enroll-gate__auth-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0;
}

.enroll-gate__auth-reveal[hidden] {
  display: none;
}

/* The reveal is a plain block dropped into the flex form, so its own children
   fell outside the form's 12px row rhythm: the "Country" caption sat flush on
   the bottom edge of the name inputs and the terms line all but touched the
   select. Restating the form's gap here lines the revealed rows up with the
   Email/Password rows above — same 12px, matched rather than re-invented. The
   `[hidden]` rule above outranks this one, so the reveal still collapses. */
.enroll-gate__auth-reveal {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.enroll-gate__auth-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* First / last name sit side by side on the register reveal. */
.enroll-gate__auth-grid {
  display: flex;
  gap: 12px;
}

.enroll-gate__auth-grid .enroll-gate__auth-row {
  flex: 1;
}

.enroll-gate__auth-label {
  margin: 0;
  font-weight: 600;
}

/* «У Email размер шрифта как тут» — the field's own text was 0.95rem against a
   0.78rem label and a 0.9rem lead, three sizes stacked inside one 44px row.
   Both now read the shared token, so the label and what you type into it match.
   Form controls do not inherit type from their ancestors, which is exactly how
   this one drifted in the first place; the token is stated explicitly for the
   input (and the country <select>) in the sweep at the top. */
.enroll-gate__auth-input {
  width: 100%;
  min-height: 44px;
  padding: 8px 12px;
  border: 1px solid #d7d7de;
  border-radius: var(--field-radius, 8px);
  background: #fff;
}

.enroll-gate__auth-input:focus {
  outline: none;
  border-color: #272639;
  box-shadow: 0 0 0 2px rgba(39, 38, 57, 0.12);
}

/* Colour carve-out — see the header block. Red is the whole message here; a
   validation notice rendered in body grey just looks like more instructions. */
.enroll-gate__auth-field-error {
  margin: 0;
  color: #d9534f;
}

.enroll-gate__auth-field-error:empty {
  display: none;
}

/* The reveal's own 12px gap now spaces this off the country select, so the
   4px nudge that used to stand in for it would only stack on top. */
.enroll-gate__auth-terms {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  font-weight: 400;
  cursor: pointer;
}

.enroll-gate__auth-terms-box {
  flex-shrink: 0;
  margin: 2px 0 0;
}

.enroll-gate__auth-terms-text {
  line-height: 1.5;
}

.enroll-gate__auth-submit {
  margin-top: 4px;
}

/* Divider between the email form and the Google option. Was #9a9aa3 — the
   lightest text in the modal at 2.9:1 on white, i.e. below the contrast minimum
   as well as off-palette. The rules either side already say "this is a seam";
   the word does not need to fade out too. */
.enroll-gate__auth-or {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 16px 0;
}

.enroll-gate__auth-or::before,
.enroll-gate__auth-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: #ececef;
}

/* Label carve-out — #3c4043 and the 600 weight are Google's own button
   styling, which their branding terms require us to keep, as is the logo mark
   in the widget view. Left out of the body-copy sweep on purpose; do not fold
   the colour or the weight in for consistency's sake.

   The FACE is a different question and is NOT protected. On 2026-08-14 the
   client asked for the sign-up page's Google button to stop being white
   («Кнопку Гугл засерить»), and on 2026-08-15 he pointed at this modal and
   asked for the same here («а тут может тоже засерить?»). A neutral grey face
   is one of Google's own published sign-in button variants, so the request
   sits inside their guidelines — it is only the label and the mark that are
   theirs. This button therefore now carries the exact pair from
   `.btn.party-join__google-btn` in public/static/css/party.css: face #f1f3f4,
   hover #e8eaed. Both places are one instruction; change them together.

   Borders, same day («и окантовку кнопки сделать только чуть темнее чем сам
   цвет кнопки»). Each border is the face minus 14 points per channel, so it
   keeps the face's own hue rather than importing the modal's cooler #d7d7de:
   #f1f3f4 → #e3e5e6 (L* 95.7 → 90.8, ΔL* 4.9) and #e8eaed → #dadcdf (L* 92.6
   → 87.7, ΔL* 4.9). The same step at rest and on hover keeps the edge reading
   the same way while the face moves. He named no hex values — these shades are
   our reading of "чуть темнее" and await his confirmation.

   :focus repeats the RESTING face and border rather than inheriting `.btn`'s
   white, for the reason recorded on the sign-up rule: a link-shaped button
   holds focus after a click, and without this it flashes back to white the
   moment the visitor taps it — which is the exact complaint being fixed. */
.enroll-gate__auth-google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 48px;
  border: 1px solid #e3e5e6;
  border-radius: 10px;
  background: #f1f3f4;
  color: #3c4043;
  font-weight: 600;
}

.enroll-gate__auth-google:hover {
  background: #e8eaed;
  border-color: #dadcdf;
  color: #3c4043;
}

.enroll-gate__auth-google:focus {
  background: #f1f3f4;
  border-color: #e3e5e6;
  color: #3c4043;
}

.enroll-gate__auth-google svg {
  flex-shrink: 0;
}

/* While an auth request is in flight, dim + lock the form. */
.enroll-gate.is-busy .enroll-gate__auth-form,
.enroll-gate.is-busy .enroll-gate__auth-google {
  opacity: 0.6;
  pointer-events: none;
}
