/* ==========================================================================
   Brewed Rituals — Founding Membership
   Lifted from design/screens.html after Phase 01.

   Everything is scoped under .br-app and every custom property is namespaced,
   so the plugin cannot leak into the theme and Elementor cannot leak in here.
   Logical properties throughout, so the Arabic RTL build in Phase B is a
   dir="rtl" and a font swap rather than a rewrite.
   ========================================================================== */

.br-app {
  /* Brand palette — exact values from Brewed_Branding_V4.pdf */
  --br-cream:        #E1D9CE;
  --br-brown:        #463335;
  --br-mauve:        #9E809B;
  --br-plum:         #50374D;

  /* Derived neutrals, mixed from plum + brown. Contrast vs --br-cream noted. */
  --br-ground:       #E9E3DA;
  --br-surface:      #F4F0EA;
  --br-ink:          #463335;   /* 8.2:1  AAA */
  --br-ink-muted:    #6B5460;   /* 4.9:1  AA  */
  --br-ink-faint:    #8C7681;   /* disabled only */
  --br-line:         #CFC4BA;
  --br-line-strong:  #B9A9AC;
  --br-on-plum:      #F0EAE1;   /* 7.6:1 on plum */
  --br-error:        #8C3A3A;   /* 5.6:1 on surface */

  /* Sofia Pro, ITC Garamond and Omnia Naskh are commercial licences. Named
     first; the Google faces are the approved substitutes pending that call. */
  --br-font-display: 'ITC Garamond', 'Newsreader', 'Iowan Old Style', Georgia, serif;
  --br-font-title:   'Sofia Pro', 'Montserrat', 'Helvetica Neue', Arial, sans-serif;
  --br-font-body:    'Jost', 'Helvetica Neue', Arial, sans-serif;
  --br-font-arabic:  'Omnia Naskh', 'Noto Naskh Arabic', 'Geeza Pro', serif;

  --br-t-xs: 11px;  --br-t-sm: 13px;  --br-t-base: 16px;
  --br-t-md: 18px;  --br-t-lg: 22px;  --br-t-xl: 28px;
  --br-t-2xl: 36px; --br-t-3xl: 52px;

  --br-track-label: .22em;
  --br-track-btn:   .16em;
  --br-track-code:  .1em;

  --br-s-1: 4px;  --br-s-2: 8px;  --br-s-3: 12px; --br-s-4: 16px;
  --br-s-5: 20px; --br-s-6: 24px; --br-s-8: 32px; --br-s-10: 40px;

  --br-radius: 14px;
  --br-radius-lg: 20px;
  --br-tap: 48px;
  --br-shadow-card: 0 1px 2px rgba(70,51,53,.05), 0 18px 40px -28px rgba(80,55,77,.45);
  --br-shadow-sel:  0 6px 18px -10px rgba(80,55,77,.5);
  --br-ease: cubic-bezier(.22,.61,.36,1);
  --br-dur: .22s;

  /* container */
  display: flex;
  justify-content: center;
  padding: var(--br-s-8) var(--br-s-4);
  /* No background of its own. The shortcode is dropped into whatever section a
     page already has — on brewedrituals.com that is a photograph — and a solid
     cream block across the full width read as a slab pasted over the design.
     The card below provides the surface instead. */
  background: transparent;
  font-family: var(--br-font-body);
  font-weight: 300;
  font-size: var(--br-t-base);
  line-height: 1.6;
  color: var(--br-ink);
  -webkit-font-smoothing: antialiased;
}

.br-app[dir="rtl"] {
  --br-font-display: var(--br-font-arabic);
  --br-font-title:   var(--br-font-arabic);
  --br-font-body:    var(--br-font-arabic);
  --br-track-label: 0;
  --br-track-btn:   0;
  --br-track-code:  0;
}

.br-app *, .br-app *::before, .br-app *::after { box-sizing: border-box; }

/* --- the card ------------------------------------------------------------ */
.br-app .br-screen {
  inline-size: 100%;
  max-inline-size: 460px;
  background: var(--br-surface);
  border: 1px solid var(--br-line);
  border-radius: 34px;
  padding: var(--br-s-6) var(--br-s-6) var(--br-s-8);
  box-shadow: var(--br-shadow-card);
}

/* Frosted glass, but only where the browser can genuinely blur what is behind
   it. Applying the translucent background unconditionally would leave the text
   sitting directly on whatever photograph the section uses in browsers without
   backdrop-filter, so the solid surface above stays as the fallback.

   .82 rather than something more dramatic: this card holds body copy and a
   form, and legibility over an unknown background beats the effect. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .br-app .br-screen {
    background: rgba(244, 240, 234, .82);
    border-color: rgba(255, 255, 255, .55);
    -webkit-backdrop-filter: blur(20px) saturate(115%);
    backdrop-filter: blur(20px) saturate(115%);
    /* Deeper than --br-shadow-card: the card has to lift off a busy photo now,
       not off a flat cream panel. */
    box-shadow: 0 1px 2px rgba(70, 51, 53, .06),
                0 28px 64px -30px rgba(38, 24, 36, .7);
  }

  /* The options were solid fills chosen to sit on a solid card. Left opaque
     they read as tiles stuck onto the glass, so they get the same treatment at
     a lower strength — enough to still separate from the card behind them. */
  .br-app .br-screen .br-option {
    background: rgba(233, 227, 218, .72);
  }
  .br-app .br-screen .br-option[aria-checked="true"] {
    background: rgba(250, 248, 245, .92);
  }
}

.br-app .br-top {
  display: flex; justify-content: space-between; align-items: center;
  margin-block-end: var(--br-s-6);
  font-size: var(--br-t-sm); color: var(--br-ink-muted);
  min-block-size: 24px;
}
.br-app .br-top button {
  font: inherit; background: none; border: 0; color: inherit;
  padding: var(--br-s-1) var(--br-s-2); cursor: pointer; border-radius: 6px;
}
.br-app .br-top button:hover { color: var(--br-plum); }
.br-app .br-lang {
  font-family: var(--br-font-title); font-size: var(--br-t-xs); font-weight: 500;
  letter-spacing: .1em; color: var(--br-ink-faint);
}

/* --- headings ------------------------------------------------------------ */
.br-app .br-eyebrow {
  font-family: var(--br-font-title); font-size: var(--br-t-xs); font-weight: 500;
  letter-spacing: var(--br-track-label); text-transform: uppercase;
  color: var(--br-ink-muted); text-align: center; margin: 0;
}
.br-app .br-title {
  font-family: var(--br-font-display); font-weight: 400; font-size: var(--br-t-xl);
  line-height: 1.2; text-align: center; margin: var(--br-s-2) 0 0;
  text-wrap: balance; color: var(--br-ink);
}
.br-app .br-sub {
  font-family: var(--br-font-title); font-size: var(--br-t-xs); font-weight: 400;
  letter-spacing: var(--br-track-label); text-transform: uppercase;
  color: var(--br-ink-muted); text-align: center; margin: var(--br-s-3) 0 0;
}
.br-app .br-question {
  font-family: var(--br-font-display); font-style: italic; font-weight: 400;
  font-size: var(--br-t-lg); line-height: 1.3; text-align: center;
  margin: var(--br-s-6) 0 var(--br-s-5); text-wrap: balance;
}

/* --- progress ------------------------------------------------------------ */
.br-app .br-progress {
  display: flex; gap: 5px; justify-content: center; margin-block-end: var(--br-s-5);
}
.br-app .br-progress span {
  block-size: 2px; inline-size: 34px; background: var(--br-line); border-radius: 2px;
  transition: background var(--br-dur) var(--br-ease);
}
.br-app .br-progress span.is-done { background: var(--br-plum); }

/* --- options ------------------------------------------------------------- */
.br-app .br-options { display: flex; flex-direction: column; gap: var(--br-s-2); }
.br-app .br-option {
  display: flex; justify-content: space-between; align-items: center; gap: var(--br-s-4);
  min-block-size: var(--br-tap);
  padding: var(--br-s-3) var(--br-s-5);
  border: 1px solid var(--br-line); border-radius: var(--br-radius);
  background: var(--br-ground); color: var(--br-ink);
  font: inherit; text-align: start; cursor: pointer; inline-size: 100%;
  transition: border-color var(--br-dur) var(--br-ease),
              background var(--br-dur) var(--br-ease),
              box-shadow var(--br-dur) var(--br-ease);
}
/*
 * The colour is restated on hover, not just the border.
 *
 * These are <button> elements dropped into somebody else's theme, and this one
 * ships `button:hover { color: #fff }` in its reset — so a selected answer went
 * white-on-cream and became unreadable the moment the pointer touched it. Any
 * theme can do this, so every interactive state of ours declares its own colour
 * rather than inheriting one.
 */
.br-app .br-option:hover,
.br-app .br-option:focus-visible {
  border-color: var(--br-line-strong);
  color: var(--br-ink);
}
.br-app .br-option[aria-checked="true"]:hover,
.br-app .br-option[aria-checked="true"]:focus-visible { color: var(--br-ink); }
.br-app .br-option[aria-checked="true"] {
  border-color: var(--br-plum); background: var(--br-surface);
  box-shadow: var(--br-shadow-sel);
}

/* --- the QR --------------------------------------------------------------- */
/*
 * Opaque white, deliberately, even though the card around it is frosted glass.
 * A scanner needs hard black-on-white contrast and a quiet margin; a QR shown
 * at 82% opacity over a photograph is a QR that sometimes will not read.
 */
.br-app .br-qr-wrap {
  display: flex; flex-direction: column; align-items: center;
  flex: 0 0 auto;
}

/*
 * Code on the left, its QR on the right — two ways of handing over the same
 * thing, so they belong on one line. flex-wrap does the responsive work: below
 * roughly 420px of card the left column cannot hold its 200px basis beside the
 * code, and the two stack instead. No media query, because what matters is the
 * width of this card, not the width of the screen it happens to be on.
 */
.br-app .br-claim {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: center; gap: var(--br-s-5);
}
.br-app .br-claim-main {
  flex: 1 1 240px; min-inline-size: 0; text-align: center;
  /* Makes this column a query container so the code below can size itself
     against the space it actually has. */
  container-type: inline-size;
}

/*
 * The reference code is set in nowrap, so it has to fit or it spills out of the
 * card. Sizing it against the viewport was fine when it had the full card
 * width; beside a QR it does not, and BR-RECHARGE-8888 — the longest code the
 * six rituals can produce — needed 343px in a 300px column.
 *
 * cqi is the column's own width, so this holds at any card size instead of
 * guessing from the screen. Browsers without container queries drop the
 * declaration and keep the viewport-based clamp above it.
 */
.br-app .br-claim-main .br-code { font-size: clamp(20px, 8.4cqi, 30px); }
.br-app .br-claim-note { margin-block: var(--br-s-4); }
/* The copy button belongs to the column, not the card, so it stops short of
   the QR rather than running the full width beneath it. */
.br-app .br-claim-main .br-btn { margin-block-start: var(--br-s-2); }
/* Top-aligned: with the button in the left column the two sides are close
   enough in height that centring them just introduces a lopsided gap. */
.br-app .br-claim { align-items: flex-start; }
.br-app .br-qr-wrap { padding-block-start: var(--br-s-5); }

/* The success card earns more room than the quiz screens: it has to hold the
   code and the QR beside each other without cramping either. */
.br-app .br-screen--claim { max-inline-size: 560px; }
.br-app .br-qr {
  /* A fixed size, not a percentage. The QR now sits in a shrink-to-fit flex
     item, where a percentage width resolves against a width that depends on
     this element — and collapsed it to about 100px, small enough to make
     scanning fussy. */
  inline-size: 150px;
  padding: var(--br-s-3);
  background: #fff;
  border: 1px solid var(--br-line);
  border-radius: var(--br-radius);
}
/* Physical properties, not logical ones: the code is square and has no reading
   direction, and width/height on a replaced element is the better-supported
   pair. Height comes from the viewBox's aspect ratio. */
.br-app .br-qr svg { display: block; width: 100%; height: auto; }
.br-app .br-qr-hint { margin-block-start: var(--br-s-3); text-align: center; max-inline-size: 150px; }

/* Printing a card should give a clean code and nothing else around it. */
@media print {
  .br-app .br-qr { border: 0; padding: 0; }
}

/* --- buttons ------------------------------------------------------------- */
.br-app .br-btn {
  display: flex; align-items: center; justify-content: center;
  min-block-size: var(--br-tap); inline-size: 100%;
  padding: var(--br-s-4) var(--br-s-5);
  border: 1px solid var(--br-plum); border-radius: var(--br-radius);
  background: var(--br-plum); color: var(--br-on-plum);
  font-family: var(--br-font-title); font-size: var(--br-t-sm); font-weight: 500;
  letter-spacing: var(--br-track-btn); text-transform: uppercase;
  cursor: pointer; text-align: center; line-height: 1.35;
  margin-block-start: var(--br-s-5);
  transition: background var(--br-dur) var(--br-ease), opacity var(--br-dur) var(--br-ease);
}
.br-app .br-btn:hover:not(:disabled) { background: #452F42; }
.br-app .br-btn:disabled { opacity: .45; cursor: not-allowed; }
.br-app .br-btn:hover:not(:disabled),
.br-app .br-btn:focus-visible { color: var(--br-on-plum); }

.br-app .br-btn--ghost { background: transparent; color: var(--br-plum); }
/* Without the colour here the theme's white would make this button's label
   vanish against the card the moment it is hovered. */
.br-app .br-btn--ghost:hover:not(:disabled),
.br-app .br-btn--ghost:focus-visible {
  background: rgba(80,55,77,.06);
  color: var(--br-plum);
}

/* --- reveal -------------------------------------------------------------- */
.br-app .br-reveal { text-align: center; }
.br-app .br-ritual-name {
  font-family: var(--br-font-display); font-weight: 400;
  font-size: var(--br-t-3xl); line-height: 1; letter-spacing: .06em;
  margin: var(--br-s-3) 0 0; color: var(--br-plum);
}
.br-app .br-ritual-meta {
  font-family: var(--br-font-display); font-style: italic; font-size: var(--br-t-md);
  color: var(--br-ink-muted); margin: var(--br-s-3) 0 0;
}
.br-app .br-rule {
  block-size: 1px; background: var(--br-line); border: 0;
  margin: var(--br-s-6) auto; inline-size: 56px;
}
.br-app .br-body {
  font-size: var(--br-t-base); line-height: 1.7; color: var(--br-ink);
  text-align: center; margin: 0; max-inline-size: 34ch; margin-inline: auto;
}
.br-app .br-body b { font-weight: 500; }

/* --- form ---------------------------------------------------------------- */
/* The subtitle above ("3,250 founding seats") sat hard against this toggle. */
.br-app .br-toggle {
  position: relative;
  display: flex; border: 1px solid var(--br-plum); border-radius: var(--br-radius);
  overflow: hidden;
  margin-block: var(--br-s-6) var(--br-s-5);
}

/*
 * The fill slides between the two halves rather than the pressed button simply
 * turning plum, so the switch reads as one thing moving instead of two things
 * changing colour at once.
 *
 * It is a separate element because the two halves are separate elements: there
 * is nothing a background on the buttons themselves could animate ACROSS. The
 * DOM order is [all, single], so the second half is the translated one.
 *
 * inset-inline-start, so in Arabic the fill starts against the right-hand edge
 * and the translate walks it leftwards to the second half. A physical `left`
 * would put the resting position on the wrong side of an RTL toggle.
 */
.br-app .br-toggle-fill {
  position: absolute; inset-block: 0; inset-inline-start: 0;
  inline-size: 50%; background: var(--br-plum);
  transition: transform .34s var(--br-ease);
}
.br-app .br-toggle[data-plan="single"] .br-toggle-fill { transform: translateX(100%); }
.br-app[dir="rtl"] .br-toggle[data-plan="single"] .br-toggle-fill { transform: translateX(-100%); }

.br-app .br-toggle button {
  position: relative; z-index: 1;
  flex: 1; min-block-size: var(--br-tap); border: 0; background: transparent;
  font-family: var(--br-font-body); font-size: var(--br-t-sm); font-weight: 400;
  color: var(--br-plum); cursor: pointer; padding: var(--br-s-3) var(--br-s-2);
  line-height: 1.35;
  transition: color .34s var(--br-ease);
}
/* No background here any more — the fill behind provides it. Only the label
   colour changes, and it crossfades over the same duration as the slide. */
.br-app .br-toggle button[aria-pressed="true"] {
  color: var(--br-on-plum); font-weight: 500;
}
.br-app .br-toggle button:hover,
.br-app .br-toggle button:focus-visible { color: var(--br-plum); }
.br-app .br-toggle button[aria-pressed="true"]:hover,
.br-app .br-toggle button[aria-pressed="true"]:focus-visible { color: var(--br-on-plum); }

/* --- what the chosen plan includes --------------------------------------- */
.br-app .br-perks {
  list-style: none; margin: 0 0 var(--br-s-5); padding: 0;
  display: flex; flex-direction: column; gap: var(--br-s-2);
}
.br-app .br-perks li {
  display: flex; align-items: flex-start; gap: var(--br-s-3);
  font-size: var(--br-t-sm); line-height: 1.5; color: var(--br-ink-muted);
}
/* A drawn mark rather than a list bullet, so it keeps the plum and sits on the
   first line of a perk that wraps. */
.br-app .br-perks li::before {
  content: ""; flex: none; inline-size: 13px; block-size: 13px;
  margin-block-start: 3px; background: var(--br-plum);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-6' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-6' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
}

.br-app .br-field {
  display: flex; flex-direction: column; gap: var(--br-s-2);
  margin-block-end: var(--br-s-4);
}
.br-app .br-field label {
  font-family: var(--br-font-title); font-size: var(--br-t-xs); font-weight: 500;
  letter-spacing: var(--br-track-label); text-transform: uppercase;
  color: var(--br-ink-muted);
}
.br-app .br-field input,
.br-app .br-field select {
  min-block-size: var(--br-tap); inline-size: 100%;
  padding: var(--br-s-3) var(--br-s-4);
  border: 1px solid var(--br-line); border-radius: var(--br-radius);
  background: var(--br-ground); color: var(--br-ink);
  font-family: var(--br-font-body); font-size: var(--br-t-base); font-weight: 300;
  transition: border-color var(--br-dur) var(--br-ease);
}
.br-app .br-field input::placeholder { color: var(--br-ink-faint); }
.br-app .br-field input:focus,
.br-app .br-field select:focus {
  outline: none; border-color: var(--br-plum); box-shadow: var(--br-shadow-sel);
}
/* Phone numbers and emails are LTR content even inside an RTL form, or
   "+966 5x…" renders reversed. Found by testing dir="rtl" in Phase 01. */
.br-app .br-field input[type="tel"],
.br-app .br-field input[type="email"] { direction: ltr; text-align: start; }

.br-app .br-field.has-error input,
.br-app .br-field.has-error select { border-color: var(--br-error); }
.br-app .br-error {
  font-size: var(--br-t-sm); color: var(--br-error); line-height: 1.45;
}

.br-app .br-consent {
  display: flex; gap: var(--br-s-3); align-items: flex-start;
  font-size: var(--br-t-sm); color: var(--br-ink-muted); line-height: 1.55;
  margin-block-start: var(--br-s-2); cursor: pointer;
}
.br-app .br-consent input[type="checkbox"] {
  appearance: none; inline-size: 20px; block-size: 20px; flex: none; margin: 2px 0 0;
  border: 1px solid var(--br-plum); border-radius: 5px; background: transparent;
  cursor: pointer;
}
.br-app .br-consent input[type="checkbox"]:checked {
  background: var(--br-plum);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-6' fill='none' stroke='%23F0EAE1' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-size: 15px; background-position: center; background-repeat: no-repeat;
}
.br-app .br-consent a { color: var(--br-plum); text-underline-offset: 3px; }

/* --- success ------------------------------------------------------------- */
/*
 * The claim box carries a photograph of the physical card, so the thing on
 * screen and the thing they will be handed at the counter are visibly the same
 * object. The file is already blurred and darkened towards the plum (see
 * tools/build_backgrounds.py) rather than being dimmed by an overlay here,
 * because the email copy of this box cannot composite anything and the two
 * have to look alike.
 *
 * background-color is the same plum, so the box still reads correctly in the
 * moment before the image loads, and if it never loads at all.
 */
.br-app .br-code-card {
  background-color: #2A1A28;
  background-image: url("../img/claim-bg.jpg");
  background-size: cover;
  background-position: center;
  border: 1px solid rgba(225, 217, 206, .18);
  border-radius: var(--br-radius-lg); padding: var(--br-s-6) var(--br-s-5);
  text-align: center; margin-block-start: var(--br-s-5);
}
/* Everything inside inverts: the box is now the dark element on a light card,
   rather than the light element on a light card it used to be. */
.br-app .br-code-card .br-eyebrow,
.br-app .br-code-card .br-fine { color: rgba(240, 234, 225, .72); }
.br-app .br-code-card .br-body { color: var(--br-on-plum); }
/* .br-fine b further down is inked dark for the light card, and comes after
   this rule in the file. Inside the box that turned "founding card serial"
   into dark brown on near-black, which is where the one instruction on this
   screen lives — so this has to out-specify it, not merely follow it. */
.br-app .br-code-card p b { color: var(--br-cream); }
.br-app .br-code-card .br-btn--ghost {
  border-color: rgba(240, 234, 225, .45); color: var(--br-on-plum);
}
.br-app .br-code-card .br-btn--ghost:hover:not(:disabled),
.br-app .br-code-card .br-btn--ghost:focus-visible {
  background: rgba(240, 234, 225, .12); color: var(--br-on-plum);
}
.br-app .br-code-card :focus-visible { outline-color: var(--br-cream); }

.br-app .br-code {
  font-family: var(--br-font-display); font-weight: 500;
  font-size: clamp(22px, 7.4vw, 30px);
  letter-spacing: var(--br-track-code); color: var(--br-plum);
  margin: var(--br-s-3) 0; font-variant-numeric: tabular-nums;
  white-space: nowrap; direction: ltr;
}
.br-app .br-code-card .br-code { color: var(--br-cream); }
@media (max-width: 360px) { .br-app .br-code { letter-spacing: .03em; } }
.br-app .br-fine {
  font-size: var(--br-t-sm); line-height: 1.65; color: var(--br-ink-muted);
  text-align: center; margin: var(--br-s-5) 0 0;
}
.br-app .br-fine b { color: var(--br-ink); font-weight: 500; }

/* --- state --------------------------------------------------------------- */
.br-app .br-step[hidden] { display: none; }
/*
 * Busy state.
 *
 * busy() puts this class on .br-app itself, so the selector has to be
 * .br-app.br-busy. It was written as a descendant selector, which never
 * matched anything — the app has been going to the server with no visible
 * response at all since the class was added.
 */
.br-app.br-busy { pointer-events: none; }
/* filter, not opacity: the card's entrance animation owns the opacity property
   and, filling forwards, would beat any static rule set on it. */
.br-app.br-busy .br-screen { filter: opacity(.75); }

/*
 * The button that was pressed becomes the spinner, rather than dimming the
 * whole card and leaving people wondering whether the tap registered.
 *
 * Keyed off a class the script puts on the button it actually sent, not off
 * "the one that is not a ghost". That guess held only while every screen had a
 * single primary button, and broke as soon as one grew a second: the press went
 * to one button and the spinner appeared in the other.
 */
.br-app.br-busy .br-btn.is-busy {
  color: transparent;
  position: relative;
}
.br-app.br-busy .br-btn.is-busy::after {
  content: '';
  position: absolute;
  inline-size: 18px; block-size: 18px;
  border: 2px solid rgba(240, 234, 225, .35);
  border-block-start-color: var(--br-on-plum);
  border-radius: 50%;
  animation: br-spin .6s linear infinite;
}
/* A ghost button is plum on the card rather than cream on plum, so its spinner
   has to be too or it is invisible against its own background. */
.br-app.br-busy .br-btn--ghost.is-busy::after {
  border-color: rgba(80, 55, 77, .28);
  border-block-start-color: var(--br-plum);
}
.br-app.br-busy .br-code-card .br-btn--ghost.is-busy::after {
  border-color: rgba(240, 234, 225, .35);
  border-block-start-color: var(--br-on-plum);
}

.br-app .br-formerror {
  background: var(--br-ground); border: 1px solid var(--br-error);
  border-radius: var(--br-radius); padding: var(--br-s-3) var(--br-s-4);
  color: var(--br-error); font-size: var(--br-t-sm); margin-block-end: var(--br-s-4);
}

.br-app :focus-visible { outline: 2px solid var(--br-plum); outline-offset: 3px; }

/* A quiz cannot work without JavaScript; say so rather than showing a
   dead card. */
.br-app .br-noscript {
  text-align: center; color: var(--br-ink-muted); font-size: var(--br-t-sm);
}

@media (prefers-reduced-motion: reduce) {
  .br-app * { transition: none !important; animation: none !important; }
}

/* ==========================================================================
   Counter lookup tool — the barista-facing screen.
   Same tokens, but tuned for one-handed use on a phone behind a counter:
   larger tap targets, a denser fact list, and no decorative flourish.
   ========================================================================== */

.br-app--staff .br-screen { max-inline-size: 420px; }

.br-app--staff .br-top { border-block-end: 1px solid var(--br-line); padding-block-end: var(--br-s-3); }
.br-app--staff .br-top .br-lang { text-transform: uppercase; letter-spacing: var(--br-track-label); }

.br-app--staff .br-title {
  font-size: var(--br-t-lg); margin-block-start: var(--br-s-1);
}

/* Reference code above the name — it is what was just read aloud. */
.br-app--staff .br-eyebrow {
  font-family: var(--br-font-title); letter-spacing: var(--br-track-code);
  color: var(--br-plum); font-size: var(--br-t-sm);
}

.br-app .br-facts {
  display: grid; grid-template-columns: auto 1fr; gap: var(--br-s-2) var(--br-s-5);
  margin: var(--br-s-6) 0 0; padding: var(--br-s-5) 0 0;
  border-block-start: 1px solid var(--br-line);
  font-size: var(--br-t-base);
}
.br-app .br-facts dt {
  font-family: var(--br-font-title); font-size: var(--br-t-xs); font-weight: 500;
  letter-spacing: var(--br-track-label); text-transform: uppercase;
  color: var(--br-ink-muted); padding-block-start: 3px;
}
.br-app .br-facts dd { margin: 0; color: var(--br-ink); }
.br-app .br-ltr { direction: ltr; unicode-bidi: isolate; }

.br-app .br-card-linked {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--br-s-4); margin-block-start: var(--br-s-6);
  padding: var(--br-s-4) var(--br-s-5);
  background: var(--br-ground); border: 1px solid var(--br-line);
  border-radius: var(--br-radius);
}
.br-app .br-serial {
  font-family: var(--br-font-display); font-size: var(--br-t-xl);
  color: var(--br-plum); font-weight: 500; font-variant-numeric: tabular-nums;
  direction: ltr;
}

/* A status line, not a control. It previously carried the same border and
   radius as .br-btn and read as a second button to press. */
.br-app .br-ok {
  display: flex; align-items: center; justify-content: center; gap: var(--br-s-2);
  background: none; border: 0; padding: var(--br-s-3) 0 0;
  color: var(--br-ink-muted); font-size: var(--br-t-sm);
  margin-block-start: var(--br-s-4); text-align: center;
}
.br-app .br-ok::before {
  content: ""; inline-size: 14px; block-size: 14px; flex: none;
  background: var(--br-plum);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-6' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-6' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
}

/* --- membership state ----------------------------------------------------- */
/*
 * Three states, three readings, and the counter has to tell them apart from
 * arm's length while holding a phone in one hand. Colour alone would not do
 * it — the words carry the meaning and the tint only reinforces it, which is
 * also what keeps this legible to anyone who cannot separate the two greens.
 */
.br-app .br-pill {
  display: inline-block; padding: 3px var(--br-s-3);
  border: 1px solid var(--br-line-strong); border-radius: 999px;
  font-family: var(--br-font-title); font-size: var(--br-t-xs); font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; white-space: nowrap;
  color: var(--br-ink); background: var(--br-surface);
}
.br-app .br-pill--active {
  color: #1F5B33; background: #E7F1E9; border-color: #B7D3BF;
}
.br-app .br-pill--trial {
  color: var(--br-plum); background: #EFE7ED; border-color: var(--br-mauve);
}
.br-app .br-pill--trial_ended {
  color: var(--br-error); background: #F6E9E7; border-color: #D9B3AE;
}

/* The days-left line on the activation page, under the heading. */
.br-app .br-state {
  font-family: var(--br-font-title); font-size: var(--br-t-xs); font-weight: 500;
  letter-spacing: var(--br-track-label); text-transform: uppercase;
  color: var(--br-plum); margin-block: var(--br-s-3) 0;
}

/*
 * The payment QR on the counter tool is turned round to face a customer
 * across a counter, so it is bigger than the one on the success screen and
 * centred in the card rather than tucked beside a column of text.
 */
.br-app .br-pay-qr {
  margin-block-start: var(--br-s-6); padding-block-start: var(--br-s-6);
  border-block-start: 1px solid var(--br-line);
}
.br-app .br-pay-qr .br-qr { inline-size: 190px; }
.br-app .br-pay-qr .br-qr-hint { max-inline-size: 240px; }

/* The card-linking form sat directly against the fact list above it. */
.br-app--staff .br-facts + form,
.br-app--staff .br-ok + form,
.br-app--staff .br-pay-qr + form { margin-block-start: var(--br-s-6); }

/* The locked state has no .br-top, so its heading sat directly on the first
   field label. Give any form following a heading room to breathe. */
.br-app--staff .br-eyebrow + form,
.br-app--staff .br-title + form,
.br-app--staff .br-eyebrow + .br-noscript { margin-block-start: var(--br-s-6); }
.br-app--staff form .br-btn { margin-block-start: var(--br-s-5); }

/* ==========================================================================
   Language toggle — EN | عربي
   Each label is written in its own script, so a reader recognises their own
   language without knowing the other one.
   ========================================================================== */

.br-app .br-lang {
  display: flex; align-items: center; gap: var(--br-s-1);
  font-family: var(--br-font-title);
}
.br-app .br-lang-btn {
  font: inherit; font-size: var(--br-t-xs); font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase;
  background: none; border: 0; cursor: pointer;
  color: var(--br-ink-faint);
  padding: var(--br-s-1) var(--br-s-2);
  min-block-size: 32px;                 /* comfortable tap without bulking the bar */
  border-radius: 6px;
  transition: color var(--br-dur) var(--br-ease);
}
.br-app .br-lang-btn:hover { color: var(--br-ink-muted); }
.br-app .br-lang-btn.is-on { color: var(--br-plum); }

/* Arabic is not uppercased and never letter-spaced — both mangle the script. */
.br-app .br-lang-btn[lang="ar"] {
  font-family: var(--br-font-arabic);
  text-transform: none; letter-spacing: 0; font-size: var(--br-t-sm);
}
.br-app .br-lang-sep { color: var(--br-line-strong); font-size: var(--br-t-xs); }

/* In Arabic the body font changes but the ritual NAMES stay Latin brand marks,
   so they keep the Latin display face rather than inheriting the Naskh. */
.br-app[dir="rtl"] .br-ritual-name,
.br-app[dir="rtl"] .br-code {
  font-family: 'ITC Garamond', 'Newsreader', Georgia, serif;
  letter-spacing: .06em;
}

/* Arabic sets a touch looser — Naskh needs the leading. */
.br-app[dir="rtl"] { line-height: 1.85; }
.br-app[dir="rtl"] .br-question { font-style: normal; }
.br-app[dir="rtl"] .br-ritual-meta { font-style: normal; }

/* --------------------------------------------------------------------------
   Arabic type sizing.

   The Latin UI leans on 11-13px uppercase with wide tracking — a register
   Arabic does not have. Naskh has no capitals, so text-transform does nothing
   but the small sizes make it genuinely hard to read, and letter-spacing
   breaks the joins between characters. Size up, drop the transform.
   -------------------------------------------------------------------------- */

.br-app[dir="rtl"] {
  --br-t-xs: 13px;
  --br-t-sm: 15px;
}

.br-app[dir="rtl"] .br-btn,
.br-app[dir="rtl"] .br-eyebrow,
.br-app[dir="rtl"] .br-sub,
.br-app[dir="rtl"] .br-field label,
.br-app[dir="rtl"] .br-facts dt,
.br-app[dir="rtl"] .br-toggle button {
  text-transform: none;
  letter-spacing: 0;
}

.br-app[dir="rtl"] .br-btn { font-size: var(--br-t-base); font-weight: 400; }
.br-app[dir="rtl"] .br-field label,
.br-app[dir="rtl"] .br-facts dt { font-weight: 400; }

/* --------------------------------------------------------------------------
   Programmatic focus target.

   Each new view moves focus to its heading so screen readers announce the
   change. That heading is not interactive and the visitor did not Tab to it,
   so it must not draw the keyboard focus ring — which otherwise appeared as a
   box around the question and came back on every answer.

   Keyboard users are unaffected: tabindex="-1" is not reachable by Tab.
   -------------------------------------------------------------------------- */
.br-app .br-focus-target:focus,
.br-app .br-focus-target:focus-visible { outline: none; }

/* --- motion ---------------------------------------------------------------
 *
 * Deliberately small. This is a form someone fills in once at a counter or on
 * the sofa; movement is here to show that a screen changed and that a tap
 * landed, not to be noticed. Everything below is switched off wholesale for
 * anyone who has asked their system for reduced motion.
 */
@keyframes br-spin { to { transform: rotate(360deg); } }

@keyframes br-rise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* Each screen fades up as it replaces the last.
 *
 * Scoped to .br-enter, which the script adds only when the view genuinely
 * changed. Without that gate every re-render replayed this: choosing an answer
 * rebuilds the card, so the whole thing flashed on each tap.
 */
.br-app .br-screen.br-enter { animation: br-rise .32s var(--br-ease) both; }

/* Options arrive a beat after the question, and stagger, so the eye is led
   down the list instead of having six rows appear at once. */
@keyframes br-fade {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.br-app .br-enter .br-option { animation: br-fade .3s var(--br-ease) both; }
.br-app .br-enter .br-option:nth-child(1) { animation-delay: .04s; }
.br-app .br-enter .br-option:nth-child(2) { animation-delay: .08s; }
.br-app .br-enter .br-option:nth-child(3) { animation-delay: .12s; }
.br-app .br-enter .br-option:nth-child(4) { animation-delay: .16s; }
.br-app .br-enter .br-option:nth-child(5) { animation-delay: .20s; }
.br-app .br-enter .br-option:nth-child(6) { animation-delay: .24s; }

/* A tap should feel like it pressed something. */
.br-app .br-option:active { transform: scale(.988); }
.br-app .br-btn:active:not(:disabled) { transform: translateY(1px); }
.br-app .br-btn { transition: background var(--br-dur) var(--br-ease),
                              border-color var(--br-dur) var(--br-ease),
                              transform .08s var(--br-ease); }

/* The reveal is the one moment worth a little ceremony: the ritual name is the
   answer the whole quiz was building to. */
@keyframes br-reveal {
  from { opacity: 0; transform: scale(.94); letter-spacing: .28em; }
  to   { opacity: 1; transform: none; }
}
.br-app .br-enter .br-ritual-name { animation: br-reveal .6s var(--br-ease) both; }

/* The perk list is replaced wholesale when the plan changes, so it fades in
   to tie the new text to the fill that just slid under the other half. */
.br-app .br-perks.br-perks--in { animation: br-fade .28s var(--br-ease) both; }

/* And the code, once it exists, is what they came for. */
.br-app .br-enter .br-code { animation: br-rise .45s var(--br-ease) .1s both; }
.br-app .br-enter .br-qr   { animation: br-rise .45s var(--br-ease) .2s both; }

@media (prefers-reduced-motion: reduce) {
  .br-app *,
  .br-app *::before,
  .br-app *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  /* The spinner still needs to turn, or a busy button looks broken. */
  .br-app.br-busy .br-btn.is-busy::after {
    animation-duration: .8s !important;
    animation-iteration-count: infinite !important;
  }
  .br-app .br-option:active,
  .br-app .br-btn:active:not(:disabled) { transform: none; }
}
