/* /give/ — the giving page (round 5, 2026-08-21).
   ---------------------------------------------------------------------------
   DIRECTION: a giving card, not a form. ONE dominant amount at the head of the
   card, compact wallet marks in a single quiet row underneath, and everything
   else — the presets, the monthly line, the card fold — whisper-quiet. The
   parchment canvas of the hub is kept exactly as it is.

   What round 5 changed, and why (Daniel, 2026-08-21, from his own iPhone: "not
   a fan" of the three big stacked wallet pills, and of the page overall):

   1. THE THREE FULL-WIDTH WALLET BARS ARE ONE ROW OF MARKS. Three 56px-tall
      buttons stacked full-width were 188px of black in the middle of a giving
      page — the loudest thing on it, and the amount was not. They are now three
      48px marks side by side in a single row: 48px is the height Square's own
      Cash App button already IS at size `medium`, so the row is the buttons'
      natural height rather than a height we painted around them.

   2. THE AMOUNT IS THE DOMINANT THING. It was a 34px figure inside one of five
      72px tiles — the same size as the four amounts nobody picked. It is now a
      single ~66px figure at the head of the card with the presets demoted to a
      quiet row of pills beneath it. This also closes a real hole: with the
      wallets leading and the card folded away, the page never said out loud
      what a tap on Apple Pay was about to charge. Now it does, in the largest
      type on the page, and the line under it says one-time or every month.

   3. THE PAGE GOT ITS TOP BACK. The head band and the section under it carried
      the site's full marketing rhythm (up to 104px of padding each), which put
      the first wallet button 809px down a 844px screen. Both are tightened HERE
      rather than in styles.css, so no other page moves.

   Everything structural — .section, .container, .eyebrow, .btn, .field,
   .form-status — still comes from styles.css. */

/* ── the page's own rhythm ───────────────────────────────────────────────────
   A giving page is not a marketing section: a visitor who tapped Give has
   decided, and every pixel above the amount is in the way. These two overrides
   are scoped to this page's ids/section and never leave it. */
/* ROUND 11 (2026-08-28) TOOK ANOTHER BITE OUT OF THE TOP, for the same reason
   round 5 took the first one: on a browser that draws NO wallet the card boxes
   are the only tender, they open themselves, and the Give button under them was
   landing 67px below a 390x844 screen. The h1's own inherited 12px top margin
   was doing nothing here — the band's padding is what separates it from the
   header — so it goes, and both padding steps come in one notch. The type size
   is untouched. */
#give-intro { padding-block: clamp(20px, 5vw, 36px) clamp(16px, 3vw, 24px); }
#give-intro h1 {
  font-size: clamp(2.05rem, 8.5vi, 3rem);
  margin-top: 0;
  margin-bottom: 6px;
}
#give-intro .lede { margin-bottom: 0; max-width: 34ch; }
main .section.on-paper { padding-block: clamp(18px, 2.5vw, 28px) clamp(44px, 5vw, 72px); }

/* ── the giving card ─────────────────────────────────────────────────────────
   Centred in its container, the same as /qa/'s ask-card: on a wide screen a
   560px column pinned to the left gutter reads as a form that failed to lay
   out, not as a decision.

   16px of padding at phone width, not 24. That is not a cosmetic trim: the
   wallet row below needs 3 × 98px + 2 × 6px = 306px of inner width to keep
   three marks on one line, and at 390px this padding leaves 316px — ten pixels
   of slack, which is what stops a one-pixel difference in a real browser from
   silently dropping the third mark onto its own line. Every number in this file
   that looks arbitrary is that sum. */
.give-form, .give-done, .give-manage, .give-outro {
  max-width: 560px;
  margin-inline: auto;
}
.give-form {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 16px;
  box-shadow: var(--shadow-control);
  container-type: inline-size;
}
@media (min-width: 560px) {
  .give-form { padding: 30px; }
}
.give-form .btn { width: 100%; justify-content: center; min-height: 52px; }
.give-form .give-total { font-variant-numeric: tabular-nums; }

/* ── THE AMOUNT: the one bold thing on the page ──────────────────────────────
   The fieldset is now a head (the figure) and a rail (the presets). The legend
   still exists for the grouping a screen reader needs, but it is not drawn: a
   66px "$25" with five pills under it does not need the word AMOUNT over it,
   and the line it saves is a line closer to the buttons. */
.amount-grid {
  display: block;
  margin: 0 0 18px;
  padding: 0;
  border: 0;
  min-inline-size: 0; /* a fieldset refuses to shrink below its content without this */
}
.amount-grid legend {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.amount-head {
  text-align: center;
  padding: 2px 0 14px;
}
/* The figure and its currency mark. The $ is deliberately small and gold and
   sits at the top of the number's own cap height — the house rule is the value
   dominant with the currency tucked in its corner, not two things the same
   size shouting at each other. */
.amount-display {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 3px;
  margin: 0;
  line-height: 1;
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.amount-display .cur {
  font-size: clamp(1.35rem, 5vi, 1.85rem);
  color: var(--gold-text);
  margin-top: 0.34em;
  letter-spacing: 0;
}
.amount-display .fig {
  position: relative;
  display: inline-block;
  font-size: clamp(3.3rem, 16.5vi, 4.75rem);
  letter-spacing: -0.045em;
}
/* THE FIGURE IS THE FIELD (2026-09-12). The head's .fig is an <input> now: same
   face, same size, same place, and its width follows its own digits (give.js
   writes it in ch, tabular figures make ch exact). The only thing that says
   "you can type here" is the caret and a hairline that turns gold under the
   number while it has focus; nothing else on the card moves. */
.amount-display input.fig {
  appearance: none;
  -webkit-appearance: none;
  width: 2ch;
  min-width: 1ch;
  max-width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  outline: none;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: clamp(3.3rem, 16.5vi, 4.75rem);
  letter-spacing: -0.045em;
  line-height: 1;
  /* An input takes its own normal line-height unless told its height; 1em is
     what the figure was as a span, so the card is the same height it was
     (give-fold-check at 375x667 caught the 10px it grew). */
  box-sizing: content-box;
  height: 1em;
  text-align: center;
  caret-color: var(--accent-deep);
  font-variant-numeric: tabular-nums;
  transition: border-color var(--t-fast) var(--ease);
  min-height: 44px;
}
.amount-display input.fig:focus { border-bottom-color: var(--accent-deep); }
.amount-display input.fig::selection { background: rgba(236, 195, 92, 0.45); }
.amount-display input.fig[aria-invalid="true"] { color: #a33; }
.amount-display input.fig.is-changing { animation: give-amount-in 160ms var(--ease); }
/* A preset tap hands the old figure upward while the chosen figure settles
   into the same reserved box. The custom input never receives this class. */
.amount-display .fig.is-changing { animation: give-amount-in 160ms var(--ease); }
.amount-display .fig.is-changing::after {
  content: attr(data-previous);
  position: absolute;
  inset: 0;
  color: var(--ink);
  pointer-events: none;
  animation: give-amount-out 160ms var(--ease) both;
}
@keyframes give-amount-in {
  from { opacity: 0.35; transform: translateY(4px); }
}
@keyframes give-amount-out {
  from { opacity: 1; transform: translateY(0); }
  to { opacity: 0; transform: translateY(-4px); }
}
/* Nothing picked yet (an empty Other box): a dash, never a blank hole where the
   biggest thing on the page was. */
.amount-display.is-blank .fig { color: var(--steel); opacity: 0.45; }

/* One line under the figure, EMPTY until the figure is not a gift this page
   takes. Body voice, not the mono label voice: it is a sentence to read. */
.amount-range {
  margin: 0;
  min-height: 0;
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.4;
  color: #a33;
  font-weight: 600;
}
.amount-range:not(:empty) { margin-top: 6px; }

.amount-cadence {
  margin: 10px 0 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--steel);
}


/* ── the presets ─────────────────────────────────────────────────────────────
   Five pills on ONE row, demoted on purpose. They are the input; the figure
   above is the value, and the house rule is that the value wins. 44px tall
   clears the tap floor and (390 − 40 container − 36 card − 24 gaps) ÷ 5 gives
   each pill 58px, which holds "$100" at 15px with room to spare. */
.amount-chips {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}
/* The radio is the accessible control and the pill IS the tap target. The input
   is clipped rather than stretched over the pill: stretched, it and the pill
   take turns intercepting the pointer depending on stacking (WebKit refused to
   resolve which one a tap belonged to, 2026-08-21), and display:none would take
   the control out of the tab order. Clipped-but-rendered keeps focus, keyboard
   arrows and the checked state, and the ring is drawn on the pill through
   `:focus-visible + span`. */
/* The picks are plain <button>s now (2026-09-12): no hidden radio, no Other. */
.amount-opt {
  appearance: none;
  -webkit-appearance: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 44px;
  margin: 0;
  padding: 8px 2px;
  cursor: pointer;
  touch-action: manipulation;
  border: 1px solid var(--control-line);
  border-radius: 999px;
  background: var(--control-bg);
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1;
  white-space: nowrap;
  transition: border-color var(--t-fast) var(--ease),
              background-color var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease),
              box-shadow var(--t-med) var(--ease),
              transform var(--t-med) var(--ease);
}
/* "Other" is a word, not a figure — the body face, so it never competes with
   the four numbers beside it. */

@media (min-width: 420px) {
  .amount-chips { gap: 8px; }
  .amount-opt { font-size: 16.5px; min-height: 46px; }
}
@media (any-hover: hover) {
  .amount-opt:hover { border-color: var(--gold-ring); }
}
.amount-opt:active { transform: scale(0.96); transition-duration: var(--press-down); }
/* ACTIVE = TWO changes, not one: the brass fill AND the ring, so the choice is
   never carried by hue alone. */
.amount-opt[aria-pressed="true"] {
  border-color: var(--accent-deep);
  box-shadow: inset 0 0 0 1px var(--accent-deep), var(--shadow-accent-soft);
  background: linear-gradient(180deg, rgba(236, 195, 92, 0.34), rgba(201, 154, 46, 0.18));
}
.amount-opt:focus-visible {
  outline: 2px solid var(--gold-ring);
  outline-offset: 3px;
}

/* ── the monthly line ────────────────────────────────────────────────────────
   A checkbox row, not a segmented control: monthly is a modifier on the amount
   above, not a second thing to choose. The whole row is the target.

   The meta under it is now three words. It was "SAME DAY EACH MONTH · STOP ANY
   TIME", which is 36 tracked mono characters — it wrapped to two lines at 390px
   and read as debris under the heading it belonged to. "Stop any time" moved to
   the cadence line under the figure, where it appears only once monthly is
   actually on, which is the only moment it answers anything. */
.monthly-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 10px 14px;
  margin: 0 0 18px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--control-bg);
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
}
.monthly-row:has(input:checked) {
  border-color: var(--accent-deep);
  background: linear-gradient(180deg, rgba(236, 195, 92, 0.18), transparent);
}
.monthly-row:active { transition-duration: var(--press-down); }
@media (any-hover: hover) {
  .monthly-row:hover { border-color: var(--gold-ring); }
}
/* A 44px CONTROL WITH A 22px BOX IN IT. A native checkbox refuses to be bigger
   than its own glyph — width, height and padding are all ignored by the UA
   (measured in both WebKit and Chromium, 2026-08-21), so `accent-color` on a
   native box gave a 22x22 tap target and the mobile gate failed it. Dropping
   `appearance` makes the input an ordinary element again: it can be 44x44 with
   the 22px box drawn in the middle by ::before, which is what the eye sees and
   the finger gets. */
.monthly-row input {
  appearance: none;
  -webkit-appearance: none;
  width: 44px;
  height: 44px;
  margin: 0 -11px;
  flex: none;
  display: grid;
  place-content: center;
  border: 0;
  background: none;
  cursor: pointer;
}
.monthly-row input::before {
  content: "";
  width: 22px;
  height: 22px;
  border-radius: 6px;
  border: 1.5px solid var(--control-line);
  background: var(--paper) no-repeat center;
  background-size: 14px 14px;
  transition: background-color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.monthly-row input:checked::before {
  border-color: var(--accent-deep);
  background-color: var(--accent-deep);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.5l3.2 3.2L13 4.8'/%3E%3C/svg%3E");
}
.monthly-row input:focus-visible { outline: 2px solid var(--gold-ring); outline-offset: -8px; border-radius: 14px; }
.monthly-row .monthly-text { display: block; }
.monthly-row strong {
  display: block;
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
}
.monthly-row .mono-meta {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--steel);
}

/* ── receipt identity ─────────────────────────────────────────────────────── */
.receipt-identity {
  margin: 14px 0;
  padding: 13px 14px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-input);
  background: var(--control-bg);
}
.receipt-identity[hidden] { display: none; }
.receipt-status {
  margin: 0;
  color: #a33;
  font-size: 16px;
  line-height: 1.45;
}
.receipt-status:not(:empty) { margin-bottom: 10px; }
.receipt-fields { display: grid; gap: 10px; }
/* The group label. Mono, because it labels a group — and it is the only line in
   this block, so it is not competing with a second one. */
.receipt-lede {
  grid-column: 1 / -1;
  margin: 0 0 2px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--steel);
}
/* "optional" rides on the label rather than sitting in the box as placeholder
   text, which disappears the moment somebody starts typing. */
.field-optional {
  margin-inline-start: 6px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--steel);
}
.receipt-fields[hidden] { display: none; }
.receipt-fields .field { margin: 0; }
@media (min-width: 560px) {
  .receipt-fields { grid-template-columns: 1fr 1fr; gap: 12px; }
}
.anonymous-toggle {
  appearance: none;
  min-height: 44px;
  margin: 1px 0 0;
  padding: 6px 0;
  border: 0;
  background: transparent;
  color: var(--gold-text);
  font: 600 14px/1.4 var(--font-body);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.anonymous-toggle:focus-visible { outline: 2px solid var(--gold-ring); outline-offset: 2px; border-radius: 6px; }
/* NO RESERVED HOLE (2026-08-23). This reserved 22px under Name and Email
   whether or not anything was wrong, which is 44px of blank card in the state
   every giver who types correctly is in — and the block is already the tallest
   thing between the amount and the buttons. It takes its space when it has
   something to say, the way .wallet-reason two blocks down already does. The
   error is still announced by the same aria-live span; only the empty box goes. */
.field-error {
  display: block;
  color: #a33;
  font-size: 14px;
  line-height: 20px;
}
.field-error:not(:empty) { padding-top: 2px; }
.receipt-fields input[aria-invalid="true"] { border-color: #a33; }

/* ── the pay block ───────────────────────────────────────────────────────────
   A hairline is what separates "how much" from "how" — a heading would be a
   fourth voice on a card that is already a figure, a row of pills and three
   brand marks. */
.pay-block {
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

/* ── the wallets ─────────────────────────────────────────────────────────────
   ONE ROW OF MARKS (Daniel, round 5). Three full-width 56px bars stacked down
   the middle of the card were the loudest thing on a giving page; three 48px
   marks side by side are a quiet row that still clears the 44px tap floor with
   room over it.

   Three fixed columns are the product rule. At 390px each is 98px wide; at
   375px each is 93px wide, while all three retain the same 48px height. An SDK
   never removes a column: it either replaces the muted mark in place or leaves
   that named fallback visible. Apple Pay is the special case because Square
   attaches no child for it; the page owns that live button and toggles the
   `hidden` attribute only as a state marker. */
/* THREE COLUMNS, FIXED, FOREVER (Daniel, 2026-08-22 — his third report of a
   wallet moving on him). This was `flex-wrap` with `flex: 1 1 98px` slots, so
   the row's shape was a function of how many wallets answered: three marks on an
   iPhone, two in Chrome, one in desktop Firefox, each survivor growing to eat
   the space the missing one left. The mark under his thumb was never twice in
   the same place. `repeat(3, 1fr)` makes the geometry a constant — the columns
   exist before the SDKs answer and do not move when they do. */
.wallet-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  margin: 0;
}
/* THE ROW IS AS WIDE AS IT HAS SLOTS. Three columns is the default and, since
   round 9 (2026-08-24), the answer on every platform: nothing removes a slot
   any more, because a wallet the page does not mention reads as one the church
   does not take. give.js writes the count once, at parse time, so these two
   rules are a standing fallback rather than something that fires today — if a
   slot is ever dropped again, the survivors share the width instead of sitting
   beside a hole, and the count still cannot change under a thumb. */
.wallet-row[data-slots="2"] { grid-template-columns: repeat(2, 1fr); }
.wallet-row[data-slots="1"] { grid-template-columns: 1fr; }
/* A WALLET THAT COULD NOT ATTACH IS NOT ON THE ROW (Daniel 2026-09-12 12:48, on
   his iPhone in Safari: "I clicked Google Pay and it says it's not available in
   this browser… this needs to be fully cleaned up"). The muted, named mark that
   stood in for a failed wallet since 08-21 was the thing he tapped: a grey
   button that answers with a sentence is still a button that does not work.
   The row is decided ONCE, at the reveal, from what actually attached — the
   survivors share the width (data-slots above) — so nothing changes shape under
   a thumb, which is what the 08-21 rule was for. give.js writes the state and
   the count in the same frame. */
.wallet-row > .wallet-slot[data-wallet-state="failed"] { display: none !important; }
/* HIDDEN BY ATTRIBUTE, NOT BY EMPTINESS (round 7). This rule used to read
   `.wallet-row > div:empty`, and that was half of the flicker Daniel reported:
   a slot is momentarily EMPTY while Square rebuilds the Cash App button, so a
   rule keyed on emptiness deleted a third of the row for ~300ms and let the
   buttons beside it stretch into the gap and snap back. give.js now sets
   `hidden` while a wallet is unavailable or being rebuilt. CSS still paints
   that state in the same fixed column, so the row cannot change shape. */
/* A SLOT ITS SDK COULD NOT FILL KEEPS ITS COLUMN (round 8). This rule used to be
   `display: none`, which is what actually removed the mark from under Daniel.
   give.js still sets `hidden` at the end of its wallet probe and still means the
   same thing by it — "Square could not draw this one here" — nothing about that
   logic changed. What changed is what the page does with the answer: the column
   stays, and it draws a muted mark wearing the wallet's own name instead of a
   hole. Tapping it fills #wallet-reason with the one line underneath. */
/* A QUIET TILE, NOT A DASHED HOLE (2026-08-23). The muted mark was a dashed
   outline on the page's own background — the drop-zone idiom — sitting beside
   two solid black wallet buttons, so a slot Square simply could not fill read as
   a control that had failed to draw. It is now a filled tile on the same 12px
   silhouette as the live buttons: present, obviously not pressable, nothing
   broken about it. That also fixes the first paint, where ALL THREE slots are
   muted until the SDKs answer and the row opened as three dashed holes.

   AND THE LOCKUP STACKS. Side by side, "APPLE PAY" and "GOOGLE PAY" needed more
   inline room than a 375px column has (93px), so at iPhone SE width the label
   broke to two lines inside a 48px box and read as debris. Mark over name uses
   the height the tile already has and holds one line down to 320px. */
.wallet-row > .wallet-slot[hidden] {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 0 4px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--control-bg);
  color: var(--steel);
  font-family: var(--font-mono);
  /* 11px is the house floor for a label/eyebrow (tools/mobile-qa.js
     --label-font-min). The gate cannot see into a ::after, so this one is on us
     to keep honest rather than something a run would have caught. */
  font-size: 11px;
  line-height: 1.2;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  cursor: pointer;
}
.wallet-row > .wallet-slot[hidden]::before {
  content: "";
  flex: none;
  width: 17px;
  height: 17px;
  background: currentColor;
  opacity: 0.48;
  -webkit-mask: var(--wallet-mark) center / contain no-repeat;
  mask: var(--wallet-mark) center / contain no-repeat;
}
/* THE NAME IS THE ONE THING THIS TILE EXISTS TO SAY (2026-09-04, L-02). At
   0.72 the label measured 3.64:1 by pixel sampling — under the 4.5:1 floor for
   11px type, on the only text that tells the buyer WHICH wallet is missing.
   The muting that makes the tile read as not-pressable is the MARK above
   (still 0.48), the quiet fill and the absence of a black button; it was never
   this word's job. --steel on --control-bg is 7:1. */
.wallet-row > .wallet-slot[hidden]::after {
  content: attr(data-wallet);
  opacity: 1;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}
#apple-pay {
  --wallet-mark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701'/></svg>");
}
#cash-app-pay {
  --wallet-mark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M23.59 3.475a5.1 5.1 0 00-3.05-3.05c-1.31-.42-2.5-.42-4.92-.42H8.36c-2.4 0-3.61 0-4.9.4a5.1 5.1 0 00-3.05 3.06C0 4.765 0 5.965 0 8.365v7.27c0 2.41 0 3.6.4 4.9a5.1 5.1 0 003.05 3.05c1.3.41 2.5.41 4.9.41h7.28c2.41 0 3.61 0 4.9-.4a5.1 5.1 0 003.06-3.06c.41-1.3.41-2.5.41-4.9v-7.25c0-2.41 0-3.61-.41-4.91zm-6.17 4.63l-.93.93a.5.5 0 01-.67.01 5 5 0 00-3.22-1.18c-.97 0-1.94.32-1.94 1.21 0 .9 1.04 1.2 2.24 1.65 2.1.7 3.84 1.58 3.84 3.64 0 2.24-1.74 3.78-4.58 3.95l-.26 1.2a.49.49 0 01-.48.39H9.63l-.09-.01a.5.5 0 01-.38-.59l.28-1.27a6.54 6.54 0 01-2.88-1.57v-.01a.48.48 0 010-.68l1-.97a.49.49 0 01.67 0c.91.86 2.13 1.34 3.39 1.32 1.3 0 2.17-.55 2.17-1.42 0-.87-.88-1.1-2.54-1.72-1.76-.63-3.43-1.52-3.43-3.6 0-2.42 2.01-3.6 4.39-3.71l.25-1.23a.48.48 0 01.48-.38h1.78l.1.01c.26.06.43.31.37.57l-.27 1.37c.9.3 1.75.77 2.48 1.39l.02.02c.19.2.19.5 0 .68z'/></svg>");
}
#google-pay {
  --wallet-mark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z'/></svg>");
}
/* THE THIRD MARK IS ALWAYS THERE, AND IT IS NOT GREY (round 9, Daniel
   2026-08-24). Google Pay cannot attach under WebKit, so on his iPhone this
   slot is unfilled every single time — a permanent state, not a "not today".
   Round 8 painted it in the cream muted-tile treatment the other two wear, and
   beside a live black Apple Pay and a live black Cash App that one cream box
   read to him as the broken third of a row: a dead grey box. Deleting it was
   worse — then the page showed two marks and he asked where Google Pay went.

   So it keeps its column, and it wears the row's OWN silhouette: #000 at the
   same 12px corner and 48px height as the two live marks, mark and name in
   white. The row now reads as three marks of one set on every platform, which
   is the whole of his 08-21 rule.

   The honesty is in the behaviour, not in a grey: the slot is `hidden` to
   give.js, so it carries `aria-disabled` and the "is not available here" label
   the probe wrote, it never tokenizes, and one tap writes the line under the
   row saying what Google Pay needs and that the card does the same job. Scoped
   to this ID on purpose — Apple Pay and Cash App keep the cream tile, because
   for them an unfilled slot really is a "not in THIS browser". */
#google-pay[hidden] {
  border-color: #000;
  background: #000;
  color: #fff;
}
#google-pay[hidden]::before { opacity: 0.9; }
#google-pay[hidden]::after { opacity: 0.82; }

/* Belt and braces. give.js only ever hides a slot its SDK left EMPTY, so this
   should never have anything to do — but if a processor ever draws a button and
   is then withdrawn, the muted mark is what the column shows, not both at once
   spilling over each other. */
.wallet-row > .wallet-slot[hidden] > * { display: none; }
.wallet-row > .wallet-slot[hidden]:focus-visible {
  outline: 2px solid var(--gold-ring);
  outline-offset: 3px;
}
/* The line the muted marks write into. No margin and no content until one is
   tapped, so it cannot change the height of anything above it. */
.wallet-reason {
  margin: 0;
  color: var(--steel);
  font-size: 14px;
  line-height: 1.5;
}
.wallet-reason:not(:empty) { margin-top: 8px; }
/* FLEX WRAP, NOT `grid auto-fit` — and the difference is visible. auto-fit puts
   the wrapped third mark in column 1 of a new row at one-third width with an
   empty half beside it; a wrapping flex line stretches whatever is on it, so
   the third mark arrives full width and the row still reads as a designed
   block. Basis 98px is the floor; `flex-grow` shares out whatever is left. */
/* `min-width: 0` is the load-bearing half of this: a grid track refuses to shrink
   below its content's min-content width by default, so one wide button (Google's
   worded fallback) could otherwise push its column out and squeeze the other two.
   With it, the three columns stay exactly equal whatever lands inside them. */
.wallet-slot { height: 48px; min-height: 48px; min-width: 0; }

/* ── THE ROW MOUNTS AS ONE THING (2026-08-27, Daniel off his iPhone) ─────────
   "those different buttons like Cash App or whatever, they seem to be loading in
   and out … each one of those buttons gets individually loaded up, first it
   comes without a logo, then the logo loads up. It's a mess."

   The geometry was never the problem — three fixed 48px columns have been the
   rule since round 8 and they hold. What he is describing is the CONTENT of
   those columns changing three times each: our muted mark, then the processor's
   empty button, then the processor's artwork, each on its own frame, each column
   on its own schedule. Measured before this pass (WebKit, iPhone 13, 390x844):
   Google Pay's mark landed at 3.40s and Cash App's at 4.60s.

   `data-state` is written in the markup as "mounting" so this is the row's state
   from FIRST PAINT, not from whenever the deferred script runs; give.js flips it
   to "ready" once, in revealWallets(), when every wallet has settled AND painted.
   In between the row is three quiet tiles at their final size — no mark, no
   name, nothing to swap out, because a placeholder wearing the wallet's own logo
   is itself a logo that has to change. */
/* ── AND A COLUMN THAT HAS NOT ANSWERED KEEPS IT (2026-08-27, round 11) ──────
   Measured on a real iPhone 15 tonight: the row revealed at give.js's 8s ceiling
   with the Cash App column already wearing "Cash App is not available here", and
   Cash App attached at 13-16s and turned that column into a live button. The
   ceiling is a ceiling on the ROW, not a verdict on a WALLET — so give.js now
   tells the three states apart and writes `data-wallet-state` on each slot, and
   a pending column goes on wearing the tile it has worn since first paint.

   These are the SAME declarations the mounting row uses, in the same rule, on
   purpose: there is one skeleton on this page and a pending column is provably
   the same 48px box it was a frame earlier, because it is being drawn by the
   same lines. Nothing new is introduced visually — the only thing that changes
   at the reveal is that two of the three tiles stop being tiles.

   The `#apple-pay` / `#google-pay` arms are specificity, not taste: `#google-pay
   [hidden]` further up is (1,1,0) and paints that column solid black, so a class
   selector cannot take the skeleton back off it. */
.wallet-row[data-state="mounting"] > .wallet-slot,
.wallet-row[data-state="mounting"] > .wallet-slot[hidden],
.wallet-row[data-state="mounting"] > #apple-pay[hidden],
.wallet-row[data-state="mounting"] > #google-pay[hidden],
.wallet-row > .wallet-slot[data-wallet-state="pending"],
.wallet-row > #apple-pay[data-wallet-state="pending"],
.wallet-row > #google-pay[data-wallet-state="pending"] {
  display: block;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 12px;
  background-color: var(--control-bg);
  background-image: none;
  color: transparent;
  cursor: default;
  overflow: hidden;
  opacity: 0.55;
  -webkit-appearance: none;
  appearance: none;
}
/* NO MARK AND NO NAME ON A COLUMN THAT HAS NOT ANSWERED. This is the half that
   makes the pending tile silent: `::after` is what draws `attr(data-wallet)`, so
   without this the reveal would put the word "CASH APP" on a tile that is still
   waiting — which is quieter than the false sentence, but is still the page
   guessing out loud. */
.wallet-row[data-state="mounting"] > .wallet-slot::before,
.wallet-row[data-state="mounting"] > .wallet-slot::after,
.wallet-row > .wallet-slot[data-wallet-state="pending"]::before,
.wallet-row > .wallet-slot[data-wallet-state="pending"]::after { content: none; }
/* A SKELETON IS NOT A BUTTON. Apple Pay's slot IS its button and Google's slot
   carries the click handler, so without this a thumb landing on the row while it
   mounts could open a payment sheet from a blank tile. Nothing in the row takes
   a tap until the row is real; give.js carries the same guard, because a
   stylesheet that failed to arrive must not be what stands between a skeleton
   and a payment sheet. */
.wallet-row[data-state="mounting"] > .wallet-slot,
.wallet-row > .wallet-slot[data-wallet-state="pending"] { pointer-events: none; }
/* THE BUTTONS ARE LAID OUT INSIDE THE SKELETONS, and this is the load-bearing
   line. `.wallet-slot[hidden] > *` above is `display: none`, and Pay Kit will
   not draw its logotype into a box that has no layout — so a row that waited for
   the artwork before revealing would have waited for artwork that could never
   arrive. Laid out but not painted: each SDK measures a real 98px cell and
   finishes its own drawing off screen, and the row shows finished buttons. */
/* THE BUTTONS ARE LAID OUT INSIDE THE SKELETONS — and this line is why a
   pending column can exist at all. Pay Kit will not draw its logotype into a box
   with no layout, so the straggler this pass is about would never finish if its
   slot fell back to `.wallet-slot[hidden] > * { display: none }` at the reveal.
   Laid out, not painted: Cash App keeps measuring a real 98px cell behind the
   skeleton and lands into it. */
.wallet-row[data-state="mounting"] > .wallet-slot[hidden] > *,
.wallet-row > .wallet-slot[data-wallet-state="pending"] > * {
  display: block;
  visibility: hidden;
}
/* One reveal, all three columns, one pass of the page's own timing. Deliberately
   not a shimmer: three tiles pulsing under the amount is more movement than the
   pop-in it replaces, and this card's voice is quiet. */
@keyframes wallet-row-in { from { opacity: 0; } to { opacity: 1; } }
.wallet-row[data-state="ready"] > .wallet-slot {
  animation: wallet-row-in var(--t-med) var(--ease) both;
}
/* EXCEPT THE ONE THAT IS NOT ARRIVING YET. The reveal fade runs 0 -> 1, and the
   skeleton sits at 0.55 — so without this a pending column would blink out and
   come back BRIGHTER at the exact moment the row resolves, which is a wallet
   changing under a thumb by another name. It does not animate, because nothing
   about it has changed. Taking the attribute off later (walletArrived, or the
   25s seal) is what starts the fade, so a late arrival still joins the row the
   way the row itself arrived. */
.wallet-row[data-state="ready"] > .wallet-slot[data-wallet-state="pending"] {
  animation: none;
}

/* ── THE STAGED CASH APP REBUILD (2026-08-27) ────────────────────────────────
   Pay Kit cannot be update()d, so a changed amount needs a new button. It used
   to be built by emptying this column and setting `hidden` on it, which does not
   mean invisible here — it means the muted "$ CASH APP" tile — so shifting the
   amount replaced a live black button with a cream placeholder and back again,
   183ms of it in Chromium and 322ms in WebKit, every time.

   The new button is now attached to its own element stacked over the live one
   and swapped in when it has drawn. Absolute, so the column's 48px never depends
   on how many stages are in it; `pointer-events: none` so the button being built
   cannot be tapped before it is the real one. */
#cash-app-pay { position: relative; }
.cash-app-stage { height: 100%; }
.cash-app-stage.is-staging {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
}
/* The freeze frame — Cash App's own rendered artwork, copied into a shadow root
   of ours and held over the column while the live button is rebuilt underneath
   it. On top, and untouchable: a picture of a button must not take a tap. */
.cash-app-freeze {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}
/* Google Pay's fallback, and the one place the fixed row costs something. When
   the gstatic mark cannot be fetched give.js asks Google for the WORDED button
   ("Buy with G Pay", ~150px) instead of a mark that would render as a blank
   black rectangle. This class used to give it a line of its own — which is a
   reflow, the exact thing the row now refuses to do. It keeps its column and
   Google's own `buttonSizeMode: 'fill'` scales the lockup into it; if the words
   still do not fit, they clip rather than moving the marks beside them. A
   clipped label on a rare fallback is a smaller harm than a row that rearranges
   itself under somebody's thumb. */
.wallet-slot-wide { overflow: hidden; }

/* THE PAINTED CASH APP SLOT IS GONE, and that is the point of the 48px row.
   Pay Kit's button sets its own height inside a shadow root — 48px at size
   `medium`, 32px at `small` — and a shadow root is exactly the thing our
   stylesheet cannot reach into. The old row was 56px tall, so the 48px button
   had to sit inside a slot painted the same black to hide the seam. A 48px row
   is the button's own height: nothing to paint, nothing to hide, and the row
   cannot drift out of alignment if Square restyles anything.

   The lockup inside scales with the cell — measured on the live page, the same
   button draws its mark at 98px wide and at 182px wide without clipping — so a
   three-across row and a two-across row both read correctly. */
#cash-app-pay { overflow: hidden; border-radius: 12px; }

/* THE APPLE PAY BUTTON. `-apple-pay-button` is a real appearance value in
   Safari and draws Apple's own approved button; the declarations under it are
   the fallback for a Safari old enough to want the named-image recipe instead,
   and they are harmless everywhere else because the element is only ever
   unhidden when Square says Apple Pay is available.

   TYPE `plain` (Daniel, 2026-08-21, from his own iPhone). `donate` draws
   "Donate with  Pay" — a sentence in a row that otherwise speaks only in marks,
   and at 100px wide the words would leave the logo a sliver. `plain` is Apple's
   mark alone.

   HEIGHT, not min-height: with -webkit-appearance: -apple-pay-button Safari
   draws a button at its OWN intrinsic height and a min-height floor does not
   move it — an explicit height is the only thing Safari honours. 48px is inside
   Apple's supported 30–64px range. */
#apple-pay {
  display: block;
  width: 100%;
  height: 48px;
  min-height: 48px;
  border: 0;
  padding: 0;
  /* 12px, the corner Square's Cash App button draws at shape `semiround`, and
     the corner give.js hands Google Pay — so the three marks share a silhouette
     instead of each wearing its own. */
  border-radius: 12px;
  cursor: pointer;
  background-color: #000;
  background-image: -webkit-named-image(apple-pay-logo-white);
  background-size: auto 46%;
  background-repeat: no-repeat;
  background-position: 50% 50%;
  -webkit-appearance: -apple-pay-button;
  -apple-pay-button-type: plain;
  -apple-pay-button-style: black;
  transition: transform var(--t-med) var(--ease);
}
/* Apple Pay's muted mark. The `#apple-pay` block above wins on specificity over
   the shared `[hidden]` rule, so the Apple-drawn button has to be switched off
   here explicitly — otherwise Safari keeps painting the black Apple Pay lockup
   on a slot that cannot actually pay. */
#apple-pay[hidden] {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  -webkit-appearance: none;
  background-color: var(--control-bg);
  background-image: none;
  /* `border: 0` in the block above wins on specificity, so the hairline the
     other two muted marks get has to be restated here or Apple Pay's column
     reads as an empty gap — the exact thing this row exists to prevent. */
  border: 1px solid var(--line);
  height: 48px;
  transition: none;
}
#apple-pay:active { transform: scale(0.97); transition-duration: var(--press-down); }
#apple-pay:focus-visible { outline: 2px solid var(--gold-ring); outline-offset: 3px; }

/* The one line above the row. Mono label voice, so it reads as a heading for
   the marks and not as a sentence to get through. */
.wallet-lede {
  margin: 0 0 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--steel);
  /* The anchor for the loading caption below, which is out of flow so that it
     cannot touch this line's height. */
  position: relative;
}
/* ── THE SKELETON GETS A CAPTION (2026-09-04) ───────────────────────────────
   Measured from a loopback serve of these files at 390x844, three runs each,
   DOMContentLoaded to `data-state="ready"`: 1.40s median unthrottled Chromium,
   2.73s on a good 4G, 5.42s on a poor one, 2.94s unthrottled WebKit. For that
   whole window the row is three quiet tiles saying nothing, which the 09-04 QA
   sweep photographed and read as empty outlined boxes.

   No shimmer, deliberately: the reveal rule further up already turned one down
   because three tiles pulsing under the amount is more movement than the pop-in
   it replaces, and nothing about that argument changed. The row keeps its
   geometry and its stillness; the LEDE is what says what is happening. Label
   left, status right is the same lockup the house filter bar uses, in the mono
   voice this line already wears — no new type, no new colour, no new motion.

   HIDDEN BY DEFAULT, TURNED ON WHILE MOUNTING — that order matters. Keyed the
   other way, a browser without `:has()` would be left with a caption that says
   "Loading…" for as long as the page is open. This way its failure mode is the
   page exactly as it shipped yesterday.

   It answers the ROW's state, not a column's. A column that misses the reveal
   goes on wearing its own skeleton and gets its own reason line at the 25s
   ceiling — that is the page's existing answer to a straggler and this caption
   does not second-guess it by hanging around beside two live buttons. */
.wallet-lede-note { display: none; }
#wallet-wrap:has(> .wallet-row[data-state="mounting"]) > .wallet-lede > .wallet-lede-note {
  display: block;
  /* Out of flow: same top and the same font metrics as the lede's own text, so
     the two sit on one baseline without the lede becoming a flex container and
     without a single pixel of it depending on whether this is on screen. */
  position: absolute;
  top: 0;
  right: 0;
  /* THE RESERVATION IS IN `em`, WHICH IS THE WHOLE POINT. Both this word and
     the lede's own "PAY WITH" are one mono face at one size, so they grow and
     shrink together — measured at 320px, "PAY WITH" is 9.45em wide at every
     text-zoom step, and 10.5em leaves it that plus a gap it can never be
     pushed out of. Without this the two collide at 320px under 200% text-only
     zoom (measured: 27.6px of overlap), which is the trap the house segmented
     -control rule was written about. It degrades by truncating from the right
     rather than by pushing anything: measured at 320px it is the whole word at
     1x, "LOADIN…" at 1.5x and a 5px sliver at 2x — by which point "PAY WITH"
     alone is 170px of a 248px column and the caption is the least of it. It
     never lands on the label at any step, which is the property being bought. */
  max-width: calc(100% - 10.5em);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  opacity: 0.72;
}
#wallet-wrap[hidden] + .wallet-none { display: none; }
/* THE ANSWER TO "WHERE DID THE BUTTONS GO". Same face and size as .wallet-none,
   because it is the same kind of statement: a sentence a giver reads once at the
   moment the row disappeared under their thumb. */
.wallet-monthly {
  margin: 0;
  color: var(--steel);
  font-size: 16px;
  line-height: 1.55;
}
.wallet-monthly[hidden] { display: none; }
.wallet-none {
  margin: 0;
  color: var(--steel);
  font-size: 16px;
  line-height: 1.55;
}

/* ── the Cash App review panel ───────────────────────────────────────────────
   Round 7, Daniel: Cash App used to leave the page on the first tap while Apple
   Pay shows a sheet with the figure on it and waits. This is that sheet, and it
   is a real gate rather than a courtesy — it is what Square's `shouldTokenize`
   hook is waiting on, so nothing has been sent while it is up.

   It sits directly under the row it belongs to and pushes nothing above it, so
   the three marks stay exactly where the buyer's thumb just left them. The
   voice is the head of this same card, one step quieter: figure dominant, gold
   $ tucked at its shoulder, one gold action, one plain way back. Nothing new is
   invented for a panel that is on screen for a second. */
.cash-confirm {
  margin: 10px 0 0;
  padding: 16px 14px 14px;
  border: 1px solid var(--gold-ring);
  border-radius: 16px;
  /* One tone off the card it sits on, so it reads as a thing laid over the
     form rather than a paragraph of it. */
  background: var(--shell);
  box-shadow: var(--shadow-accent-soft);
  text-align: center;
}
.cash-confirm[hidden] { display: none; }
.cash-confirm:focus { outline: none; }
.cash-confirm:focus-visible { outline: 2px solid var(--gold-ring); outline-offset: 3px; }
.cash-confirm-label {
  margin: 0 0 6px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--steel);
}
/* The same figure as the head of the card, one step down: this is a check on a
   number that is already on screen, not a second place to set it. */
.cash-confirm-amount { padding: 0; }
.cash-confirm-amount .fig { font-size: clamp(2.4rem, 11vi, 3.2rem); }
.cash-confirm-amount .cur { font-size: clamp(1.05rem, 4vi, 1.35rem); }
/* 16px, not 14: this is a sentence a giver reads, so it is body copy and it
   meets the body floor. 14px is the house's secondary size, not its reading
   size, and the mobile gate is right to call it. */
.cash-confirm-line {
  margin: 8px 0 14px;
  color: var(--steel);
  font-size: 16px;
  line-height: 1.5;
}
.cash-confirm-identity { margin: 14px 0 12px; text-align: left; }
.cash-confirm-identity[hidden] { display: none; }
.cash-confirm-why {
  margin: 0 0 10px;
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.45;
  color: var(--steel);
}
.cash-confirm-identity .receipt-fields { display: grid; gap: 10px; grid-template-columns: 1fr; }
.cash-confirm .btn { width: 100%; justify-content: center; }
.cash-confirm .btn-ghost { margin-top: 8px; }

/* ── the card field ──────────────────────────────────────────────────────────
   Square draws its own iframe in here. Reserve height for the MEDIA, which this
   is — an iframe of a known shape — never for text. */
.card-slot {
  min-height: 92px;
  margin: 0 0 8px;
}
/* THE CARD FOLD'S OWN BAD NEWS (2026-08-24). One quiet line, in the page's own
   steel rather than the error red — the red sentence is reserved for the case
   where nothing on the page can take money, and a card that will not mount
   beside two live wallet buttons is not that case. It holds no height until
   give.js writes into it, so the fold's shape never depends on it. */
.card-note {
  margin: 0;
  color: var(--steel);
  font-size: 14px;
  line-height: 1.5;
}
.card-note:not(:empty) { margin: 0 0 12px; }
/* And when there IS no card field, the empty iframe placeholder and the button
   that would submit it are not left on screen pretending otherwise. */
.card-fold.is-cardless .card-slot,
.card-fold.is-cardless #give-submit { display: none; }

.card-slot:empty::after {
  content: "";
  display: block;
  height: 72px;
  /* Solid, for the same reason the muted wallet tiles are: a dashed rectangle
     is the drop-zone idiom and reads as something that failed, not as an iframe
     on its way. */
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--control-bg);
}

/* ── the card fold ───────────────────────────────────────────────────────────
   A native <details>. No hand-rolled toggle: the disclosure is a browser
   primitive with the right semantics, keyboard behaviour and find-in-page
   support already in it. The summary is a full-width 48px row so it clears the
   tap floor, and the marker is our own chevron because the platform triangles
   differ per engine. */
.card-fold {
  margin: 14px 0 0;
  border-top: 1px solid var(--line);
}
/* Monthly hides the whole wallet block, and with it the reason the fold has a
   rule of its own: the pay block's rule and the fold's rule ended up stacked
   48px apart with nothing between them, which reads as an empty row somebody
   forgot to fill. When the fold is the only thing left in the block it uses the
   block's own rule. */
#wallet-wrap[hidden] + .wallet-none[hidden] + .wallet-monthly[hidden] + .card-fold {
  margin-top: 0;
  border-top: 0;
}
.card-fold > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 48px;
  padding: 12px 2px;
  cursor: pointer;
  list-style: none;
  color: var(--steel);
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 600;
}
.card-fold > summary::-webkit-details-marker { display: none; }
.card-fold > summary::after {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--steel);
  border-bottom: 2px solid var(--steel);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--t-med) var(--ease);
}
.card-fold[open] > summary { color: var(--ink); }
.card-fold[open] > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.card-fold > summary:focus-visible { outline: 2px solid var(--gold-ring); outline-offset: 2px; border-radius: 10px; }
@media (any-hover: hover) {
  .card-fold > summary:hover { color: var(--gold-text); }
}
.card-fold:not([open]) > .card-fold-body { display: none; }
.card-fold-body { position: relative; padding: 4px 0 0; }
/* The gold button belongs to the boxes above it, not to the fold's own bottom
   edge: `.field`'s site-wide 20px is the gap between two form rows, and there is
   no second row here. 12px, and the button comes up with it. */
.card-fold-body > .field { margin-bottom: 12px; }
/* Native disclosure is the baseline. Engines that support intrinsic keyword
   interpolation receive a contained height handoff without JS measurement. */
@supports (interpolate-size: allow-keywords) and selector(details::details-content) {
  :root { interpolate-size: allow-keywords; }
  .card-fold::details-content {
    height: 0;
    overflow: clip;
    transition: height 220ms var(--ease);
  }
  .card-fold[open]::details-content { height: auto; }
  .card-fold:not([open]) > .card-fold-body { display: block; }
}
/* Monthly is card-only, so the fold is forced open and its summary stops
   pretending to be a choice. */
.card-fold.is-forced > summary { pointer-events: none; color: var(--ink); }
.card-fold.is-forced > summary::after { display: none; }
/* AND WHEN IT IS NOT A CHOICE IT IS NOT A ROW EITHER (2026-08-28). Forced open,
   the summary is a 48px label reading "Card" directly under the sentence that
   just said a card is the only route here — either "This browser does not offer
   Apple Pay, Cash App or Google Pay. A card works just the same." or the monthly
   line — and directly over Square's own boxes, which label themselves. It is 48
   dead pixels between the giver and the button, on the one screen where the
   button was already below the fold. Hidden the accessible way, not deleted:
   <details> needs its summary for the disclosure semantics and the name it gives
   the group, and this state is reached by keyboard too. */
.card-fold.is-forced > summary {
  position: absolute;
  width: 1px;
  height: 1px;
  min-height: 0;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* The hairline above the fold now has the boxes right under it, so the body
   takes back the breathing room the summary used to hold. */
.card-fold.is-forced > .card-fold-body { padding-top: 14px; }

/* ── the two ordinary fields ────────────────────────────────────────────────
   A LABEL THAT IS A SENTENCE. `.field label` in styles.css is the 12px tracked
   uppercase mono CAPTION voice — right for "CARD", wrong for "Email for your
   receipt (optional)", which shipped as two shouting lines at 390px. Identical
   reset to the one /qa/ and /prayer/ each carry; it rides here rather than in
   styles.css because fourteen pages depend on that element rule for captions.

   And the input: styles.css styles `.field input[type="text"]` and
   `[type="tel"]` only, so an email field renders as the browser's own narrow
   default in the middle of a designed form. Same declarations, same tokens. */
.give-form .field label.field-note {
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.005em;
  text-transform: none;
  line-height: 1.45;
  color: var(--ink);
}
.give-form .field input[type="email"],
.give-form .field input[type="text"] {
  width: 100%;
  font-family: var(--font-body);
  font-size: 16px;
  min-height: 44px;
  padding: 12px 16px;
  border-radius: var(--radius-input);
  border: 1px solid var(--control-line);
  background: var(--paper);
  color: var(--ink);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}

/* 16px, not 14: these are SENTENCES a giver reads (who handles the card, where
   the other ways to give went), not the mono caption voice, and the mobile gate
   holds body copy to 16 — mobile-qa flagged all four nodes at 14 on
   2026-08-21. */
/* AND NOT THE MONO VOICE EITHER (2026-08-23). styles.css sets .give-fine to
   the house label face — mono, 11px, .16em, uppercase — which is right for a
   badge and wrong for the two sentences this page uses it for: the deductible
   line under the card and "Stopped. Nothing more will be charged." in the manage
   panel. Both are prose. give.css already lifted the size to 16 and left the
   caps behind; the whole voice is reset here. */
.give-fine {
  margin: 16px 0 0;
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--steel);
  line-height: 1.55;
}
.give-fine a { color: var(--gold-text); }

/* Busy: the button says what it is doing and stops taking taps. Never a spinner
   that leaves the old label sitting there. */
.give-form[data-busy="1"] .btn { opacity: 0.6; pointer-events: none; }
#give-submit {
  min-width: 100%;
  white-space: nowrap;
}
#give-submit.is-state-changing { animation: give-submit-state 150ms var(--ease); }
#give-submit[data-state="busy"]::after {
  content: "";
  width: 14px;
  height: 14px;
  margin-left: 9px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: give-submit-spin 700ms linear infinite;
}
@keyframes give-submit-state {
  from { opacity: 0.58; }
}
@keyframes give-submit-spin { to { transform: rotate(360deg); } }

/* ── the confirmation ────────────────────────────────────────────────────────
   Replaces the form in place. No fixed height anywhere — the monthly version
   carries a manage link and the one-time version does not. */
.give-done { display: none; }
/* The same card the form was, so the page does not change shape underneath a
   giver at the one moment they are looking for confirmation. */
.give-done.on {
  display: block;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 20px 16px 22px;
  box-shadow: var(--shadow-control);
}
.give-success-mark {
  width: 32px;
  height: 32px;
  display: block;
  margin-bottom: 12px;
  fill: none;
  stroke: var(--accent-deep);
  stroke-width: 2.25;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.give-success-mark circle { opacity: 0.34; }
.give-success-mark path { stroke-dasharray: 1; stroke-dashoffset: 1; }
.give-done.on .give-success-mark path {
  animation: give-success-draw 300ms var(--ease) forwards;
}
@keyframes give-success-draw { to { stroke-dashoffset: 0; } }
/* THE "GIFT CONFIRMED" PILL IS NOT DRAWN ANY MORE (2026-08-23). give.js still
   moves the real submit button in here, still disabled, so the duplicate-submit
   guard and the button's own state machine are untouched — but a full-width gold
   gradient pill was the loudest thing on the confirmation card and it did
   nothing when pressed. It was also the THIRD way that card said the gift went
   through, after the check mark and the thank-you line. A giver needs the
   amount, the fact that it worked, and (if it is monthly) the link. */
.give-confirm-control { display: none; }
@media (min-width: 560px) {
  .give-done.on { padding: 30px; }
}
.give-done h2 { margin: 10px 0 8px; }
/* A paragraph's own em-margins put 68px of nothing between the figure and the
   line under it; these are the margins the block actually wants. */
.give-thanks-amount {
  margin: 6px 0 10px;
  font-family: var(--font-display);
  font-size: clamp(3rem, 8vi + 1rem, 4.5rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.give-done .lede { margin-bottom: 18px; }
/* The facts a giver looks for after the tap (2026-09-12): receipt address, how
   it was paid, where it goes, the tax line. Mono labels, hairline rows, no card
   inside the card. */
.done-facts { margin: 0 0 20px; padding: 0; border-top: 1px solid var(--line); }
.done-facts > div {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr);
  gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--line);
}
.done-facts dt {
  margin: 0;
  padding-top: 2px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--steel);
}
.done-facts dd { margin: 0; font-size: 15px; line-height: 1.45; color: var(--ink); overflow-wrap: anywhere; }
.done-facts dd a { color: var(--gold-text); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
.done-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.done-actions .btn { flex: 1 1 160px; text-align: center; text-decoration: none; }
.give-done:focus { outline: none; }
.manage-note {
  margin: 22px 0 0;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--paper);
}
.manage-note p { margin: 0 0 10px; font-size: 16px; line-height: 1.55; color: var(--steel); }
.manage-link {
  display: block;
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.5;
  word-break: break-all;
  color: var(--gold-text);
  background: var(--bone);
  border-radius: 10px;
  padding: 12px;
  text-decoration: none;
}
/* THE ONLY CONTROL ON A MONTHLY CONFIRMATION, so it takes the full width every
   other primary on this page takes rather than sitting as a half-width ghost —
   nobody transcribes a 32-character token off a phone. */
.copy-link {
  margin-top: 12px;
  width: 100%;
  min-height: 48px;
  justify-content: center;
}

/* ── the manage panel ────────────────────────────────────────────────────────
   Same page, reached by the fragment in the giver's own link. */
.give-manage { display: none; }
.give-manage.on { display: block; }
.manage-rows {
  display: grid;
  gap: 0;
  margin: 0 0 22px;
  border-top: 1px solid var(--line);
}
.manage-rows div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.manage-rows dt, .manage-rows .k {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--steel);
}
.manage-rows dd, .manage-rows .v {
  margin: 0;
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Cyrillic all-caps wants less tracking than Latin does; the hub keeps a single
   language today, but the label voice is shared and this is where the page
   would carry its own correction. */
:lang(ru) .amount-cadence,
:lang(ru) .wallet-lede,
:lang(ru) .manage-rows .k { letter-spacing: 0.08em; }

/* The foot of the page. It used to carry the Breeze link and now carries the
   one deductible line (Daniel, 2026-08-21) — the hairline still sets it apart,
   and the mono label voice keeps it a footnote rather than a sentence. */
.give-outro {
  margin-top: 24px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
/* .give-fine already sets 16px steel — the deductible line now inherits it
   instead of being overridden to the 11px mono label voice by a span. */

@media (prefers-reduced-motion: reduce) {
  .amount-opt, .monthly-row, #apple-pay,
  .card-fold > summary::after { transition-duration: 0.001ms; }
  .amount-display .fig.is-changing,
  #give-submit.is-state-changing,
  #give-submit[data-state="busy"]::after,
  /* The row still arrives all at once; it just arrives without the fade. */
  .wallet-row[data-state="ready"] > .wallet-slot,
  .give-done.on .give-success-mark path { animation: none; }

  #give-submit[data-state="busy"]::after { display: none; }
  .give-success-mark path { stroke-dashoffset: 0; }
  .card-fold::details-content { transition: none; }
}

/* ── THE SHORT-SCREEN COLLAPSE (2026-09-01) ──────────────────────────────────
   ROUND 12, and it is round 11's fold seen from the other side. Round 11 pulled
   the Give button back above a 390x844 phone screen and left the note that the
   same button was still 86px under a 1280x800 laptop — measured again this
   morning at exactly that, top 833.8 in an 800px window, and reported by the
   walkthrough pass every day since.

   The page's rhythm is written in width clamps, so a laptop gets the widest
   spacing on the SHORTEST screen anybody uses it on. That is the whole bug: how
   much air the page can afford is a question about height, not width.

   Every declaration below is a spacing token coming in one notch when the
   viewport is 920px tall or less. Nothing is removed and no type size moves.
   NOTHING INSIDE #wallet-wrap IS LISTED: not the row, not the lede's gap, not a
   slot — the three marks keep their order, their 48px height and their exact
   column offsets at every width, measured. The block moves up with the page and
   nothing about it changes. Above 920px the page is exactly what it was. */
@media (max-height: 920px) {
  #give-intro { padding-block: clamp(14px, 3.5vw, 20px) clamp(10px, 2vw, 12px); }
  /* The band's own padding is what separates the headline from the header and
     from the card; the h1's trailing margin is a second gap doing the same job. */
  #give-intro h1 { margin-bottom: 0; }
  main .section.on-paper { padding-top: clamp(12px, 1.6vw, 16px); }
  .amount-head { padding: 0 0 8px; }
  .amount-cadence { margin-top: 6px; }
  .amount-grid { margin-bottom: 10px; }
  .monthly-row { margin-bottom: 10px; }
  .pay-block { padding-top: 10px; }
  .card-fold { margin-top: 8px; }
  .card-fold.is-forced > .card-fold-body { padding-top: 8px; }
  .card-fold-body > .field { margin-bottom: 8px; }
}
/* The card's own inset and the monthly row's height only have room to give at
   the wide end, where they doubled with the screen and the fold did not. Sides
   stay at 30px: the wallet row's three-across width is measured off them. */
@media (min-width: 560px) and (max-height: 920px) {
  .give-form { padding-block: 22px; }
  .monthly-row { padding-block: 8px; }
}

/* ── THE SHORT-SCREEN AMOUNT ROW (2026-09-01) ────────────────────────────────
   ROUND 13. Round 12 brought every spacing token in one notch under 920px of
   height and left the Give button 38.1px under a 375x667 screen with the card
   open — the smallest phone anybody still gives on, and the one screen where
   the notch was not enough. Measured, not guessed: the amount chooser is two
   stacked rows there, the figure block (93.5px) over the five presets (44px),
   and the presets already sit one-across at every width.

   So the CHOOSER collapses to a single row: the figure keeps its size and its
   place at the head of the card, and the five presets ride beside it as a rail
   that scrolls sideways inside its own box. Nothing is removed, no type size
   moves, every pill keeps its 44px tap height, and the page itself never
   scrolls sideways. Above 700px of height the chooser is exactly what it was,
   so 390x844 and every desktop are byte-identical. Nothing in #wallet-wrap is
   named here. */
@media (max-height: 700px) {
  .amount-grid {
    display: flex;
    align-items: center;
    gap: 14px;
  }
  .amount-head {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
    text-align: left;
  }
  .amount-display { justify-content: flex-start; }
  .amount-cadence { margin-top: 4px; }

  /* The rail. Native scrolling with the next pill peeking is the affordance;
     the vertical padding is the room the checked pill's ring and its focus
     outline need, which an overflow box would otherwise cut. */
  .amount-chips {
    flex: 0 1 auto;
    max-width: 56%;
    min-width: 0;
    display: flex;
    gap: 6px;
    overflow-x: auto;
    overflow-y: hidden;
    padding-block: 6px;
    scroll-snap-type: inline proximity;
    scroll-padding-inline: 6px;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-inline: contain;
  }
  .amount-chips::-webkit-scrollbar { display: none; }
  /* `width: 100%` from the grid rule made every pill as wide as the rail, so
     the rail showed ONE pill and nothing peeked (390px walk, 2026-09-13):
     $10, $50 and $100 were off screen with no hint they existed. */
  .amount-opt { flex: 0 0 auto; width: auto; scroll-snap-align: center; }
  .amount-opt { min-width: 58px; padding-inline: 10px; }
}
/* A short LAPTOP has the width the phone does not: the five pills fit side by
   side there, so the rail is only a rail when it has to be. Same shape as the
   companion round 12 already carries for its spacing notch. */
@media (min-width: 560px) and (max-height: 700px) {
  .amount-chips { max-width: none; }
}

/* ── THE LAST FIVE PIXELS (2026-09-01) ───────────────────────────────────────
   ROUND 14, and it is one notch, not a new idea. Round 13's own note says the
   Give button lands 15.9px ABOVE a 375x667 first screen with the card open.
   Measured on the live page tonight, fold open and $50 picked, it lands at
   top 620.1 / bottom 672.1 in a 667px window — 5.1px BELOW it. (The page does
   still scroll: #page-scroll had 651px of room, so the button was reachable,
   just not on the first screen the way round 13 set out to leave it.)

   Four spacing tokens come in one notch under 700px — 4px off the paper's own
   top inset, 4px above the PAY WITH hairline, the fold body's own 4px lead-in,
   and 2px between the card box and the button — measured at 12px together,
   which puts the button's bottom at 660.1 in a 667px window. Nothing is
   removed, no type size moves, nothing inside #wallet-wrap is named, and above
   700px of height the page is byte-for-byte what it was. */
@media (max-height: 700px) {
  main .section.on-paper { padding-top: 8px; }
  .pay-block { padding-top: 6px; }
  .card-fold-body { padding-top: 0; }
  .card-fold-body > .field { margin-bottom: 6px; }
}

/* ── THE CARD-ONLY SHORT SCREEN (2026-09-01) ─────────────────────────────────
   ROUND 15, and it is the one state the first fourteen never measured. Rounds
   12–14 tuned this page with the three marks ON it. A browser with no wallet
   gets a different page: the marks go muted but keep their 48px row, the
   sentence explaining why appears under them (74px at 375px — three lines),
   and give.js forces the card fold open. Measured on the live page tonight in
   that state: Give lands at bottom 694.5 in a 667px window, 27.5px under the
   first screen, while the same page with wallets lands at 660.1 — above it.
   So the shortfall belongs to the card-only state, not to the page.

   Every declaration below is therefore scoped to `:has(.card-fold.is-forced)`
   and to viewports 700px tall or less: a visitor whose wallets DID appear sees
   byte-for-byte the page round 14 measured, at every width and every height,
   and so does every screen taller than 700px. Nothing is removed, no type size
   moves, no giving method or sentence changes, and nothing inside #wallet-wrap
   is named — the three marks keep their order, their row and their 48px.
   Without :has() the page is exactly what it was: this is a tightening, and a
   browser that cannot read it still scrolls to the button as it does today. */
@media (max-height: 700px) {
  body:has(.card-fold.is-forced) #give-intro { padding-block: 10px 8px; }
  .give-form:has(.card-fold.is-forced) { padding-top: 12px; }
  .give-form:has(.card-fold.is-forced) .amount-grid { margin-bottom: 6px; }
  .give-form:has(.card-fold.is-forced) .monthly-row {
    padding-block: 6px;
    margin-bottom: 6px;
  }
  /* The fold's own hairline is the separator; the card box labels itself and
     sits directly under it, so the 8px round 12 left here has no work to do at
     667px. */
  .card-fold.is-forced { margin-top: 4px; }
  .card-fold.is-forced > .card-fold-body { padding-top: 0; }
  .card-fold.is-forced > .card-fold-body > .field { margin-bottom: 4px; }
}
