/*!******************************************************************************************************************!*\
  !*** css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[2].use[1]!./app/javascript/stylesheets/spa.css ***!
  \******************************************************************************************************************/
:root {
  --or-ink: #17231f;
  --or-muted: #5c665f;
  --or-paper: #fbfaf6;
  --or-line: #d9ded6;
  --or-accent: #20824a;
}

html,
body,
.js-heavy-body {
  min-height: 100%;
  margin: 0;
  background: var(--or-paper);
  color: var(--or-ink);
}

.js-heavy-body {
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

.or-loader {
  min-height: 100vh;
  display: grid;
  place-items: center;
}

.or-shell {
  min-height: 100vh;
  overflow-x: clip;
  display: flex;
  flex-direction: column;
  background: var(--or-paper);
}

.or-md-list {
  margin: 0 0 1rem;
  padding-left: 1.3rem;
  color: var(--or-muted);
  line-height: 1.65;
}

.or-header,
.or-footer,
.or-main {
  width: min(1120px, calc(100% - 32px));
  margin: 0 auto;
}

.or-header {
  min-height: 72px;
}

.or-footer {
  min-height: 80px;
  display: flex;
  align-items: center;
  padding: 18px 0 28px;
}

.or-main {
  flex: 1;
  padding-bottom: 48px;
}

.or-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: inherit;
}

.or-brand-fallback {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 4px;
  background: var(--or-ink);
  color: #fff;
  font-weight: 800;
}

.or-nav-link {
  color: var(--or-muted);
  text-decoration: none;
  font-size: 0.95rem;
}

.or-nav-link:hover,
.or-nav-link.is-active {
  color: var(--or-ink);
}

.or-eyebrow {
  margin: 0 0 8px;
  color: var(--or-accent);
  font-size: 0.78rem;
  font-weight: 750;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* The hero breaks out of .or-main's 1120px measure so the artwork can run all
   the way to the right edge of the window, the way the mockup has it, while the
   copy stays padded back to the measure and lines up with everything below.

   There is deliberately NO css wash behind this band. The artwork carries its
   own green glow, baked in and already blended into its own cream ground
   (rgb(251, 248, 243) — the page paper to within three levels). A radial
   gradient painted around the opaque image is what made the art read as a
   lighter rectangle pasted onto the page. */
.or-hero {
  --or-gutter: calc((100vw - min(1120px, 100vw - 32px)) / 2);

  position: relative;
  /* width: auto plus negative side margins = exactly the viewport width. */
  margin-inline: calc((100% - 100vw) / 2);
  display: grid;
  /* The copy column is the wider of the two: it has to hold "Freedom tech for"
     on one line, which is what makes the headline break into the two lines the
     mockup shows instead of three. */
  grid-template-columns: minmax(0, 1.18fr) minmax(0, 1fr);
  /* start, not center. The artwork column is the taller of the two by a wide
     margin (a 4:3 crop across ~45% of the window), so centring the copy against
     it split the leftover height into a growing band of dead space between the
     header and the badges — ~80px on a wide desktop, and more the wider the
     window got. Top-aligning pins the copy under the header instead, leaving
     the hero's own padding as the only gap above it. */
  align-items: start;
  gap: clamp(16px, 2.5vw, 40px);
  padding: clamp(8px, 2vw, 32px) 0 clamp(4px, 1vw, 16px);
  padding-inline-start: var(--or-gutter);
  /* Every crop has real content in its last pixel column, so the art is never
     bled off-screen. This keeps it clear of the edge — and of the scrollbar
     that 100vw counts but .or-shell's overflow-x: clip hides. */
  padding-inline-end: 12px;
}

.or-hero-copy {
  position: relative;
  z-index: 1;
  min-width: 0;
}

.or-hero-title {
  /* Sized so "inbound payments." stays on one line inside the copy column —
     that is what gives the two-line break the mockup has instead of three. */
  max-width: 21ch;
  font-size: clamp(2.3rem, 4.2vw, 4rem) !important;
  letter-spacing: -0.035em;
  line-height: 0.98 !important;
}

/* Scoped to the hero rather than left as a bare .or-hero-lede: the generic
   .or-lede is declared further down this file at the same specificity, so a
   one-class hero rule loses to it on source order and silently does nothing. */
.or-hero-copy .or-hero-lede {
  max-width: 34rem;
  margin-top: 22px;
  font-size: 1.16rem;
}

/* The free-and-open-source pills above the headline. Not Mantine Badges: these
   sit at body-text size so they read as a claim rather than as chrome. */
.or-hero-badges {
  row-gap: 8px;
}

.or-hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border: 1px solid var(--or-accent);
  border-radius: 999px;
  background: rgba(32, 130, 74, 0.08);
  color: var(--or-accent);
  font-size: 0.86rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.2;
  white-space: nowrap;
}

.or-hero-badge-quiet {
  border-color: var(--or-line);
  background: transparent;
  color: var(--or-muted);
  font-weight: 600;
}

/* Where to get help, directly under the CTA row. The same pill vocabulary as
   the badges above the headline, one size down, so the row reads as a quiet
   footnote to the buttons rather than as a second row of them. Plain <a>s, not
   Mantine Buttons: a Button clips a long label like the email address instead
   of letting the row wrap around it. */
.or-hero-support {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  margin-top: 14px;
}

.or-hero-support-label {
  margin-right: 2px;
  color: var(--or-muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.or-hero-support-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--or-line);
  border-radius: 999px;
  color: var(--or-ink);
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  transition: border-color 120ms ease, color 120ms ease, background-color 120ms ease;
}

.or-hero-support-link svg {
  color: var(--or-muted);
  transition: color 120ms ease;
}

.or-hero-support-link:hover,
.or-hero-support-link:focus-visible {
  border-color: var(--or-accent);
  background: rgba(32, 130, 74, 0.08);
  color: var(--or-accent);
}

.or-hero-support-link:hover svg,
.or-hero-support-link:focus-visible svg {
  color: var(--or-accent);
}

/* One stack per row inside the agent-directions modal, ruled apart so the
   label and the button that copies it read as a pair. */
.or-agent-row + .or-agent-row {
  border-top: 1px solid var(--or-line);
  padding-top: 12px;
}

/* The copy control names the thing being copied above two plainly labelled
   actions. The first action takes the requested page variant; the link remains
   secondary. It keeps its natural width in layouts that stretch buttons. */
.or-copy-directions {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  width: auto;
}

.or-copy-directions-actions .mantine-Button-root {
  flex: 0 0 auto;
}

.or-copy-directions-label {
  color: var(--or-muted);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1.2;
  text-transform: uppercase;
  user-select: none;
}

/* The artwork is a complete illustration — every card, token and connecting
   line is inside the file, right out to its last pixel column — so it is fitted
   whole. No cover crop, no negative margins, no over-100% width: any of those
   clip the corner cards off the composition. */
.or-hero-art {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  min-width: 0;
}

.or-hero-art img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  margin: 0;
  object-fit: contain;
  object-position: center;
}

/* Desktop only: the art column is the taller of the two, and the grid row
   takes its height, so an uncapped picture leaves a band of dead space under
   the hero's buttons before the points strip. Capping the picture's height
   keeps the row about as tall as the copy; width follows from the 4:3 ratio.
   The vacuum mascot overrides this below — that file is portrait, so the same
   cap renders it as a small sticker. */
@media (min-width: 992px) {
  .or-hero-art {
    display: flex;
    justify-content: center;
  }

  .or-hero-art img {
    width: auto;
    max-height: clamp(300px, 27vw, 400px);
  }
}

/* vacuum-0-shadows.webp is 1122×1402, portrait, with the character filling the
   width and empty canvas above and below. On desktop it should occupy the art
   column, not the 400px cap written for the landscape engraving. On a phone
   the same file at column width towers over the headline, so it is capped. */
.or-hero-art[data-hero-art="vacuum"] {
  width: clamp(220px, 52vw, 300px);
  margin-inline: auto;
}

.or-hero-art[data-hero-art="vacuum"] img {
  width: 100%;
  max-width: 100%;
  /* 196px of the 1402px canvas under the wheels is empty. A percentage margin
     is of the picture's width, which here is the image width, and
     196/1122 of that width is the film's rendered height. */
  margin-bottom: calc(-196 / 1122 * 100%);
}

/* Homepage only. The same picture is in normal flow on the integration
   pages; these offsets assume the full-bleed .or-hero grid. */
@media (min-width: 992px) {
  .or-hero .or-hero-art[data-hero-art="vacuum"] {
    position: absolute;
    z-index: 0;
    display: block;
    top: clamp(8px, 2vw, 32px);
    bottom: clamp(4px, 1vw, 16px);
    /* The right grid track runs to the window edge, so a box that fills it
       parks the mascot in the margin on a wide screen. Anchor its right edge
       to the same gutter the points row uses — the edge of the 1120px
       measure — and give it a fixed width so it sits beside the copy. */
    right: var(--or-gutter);
    left: auto;
    width: min(380px, 34vw);
    height: auto;
    margin: 0;
  }

  .or-hero .or-hero-art[data-hero-art="vacuum"] img {
    width: 100%;
    height: 100%;
    max-height: none;
    margin: 0;
    object-fit: contain;
    object-position: center right;
    transform: none;
  }
}

/* Scoped to the ENGRAVED sets. They are dense black linework on a transparent
   ground, so unlike the flat original they land at full ink strength right next
   to the headline and win the eye. Dropping the ink to ~60% pushes the art back
   into being a backdrop without touching the file.

   ONE KNOB: --or-hero-ink. Raise toward 1 for more presence, lower for fainter.

   Deliberately NOT a drop-shadow: filter: drop-shadow() traces the alpha
   channel, and this art's alpha is per-stroke stipple, so a shadow would double
   every line and read as mud rather than lift. */
.or-hero-art[data-hero-art^="engraved"] img {
  --or-hero-ink: 0.75;
  opacity: var(--or-hero-ink);
}

/* Scoped to the ORIGINAL set, which is opaque. That illustration is a rectangle
   of near-white ground with green flow lines running straight off its right
   edge, so butting it against the page paper leaves a visible box and a hard cut
   through the lines. Fading the outer few percent dissolves both. The stops
   clear the real content, which starts at 4.6% from the left and 9.2% from the
   top and ends at 97.4% down.

   The engraved set is drawn on a transparent ground and has no box to dissolve,
   so masking it would only eat its own linework. data-hero-art comes from
   AppController::HERO_ART_VARIANT. */
.or-hero-art[data-hero-art="original"] img {
  --or-art-fade:
    linear-gradient(to right, transparent, #000 4%, #000 95%, transparent),
    linear-gradient(to bottom, transparent, #000 5%, #000 98%, transparent);
  -webkit-mask-image: var(--or-art-fade);
  mask-image: var(--or-art-fade);
  /* Multiple mask layers default to `add`, which unions to fully opaque and
     undoes the fade. Both edges have to be intersected. */
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

/* The four-up summary strip under the hero: the same four claims the prose
   below spells out, in the scannable form the mockup uses. */
.or-hero-points {
  --or-gutter: calc((100vw - min(1120px, 100vw - 32px)) / 2);

  margin-inline: calc((100% - 100vw) / 2);
  padding-inline: var(--or-gutter);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(14px, 2vw, 26px);
  list-style: none;
  margin-block: 0;
}

/* The engraved crops carry a band of transparent film below the drawing — the
   desktop file's ink is 1391x963 inside 1448x1086, so ~5% of the rendered
   height under the coins is empty picture, not artwork. That film lands on top
   of the Stack's 48px section gap, which is what made the run-up to the points
   row read as a hole. Pulling the row up by the film's own height restores the
   gap the rest of the page uses. Scales with the art, because the film does. */
.or-hero + .or-hero-points {
  margin-top: calc(-1 * clamp(12px, 1.6vw, 26px));
}

/* The vacuum picture already pulls its own empty canvas back with
   margin-bottom, so the engraved-film pull above would overlap the points. */
.or-hero:has([data-hero-art="vacuum"]) + .or-hero-points {
  margin-top: 0;
}

.or-hero-point {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-width: 0;
}

.or-hero-point-icon {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: 40px;
  block-size: 40px;
  border: 1px solid var(--or-line);
  border-radius: 999px;
  background: #fff;
  color: var(--or-ink);
}

.or-hero-point-title {
  color: var(--or-accent);
  font-size: 0.97rem;
  font-weight: 700;
  line-height: 1.3;
}

.or-hero-point-body {
  margin-top: 2px;
  color: var(--or-muted);
  font-size: 0.92rem;
  line-height: 1.45;
}

.or-hero-detail {
  display: flow-root;
}

@media (max-width: 991px) {
  .or-hero {
    grid-template-columns: minmax(0, 1fr);
    gap: 12px;
    padding-inline-end: var(--or-gutter);
  }

  .or-hero-title {
    max-width: 14ch;
  }

  .or-hero-copy .or-hero-lede {
    max-width: none;
  }

  .or-hero-art img {
    width: min(560px, 100%);
    margin: 0 auto;
  }

  .or-hero-points {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 575px) {
  /* On the narrowest phones the secondary CTAs pair up two to a row instead of
     stacking one per row. */
  .or-hero-actions .mantine-Button-root {
    flex: 1 1 9.5rem;
  }

  /* ...but not the copy control: a row of its own (basis 100%), on which its
     three segments keep their natural width rather than pairing up — and the
     button beside it would otherwise be squeezed into the leftover and clip. */
  .or-hero-actions .or-hero-cta-primary {
    flex: 0 0 100%;
  }

  .or-hero-actions .or-hero-cta-primary .mantine-Button-root {
    flex: 0 0 auto;
  }

  .or-hero-points {
    grid-template-columns: minmax(0, 1fr);
  }

}

.or-coin-icon {
  display: block;
  flex: none;
}

/* The inbound-currency chips under the swap paragraph. A quiet wrapping row,
   not a table: it is a supporting detail, and the page was reading it as a
   headline. */
.or-currencies {
  scroll-margin-top: 88px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.or-currency {
  border: 1px solid var(--mantine-color-gray-3);
  border-radius: 999px;
  padding: 3px 10px 3px 4px;
  background: #fff;
}

.or-lede {
  max-width: 860px;
  margin-top: 18px;
  color: var(--or-muted);
  font-size: 1.08rem;
  line-height: 1.7;
}

/* The safety callout in the hero detail. It shares the quick-setup panel's
   green so the two "this is the safe path" surfaces read as one voice. */
.or-safety {
  max-width: 860px;
  margin-top: 22px;
  padding: 18px 22px;
  border: 1px solid #38b46b;
  border-left-width: 6px;
  border-radius: 10px;
  background: linear-gradient(180deg, #f2fbf5 0%, #ffffff 100%);
  box-shadow: 0 12px 32px rgba(32, 130, 74, 0.1);
}

.or-safety-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  color: #20824a;
  font-size: 1.25rem !important;
  line-height: 1.2 !important;
}

.or-safety-body {
  margin-top: 10px;
  color: var(--or-ink);
  font-size: 1.08rem;
  line-height: 1.7;
}

.or-safety-body + .or-safety-body {
  margin-top: 6px;
  color: var(--or-muted);
  font-size: 1rem;
}

.or-quicksetup {
  background: linear-gradient(180deg, #f2fbf5 0%, #ffffff 62%);
  border-color: #38b46b !important;
  box-shadow: 0 16px 40px rgba(32, 130, 74, 0.1);
  scroll-margin-top: 88px;
}

.or-quicksetup-title {
  font-size: 1.9rem !important;
  line-height: 1.15 !important;
}

.or-quicksetup-lede {
  max-width: 720px;
  margin-top: 10px;
  color: var(--or-muted);
  font-size: 1.05rem;
  line-height: 1.65;
}

.or-quicksetup-step {
  max-width: 760px;
  padding-left: 18px;
  border-left: 3px solid #38b46b;
}

.or-quicksetup-step-title {
  font-size: 1.12rem;
  font-weight: 700;
}

.or-quicksetup-optional {
  color: var(--or-muted);
  font-weight: 500;
}

.or-quicksetup-step-body {
  margin-top: 8px;
  color: var(--or-muted);
  line-height: 1.65;
}

.or-quicksetup-step-wide {
  max-width: none;
}

.or-envvar-table {
  background: #fff;
}

.or-envvar-tabs .mantine-Tabs-list {
  gap: 8px;
}

.or-envvar-platform-title {
  font-size: 1.05rem !important;
}

.or-envvar-blurb {
  max-width: 760px;
  line-height: 1.65;
}

.or-envvar-howto-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  align-self: flex-start;
  padding: 6px 4px;
  border-radius: 8px;
  color: var(--or-ink);
}

.or-envvar-howto-toggle:hover {
  color: var(--or-accent);
}

.or-envvar-howto-chevron {
  flex: none;
  transition: transform 150ms ease;
}

.or-envvar-howto-chevron[data-open] {
  transform: rotate(90deg);
}

.or-heading {
  scroll-margin-top: 88px;
}

.or-heading-row {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.or-inline-code {
  font-size: 0.86em;
}

.or-md p:last-child {
  margin-bottom: 0;
}

/* The BTCPay README's screenshots: raw <img width=…> tags sized by the
   author (260–600px), capped at the column so none overflows a phone. */
.or-md img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 8px 0 16px;
  border: 1px solid var(--or-line);
  border-radius: 8px;
}

/* The README's demo video — in place of its GitHub attachment URL on the
   page, under the title in the wizard. Poster and file are the contract's
   assets[], served under /assets/. */
.or-video {
  display: block;
  width: 100%;
  max-width: 720px;
  margin: 8px 0 16px;
  border: 1px solid var(--or-line);
  border-radius: 12px;
  background: #000;
}

.or-docs-layout,
.or-guide-layout {
  display: grid;
  grid-template-columns: minmax(220px, 260px) minmax(0, 1fr);
  gap: 28px;
  align-items: start;
}

.or-docs-toc,
.or-guide-toc {
  position: sticky;
  top: 16px;
}

.or-api-entry,
.or-guide-card {
  background: #fff;
}

.or-guide-card {
  text-decoration: none;
  color: inherit;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

.or-guide-card:hover {
  border-color: #38b46b;
  box-shadow: 0 10px 28px rgba(32, 130, 74, 0.08);
}

.app-fatal {
  max-width: 640px;
  margin: 3rem auto;
  padding: 1.5rem;
  border: 1px solid #e8c4c0;
  border-radius: 12px;
  background: #fff6f4;
  color: #7a2218;
}

@media (max-width: 900px) {
  .or-docs-layout,
  /* minmax(0, …) as on desktop: a bare 1fr column is min-content wide, and a
     long code line or URL in the body would push the article past the phone. */
  .or-guide-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .or-docs-toc,
  .or-guide-toc {
    display: none;
  }
}

/* ============================================================================
   The framework landing pages (/integrations/<id>) and the homepage grid that
   links them. Built from the quick-setup vocabulary above so the two read as
   one site. The hero shares HeroMascot with the homepage; the sections below
   stay on the 860px measure.
   ========================================================================= */

.or-frameworks {
  scroll-margin-top: 88px;
}

.or-frameworks-title,
.or-integration-title {
  line-height: 1.12 !important;
}

.or-frameworks-title {
  font-size: 1.9rem !important;
}

.or-frameworks-lede,
.or-integration-requires {
  max-width: 720px;
  margin-top: 10px;
  color: var(--or-muted);
  font-size: 1.05rem;
  line-height: 1.65;
}

.or-framework-cards,
.or-benefit-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 16px;
  margin: 24px 0 0;
  padding: 0;
  list-style: none;
}

/* Three across at the full measure: wide enough for "Copy agent directions"
   to fit a card's button without clipping, and five cards fall 3 + 2 rather
   than 4 + 1. */
.or-framework-cards {
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px;
  margin: 24px 0 0;
  padding: 0;
  list-style: none;
}

.or-framework-card,
.or-benefit-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px;
  border: 1px solid var(--or-line);
  border-radius: 14px;
  background: #fff;
}

.or-framework-card {
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

.or-framework-card:hover,
.or-framework-card:focus-within {
  border-color: #38b46b;
  box-shadow: 0 10px 28px rgba(32, 130, 74, 0.08);
}

.or-framework-card-link {
  flex: 1;
  color: inherit;
  text-decoration: none;
}

/* The project's own mark, at one height so the row of cards reads evenly
   whatever each file's own proportions are. Wordmarks (Express, Fastify,
   Rails) run wide; the Next and BTCPay marks are near-square. */
.or-framework-logo {
  display: block;
  height: 36px;
  margin-bottom: 14px;
}

.or-framework-logo img {
  display: block;
  height: 100%;
  width: auto;
  max-width: 100%;
}

.or-integration-logo {
  height: 52px;
  margin-bottom: 18px;
}

.or-framework-card-title,
.or-benefit-title {
  font-size: 1.15rem;
  font-weight: 750;
}

.or-framework-card-pitch,
.or-benefit-body {
  margin-top: 6px;
  color: var(--or-muted);
  line-height: 1.55;
}

.or-framework-card-requires {
  margin-top: 10px;
  color: var(--or-muted);
  font-size: 0.82rem;
}

.or-framework-card-actions > .mantine-Button-root {
  flex: 1 1 auto;
}

/* Copy on the left, the shared mascot on the right. Below 992px the picture
   drops under the copy at the phone size in .or-hero-art[data-hero-art="vacuum"]. */
.or-integration-hero {
  max-width: none;
  padding-top: 8px;
}

@media (min-width: 992px) {
  .or-integration-hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(200px, 300px);
    align-items: center;
    column-gap: clamp(16px, 3vw, 36px);
  }

  .or-integration-hero .or-hero-art[data-hero-art="vacuum"] {
    width: 100%;
    margin: 0;
  }
}

.or-integration-title {
  max-width: 18ch;
  font-size: clamp(2rem, 4.2vw, 3rem) !important;
  letter-spacing: -0.01em;
}

.or-integration-lede {
  max-width: 640px;
  margin-top: 16px;
}

.or-integration-copy-caption {
  max-width: 640px;
  margin-top: 12px;
}

.or-integration-copy-caption svg {
  vertical-align: -2px;
}

.or-integration-demo,
.or-integration-benefits,
.or-integration-video,
.or-integration-faq,
.or-integration-next {
  scroll-margin-top: 88px;
}

.or-integration-demo .or-demo-lede,
.or-integration-video .or-demo-video-player {
  margin-bottom: 20px;
}

.or-integration-video {
  max-width: 860px;
}

.or-integration-video .or-demo-video-player {
  margin-top: 18px;
}

.or-integration-setup {
  background: linear-gradient(180deg, #f2fbf5 0%, #ffffff 62%);
  border-color: #38b46b !important;
  box-shadow: 0 16px 40px rgba(32, 130, 74, 0.1);
}

.or-integration-code {
  margin-top: 10px;
  max-width: 860px;
}

/* Practical questions: a definition list, one rule between items. */
.or-faq {
  max-width: 860px;
  margin: 20px 0 0;
}

.or-faq-item + .or-faq-item {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--or-line);
}

.or-faq dt {
  font-size: 1.05rem;
  font-weight: 750;
}

.or-faq dd {
  margin: 6px 0 0;
  color: var(--or-muted);
  line-height: 1.65;
}

.or-faq dd svg {
  vertical-align: -2px;
}

/* BTCPay's page: the plugin walkthrough instead of the shared checkout. */
/* One column at the author's own size (the crops range from a 260px button
   to a 600px form), capped at the README's 600px — a grid of equal cells made
   the crops enormous and the full screens tiny. */
.or-screenshots {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 600px;
  margin: 24px 0 0;
  padding: 0;
  list-style: none;
  counter-reset: screenshot;
}

.or-screenshot {
  counter-increment: screenshot;
}

.or-screenshot img {
  display: block;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--or-line);
  border-radius: 10px;
  background: #fff;
}

.or-screenshot .mantine-Text-root::before {
  content: counter(screenshot) ". ";
  font-weight: 700;
}

/* The header's Frameworks menu trigger sits in the nav row as a link would. */
.or-nav-menu {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.95rem;
}

.or-footer-frameworks {
  width: 100%;
  margin-bottom: 10px;
}

.or-footer {
  flex-wrap: wrap;
}

/* The round Bitcoin mark that opens the "Bitcoin by default." lede. Mantine's
   Image is display:block by default, which would drop it onto its own line. */
.or-lede-bitcoin {
  display: inline-block;
  vertical-align: -4px;
  margin-right: 4px;
}

/* ============================================================================
   The shop, copied from the OpenReceive buttons demo
   (examples/buttons/shared/shop.css) and kept a copy on purpose: the panel's
   markup is a verbatim copy of that demo's components, so its stylesheet is
   too. Everything above this line is this site's own.

   The five `--or-*` tokens the rules below are built from are already declared
   at the top of this file, with the same values, so the demo's `:root`, `body`
   and `.or-page` blocks are the only thing left out — this site owns its own
   page ground.
   ========================================================================= */

/* The site's own introduction to the shop below it. This lives above the shop
   divider because the shop section of this file is a copy of the demo's
   shop.css; the demo has no equivalent of this heading, and never should. */
.or-demo-video {
  max-width: 860px;
  margin: 56px 0 0;
}

.or-demo-video-player {
  display: block;
  width: 100%;
  margin-top: 18px;
  border: 1px solid var(--or-line);
  border-radius: 12px;
  background: #000;
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.12);
}

.or-demo-intro {
  max-width: 860px;
  margin: 56px 0 20px;
}

.or-demo-title {
  font-size: 1.9rem !important;
  line-height: 1.15 !important;
}

.or-demo-lede {
  margin-top: 10px;
  color: var(--or-muted);
  font-size: 1.05rem;
  line-height: 1.65;
}

/* ---------------------------------------------------------------- the shop */

/* THE PANEL IS AS TALL AS ITS CONTENT. There is no fixed height and nothing
   inside the panel scrolls on its own: the page is the only scroller, so a
   viewport with room to spare shows the whole stage rather than a short box
   with a scrollbar in it.

   That trades a stable box for a taller one, so the way this stays honest is
   HORIZONTAL: the checkout splits into a summary column and a payment column at
   880px, and the QR moves beside its copy rows at 1000px. Anything new that is
   tall enough to push a desktop checkout past the fold belongs in one of those
   columns, not under them.

   Consequences for anything new inside the panel: a stage puts its content in
   an `.or-shop-stage` and its actions in an `.or-shop-footer`; empty and
   loading states still RESERVE height, because a feed that collapses to nothing
   while it loads still moves the page under the pointer. */
.or-shop {
  display: flex;
  flex-direction: column;
  overflow: hidden; /* corner clipping; the panel never scrolls */
  background: #fff;
}

/* No border-bottom: the tab strip below it owns that rule. Two hairlines 44px
   apart is what putting one here gets you. */
.or-shop-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 18px 20px 12px;
}

.or-shop-subtitle {
  font-size: 0.85rem;
  color: var(--or-muted);
  max-width: 42ch;
}

.or-shop-tabs {
  padding: 0 20px 10px;
  border-bottom: 1px solid var(--or-line);
}

.or-shop-body {
  display: flex;
  flex-direction: column;
}

.or-shop-stage {
  padding: 16px 20px;
}

.or-shop-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 20px;
  border-top: 1px solid var(--or-line);
  background: var(--or-paper);
}

/* ---- catalog */

.or-shop-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 14px;
}

.or-shop-card {
  border: 1px solid var(--or-line);
  border-radius: 12px;
  padding: 10px;
  display: grid;
  gap: 6px;
  justify-items: center;
  background: #fff;
}

/* Selection is an inset ring, never a background change — a background would
   fight the product photography. */
.or-shop-card[data-in-cart] {
  border-color: var(--or-accent);
  box-shadow: 0 0 0 1px var(--or-accent) inset;
}

.or-shop-card-image {
  width: 100%;
  max-width: 120px;
  aspect-ratio: 1 / 1;
  object-fit: contain;
}

.or-shop-card-name {
  font-size: 0.85rem;
  font-weight: 700;
}

.or-shop-card-price {
  font-size: 0.85rem;
  color: var(--or-muted);
}

.or-shop-card-qty {
  min-width: 22px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

/* ---- checkout */

/* Summary on the left, the thing the payer has to act on on the right. Below
   880px it is one column in source order, so the order and its status stay
   above the QR code. */
.or-checkout {
  display: grid;
  gap: 16px;
  align-items: start;
}

/* A grid item's automatic minimum size is its content; a bolt11 would blow the
   track out without this. */
.or-checkout-summary,
.or-checkout-pay,
.or-checkout-return,
.or-checkout-record {
  min-width: 0;
}

@media (min-width: 880px) {
  .or-checkout {
    grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
    gap: 24px;
  }

  .or-checkout-record {
    grid-column: 1 / -1;
  }

  /* The return link follows payment controls on mobile and in reading order.
     A desktop sidebar keeps it directly below the summary. */
  .or-checkout:has(> .or-checkout-return) {
    grid-template-rows: min-content 1fr auto;
  }

  .or-checkout:has(> .or-checkout-return) > .or-checkout-pay {
    grid-column: 2;
    grid-row: 1 / span 2;
  }

  .or-checkout-return {
    grid-column: 1;
    grid-row: 2;
  }
}

/* The packaged checkout prints the order description above its own panel. In
   the stack that embeds it (node-express) the summary column beside it already
   shows the product — its picture, its name and its price — so the library's
   line is the same sentence twice on one screen. Hidden through the two seams
   the packages expose for exactly this: the data attribute React stamps on it,
   and the shadow part the element wrappers expose. */
.or-checkout-pay [data-openreceive-order-description],
.or-checkout-pay openreceive-checkout::part(order-description) {
  display: none;
}

/* A QR code and the values beside it, rather than under them: stacked, the pair
   is ~380px of height that a desktop checkout does not have to spend. The `auto`
   track is the QR at its natural size. */
.or-shop-payload {
  display: grid;
  gap: 12px;
  align-items: start;
}

.or-shop-payload > * {
  min-width: 0;
}

/* A rail whose payload has no QR code (or a QR that has not rendered yet) gets
   the whole width rather than the narrow `auto` track meant for the code. */
.or-shop-payload > :only-child {
  grid-column: 1 / -1;
}

@media (min-width: 1000px) {
  .or-shop-payload {
    grid-template-columns: auto minmax(0, 1fr);
    gap: 16px;
  }
}

.or-shop-order-strip {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--or-line);
  border-radius: 10px;
  background: var(--or-paper);
}

.or-shop-order-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  flex-shrink: 0;
}

.or-shop-order-thumbs img {
  width: 34px;
  height: 34px;
  object-fit: contain;
  border-radius: 4px;
  border: 1px solid var(--or-line);
  background: #fff;
}

.or-shop-order-title {
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.3;
}

.or-shop-order-total {
  font-size: 1.05rem;
  font-weight: 800;
}

.or-shop-status {
  border: 1px solid var(--or-line);
  border-left: 3px solid var(--or-muted);
  border-radius: 8px;
  padding: 10px 12px;
}

.or-shop-status[data-phase="settled"] {
  border-left-color: var(--or-accent);
}

.or-shop-status[data-phase="expired"],
.or-shop-status[data-phase="failed"],
.or-shop-status[data-phase="cancelled"] {
  border-left-color: #b4322c;
}

.or-shop-status-title {
  font-weight: 700;
  font-size: 0.95rem;
}

.or-shop-status-detail,
.or-shop-status-countdown {
  font-size: 0.85rem;
  color: var(--or-muted);
}

.or-shop-status-countdown {
  margin-top: 4px;
  font-variant-numeric: tabular-nums;
}

.or-shop-section-title {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--or-muted);
}

.or-shop-methods {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
}

.or-shop-method {
  border: 1px solid var(--or-line);
  border-radius: 10px;
  padding: 10px;
  display: grid;
  gap: 2px;
  justify-items: center;
  text-align: center;
  background: #fff;
}

.or-shop-method[data-selected] {
  border-color: var(--or-accent);
  box-shadow: 0 0 0 1px var(--or-accent) inset;
}

.or-shop-method:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.or-shop-method-title {
  font-size: 0.9rem;
  font-weight: 700;
}

.or-shop-method-detail {
  font-size: 0.75rem;
  color: var(--or-muted);
  line-height: 1.3;
}

/* A <fieldset>: these are mutually exclusive choices, and `aria-labelledby`
   means nothing on an element with no grouping role. The three resets below
   undo the browser's default fieldset chrome — in particular
   `min-inline-size: min-content`, which would stop it shrinking inside the
   panel. */
.or-shop-networks {
  margin: 0;
  min-inline-size: 0;
  border: 1px solid var(--or-line);
  border-radius: 10px;
  padding: 10px 12px;
}

.or-shop-network {
  display: flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--or-line);
  border-radius: 999px;
  padding: 6px 12px;
  background: #fff;
}

.or-shop-network[data-selected] {
  border-color: var(--or-accent);
  color: var(--or-accent);
}

.or-shop-qr {
  display: grid;
  place-items: center;
  padding: 12px;
  background: #fff;
  border: 1px solid var(--or-line);
  border-radius: 10px;
}

.or-shop-qr svg {
  width: 208px;
  height: 208px;
}

/* Every value the payer has to reproduce: a label, the value, a copy button.
   `overflow-wrap: anywhere` because a bolt11 is one 300-character word. */
.or-shop-copyrow {
  display: grid;
  gap: 3px;
}

/* A grid item's automatic minimum size is its CONTENT, so the nowrap value row
   below would size the track to a 300-character invoice and push the copy
   button off the panel. Grid items opt out of that with min-width: 0. */
.or-shop-copyrow > * {
  min-width: 0;
}

.or-shop-copyrow-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--or-muted);
}

.or-shop-copyrow-value {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: 0.8rem;
  padding: 5px 8px;
}

/* THE ELLIPSIS IS CSS, NEVER THE STRING. A bolt11 is 300 characters and does
   not get five lines of the panel, but shortening it in JavaScript puts a `…`
   in the middle of the text a payer can select — and with `user-select: all`
   below, one click hands them a broken invoice that looks like a whole one.
   The full value stays in the DOM and the overflow is hidden, so a selection
   copies the real string whatever is on screen. */
.or-shop-copyrow-value[data-truncate] {
  overflow: hidden;
  overflow-wrap: normal;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A deposit address or an invoice should select whole on one click. */
.or-shop-copyrow-value[data-selectable] {
  user-select: all;
}

.or-shop-wallets {
  border-top: 1px solid var(--or-line);
  padding-top: 12px;
}

.or-shop-wallet-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 8px;
}

.or-shop-wallet {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--or-line);
  border-radius: 8px;
  padding: 8px 10px;
  background: #fff;
  text-decoration: none;
  color: inherit;
}

/* Several registry marks are white-on-transparent, so they need something
   behind them to be visible at all on a white panel. */
.or-shop-wallet img {
  flex: none;
  border-radius: 4px;
  background: var(--or-paper);
}

.or-shop-fees,
.or-shop-refund,
.or-shop-keeplink {
  border: 1px solid var(--or-line);
  border-radius: 10px;
  padding: 10px 12px;
}

/* ---- the way back into an order

   The order id is the only thing a payer with no account can use to reach
   their payment again, so it gets a panel of its own on the checkout screen
   and a full-width alert on the refund screen. */

.or-shop-keeplink {
  display: grid;
  gap: 6px;
  background: var(--or-paper);
}

/* A refund REPLACES the deposit panel — no QR, no address, no amount — so the
   column it takes over is the whole width it had. */
.or-shop-refund-stage {
  min-width: 0;
}

/* The "already have an order id?" footnote under the catalog. Deliberately
   quiet: it is a way back for the payer who needs one, not a second entrance
   competing with the cart. */
.or-shop-resume {
  border-top: 1px solid var(--or-line);
  margin-top: 4px;
  padding-top: 10px;
  display: grid;
  gap: 6px;
}

.or-shop-resume-input {
  flex: 1;
  min-width: 0;
}

.or-shop-resume-hint {
  color: var(--or-muted);
  font-size: 0.75rem;
  margin: 0;
}

/* ---- transaction details, collapsed behind a caret */

.or-shop-details {
  border-top: 1px solid var(--or-line);
  padding-top: 10px;
}

.or-shop-details-toggle {
  width: 100%;
  text-align: left;
}

.or-shop-details-chevron {
  transition: transform 120ms ease;
}

.or-shop-details-chevron[data-open] {
  transform: rotate(90deg);
}

.or-shop-details-link {
  display: inline-block;
  margin-top: 2px;
}

/* ---- receipt */

.or-shop-paid-icon {
  color: var(--or-accent);
  flex: none;
}

.or-shop-receipt-line {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--or-line);
  border-radius: 10px;
  padding: 8px 10px;
}

.or-shop-receipt-copy {
  flex: 1;
  min-width: 0;
}

/* ---- the recent-orders feed */

.or-shop-feed {
  display: grid;
  gap: 8px;
}

.or-shop-feed-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--or-line);
  border-radius: 10px;
  padding: 8px 10px;
  background: #fff;
}

/* Same inset ring the cart card and the method tile use, for the same reason:
   "this one is yours" is the same kind of statement as "this one is chosen". */
.or-shop-feed-row[data-you] {
  border-color: var(--or-accent);
  box-shadow: 0 0 0 1px var(--or-accent) inset;
}

.or-shop-feed-thumbs {
  display: flex;
  gap: 4px;
  flex: none;
}

.or-shop-feed-thumbs img {
  width: 28px;
  height: 28px;
  border-radius: 4px;
  border: 1px solid var(--or-line);
  background: #fff;
  object-fit: contain;
}

/* min-width:0 is what lets the title ellipsize inside a 1fr grid track. */
.or-shop-feed-main {
  min-width: 0;
}

.or-shop-feed-title {
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.or-shop-feed-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
  font-size: 0.75rem;
  color: var(--or-muted);
}

/* The buyer's public uuid. Monospace so eight hex characters are comparable at
   a glance across rows; the full value lives in the title attribute. */
.or-shop-feed-buyer {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
}

.or-shop-feed-you {
  border: 1px solid var(--or-accent);
  color: var(--or-accent);
  border-radius: 999px;
  padding: 0 6px;
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 1.6;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.or-shop-feed-amount {
  font-size: 0.95rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Reserves height rather than collapsing, so switching to an empty feed does
   not change the panel's inner geometry. */
.or-shop-feed-empty {
  display: grid;
  place-items: center;
  align-content: center;
  gap: 4px;
  min-height: 180px;
  text-align: center;
  font-size: 0.9rem;
  color: var(--or-muted);
}

/* Same box as a real row, so the first paint and the loaded paint are the same
   shape. Three of these while loading. */
.or-shop-feed-skeleton {
  height: 46px;
  border: 1px solid var(--or-line);
  border-radius: 10px;
  background: linear-gradient(90deg, var(--or-paper), #fff, var(--or-paper));
  background-size: 200% 100%;
  animation: or-shop-feed-shimmer 1.4s linear infinite;
}

@keyframes or-shop-feed-shimmer {
  from {
    background-position: 200% 0;
  }
  to {
    background-position: -200% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .or-shop-feed-skeleton {
    animation: none;
  }

  .or-shop-details-chevron {
    transition: none;
  }
}

.or-shop-feed-totals {
  font-size: 0.8rem;
  color: var(--or-muted);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 600px) {
  .or-shop-qr svg {
    width: 168px;
    height: 168px;
  }

  /* At 360px the thumbnails cost more than they say. The row still carries the
     product name, the buyer and the amount. */
  .or-shop-feed-row {
    grid-template-columns: 1fr auto;
  }

  .or-shop-feed-thumbs {
    display: none;
  }
}


/*# sourceMappingURL=js-heavy.css.map*/