/* Attendee component layer.
 *
 * Every class here is prefixed .gp- and matches nothing that exists today, so this
 * file is inert until markup opts in. That is deliberate: new screens can build on
 * it immediately, while the pages still styled by Setting[custom_css] are untouched
 * until after the run.
 *
 * Values come from tokens.css. Nothing in this file hardcodes a colour or a radius.
 * Recipes are extracted from design/custom-css-baseline.css — the duplication count
 * for each is noted, because that is what justified extracting it.
 *
 * .gp-btn and .gp-field were reconciled against what production actually renders on
 * 2026-09-14. The differences, and which way each was settled, are in
 * docs/attendee-component-reconciliation.md. Read that before "correcting" anything
 * here back towards the baseline text: several baseline declarations have never
 * painted a pixel, and copying them in would ship a new design as if it were an
 * extraction.
 */

/* ---------------------------------------------------------------------------
 * Glass surface — the most duplicated recipe in the baseline. 16 literal copies,
 * but the honest count is lower: five attach to elements that cannot render a
 * pseudo-element at all (baseline:704 and :1096 target <input>, and the three
 * .btn-primary::after copies at :1552, :1667 and :1795 target submit inputs), and
 * two of the survivors use different values — drop-shadow(10px 3px 6px #888)
 * brightness(122%), and a misspelt --backdrop-filter custom property, at
 * baseline:1148-1152 and :1482-1487. So: 16 written, 11 that can paint.
 *
 * Careful with that last number. All 11 carry an IDENTICAL box-shadow — the two
 * variants differ only in the filter chain and the misspelt property — so shadow/rim
 * has 11 live copies, not 9. It is the wider recipe that has 9. This rule implements
 * the shadow and deliberately not the filter: the baseline's
 * `filter: blur(1px) drop-shadow(10px 4px 6px black) brightness(115%)` and
 * `backdrop-filter: blur(1px)` are NOT here, which is an open difference on every
 * glass surface and is listed as unreconciled in the reconciliation doc.
 *
 * The rim highlight is a ::after overlay rather than a border so it can sit inside
 * the rounded corner. It is inert to pointers, and `z-index: -1` is what actually
 * puts it behind content: a positioned ::after is generated last, so at the default
 * `z-index: auto` it paints OVER every ordinary child and washes them out. All 16
 * copies of this recipe in the baseline carry that -1; the first extraction dropped
 * it and claimed the behaviour anyway.
 *
 * The -1 only stays *inside* this element because .gp-glass has a backdrop-filter,
 * which makes it a stacking context. Drop that and the rim paints behind the
 * element's own background instead of behind its content.
 * ------------------------------------------------------------------------ */
.gp-glass {
  position: relative;
  background: var(--gp-surface-glass);
  border: 1px solid var(--gp-border-glass);
  border-radius: var(--gp-radius-card);
  box-shadow: var(--gp-shadow-card);
  backdrop-filter: blur(var(--gp-dimension-blur-glass)) saturate(180%);
  -webkit-backdrop-filter: blur(var(--gp-dimension-blur-glass)) saturate(180%);
}

.gp-glass::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background: var(--gp-color-glass-sheen);
  box-shadow: var(--gp-shadow-rim);
  opacity: 0.6;
  z-index: -1;
}

.gp-glass--pill,
.gp-glass--pill::after {
  border-radius: var(--gp-radius-pill);
}

.gp-glass--strong {
  background: var(--gp-surface-glass-strong);
}

/* ---------------------------------------------------------------------------
 * Primary action — the green pill. 3 byte-identical copies in the baseline
 * (#vouchers:1534, [id$="-edit"]:1649, #referrals-new:1777).
 *
 * Reconciled 2026-09-14. Four things that reconciliation settled, each of which
 * is easy to "fix" back the wrong way:
 *
 *   - Weight is 400, not 600. All three copies ship 400; the first extraction
 *     typed 600 and would have shipped a bolder button as if nothing changed.
 *   - The glass box-shadow is restored. The first extraction dropped it, which
 *     is the difference between this button and a flat one.
 *   - The baseline's `backdrop-filter: blur(2px) saturate(180%)` is deliberately
 *     NOT here. The fill is opaque #1aaf13, so there is nothing behind it to show
 *     through. Measured: 114 of 12,000 pixels differ, by 1 of 255, and all of it
 *     is antialiasing on the rounded border. A compositing layer for no pixels.
 *   - The baseline's `.btn-primary::after` rim is deliberately NOT here either.
 *     Every call site is an <input type="submit"> (submit_tag at
 *     vouchers/index.html.erb:14, f.submit at referrals/new.html.erb:15 and
 *     attendees/edit.html.erb:20), and a replaced element generates no
 *     pseudo-elements. That rule has never painted. Adding it would be a new
 *     visual dressed up as an extraction.
 *
 * Width is NOT fixed here. The baseline pinned 150px because those form pages
 * wanted it — and #referrals-new immediately overrides it with .w-100, so only
 * two of the three ever render 150px. A width belongs to the layout.
 *
 * Known and carried forward, not introduced here: white on #1aaf13 is 2.92:1, so
 * the button label fails WCAG AA at this size. It fails in production today too.
 * Fixing it is a visual change and belongs after 4 October.
 * ------------------------------------------------------------------------ */
.gp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: var(--gp-control-height);
  padding: 0.25rem 1rem;
  border: 1px solid var(--gp-border-glass-bright);
  border-radius: var(--gp-radius-pill);
  background: var(--gp-action-primary);
  color: var(--gp-action-on-primary);
  box-shadow: var(--gp-shadow-glass);
  font-size: var(--gp-font-size-body);
  font-weight: var(--gp-font-weight-regular);
  line-height: 1.5;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}

/* Bootstrap repaints .btn-primary blue in six places, and .gp-btn at (0,1,0) loses
 * to every one of them on specificity, which source order cannot fix. While the
 * Setting row is live its ID-scoped block hides all of it — so this only surfaces
 * at cutover, which is the worst moment to discover it.
 *
 * The full list, because "every state" was claimed once already and was wrong:
 *   .btn-primary:hover                       (0,2,0)  bootstrap.css:2949
 *   .btn:focus / .btn-primary:focus          (0,2,0)  bootstrap.css:2935, :2954
 *   .btn-primary:active, .btn-primary.active (0,2,0)  bootstrap.css:2960
 *   .btn-primary:disabled, .disabled         (0,2,0)  bootstrap.css:2968
 *   .btn-primary:active:focus                (0,3,0)  bootstrap.css:2965
 *
 * The .active and .disabled CLASS forms matter separately from their pseudo-class
 * twins — Bootstrap applies them to anchors, which have neither :active in that
 * sense nor :disabled at all. And :disabled is the one that bites hardest: it
 * repaints the background to #0d6efd, so a disabled migrated button was going
 * Bootstrap blue while .gp-btn[disabled] dimmed it to 55%. */
.gp-btn:hover,
.gp-btn:focus,
.gp-btn:active,
.gp-btn.active,
.gp-btn:disabled,
.gp-btn.disabled,
.gp-btn[disabled],
.gp-btn[aria-disabled="true"] {
  background: var(--gp-action-primary);
  border-color: var(--gp-border-glass-bright);
  color: var(--gp-action-on-primary);
  box-shadow: var(--gp-shadow-glass);
}

/* The only Bootstrap button selector that needs three, so it gets its own rule
 * rather than dragging the whole list up to (0,3,0). */
.gp-btn:active:focus {
  box-shadow: var(--gp-shadow-glass);
}

/* No hover brightness. An earlier draft lit the button 6% on hover, which is a real
 * improvement over a button that today gives no hover feedback at all — the fill is
 * pinned by !important and Bootstrap's hover cannot move it. But /vouchers migrates
 * BEFORE the run, and a visible hover change on a shipped page is a redesign however
 * small, which is the one thing this whole pass exists to avoid. It also fails a
 * strict pixel diff in the harness. After 4 October, not now.
 *
 * The focus ring below is the deliberate exception: an accessibility fix is not
 * polish, and removing focus outlines is a defect rather than a look. */

/* Keyboard and switch users need a visible focus indicator, and the control this
 * replaces has none.
 *
 * Attributed carefully, because it was wrong here for a while and the wrong version
 * spread into two accepted.json files and a plan: it is NOT the operator's row that
 * removes the ring. The row never sets `outline` at all -- its only `outline` matches
 * are btn-outline-* class names. `application.css` removes it, on
 * `#performers-pay .qty-pill .qty-btn:focus` and `.qty-input:focus`, which is the OLD
 * checkpoint stepper that .gp-stepper__btn replaces. So this is a repo stylesheet's
 * omission being corrected by a repo stylesheet, and it does not depend on the row
 * being emptied to take effect -- which is why it is visible in compat.
 *
 * Live in production since v196 on six elements: the /vouchers field and Redeem
 * button, and the checkpoint frame's two stepper buttons, Send and Buy More Chuds.
 * Corey confirmed 2026-09-15 that /referrals/new matches them. */
.gp-btn:focus-visible,
.gp-field__control:focus-visible,
.gp-stepper__btn:focus-visible {
  outline: 3px solid var(--gp-border-focus);
  outline-offset: 2px;
}

.gp-btn:disabled,
.gp-btn.disabled,
.gp-btn[disabled],
.gp-btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}

.gp-btn--block {
  display: flex;
  width: 100%;
}

/* ---------------------------------------------------------------------------
 * Form field — the same recipe on #login:725, #vouchers:1519, [id$="-edit"]:1628
 * and #referrals-new:1756. Those three form pages are the same page written three
 * times (251 lines total).
 *
 * These classes are MODIFIERS on Bootstrap's .form-control / .form-label / .mb-3,
 * not replacements for them, which is what the migration plan calls for: named
 * classes go on alongside the utility classes, and a utility comes off only once
 * its matched-rule inventory is empty. So this block carries only what the page
 * blocks added on top of Bootstrap — fill, border, padding and display all still
 * come from .form-control, exactly as they do in production.
 *
 * Reconciled 2026-09-14. What that changed:
 *
 *   - The glass fill and white border are gone. No .form-control anywhere in the
 *     1,815 baseline lines is glass; the extraction invented it. On the #fefefe
 *     page background both composite to white — 1.01:1, an invisible control that
 *     fails WCAG 1.4.11.
 *   - #vouchers is NOT the odd one out. Its `.form-control { color: var(--green) }`
 *     at baseline:1528 never wins: text_field_tag emits id="code", so
 *     `#vouchers #code` at :1519 outranks it and paints the same dk-green/800/
 *     centred recipe as the other three pages. That colour declaration is dead,
 *     like the .footer non-breaking space.
 *   - The label is 0.75rem. `.mb-3 label` (0,1,1,1) beats `.form-label` (0,1,1,0)
 *     on all three pages that have labels, so the 0.6rem uppercase recipe has
 *     never rendered — see the note on font/size-label in design/tokens.json.
 *   - .gp-field sets no margin. The extraction's 0.5rem was dead anyway — .mb-3 is
 *     `margin-bottom: 1rem !important` (bootstrap.css:6992) and a non-important
 *     declaration never beats it. Removed as tidy-up, not as a fix.
 * ------------------------------------------------------------------------ */
.gp-field {
  /* #referrals-new:1812 centres the label and help text; #login and the edit page
   * leave them left. Centred is what the one page migrating before the run does. */
  text-align: center;
}

.gp-field__label {
  display: block;
  margin-bottom: 0;
  font-size: var(--gp-font-size-small);
  text-transform: uppercase;
}

/* No height. 42px was here, and it is ONE PAGE'S value wearing the component's name:
 * of the four blocks that share this recipe, only #vouchers (baseline:1519) sets a
 * height on .form-control. #login (:725), [id$="-edit"] (:1628) and #referrals-new
 * (:1756) all leave Bootstrap's own 38px standing, and that is what they render --
 * measured, 38px on /referrals/new against 42px on /vouchers.
 *
 * It mattered more than it looks. The operator's row never declares a height on
 * #referrals-new .form-control, so there was nothing there to outrank this: adopting
 * .gp-field__control on that page would have made the field 42px the moment the markup
 * deployed -- in compat, before any row edit, inside the freeze. The height now lives
 * in pages.css under #vouchers .form-control, mirroring the row's own selector.
 *
 * Same call, same reason, as #vouchers #code { font-size }. */
.gp-field__control {
  border-radius: var(--gp-radius-pill);
  color: var(--gp-text-field);
  font-weight: var(--gp-font-weight-heavy);
  text-align: center;
}

/* .form-control:focus is (0,2,0) and sets `color: #212529`, which beats
 * .gp-field__control at (0,1,0) — the green text would turn near-black the moment
 * the field is focused. Today an ID-scoped rule in the Setting row wins that
 * fight; after cutover nothing does. */
.gp-field__control:focus {
  color: var(--gp-text-field);
}

/* Invalid. These deliberately hook the classes bootstrap_form ALREADY emits —
 * .is-invalid on the control, .invalid-feedback on the message — rather than
 * inventing a --error modifier it will never render. Both selectors still require
 * one of ours, so nothing outside a .gp-field is touched. */
.gp-field__control.is-invalid {
  border-color: var(--gp-border-error);
}

.gp-field .invalid-feedback {
  color: var(--gp-text-error);
  font-size: var(--gp-font-size-small);
}

/* There is no .gp-field__hint. bootstrap_form's `help:` renders Bootstrap's
 * .form-text — 0.875em, #6c757d, upright — on every page that shows help text.
 * The italic version the extraction carried across exists only under #login
 * (baseline:721), where the very same selector is display:none (baseline:707).
 * It has never been seen. Use .form-text. */

/* The form column those three pages each re-declared. */
.gp-form {
  width: min(var(--gp-dimension-form-width), 100% - 2rem);
  margin-inline: auto;
}

/* ---------------------------------------------------------------------------
 * Card
 * ------------------------------------------------------------------------ */
.gp-card {
  border-radius: var(--gp-radius-card);
  background: var(--gp-surface-glass);
  border: 1px solid var(--gp-border-glass);
  box-shadow: var(--gp-shadow-card);
}

/* ---------------------------------------------------------------------------
 * Quantity stepper — replaces #performers-pay .qty-pill and its eight
 * !important declarations, which existed only to undo Bootstrap's .btn styling.
 * Building it from nothing avoids the fight entirely.
 *
 * Note: .qty-pill is ALSO a different admin component (admin/orders/_line_item).
 * That is why this is named .gp-stepper rather than reusing the class.
 * ------------------------------------------------------------------------ */
.gp-stepper {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  height: var(--gp-control-height);
  padding-inline: 0.5rem;
}

.gp-stepper__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  line-height: 1;
  cursor: pointer;
}

.gp-stepper__btn svg {
  width: 22px;
  height: 22px;
}

.gp-stepper__input {
  width: 3rem;
  border: 0;
  background: transparent;
  font-size: 1.25rem;
  font-weight: var(--gp-font-weight-heavy);
  text-align: center;
  /* Number inputs get spinners that fight a 3rem width. */
  appearance: textfield;
  -moz-appearance: textfield;
}

.gp-stepper__input::-webkit-outer-spin-button,
.gp-stepper__input::-webkit-inner-spin-button {
  appearance: none;
  margin: 0;
}

/* ---------------------------------------------------------------------------
 * Progress meter — the attendee level bar.
 * ------------------------------------------------------------------------ */
.gp-meter {
  height: 0.4rem;
  border-radius: var(--gp-dimension-radius-round);
  background: var(--gp-color-grey-400);
  overflow: hidden;
}

.gp-meter__fill {
  height: 100%;
  background: var(--gp-action-primary);
}

/* ---------------------------------------------------------------------------
 * Respect the reader's motion and transparency settings. The baseline used
 * backdrop-filter heavily with no fallback; on a device with reduced
 * transparency the glass becomes unreadable rather than merely flat.
 *
 * .gp-field__control is not listed: it no longer sets a fill of its own, so
 * there is nothing here to opt out of.
 * ------------------------------------------------------------------------ */
@media (prefers-reduced-transparency: reduce) {
  .gp-glass,
  .gp-card {
    background: var(--gp-color-white);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  .gp-glass::after {
    display: none;
  }
}

@media (prefers-contrast: more) {
  .gp-glass,
  .gp-card {
    background: var(--gp-color-white);
    border-color: var(--gp-text-body);
  }
}
