/* ============================================================
   CAPELY: checkout page
   Owned by Agent 7. Tokens only, from css/theme.css. No hardcoded
   colour, price or product name lives in this file.
   ============================================================ */

/* ---------- page shell ----------
   Not using the shared .section class here: its clamp(72px,8vw,112px) top
   padding can fall below the fixed nav's real height at small viewports
   (nav is --nav-h + 14px top offset, about 78px), which would let this
   page's heading sit under the floating island on first load. This page
   is a hard landing (scroll position 0), not reached by an anchor jump
   that already compensates, so the clearance has to be explicit. */
.checkout-page {
  position: relative;
  padding-top: calc(var(--nav-h) + 56px);
  padding-bottom: clamp(72px, 8vw, 112px);
}

.checkout-back {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--text-mute); font-size: 13.5px; font-weight: 500;
  padding: 6px 2px; margin-bottom: 22px;
  transition: color var(--t-fast);
}
.checkout-back:hover { color: var(--text); }
.checkout-back svg { flex: 0 0 auto; }

.checkout-title { font-size: clamp(28px, 4vw, 38px); margin-bottom: 8px; }
.checkout-sub { color: var(--text-mute); font-size: 15px; margin-bottom: clamp(32px, 4vw, 48px); }

/* ---------- layout: grid handles both orders -----------
   Mobile: a single column, source order below decides the stack, which is
   how "summary above the pay button" is satisfied without duplicating any
   markup. Desktop: named areas put the summary in its own column spanning
   both rows next to the fields and the submit block. Order and grid-area
   can coexist: once an item has an explicit grid-area, that wins for
   placement, so the mobile `order` values are simply inert on desktop. */
.checkout-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
  align-items: start;
}
.checkout-fields  { order: 1; display: flex; flex-direction: column; gap: clamp(32px, 4vw, 44px); min-width: 0; }
.checkout-summary { order: 2; min-width: 0; }
.checkout-submit  { order: 3; display: flex; flex-direction: column; gap: 12px; min-width: 0; }

@media (min-width: 901px) {
  .checkout-layout {
    grid-template-columns: 1fr 380px;
    grid-template-areas: "fields summary" "submit summary";
    gap: 20px 40px;
  }
  .checkout-fields  { grid-area: fields; }
  .checkout-summary { grid-area: summary; position: sticky; top: calc(var(--nav-h) + 32px); }
  .checkout-submit  { grid-area: submit; }
}

/* ---------- field primitives ---------- */
.field-group { display: flex; flex-direction: column; gap: 18px; }
.checkout-group-title { font-size: 13px; }

.field { display: flex; flex-direction: column; gap: 8px; }
.field label, .payment-field legend { font-size: 13.5px; font-weight: 600; color: var(--text-dim); }
.payment-field { border: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
.payment-field legend { padding: 0; }

.field-hint { color: var(--text-mute); font-size: 12.5px; line-height: 1.55; }
.field-meta { color: var(--text-mute); font-size: 12.5px; }
.field-meta.is-danger { color: var(--danger); }
.field-error { color: var(--danger); font-size: 12.5px; min-height: 0; }
.field-error:empty { display: none; }

/* ---------- shared input look (contract quality bar) ---------- */
.checkout-input {
  height: 46px; width: 100%; padding: 0 14px;
  background: var(--surface-2); border: 1px solid var(--hairline);
  border-radius: var(--r-sm); color: var(--text); font-size: 14.5px;
  transition: border-color var(--t-fast), background var(--t-fast);
}
select.checkout-input { cursor: pointer; }
.checkout-input::placeholder { color: var(--text-faint); }
.checkout-input:focus { border-color: var(--accent); background: var(--surface-3); }
.checkout-input[aria-invalid="true"] { border-color: var(--danger); }

/* ---------- cape picker ---------- */
.cape-picker { display: flex; gap: 14px; align-items: flex-start; }
.cape-thumb {
  flex: 0 0 auto; width: 50px; height: 80px;
  background: var(--surface-2); border: 1px solid var(--hairline); border-radius: var(--r-xs);
}
.cape-picker-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }

/* ---------- stepper ---------- */
.stepper { display: flex; align-items: stretch; gap: 8px; max-width: 220px; }
.stepper-btn {
  flex: 0 0 auto; width: 46px; height: 46px;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2); border: 1px solid var(--hairline); border-radius: var(--r-sm);
  color: var(--text); transition: border-color var(--t-fast), background var(--t-fast), color var(--t-fast);
}
.stepper-btn:hover { border-color: var(--hairline-strong); background: var(--surface-3); }
.stepper-btn:disabled { color: var(--text-faint); pointer-events: none; }
.stepper-input { flex: 1 1 auto; min-width: 0; text-align: center; }
/* Firefox and WebKit both draw native up/down spinners on a number input;
   the custom stepper buttons replace them, so the native ones would just
   be a second, redundant control sitting on top of the field. */
.stepper-input::-webkit-outer-spin-button, .stepper-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stepper-input { -moz-appearance: textfield; appearance: textfield; }

/* ---------- payment method cards -----------
   The native radio stays in the document and in the tab order, just moved
   off-screen with position+opacity rather than display:none, so keyboard
   and screen reader behaviour is exactly a normal radio group. Checked and
   focus states are painted on the label because the input itself is
   invisible. */
.pay-methods { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
@media (max-width: 640px) { .pay-methods { grid-template-columns: 1fr; } }

.pay-method {
  position: relative; display: flex; flex-direction: column; gap: 2px;
  padding: 14px 16px; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--hairline); border-radius: var(--r-sm);
  transition: border-color var(--t-fast), background var(--t-fast);
}
.pay-method input {
  position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;
}
.pay-method:has(input:checked) { border-color: var(--accent); background: var(--surface-3); }
.pay-method:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--r-xs); }
.pay-method-body { display: flex; flex-direction: column; gap: 2px; pointer-events: none; }
.pay-method-label { font-family: 'Outfit', sans-serif; font-size: 14px; font-weight: 600; color: var(--text); }
.pay-method-note { font-size: 12px; color: var(--text-mute); }

/* ---------- discount ---------- */
.discount-row { display: flex; gap: 8px; }
.discount-row .checkout-input { flex: 1 1 auto; min-width: 0; }
.discount-row .btn { flex: 0 0 auto; }

/* An author display outranks the UA sheet's [hidden] { display: none }, so
   an element toggled with that attribute needs this pair or it never hides.
   Caught by tools/lint.js hidden-vs-display. */
.discount-chip[hidden] { display: none; }
.discount-chip {
  display: inline-flex; align-items: center; gap: 8px; align-self: flex-start;
  padding: 8px 10px 8px 14px; margin-top: 2px;
  background: var(--surface-3); border: 1px solid var(--hairline-strong); border-radius: var(--r-pill);
  font-size: 13px; font-weight: 600; color: var(--accent);
}
.discount-chip button {
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: var(--r-pill); color: var(--text-mute);
  transition: background var(--t-fast), color var(--t-fast);
}
.discount-chip button:hover { background: var(--surface-2); color: var(--text); }

/* ---------- agreement ---------- */
.agreement-label { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; }
.agreement-label input {
  flex: 0 0 auto; width: 18px; height: 18px; margin-top: 2px;
  accent-color: var(--accent); background: var(--surface-2);
}
.agreement-label span { font-size: 13px; line-height: 1.55; color: var(--text-dim); font-weight: 400; }

/* ---------- order summary ---------- */
.checkout-summary { padding: 22px; display: flex; flex-direction: column; gap: 18px; }
.checkout-summary-title { font-size: 15px; }

.summary-item { display: flex; gap: 14px; align-items: center; }
.summary-item .cape-thumb { width: 44px; height: 70px; }
.summary-item-info { min-width: 0; }
.summary-cape-name { font-family: 'Outfit', sans-serif; font-weight: 600; font-size: 14.5px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.summary-cape-qty { font-size: 12.5px; color: var(--text-mute); margin-top: 2px; }

.summary-lines { display: flex; flex-direction: column; gap: 10px; padding-top: 14px; border-top: 1px solid var(--hairline); }
.summary-line[hidden] { display: none; }
.summary-line { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: 13.5px; color: var(--text-dim); }
.summary-line dt { font-weight: 400; }
.summary-line dd { font-weight: 500; color: var(--text); font-variant-numeric: tabular-nums; }
.summary-line-discount dd { color: var(--ok); }
.summary-discount-code { color: var(--text-mute); font-weight: 400; }
.summary-line-total { padding-top: 10px; border-top: 1px solid var(--hairline); }
.summary-line-total dt, .summary-line-total dd { font-size: 16px; font-weight: 700; color: var(--text); font-family: 'Outfit', sans-serif; }

.summary-note { font-size: 12px; line-height: 1.5; color: var(--text-mute); }

/* ---------- submit ---------- */
.checkout-pay { width: 100%; position: relative; }
.checkout-form-error { text-align: center; }

.btn-spinner {
  display: none; width: 15px; height: 15px; border-radius: var(--r-pill);
  /* Derived from the token, not a hand-converted hex-to-rgb literal: the
     spinner needs a dim track under the solid arc, and --on-accent has no
     alpha variant of its own. */
  border: 2px solid color-mix(in srgb, var(--on-accent) 35%, transparent);
  border-top-color: var(--on-accent);
  animation: capely-spin .7s linear infinite;
}
.checkout-pay.is-loading .btn-spinner { display: inline-block; }
@media (prefers-reduced-motion: reduce) {
  .btn-spinner { animation: none; }
}
@keyframes capely-spin { to { transform: rotate(360deg); } }

/* ---------- hidden-attribute safety net ----------
   .discount-chip and .summary-line both set `display` unconditionally above.
   At equal specificity an author rule beats the UA stylesheet's own
   [hidden]{display:none}, so without this override JS setting .hidden=true
   on those elements is silently ignored and they stay visible empty. This
   rule sits last so it wins the cascade tie for anything in this file that
   is both toggled with the native hidden attribute and given its own
   display value. */
[hidden] { display: none; }

/* ---------- basket mode ----------
   Every rule here carries its [hidden] partner. An author `display` outranks
   the UA sheet's [hidden] { display: none }, and these three elements are all
   toggled, so without them the summary would show the basket AND the single
   item, and the cape picker would stay on a page that has no picker. */
#cape-group[hidden] { display: none; }
#summary-item[hidden] { display: none; }

.summary-basket {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 4px;
}
.summary-basket[hidden] { display: none; }

.summary-basket-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-size: 13.5px;
  color: var(--text-dim);
}
.summary-basket-name { min-width: 0; overflow-wrap: anywhere; }
.summary-basket-qty { color: var(--text-mute); }
.summary-basket-amount {
  font-family: 'Outfit', sans-serif;
  font-weight: 700;
  color: var(--text);
  white-space: nowrap;
}

.summary-currency-note { color: var(--text-mute); }
.summary-currency-note[hidden] { display: none; }
