/* ==========================================================================
   READY JET ROAM — DESIGN SYSTEM v1
   --------------------------------------------------------------------------
   Target: GeneratePress child theme (vanilla PHP/CSS, no build step).

   Structure
     01  Fonts
     02  Tokens
     03  Base
     04  Layout
     05  Typography components
     06  Buttons & links
     07  Cards
     08  Polaroid photo frames
     09  Tape
     10  Callouts & quotes
     11  Book cover
     12  Screenshot card
     13  Forms
     14  Site chrome (header / footer)
     15  Section-specific composition
     16  Motion
     17  Responsive
     18  Spacing utilities

   Notes for the child-theme port
     - Nothing here is layered (@layer) on purpose: GeneratePress ships
       unlayered CSS, which would otherwise always win.
     - Every custom property is prefixed `--rjr-` so it can be dropped into
       theme.json / GP customizer presets without collisions.
     - Paths are relative to this file (css/ → ../img/).
   ========================================================================== */


/* ==========================================================================
   01  FONTS
   Blacker Sans Display + Breathing are licensed assets - keep them out of any
   public repo until the web licence is confirmed for the live domain.

   Poppins is SIL OFL and self-hosted, so there is no Google Fonts request and
   nothing for the cookie banner to disclose. Four weights, because 400/500/600/700
   are the only ones the system actually uses. Split latin / latin-ext by
   unicode-range exactly as Google serves it, so a page of English text only ever
   downloads the latin file and the accented subset stays available for
   translations without costing anything up front.
   ========================================================================== */

@font-face {
  font-family: "Blacker Sans Display";
  src: url("../fonts/BlackerSansDisplay-Light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Blacker Sans Display";
  src: url("../fonts/BlackerSansDisplay-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Blacker Sans Display";
  src: url("../fonts/BlackerSansDisplay-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Breathing";
  src: url("../fonts/Breathing.woff2") format("woff2"),
       url("../fonts/Breathing.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Poppins — body face. Ranges are Google's own, kept verbatim. */
@font-face {
  font-family: "Poppins";
  src: url("../fonts/poppins-400-latin-ext.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Poppins";
  src: url("../fonts/poppins-400-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Poppins";
  src: url("../fonts/poppins-500-latin-ext.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Poppins";
  src: url("../fonts/poppins-500-latin.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Poppins";
  src: url("../fonts/poppins-600-latin-ext.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Poppins";
  src: url("../fonts/poppins-600-latin.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Poppins";
  src: url("../fonts/poppins-700-latin-ext.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Poppins";
  src: url("../fonts/poppins-700-latin.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}


/* ==========================================================================
   02  TOKENS
   ========================================================================== */

:root {
  /* --- Core ramps ------------------------------------------------------- */

  /* Navy — primary: headings, body text, borders, paint bars */
  --rjr-navy-50:  #EBF2F4;
  --rjr-navy-100: #D3E2E6;
  --rjr-navy-200: #A7C4CD;
  --rjr-navy-300: #7BA7B3;
  --rjr-navy-400: #458395;
  --rjr-navy-500: #0A5C73;
  --rjr-navy-600: #094F62;
  --rjr-navy-700: #084252;
  --rjr-navy-800: #063442;
  --rjr-navy-900: #052731;
  /* Footer only. navy-800 against navy-900 is a 1.18:1 step, near enough to
     invisible, so the newsletter section and the footer read as one navy slab
     over a thousand pixels tall. This takes the step to 1.37:1, which is what
     makes the footer land as its own zone without another texture. */
  --rjr-navy-950: #03181F;

  /* Teal — secondary: logo mark, light accents */
  --rjr-teal-50:  #EFF8F7;
  --rjr-teal-100: #DCEFEE;
  --rjr-teal-200: #B8DFDD;
  --rjr-teal-300: #95CFCC;
  --rjr-teal-400: #69BBB7;
  --rjr-teal-500: #3AA6A0;
  --rjr-teal-600: #32928C;
  --rjr-teal-700: #2A7D78;
  --rjr-teal-800: #226864;
  --rjr-teal-900: #1A5450;

  /* Orange — accent: CTAs, checkmarks, script highlight, brush strokes */
  --rjr-orange-50:  #FEF5ED;
  --rjr-orange-100: #FDE9D7;
  --rjr-orange-200: #FBD2AE;
  --rjr-orange-300: #F9BC86;
  --rjr-orange-400: #F7A155;
  --rjr-orange-500: #F4831F;
  --rjr-orange-600: #DA751B;
  --rjr-orange-700: #C06617;
  --rjr-orange-800: #A65813;
  --rjr-orange-900: #8C4A0F;

  /* Neutrals — never pure white */
  --rjr-cream: #F0F0EA;
  --rjr-paper: #EDEBE2;
  --rjr-text:  #1A3A4A;

  /* --- Semantic --------------------------------------------------------- */
  --rjr-bg: var(--rjr-cream);
  --rjr-surface: var(--rjr-paper);
  --rjr-surface-card: #FFFFFF;          /* polaroid mattes only, never page bg */

  --rjr-text-primary: var(--rjr-text);
  --rjr-text-muted: var(--rjr-navy-400);
  --rjr-text-on-navy: var(--rjr-cream);
  --rjr-text-on-orange: #FFFFFF;

  /* Accent = decorative orange: script words, paint blocks, quote marks,
     badges. Never an action. */
  --rjr-accent: var(--rjr-orange-500);
  --rjr-accent-hover: var(--rjr-orange-600);

  /* CTA = teal, always. Split from --rjr-accent so "what you click" and
     "what's decorative" can never drift apart again. Every button reads
     from these three and nothing else. */
  --rjr-cta: var(--rjr-navy-600);
  --rjr-cta-hover: var(--rjr-teal-700);
  --rjr-cta-text: #FFFFFF;
  /* Teal as *text* on a light background needs to go two stops darker than the
     fill to clear 4.5:1 — teal-700 on cream is only 4.27:1, teal-800 is 5.7:1. */
  --rjr-cta-ink: var(--rjr-teal-800);

  --rjr-border: var(--rjr-navy-200);

  --rjr-callout-affiliate-bg: var(--rjr-navy-50);
  --rjr-callout-tip-bg: var(--rjr-paper);

  /* Per-section contextual colours. Section tone classes reassign these so
     every component inside inherits the right palette with no extra selectors. */
  --rjr-ctx-text: var(--rjr-text-primary);
  --rjr-ctx-muted: var(--rjr-navy-400);
  --rjr-ctx-heading: var(--rjr-navy-800);
  --rjr-ctx-border: var(--rjr-border);
  --rjr-ctx-card-bg: #FBFAF5;   /* opaque paper stock, a touch lighter than cream */

  /* --- Type ------------------------------------------------------------- */
  --rjr-font-display: "Blacker Sans Display", Poppins, "Segoe UI", sans-serif;
  --rjr-font-script: "Breathing", "Caveat", cursive;
  --rjr-font-body: Poppins, "Segoe UI", sans-serif;

  --rjr-text-xs:   clamp(0.75rem, 0.71rem + 0.19vw, 0.875rem);
  --rjr-text-sm:   clamp(0.875rem, 0.83rem + 0.24vw, 1rem);
  --rjr-text-base: clamp(1rem, 0.94rem + 0.28vw, 1.125rem);
  --rjr-text-lg:   clamp(1.125rem, 1.05rem + 0.38vw, 1.375rem);
  --rjr-text-xl:   clamp(1.375rem, 1.26rem + 0.57vw, 1.75rem);
  --rjr-text-2xl:  clamp(1.75rem, 1.57rem + 0.85vw, 2.25rem);
  --rjr-text-3xl:  clamp(2.25rem, 1.99rem + 1.23vw, 3rem);
  /* Ceiling lowered from 4.25rem to 3.5rem. This is the h1 size for the WHOLE
     site, not just the front page - About, Contact, Shop and Our
     Recommendations all read it through `h1 { font-size: var(--rjr-text-4xl) }`.
     The cap starts biting around 765px wide, so 3.5rem is effectively the
     desktop and tablet size and the fluid part only does anything on phones. */
  --rjr-text-4xl:  clamp(3rem, 2.6rem + 1.89vw, 3.5rem);
  --rjr-text-5xl:  clamp(3.75rem, 3.2rem + 2.6vw, 5.5rem);

  --rjr-measure: 65ch;

  /* --- Space ------------------------------------------------------------ */
  --rjr-space-1:  0.25rem;
  --rjr-space-2:  0.5rem;
  --rjr-space-3:  0.75rem;
  --rjr-space-4:  1rem;
  --rjr-space-6:  1.5rem;
  --rjr-space-8:  2rem;
  --rjr-space-12: 3rem;
  --rjr-space-16: 4rem;
  --rjr-space-24: 6rem;

  /* Global section rhythm dial. Every section's vertical padding is multiplied
     by this, so the whole page's breathing room is one number. Currently 1.2
     — a 20% increase on the original spacing, at every viewport (it scales the
     clamp's floor, slope and ceiling alike). */
  --rjr-section-rhythm: 1.2;

  --rjr-section-padding-y: calc(clamp(3rem, 2.2rem + 4vw, 8rem) * var(--rjr-section-rhythm));

  /* Fluid rhythm scale — layout spacing, and what the spacing utilities use.
     Deliberately separate from --rjr-space-1..24 above: those are fixed and
     belong inside components, where a value must not shift with the viewport.
     These are for the gaps *between* blocks, which should. */
  --rjr-space-xs: clamp(0.75rem, 0.60rem + 0.60vw, 1.25rem);
  --rjr-space-sm: clamp(1.25rem, 1.00rem + 1.10vw, 2.00rem);
  --rjr-space-md: clamp(2.00rem, 1.60rem + 1.90vw, 3.50rem);
  --rjr-space-lg: clamp(3.00rem, 2.30rem + 3.10vw, 5.50rem);
  --rjr-space-xl: clamp(4.50rem, 3.40rem + 4.80vw, 8.50rem);
  --rjr-section-padding-x: clamp(1.25rem, 1rem + 2vw, 4rem);
  --rjr-container: 1200px;
  --rjr-container-wide: 1360px;

  /* --- Shape & depth ---------------------------------------------------- */
  /* Overwritten by navigation.js with the measured value; see .rjr-header. */
  --rjr-header-h: 102px;

  --rjr-radius-sm: 6px;
  --rjr-radius:    14px;
  --rjr-radius-lg: 22px;
  --rjr-radius-pill: 999px;

  --rjr-shadow-sm: 0 1px 2px rgba(5, 39, 49, 0.06),
                   0 2px 6px rgba(5, 39, 49, 0.05);
  --rjr-shadow-md: 0 2px 4px rgba(5, 39, 49, 0.06),
                   0 10px 24px rgba(5, 39, 49, 0.09);
  --rjr-shadow-lg: 0 4px 8px rgba(5, 39, 49, 0.07),
                   0 22px 48px rgba(5, 39, 49, 0.14);

  /* --- Assets ----------------------------------------------------------- */
  --rjr-img-paint-navy:   url("../img/navy-swoosh.png");
  --rjr-img-paint-teal:   url("../img/green-swoosh.png");
  --rjr-img-paint-orange: url("../img/orange-swoosh.png");
  --rjr-img-paint-blue:   url("../img/blue-swoosh.png");

  /* Icons: Phosphor 2.1.2 (MIT), loaded from the CDN in the document head.
     Nothing to declare here — glyphs come from the `ph-*` classes. */

  /* --- Motion ----------------------------------------------------------- */
  --rjr-ease: cubic-bezier(0.22, 0.68, 0.35, 1);
  --rjr-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --rjr-dur-fast: 160ms;
  --rjr-dur: 260ms;
  --rjr-dur-slow: 700ms;
}


/* ==========================================================================
   03  BASE
   In the child theme most of this is handled by GP's global typography;
   it is kept here so the prototype is self-contained.
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;   /* fallback only — Lenis takes over when present */
}

/* Lenis owns scrolling once it initialises; native smooth behaviour fights it. */
html.lenis { scroll-behavior: auto; }
html.lenis,
html.lenis body { height: auto; }
.lenis.lenis-smooth iframe { pointer-events: none; }
.lenis:not(.lenis-autoToggle).lenis-stopped { overflow: clip; }
.lenis [data-lenis-prevent] { overscroll-behavior: contain; }

body {
  margin: 0;
  background-color: var(--rjr-bg);
  color: var(--rjr-text-primary);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-base);
  font-weight: 400;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Same clip-not-hidden fix the sections already carry, and for a second
     reason here: overflow-x:hidden forces overflow-y to compute to `auto`,
     which made <body> a scroll container - and a sticky header inside a scroll
     container sticks to that container, not the viewport, so the header simply
     did not stick. `clip` contains the same overflow without creating one.
     The hidden line above it is the fallback for browsers predating `clip`. */
  overflow-x: hidden;
  overflow-x: clip;
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--rjr-font-display);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.015em;
  color: var(--rjr-ctx-heading);
  text-wrap: balance;
}

h1 { font-size: var(--rjr-text-4xl); }
h2 { font-size: var(--rjr-text-3xl); }
h3 { font-size: var(--rjr-text-xl); line-height: 1.2; }
h4 { font-size: var(--rjr-text-lg); line-height: 1.25; }

p { margin: 0 0 var(--rjr-space-4); }
p:last-child { margin-bottom: 0; }

img, svg { display: block; max-width: 100%; height: auto; }

a { color: var(--rjr-navy-600); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { color: var(--rjr-accent-hover); }

ul { margin: 0; padding: 0; list-style: none; }

:focus-visible {
  outline: 3px solid var(--rjr-accent);
  outline-offset: 3px;
  border-radius: var(--rjr-radius-sm);
}

::selection {
  background: var(--rjr-orange-200);
  color: var(--rjr-navy-900);
}

.rjr-skip-link {
  position: absolute;
  left: var(--rjr-space-4);
  top: -100px;
  z-index: 999;
  padding: var(--rjr-space-3) var(--rjr-space-6);
  background: var(--rjr-navy-800);
  color: var(--rjr-text-on-navy);
  border-radius: var(--rjr-radius-sm);
  text-decoration: none;
  transition: top var(--rjr-dur) var(--rjr-ease);
}
.rjr-skip-link:focus { top: var(--rjr-space-4); color: var(--rjr-text-on-navy); }

.rjr-visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}


/* ==========================================================================
   04  LAYOUT
   ========================================================================== */

.rjr-section {
  /* Published so the spacing utilities can ADD to this rather than replace it.
     Any component that owns padding and wants to be tunable does the same. */
  --rjr-pt-base: var(--rjr-section-padding-y);
  --rjr-pb-base: var(--rjr-section-padding-y);

  position: relative;
  /* Decorations are placed at negative offsets and would otherwise widen the
     page. `clip` rather than `hidden`: per spec, overflow-x:hidden forces
     overflow-y to compute to `auto`, which turns every section into a scroll
     container - #praise measured 47px of vertical overflow and would show an
     inner scrollbar. `clip` clips without creating one.
     The hidden line above it is the fallback for browsers predating `clip`. */
  overflow-x: hidden;
  overflow-x: clip;
  padding: var(--rjr-pt-base) var(--rjr-section-padding-x) var(--rjr-pb-base);
  background-color: var(--rjr-cream);
  isolation: isolate;
}

/* Same nuance as the flat paper fill, just no longer flat: a soft light source
   in the top-left falling away to a slightly deeper paper in the bottom-right.
   Stays inside a ~10-unit band around --rjr-paper so it reads as paper stock
   catching light, not as a coloured gradient. */


.rjr-section--paper {
  background-color: var(--rjr-cream);
}


/* A torn sheet, not a colour band. The scan is a light page whose bottom edge
   is torn, so placed at the top of a navy section it reads as the cream page
   above tearing away to reveal the navy underneath — and flipped at the bottom
   it does the same in reverse. Same trick as the newsletter's kraft strip.
   Deliberately NOT overflow:hidden — the book has to break the top edge. */
.rjr-section--torn::before,
.rjr-section--torn::after,
.rjr-section--torn-top::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  z-index: 2;
  /* Tall enough to contain the whole tear. At 100% width the scan's ragged
     band alone is ~7vw deep — a shorter strip clips above it and only the
     deepest points of the tear show, which reads as random flecks. */
  height: var(--rjr-torn-h, clamp(90px, 12vw, 175px));
  background-image: url("../img/torn-paper-edge.png");
  background-size: 100% auto;
  background-repeat: no-repeat;
  background-position: center bottom;
  /* The scan's paper is a neutral (236,236,236); cream is (240,240,234). Left
     alone that 4-unit step renders as a flat grey band with a hard straight
     top edge — which is what made the tear read as a separate object rather
     than the page itself tearing. sepia(0.06) lands it at (241,239,235):
     within one unit of cream, and it keeps the scan's own drop shadow. */
  filter: sepia(0.06);
  pointer-events: none;
}
/* Both strips sit fully INSIDE the section rather than overlapping outward.
   At this height the scan's solid part covers ~60px of the section's own top,
   so the cream reads as continuing past the boundary and then tearing — and
   because nothing straddles the boundary there is no seam to mismatch at all.
   Pair with `rjr-pt-lg rjr-pb-lg` so the tear has room and content clears it. */
.rjr-section--torn::before,
.rjr-section--torn-top::before { top: 0; }
.rjr-section--torn::after      { bottom: 0; transform: scaleY(-1); }

.rjr-section--navy {
  /* CTAs go orange on navy. Teal is only 3.6:1 against a navy field and sinks
     into it; orange is 5.1:1 and holds. Every button reads from these three
     tokens and nothing else, so re-pointing them here flips every CTA in a
     navy section at once — no per-button overrides. */
  --rjr-cta: var(--rjr-orange-500);
  --rjr-cta-hover: var(--rjr-orange-600);

  /* Grain, applied as a near-opaque navy wash over the crumple rather than a
     blend mode — soft-light over a light scan lifts the navy far too much. */
  --rjr-navy-grain: 0.94;
  background-image:
    linear-gradient(rgba(6, 52, 66, var(--rjr-navy-grain)),
                    rgba(6, 52, 66, var(--rjr-navy-grain))),
    url("../img/crumpled-paper-texture.jpg");
  background-size: cover, cover;
  background-position: center, center;
  background-color: var(--rjr-navy-800);
  --rjr-ctx-text: var(--rjr-cream);
  --rjr-ctx-muted: var(--rjr-navy-200);
  --rjr-ctx-heading: var(--rjr-cream);
  --rjr-ctx-border: rgba(240, 240, 234, 0.18);
  --rjr-ctx-card-bg: rgba(240, 240, 234, 0.055);
  color: var(--rjr-ctx-text);
}
.rjr-section--tight {
  --rjr-pt-base: calc(clamp(2.5rem, 1.8rem + 3vw, 5rem) * var(--rjr-section-rhythm));
  --rjr-pb-base: var(--rjr-pt-base);
  padding-block: var(--rjr-pt-base) var(--rjr-pb-base);
}

.rjr-container {
  width: 100%;
  max-width: var(--rjr-container);
  margin-inline: auto;
}
.rjr-container--wide { max-width: var(--rjr-container-wide); }

/* --- Container decorations ----------------------------------------------
   Hand-drawn objects parked in the gutter *outside* the container, so they
   never collide with content. Three orthogonal axes — asset, corner, size —
   composed on one element per object:

     <div class="rjr-container rjr-decor-zone">
       <span class="rjr-decor rjr-decor--passport rjr-decor--tl rjr-decor--md"
             aria-hidden="true"></span>
       <span class="rjr-decor rjr-decor--troller rjr-decor--br rjr-decor--lg"
             aria-hidden="true"></span>
       ...content...
     </div>

   One element per object rather than ::before/::after: pseudo-elements cap a
   container at two and, more importantly, can't pair an asset class with a
   corner class — there is only one ::before for all of them to land on.

   Sizes are a MULTIPLIER on a per-asset base, not a fixed width. The nine
   assets span 0.54 to 2.62 in aspect ratio, so a shared pixel width would make
   the troller tower over the sunglasses. Each asset carries a base tuned for
   even optical weight; sm/md/lg then mean the same thing on all of them.

   Per-instance nudging without a new class — inline styles beat these defaults:

     style="--rjr-decor-x: 60%; --rjr-decor-rot: -4deg;"

   x/y are offsets as a percentage of the object's OWN size, so a wide object
   pushes proportionally further into the gutter than a narrow one. Any length
   works too (`--rjr-decor-x: 40px`).
   ------------------------------------------------------------------------ */

.rjr-decor-zone { position: relative; }

.rjr-decor {
  position: absolute;
  z-index: -1;
  width: calc(var(--rjr-decor-base, 8rem)
              * var(--rjr-decor-scale, 1)
              * var(--rjr-decor-shrink, 1));
  aspect-ratio: var(--rjr-decor-ratio, 1);
  background-image: var(--rjr-decor-img, none);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  opacity: var(--rjr-decor-opacity, 0.4);
  pointer-events: none;
}

/* Assets. --rjr-decor-base is a width chosen so every object carries roughly
   the same visual weight at --md, despite the spread of aspect ratios. */
.rjr-decor--passport {
  --rjr-decor-img: url("../img/decorations/passport.png");
  --rjr-decor-ratio: 600 / 726;
  --rjr-decor-base: 7rem;
}
.rjr-decor--troller {
  --rjr-decor-img: url("../img/decorations/troller.png");
  --rjr-decor-ratio: 766 / 1426;
  --rjr-decor-base: 6.5rem;
}
.rjr-decor--camera {
  --rjr-decor-img: url("../img/decorations/camera.png");
  --rjr-decor-ratio: 600 / 458;
  --rjr-decor-base: 8.5rem;
}
.rjr-decor--coconut {
  --rjr-decor-img: url("../img/decorations/coconut-drink.png");
  --rjr-decor-ratio: 500 / 375;
  --rjr-decor-base: 8.5rem;
}
.rjr-decor--star {
  --rjr-decor-img: url("../img/decorations/star.png");
  --rjr-decor-ratio: 540 / 486;
  --rjr-decor-base: 8rem;
}
.rjr-decor--boat {
  --rjr-decor-img: url("../img/decorations/boat-sail.png");
  --rjr-decor-ratio: 600 / 539;
  --rjr-decor-base: 8rem;
}
.rjr-decor--flipflops {
  --rjr-decor-img: url("../img/decorations/beach-flipflops.png");
  --rjr-decor-ratio: 691 / 630;
  --rjr-decor-base: 8rem;
}
.rjr-decor--plane {
  --rjr-decor-img: url("../img/decorations/plane.png");
  --rjr-decor-ratio: 500 / 251;
  --rjr-decor-base: 10.5rem;
}
.rjr-decor--sunglasses {
  --rjr-decor-img: url("../img/decorations/sunglasses.png");
  --rjr-decor-ratio: 500 / 191;
  --rjr-decor-base: 12rem;
}

/* Sizes — multipliers, so they read the same on every asset. */
.rjr-decor--sm { --rjr-decor-scale: 1; }
.rjr-decor--md { --rjr-decor-scale: 1.5; }
.rjr-decor--lg { --rjr-decor-scale: 2; }
.rjr-decor--xl { --rjr-decor-scale: 3; }

/* Opt-in parallax. Movement is applied as --rjr-decor-py, which is woven into
   the corner transforms below — so it composes with the existing
   translate/rotate instead of overwriting it, and the percentage offsets keep
   resolving against the object's own size. Direction is opposite to the hero
   image; strength is tunable per instance with data-parallax-speed. */
.rjr-decor--parallax { will-change: transform; }

/* Corners. Anchored to the container edge, then translated out by a share of
   the object's own size. Each corner carries a default tilt so a hand-placed
   look comes for free; --rjr-decor-rot overrides it per instance. */
.rjr-decor--tl,
.rjr-decor--tr { top: 0; }
.rjr-decor--bl,
.rjr-decor--br { bottom: 0; }
.rjr-decor--tl,
.rjr-decor--bl { left: 0; }
.rjr-decor--tr,
.rjr-decor--br { right: 0; }

.rjr-decor--tl {
  --rjr-decor-rot: -13deg;
  transform: translate(calc(-1 * var(--rjr-decor-x, 92%)),
                       calc(-1 * var(--rjr-decor-y, 15%)))
             translateY(var(--rjr-decor-py, 0px))
             rotate(var(--rjr-decor-rot));
}
.rjr-decor--tr {
  --rjr-decor-rot: 11deg;
  transform: translate(var(--rjr-decor-x, 92%),
                       calc(-1 * var(--rjr-decor-y, 15%)))
             translateY(var(--rjr-decor-py, 0px))
             rotate(var(--rjr-decor-rot));
}
.rjr-decor--bl {
  --rjr-decor-rot: 9deg;
  transform: translate(calc(-1 * var(--rjr-decor-x, 92%)),
                       var(--rjr-decor-y, 15%))
             translateY(var(--rjr-decor-py, 0px))
             rotate(var(--rjr-decor-rot));
}
.rjr-decor--br {
  --rjr-decor-rot: 7deg;
  transform: translate(var(--rjr-decor-x, 92%),
                       var(--rjr-decor-y, 15%))
             translateY(var(--rjr-decor-py, 0px))
             rotate(var(--rjr-decor-rot));
}

/* Bottom-centre. Not a corner ornament but a section anchor: it sits INSIDE,
   centred, and hangs down into the section's own bottom padding rather than
   being pushed out into the page gutter. --rjr-decor-y is how far it drops. */
.rjr-decor--bc {
  --rjr-decor-rot: 0deg;
  bottom: 0;
  left: 50%;
  transform: translate(-50%, var(--rjr-decor-y, 45%))
             translateY(var(--rjr-decor-py, 0px))
             rotate(var(--rjr-decor-rot));
}

/* Below this width the container has no gutter left to park them in, and they
   would either overlap content or be clipped at the viewport edge. */
/* Mobile placement. There is no page gutter left to park objects in, so
   instead of hiding them they move INSIDE the container: 5% in from each edge,
   scaled down, with the outward push zeroed (--rjr-decor-x/y drive the
   translate that pushes a corner object out into the gutter). */
@media (max-width: 1100px) {
  .rjr-decor {
    --rjr-decor-shrink: 0.6;
    --rjr-decor-x: 0%;
    --rjr-decor-y: 0%;
  }

  .rjr-decor--tl,
  .rjr-decor--bl { left: -22%; }

  .rjr-decor--tr,
  .rjr-decor--br { right: -20%; left: auto; }

  /* the right-hand object rides higher so it clears the stacked content */
  .rjr-decor--br { bottom: 30%; top: auto; }

  .rjr-decor--bc { bottom: -5%; }
}

.rjr-stack > * + * { margin-top: var(--rjr-space-4); }
.rjr-stack--lg > * + * { margin-top: var(--rjr-space-6); }

.rjr-grid {
  display: grid;
  gap: clamp(1.25rem, 0.9rem + 1.4vw, 2rem);
}
.rjr-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.rjr-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.rjr-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }


/* ==========================================================================
   05  TYPOGRAPHY COMPONENTS
   ========================================================================== */

/* --- Eyebrow: a label on a brush stroke ---------------------------------
   One component, three paints. All three strokes are the same 1570×527 scan,
   so the stroke is picked with a single custom property — a variant never has
   to restate background-size/repeat, and a new colour is one line.
   Padding is shared: 3em on the sides keeps the text clear of the ragged
   ends of the stroke at every length. */
.rjr-eyebrow {
  --rjr-eyebrow-paint: var(--rjr-img-paint-navy);
  display: inline-block;
  padding: 0.55em 3em 0.65em;
  background-image: var(--rjr-eyebrow-paint);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  color: var(--rjr-text-on-navy);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-xs);
  font-weight: 600;
  letter-spacing: 0.05em;
  line-height: 1.2;
  text-transform: uppercase;
  transform: rotate(-0.8deg);
}

.rjr-eyebrow--teal { --rjr-eyebrow-paint: var(--rjr-img-paint-teal); }
.rjr-eyebrow--navy { --rjr-eyebrow-paint: var(--rjr-img-paint-navy); }

.rjr-section--navy .rjr-eyebrow {
  /* teal and navy strokes both sink into a navy section — invert to an
     outline chip instead. The orange variant below opts back out. */
  background-image: none;
  background-color: transparent;
  border: 1px solid rgba(240, 240, 234, 0.35);
  border-radius: var(--rjr-radius-pill);
  color: var(--rjr-orange-300);
}

/* Orange stroke — the only one that holds up on light *and* navy sections,
   which is why it doubles as the "new / featured" flag. Doubled class and
   declared after the navy override so it wins there without !important. */
.rjr-eyebrow.rjr-eyebrow--orange {
  --rjr-eyebrow-paint: var(--rjr-img-paint-orange);
  background-image: var(--rjr-eyebrow-paint);
  background-color: transparent;
  border: 0;
  border-radius: 0;
  /* White is a deliberate brand call. For the record it measures 2.59:1 on
     orange-500 — below AA at this size; navy-900 would be 6.05:1. */
  color: var(--rjr-text-on-orange);
}

/* --- Script flourish word ------------------------------------------------ */
.rjr-script {
  display: inline-block;
  font-family: var(--rjr-font-script);
  font-weight: 400;
  font-style: normal;
  font-size: 1.1em;         /* runs visually larger than the anchor word */
  line-height: 0.8;
  letter-spacing: 0;
  color: var(--rjr-accent);
  /* Script faces carry big side bearings — pull the optical gap back in. */
  margin-inline: -0.04em 0.02em;
  transform: translateY(0.09em) rotate(-1.5deg);
}
.rjr-section--navy .rjr-script { color: var(--rjr-orange-400); }

/* A script word is ~1.4× the anchor word, so its ascenders and descenders
   break out of a display line-height. Any heading containing one opens up. */
.rjr-has-script { line-height: 1.24; }
.rjr-section-head .rjr-has-script { margin-bottom: var(--rjr-space-6); }

/* Orange paint block behind a script word */
.rjr-script--painted {
  position: relative;
  padding-inline: 0.22em;
}
.rjr-script--painted::before {
  content: "";
  position: absolute;
  inset: 12% -6% 18%;
  z-index: -1;
  background-image: var(--rjr-img-paint-orange);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  transform: rotate(-1.2deg);
}
.rjr-script--painted { color: var(--rjr-cream); }
.rjr-section--navy .rjr-script--painted { color: var(--rjr-navy-900); }

/* --- Section head block -------------------------------------------------- */
.rjr-section-head {
  max-width: 46rem;
  margin-bottom: clamp(2rem, 1.4rem + 2.4vw, 3.75rem);
}
.rjr-section-head--center {
  margin-inline: auto;
  text-align: center;
}
.rjr-section-head .rjr-eyebrow { margin-bottom: var(--rjr-space-4); }
.rjr-section-head h2 { margin-bottom: var(--rjr-space-4); }

.rjr-lede {
  max-width: var(--rjr-measure);
  color: var(--rjr-ctx-muted);
  font-size: var(--rjr-text-lg);
  line-height: 1.55;
}
.rjr-section--navy .rjr-lede { color: var(--rjr-navy-100); }
.rjr-section-head--center .rjr-lede { margin-inline: auto; }

.rjr-body { max-width: var(--rjr-measure); color: var(--rjr-ctx-text); }

/* The old .rjr-badge (flat orange pill) is gone — .rjr-eyebrow--orange above
   replaces it, so "new / featured" flags now use the brush stroke like every
   other label rather than a second, unrelated shape. */


/* ==========================================================================
   05b  ICONOGRAPHY — Phosphor
   --------------------------------------------------------------------------
   Phosphor's own webfont, per its documented API:

       <i class="ph ph-books" aria-hidden="true"></i>          regular
       <i class="ph-bold ph-arrow-right" aria-hidden="true"></i>   bold
       <i class="ph-fill ph-quotes" aria-hidden="true"></i>        fill

   Glyphs inherit `color` and `font-size` from their parent, so components size
   and colour icons with ordinary type properties — no per-icon CSS. Icons are
   always decorative here and always `aria-hidden`; the adjacent text is the
   accessible label.
   ========================================================================== */

.ph,
[class*="ph-"] { flex: none; line-height: 1; }


/* ==========================================================================
   06  BUTTONS & LINKS
   ========================================================================== */

.rjr-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  padding: 0.85em 1.6em;
  border: 2px solid transparent;
  border-radius: var(--rjr-radius);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-base);
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--rjr-dur) var(--rjr-ease),
              box-shadow var(--rjr-dur) var(--rjr-ease),
              background-color var(--rjr-dur) var(--rjr-ease),
              border-color var(--rjr-dur) var(--rjr-ease),
              color var(--rjr-dur) var(--rjr-ease);
}
.rjr-btn:hover { transform: translateY(-2px); }
.rjr-btn:active { transform: translateY(0); }

.rjr-btn__arrow {
  font-size: 1.1em;
  transition: transform var(--rjr-dur) var(--rjr-ease);
}
.rjr-btn:hover .rjr-btn__arrow { transform: translateX(0.25em); }

.rjr-btn--primary {
  background: var(--rjr-cta);
  color: var(--rjr-cta-text);
  box-shadow: var(--rjr-shadow-md);
}
.rjr-btn--primary:hover {
  background: var(--rjr-cta-hover);
  color: var(--rjr-cta-text);
  box-shadow: var(--rjr-shadow-lg);
}

.rjr-btn--secondary {
  background: transparent;
  border-color: var(--rjr-cta);
  color: var(--rjr-cta-ink);
}
.rjr-btn--secondary:hover {
  background: var(--rjr-cta);
  border-color: var(--rjr-cta);
  color: var(--rjr-cta-text);
}
.rjr-section--navy .rjr-btn--secondary {
  border-color: rgba(240, 240, 234, 0.45);
  color: var(--rjr-cream);
}
.rjr-section--navy .rjr-btn--secondary:hover {
  background: var(--rjr-cream);
  border-color: var(--rjr-cream);
  color: var(--rjr-navy-800);
}

.rjr-btn--small { padding: 0.6em 1.15em; font-size: var(--rjr-text-sm); }
.rjr-btn--large { padding: 1.05em 2.1em; font-size: var(--rjr-text-lg); }

.rjr-btn-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--rjr-space-4);
}

/* Text link with a travelling arrow */
.rjr-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  color: var(--rjr-navy-600);
  font-weight: 600;
  font-size: var(--rjr-text-sm);
  text-decoration: none;
  transition: color var(--rjr-dur) var(--rjr-ease), gap var(--rjr-dur) var(--rjr-ease);
}
.rjr-link .ph-arrow-right {
  font-size: 1.05em;
  transition: transform var(--rjr-dur) var(--rjr-ease);
}
.rjr-link:hover { color: var(--rjr-accent-hover); }
.rjr-link:hover .ph-arrow-right { transform: translateX(0.28em); }
.rjr-section--navy .rjr-link { color: var(--rjr-orange-300); }
.rjr-section--navy .rjr-link:hover { color: var(--rjr-orange-200); }

/* Hand-underlined inline link */
.rjr-link-underline {
  color: var(--rjr-navy-600);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: var(--rjr-orange-300);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.2em;
}
.rjr-link-underline:hover { text-decoration-color: var(--rjr-accent); }


/* ==========================================================================
   07  CARDS
   ========================================================================== */

/* Card stock, not a UI panel. Three things do that work: an opaque fill (a
   translucent one let the background art show through and read as glass), no
   hairline border (paper has an edge, not an outline), and an offset shadow
   down-right so it sits ON the page instead of floating above it.
   Radius stays tight — a trimmed sheet, not a rounded rectangle. */
.rjr-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--rjr-space-3);
  --rjr-pt-base: clamp(1.25rem, 1rem + 0.9vw, 1.85rem);
  --rjr-pb-base: var(--rjr-pt-base);
  padding: var(--rjr-pt-base) clamp(1.25rem, 1rem + 0.9vw, 1.85rem) var(--rjr-pb-base);
  /* Real crumpled stock, multiplied into the fill rather than replacing it —
     the scan is near-white, so multiply keeps the warm paper colour and takes
     only the creases from the photograph.
     The wash on top is the intensity dial: at full strength the creases fight
     the body copy and the multiply pushes the shadows warm. Raise
     --rjr-card-texture-wash toward 1 for flatter stock, lower it for more
     crumple. */
  --rjr-card-texture-wash: 0.55;
  background-color: var(--rjr-ctx-card-bg);
  background-image:
    linear-gradient(rgba(255, 255, 253, var(--rjr-card-texture-wash)),
                    rgba(255, 255, 253, var(--rjr-card-texture-wash))),
    url("../img/crumpled-paper-texture.jpg");
  background-size: cover, cover;
  background-position: center, center;
  background-blend-mode: normal, multiply;
  border: 0;
  border-radius: 3px;
  box-shadow:
    1px 2px 3px rgba(5, 39, 49, 0.05),
    3px 7px 18px rgba(5, 39, 49, 0.07);
  color: var(--rjr-ctx-text);
  /* Nothing on this page is perfectly straight; the cards were the exception.
     Tilt and stagger are per-card variables so hover can straighten them. */
  transform:
    rotate(var(--rjr-card-tilt, 0deg))
    translateY(calc(var(--rjr-card-offset, 0px) + var(--rjr-card-lift, 0px)));
  transition: transform var(--rjr-dur) var(--rjr-ease),
              box-shadow var(--rjr-dur) var(--rjr-ease);
}

/* Hand-placed irregularity. A 4-cycle so a four-up row never repeats; a
   three-up row (the article library) just picks up a different pairing. */
.rjr-grid > .rjr-card:nth-child(4n + 1) { --rjr-card-tilt: -0.7deg; background-position: center, 12% 20%; }
.rjr-grid > .rjr-card:nth-child(4n + 2) { --rjr-card-tilt:  0.5deg; --rjr-card-offset: 10px; background-position: center, 78% 62%; }
.rjr-grid > .rjr-card:nth-child(4n + 3) { --rjr-card-tilt: -0.4deg; --rjr-card-offset: 4px; background-position: center, 40% 85%; }
.rjr-grid > .rjr-card:nth-child(4n + 4) { --rjr-card-tilt:  0.8deg; --rjr-card-offset: 14px; background-position: center, 88% 8%; }

/* Category cards keep the tilt but not the vertical drop.
   These are read as a row of parallel choices, so their headings want a shared
   line; the 0/10/4/14px stagger above put them up to 14px apart, which reads as
   misalignment rather than as hand-placement once six of them sit in a grid.
   Pinning the icon box (see .rjr-card--category .rjr-cat-icon) got them within
   14px; this closes the rest.
   :nth-child(n) is there to match the (0,2,1) of the rules above - without it
   this is (0,2,0) and loses. */
.rjr-grid > .rjr-card--category:nth-child(n) { --rjr-card-offset: 0px; }
.rjr-card h3 { color: var(--rjr-ctx-heading); }
/* navy-400 measured 4.07:1 on the card fill — a real AA failure at this size,
   and it sat over a crumpled texture which made it worse. navy-600 is 8.71:1. */
.rjr-card p {
  color: var(--rjr-navy-600);
  font-size: var(--rjr-text-sm);
  line-height: 1.6;
}
.rjr-section--navy .rjr-card p { color: var(--rjr-navy-100); }
/* The scan is opaque — on navy it would read as a white slab. */
.rjr-section--navy .rjr-card { background-image: none; }

/* Straightens as it lifts — the gesture of picking a sheet up off the desk. */
.rjr-card--link:hover {
  --rjr-card-tilt: 0deg;
  --rjr-card-lift: -6px;
  box-shadow:
    2px 4px 6px rgba(5, 39, 49, 0.06),
    6px 14px 30px rgba(5, 39, 49, 0.12);
}
.rjr-card__foot { margin-top: auto; padding-top: var(--rjr-space-2); }

/* Numbered set — opt in on the GRID, so only a set that reads as a sequence
   gets numerals ("four ways in"), not every card on the site.
   Counter rather than markup: renumbering is automatic if a card moves. */
.rjr-grid--numbered { counter-reset: rjr-card-n; }
.rjr-grid--numbered > .rjr-card::before {
  counter-increment: rjr-card-n;
  content: counter(rjr-card-n, decimal-leading-zero);
  position: absolute;
  /* Bottom-right, not top — the top edge is where tape lands, and down here it
     reads as a page number rather than a badge. */
  bottom: 0.35rem;
  right: 0.9rem;
  font-family: var(--rjr-font-display);
  font-weight: 700;
  font-size: 2.25rem;
  line-height: 1;
  color: var(--rjr-navy-800);
  opacity: 0.10;
  pointer-events: none;
}

/* Stretched hit area so the whole card is clickable without nesting links */
.rjr-card__cover::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

/* Icon tile — the emoji "sticker" */
/* Unboxed. A tinted rounded-square holding a glyph is the single strongest
   "feature card" signal there is; without the tile the same icon reads as a
   drawn object instead of UI furniture. */
.rjr-card__icon {
  display: block;
  width: auto;
  height: auto;
  background: none;
  border: 0;
  border-radius: 0;
  font-size: 2.5rem;
  line-height: 1;
  color: var(--rjr-accent);
  transform: rotate(-5deg);
  transform-origin: left center;
  transition: transform var(--rjr-dur) var(--rjr-ease-out);
}
/* the tile is tilted, so counter-rotate the glyph back to upright */
.rjr-card--link:hover .rjr-card__icon { transform: rotate(0deg) scale(1.06); }
.rjr-card:nth-child(2n) .rjr-card__icon { transform: rotate(4deg); }
.rjr-section--navy .rjr-card__icon { color: var(--rjr-orange-300); }

/* Category card. Was four stacked text levels — kicker, heading, description,
   link — where the first three restated each other ("SAFETY & INSURANCE" /
   "Travel safely, not fearfully" / "Safety habits, safety gear..."). Now two:
   the category IS the heading, because this is an index and people scan for
   the category name, and a single-line promise under it. */
.rjr-card--category {
  gap: var(--rjr-space-3);
  /* the crumple sits further back here — small type over creases was half of
     why this section read as hard work */
  --rjr-card-texture-wash: 0.72;
}
/* One icon box, so every title in the row starts on the same line.
   -------------------------------------------------------------------------
   .rjr-cat-icon sizes each drawing from its own aspect ratio and a per-asset
   width, which was tuned so the six carry similar optical weight - but it
   leaves them at six different HEIGHTS. Measured across the front page library:
   42, 70, 79, 87, 93, 139px. In a stacked card that pushes every heading to a
   different baseline down the row.

   Pinning the box fixes it without touching the per-asset widths. The drawing
   still keeps its own proportions (`background-size: contain`) and still sits
   on the floor of the box (`background-position: left bottom`), so nothing is
   stretched - the short ones simply get air above them, and the one drawing
   taller than the box, the troller at 139px, scales down to fit and stops being
   the outlier.

   `height`, not `min-height`: aspect-ratio would otherwise keep driving the
   box, which is also why it is explicitly cleared.

   Applies to both places these cards are used, the front page library and About
   Lisa's "Where to start". Reset to auto at 620 below, where the card becomes a
   centered row and there is no column of titles to line up. */
.rjr-card--category .rjr-cat-icon {
  height: 5.5rem;
  aspect-ratio: auto;
}
.rjr-card--category h3 {
  font-size: var(--rjr-text-xl);
  line-height: 1.12;
  color: var(--rjr-navy-800);
}
.rjr-card--category .rjr-card__promise {
  margin: 0;
  color: var(--rjr-navy-600);
  font-size: var(--rjr-text-base);
  line-height: 1.4;
}

.rjr-card__text { display: flex; flex-direction: column; gap: var(--rjr-space-2); }

/* Navy card — the dark object in a light section.
   -------------------------------------------------------------------------
   Two cards side by side, both on paper, read as two columns of one thing.
   This is the modifier that makes one of them the other thing.

   Built from .rjr-section--navy's own recipe (the near-opaque wash over the
   crumple scan, then the context tokens) so a navy card and a navy section are
   the same surface rather than two different navies.

   .rjr-card fills with --rjr-ctx-card-bg and blends its texture with multiply;
   both are restated here, because multiply over navy turns the creases to mud.

   Measured on navy-800: cream 11.64:1, navy-200 7.24:1. */
.rjr-card--navy {
  --rjr-navy-grain: 0.94;
  background-color: var(--rjr-navy-800);
  background-image:
    linear-gradient(rgba(6, 52, 66, var(--rjr-navy-grain)),
                    rgba(6, 52, 66, var(--rjr-navy-grain))),
    url("../img/crumpled-paper-texture.jpg");
  background-size: cover, cover;
  background-position: center, center;
  background-blend-mode: normal, normal;

  --rjr-ctx-text: var(--rjr-cream);
  --rjr-ctx-heading: var(--rjr-cream);
  --rjr-ctx-muted: var(--rjr-navy-200);
  color: var(--rjr-ctx-text);

  /* Navy SECTIONS flip CTAs to orange. A navy card goes the other way, to
     cream: the orange button belongs to the newsletter, and a card-sized copy
     of it competes with the real one further down the same page. Re-pointing
     the tokens is the whole mechanism - .rjr-btn--primary reads from these and
     nothing else, so no per-button override anywhere.
     navy-800 on cream is 11.64:1. */
  --rjr-cta: var(--rjr-cream);
  --rjr-cta-hover: #FFFFFF;
  --rjr-cta-text: var(--rjr-navy-800);
}
/* .rjr-card p sets navy-600, which is 1.9:1 on this fill. Same weight, so it is
   source order that settles it - this block must stay below that rule. */
.rjr-card--navy p { color: var(--rjr-ctx-muted); }
.rjr-card--navy h2,
.rjr-card--navy h3 { color: var(--rjr-ctx-heading); }

/* Lead card — the grid had six identical tiles and therefore no entry point.
   Laid out as a ROW: two columns wide in a stacked layout left most of the
   card empty, which read as a hole rather than as emphasis. */
.rjr-card--lead {
  grid-column: span 2;
  flex-direction: row;
  align-items: center;
  gap: clamp(1.25rem, 0.9rem + 1.6vw, 2.5rem);
}
.rjr-card--lead h3 { font-size: var(--rjr-text-2xl); }
.rjr-card--lead .rjr-card__promise { font-size: var(--rjr-text-lg); }
.rjr-card--lead .rjr-cat-icon {
  --rjr-cat-icon-w: 7.5rem;
  flex: none;
  align-self: center;
  transform-origin: center;
}
.rjr-card--lead .rjr-card__go { margin: 0 0 0 auto; padding-top: 0; align-self: flex-end; }

/* Hand-drawn category object. The eye lands on a drawing instead of reading a
   label, which is what actually removes the "wall of text" feeling.
   Darkened: the scans are a pale taupe that all but disappears on card stock. */
.rjr-cat-icon {
  display: block;
  width: var(--rjr-cat-icon-w, 4.5rem);
  aspect-ratio: var(--rjr-cat-icon-ratio, 1);
  background-image: var(--rjr-cat-icon-img, none);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: left bottom;
  filter: brightness(0.72) contrast(1.06);
  transform: rotate(var(--rjr-cat-icon-tilt, -4deg));
  transform-origin: left bottom;
  transition: transform var(--rjr-dur) var(--rjr-ease-out);
}
.rjr-card--link:hover .rjr-cat-icon { transform: rotate(3deg) scale(1.06); }

.rjr-cat-icon--passport {
  --rjr-cat-icon-img: url("../img/decorations/passport.png");
  --rjr-cat-icon-ratio: 600 / 726;
}
.rjr-cat-icon--troller {
  --rjr-cat-icon-img: url("../img/decorations/troller.png");
  --rjr-cat-icon-ratio: 766 / 1426;
}
.rjr-cat-icon--plane {
  --rjr-cat-icon-img: url("../img/decorations/plane.png");
  --rjr-cat-icon-ratio: 500 / 251;
  --rjr-cat-icon-tilt: -8deg;
  /* Widths on this row are set per asset so all three carry the same optical
     weight. At a shared 4.5rem the plane stood only 2.3rem tall against the
     troller's 8.4rem, which is why the row read as underweight. */
  --rjr-cat-icon-w: 7rem;
}
/* ⚠ TEMPORARY STAND-INS — awaiting the three new drawings.
   --stay   wants a bed / hotel key / door hanger
   --tech   wants a phone / SIM / bank card
   --terms  wants something for "Travel On Your Terms"; the label itself is
            still provisional, so this one may not survive at all
   Swap the three urls below and these comments go away; nothing else changes. */
.rjr-cat-icon--terms {
  --rjr-cat-icon-img: url("../img/decorations/sunglasses.png");
  --rjr-cat-icon-ratio: 500 / 191;
  --rjr-cat-icon-w: 6rem;
}
.rjr-cat-icon--stay {
  --rjr-cat-icon-img: url("../img/decorations/beach-flipflops.png");
  --rjr-cat-icon-ratio: 691 / 630;
  --rjr-cat-icon-w: 5.5rem;
}
.rjr-cat-icon--tech {
  --rjr-cat-icon-img: url("../img/decorations/camera.png");
  --rjr-cat-icon-ratio: 600 / 458;
  --rjr-cat-icon-w: 6rem;
}

/* Silent affordance. Six repeated "Explore →" labels said nothing the card
   didn't already say — the whole card is a link. */
.rjr-card__go {
  margin-top: auto;
  padding-top: var(--rjr-space-3);
  color: var(--rjr-accent);
  font-size: 1.15rem;
  line-height: 1;
  transition: transform var(--rjr-dur) var(--rjr-ease);
}
.rjr-card--link:hover .rjr-card__go { transform: translateX(0.35em); }

/* Tools strip. Pulled out of the category grid: the app is a different KIND of
   thing from the five article categories, and sitting in the grid it read as a
   sixth one. Horizontal, so it also breaks the tile rhythm. */
.rjr-tools-strip {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 0.6rem + 1.4vw, 1.75rem);
  margin-top: var(--rjr-space-lg);
  padding: clamp(1.25rem, 1rem + 1vw, 1.75rem) clamp(1.5rem, 1.2rem + 1.4vw, 2.25rem);
  border-radius: 3px;
  background-color: var(--rjr-navy-600);
  /* multiply, not soft-light + wash. Over a light card that combination reads
     as gentle creasing; over a saturated mid-tone it LIFTS the colour — it
     took teal-700 to (150,201,197) and dropped the white type to 1.83:1.
     multiply against a near-white scan changes the hue barely at all and
     still picks up the creases. */
  background-image: url("../img/crumpled-paper-texture.jpg");
  background-size: cover;
  background-blend-mode: multiply;
  color: #FFFFFF;
  text-decoration: none;
  box-shadow: 2px 5px 14px rgba(5, 39, 49, 0.14);
  transform: rotate(-0.4deg);
  transition: transform var(--rjr-dur) var(--rjr-ease),
              box-shadow var(--rjr-dur) var(--rjr-ease);
}
.rjr-tools-strip:hover {
  transform: rotate(0deg) translateY(-3px);
  box-shadow: 3px 10px 24px rgba(5, 39, 49, 0.2);
  color: #FFFFFF;
}
.rjr-tools-strip__icon {
  font-size: 2.1rem;
  line-height: 1;
  flex: none;
  color: var(--rjr-orange-400);
  transform: rotate(var(--rjr-tools-icon-tilt, -11deg));
  transition: transform var(--rjr-dur) var(--rjr-ease-out);
}
.rjr-tools-strip:hover .rjr-tools-strip__icon { transform: rotate(-4deg) scale(1.06); }
.rjr-tools-strip__body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.rjr-tools-strip__title {
  font-family: var(--rjr-font-display);
  font-weight: 700;
  font-size: var(--rjr-text-lg);
  line-height: 1.15;
}
.rjr-tools-strip__sub { font-size: var(--rjr-text-sm); color: rgba(255, 255, 255, 0.93); }

/* The CTA is a solid orange chip rather than orange text: orange-on-teal as
   type measures 1.9:1, which is unreadable. As a filled chip the orange only
   has to separate from the teal ground, and the label rides on the fill. */
.rjr-tools-strip__go {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  padding: 0.62em 1.15em;
  border-radius: var(--rjr-radius);
  background: var(--rjr-accent);
  color: var(--rjr-text-on-orange);
  font-size: var(--rjr-text-sm);
  font-weight: 600;
  transition: background-color var(--rjr-dur) var(--rjr-ease);
}
.rjr-tools-strip:hover .rjr-tools-strip__go { background: var(--rjr-accent-hover); }
.rjr-tools-strip__go i { transition: transform var(--rjr-dur) var(--rjr-ease); }
.rjr-tools-strip:hover .rjr-tools-strip__go i { transform: translateX(0.3em); }

/* Accent card — the one that has to win the row */
.rjr-card--accent {
  background-color: var(--rjr-orange-500);
  background-image:
    url("../img/crumpled-paper-texture.jpg"),
    linear-gradient(150deg, var(--rjr-orange-500), var(--rjr-orange-600));
  background-size: cover, cover;
  /* softer here: the creases read much harder over a saturated fill */
  background-blend-mode: soft-light, normal;
  border-color: var(--rjr-orange-600);
  color: var(--rjr-text-on-orange);
  box-shadow: var(--rjr-shadow-md);
}
.rjr-card--accent h3,
.rjr-card--accent .rjr-card__kicker { color: var(--rjr-text-on-orange); }
.rjr-card--accent p { color: rgba(255, 255, 255, 0.92); }
.rjr-card--accent .rjr-link { color: var(--rjr-text-on-orange); }
.rjr-card--accent .rjr-link:hover { color: var(--rjr-orange-100); }
.rjr-card--accent:hover { border-color: var(--rjr-orange-700); }

/* Ticket stubs — the old chips were outlined pills with an icon and an arrow,
   which is the single most SaaS shape on a page. These are paper stubs with
   real perforations. Three scans so a row never repeats itself.
   Stretched 100% 100% on purpose: the body of a stub is flat, so horizontal
   stretch is invisible, while the perforated ends stay pinned to the edges. */
.rjr-ticket {
  --rjr-ticket-img: url("../img/ticket-style-1.png");
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--rjr-space-3);
  min-height: 4.5rem;
  padding: 0.9rem 2.2rem;
  background-image: var(--rjr-ticket-img);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  color: var(--rjr-navy-800);
  font-size: var(--rjr-text-sm);
  font-weight: 600;
  line-height: 1.25;
  text-decoration: none;
  transform:
    rotate(calc(var(--rjr-ticket-tilt, 0deg) * var(--rjr-ticket-tilt-mult, 1)))
    translateY(calc(var(--rjr-ticket-lift, 0px) + var(--rjr-ticket-hover-lift, 0px)));
  transition: transform var(--rjr-dur) var(--rjr-ease),
              filter var(--rjr-dur) var(--rjr-ease);
  filter: drop-shadow(2px 4px 8px rgba(2, 20, 26, 0.35));
}
/* Hover ANGLES the stub rather than straightening it — a ticket being picked
   off the page, not a button settling. Multiplier + separate lift so it
   composes with each stub's own resting tilt instead of fighting it. */
.rjr-ticket:hover {
  --rjr-ticket-tilt-mult: var(--rjr-ticket-hover-mult, 2.6);
  --rjr-ticket-hover-lift: -5px;
  color: var(--rjr-navy-900);
  filter: drop-shadow(3px 8px 16px rgba(2, 20, 26, 0.45));
}
.rjr-ticket--1 { --rjr-ticket-img: url("../img/ticket-style-1.png"); }
/* --2 is defined but unused: its double perforation lands mid-text at these
   widths. Fine on something wider. */
.rjr-ticket--2 { --rjr-ticket-img: url("../img/ticket-style-2.png"); }
.rjr-ticket--3 { --rjr-ticket-img: url("../img/ticket-style-3.png"); }

.rjr-ticket__icon  { font-size: 1.25rem; color: var(--rjr-teal-700); flex: none; }
.rjr-ticket__arrow { font-size: 1rem; color: var(--rjr-accent); flex: none; margin-left: auto;
                     transition: transform var(--rjr-dur) var(--rjr-ease); }
.rjr-ticket:hover .rjr-ticket__arrow { transform: translateX(0.25em); }


/* ==========================================================================
   08  POLAROID PHOTO FRAMES
   --------------------------------------------------------------------------
   The frame PNGs have a genuinely transparent, *rotated* window. The photo is
   layered underneath and clipped to that exact quadrilateral, so it inherits
   the frame's tilt without any wrapper rotation — which keeps the tape,
   shadow and paper grain from the original scan perfectly intact.

   Corner percentages below were measured off the alpha channel of each PNG.
   If a frame asset is re-exported, re-measure before changing anything.
   ========================================================================== */

.rjr-polaroid {
  position: relative;
  width: 100%;
  filter: drop-shadow(0 18px 28px rgba(5, 39, 49, 0.22));
  transition: transform var(--rjr-dur-slow) var(--rjr-ease-out),
              filter var(--rjr-dur-slow) var(--rjr-ease-out);
}
.rjr-polaroid:hover {
  transform: translateY(-6px) rotate(0.6deg);
  filter: drop-shadow(0 26px 40px rgba(5, 39, 49, 0.28));
}

.rjr-polaroid__frame {
  position: relative;
  z-index: 2;
  width: 100%;
  height: auto;
  pointer-events: none;
}

/* The window is a plain box so it actually stretches to the four insets —
   a replaced <img> with `width:auto` would fall back to its intrinsic size. */
.rjr-polaroid__window {
  position: absolute;
  z-index: 1;
  inset: var(--rjr-pl-top) var(--rjr-pl-right) var(--rjr-pl-bottom) var(--rjr-pl-left);
  clip-path: var(--rjr-pl-clip);
  /* ~1% outward bleed kills the hairline seam against the matte's soft edge */
  transform: scale(1.012);
  overflow: hidden;
}

.rjr-polaroid__photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Per-instance crop control: the window aspect rarely matches the source, so
     which part survives the crop has to be steerable without touching the JS
     or re-exporting the photo. */
  object-position: var(--rjr-pl-pos, 50% 50%);
}

/* Portrait, tilted left — 981 × 1530 */
.rjr-polaroid--portrait {
  --rjr-pl-top: 16.73%;
  --rjr-pl-right: 18.55%;
  --rjr-pl-bottom: 20.20%;
  --rjr-pl-left: 6.52%;
  max-width: 26rem;
}

/* Landscape, tilted right (strong tilt) — 1374 × 1209 */
.rjr-polaroid--landscape {
  --rjr-pl-top: 25.06%;
  --rjr-pl-right: 3.57%;
  --rjr-pl-bottom: 13.73%;
  --rjr-pl-left: 18.34%;
}

/* Landscape, tilted right, no tape — 1182 × 984.
   Tilts clockwise ~5.9°, i.e. the opposite way to the portrait frame. */
.rjr-polaroid--landscape-simple {
  --rjr-pl-top: 7.93%;
  --rjr-pl-right: 3.98%;
  --rjr-pl-bottom: 16.67%;
  --rjr-pl-left: 5.08%;
  --rjr-pl-clip: polygon(6.14% 0%, 99.91% 14.02%, 93.77% 99.87%, 0% 85.71%);
  /* The scan's own tilt. Anything printed on the photo (the archive date chip)
     leans by this much or it reads as a sticker added afterwards. */
  --rjr-pl-tilt: 5.9deg;
  max-width: 28rem;
}
/* Caption rides the bottom matte, so it has to lean with the frame, not against it. */
.rjr-polaroid--landscape-simple .rjr-polaroid__caption {
  left: 8%;
  right: 16%;
  bottom: 6%;
  transform: rotate(5.9deg);
}

/* Portrait, tilted left, tape top-left and bottom-right — 981 × 1530.
   Same board as --portrait but different tape, so the window is not identical:
   re-measured off the alpha channel rather than assumed. */
.rjr-polaroid--portrait-2 {
  --rjr-pl-top: 16.73%;
  --rjr-pl-right: 18.45%;
  --rjr-pl-bottom: 20.07%;
  --rjr-pl-left: 6.52%;
  --rjr-pl-clip: polygon(0% 5.49%, 91.29% 0%, 100% 96.48%, 6.26% 100%);
  max-width: 26rem;
}

/* Landscape, tilted right (gentle tilt) — 1149 × 1089.
   Re-measured off the alpha channel: this variant had insets but no clip
   polygon, so the photo sat as an axis-aligned rectangle inside a window that
   leans 3.6deg, and the corners spilled over the matte. */
.rjr-polaroid--landscape-alt {
  --rjr-pl-top: 20.29%;
  --rjr-pl-right: 3.74%;
  --rjr-pl-bottom: 15.15%;
  --rjr-pl-left: 4.70%;
  --rjr-pl-clip: polygon(3.71% 0%, 99.90% 9.10%, 96.10% 99.86%, 0% 89.76%);
  --rjr-pl-tilt: 3.6deg;
}

/* Handwritten caption on the bottom matte */
.rjr-polaroid__caption {
  position: absolute;
  z-index: 3;
  left: 14%;
  right: 14%;
  bottom: 4%;
  font-family: var(--rjr-font-script);
  font-size: clamp(1.15rem, 0.95rem + 0.8vw, 1.6rem);
  line-height: 1;
  color: var(--rjr-navy-600);
  text-align: center;
  transform: rotate(-2deg);
  pointer-events: none;
}
/* The portrait matte sits inside the frame's own tilt — nudge to match it. */
.rjr-polaroid--portrait .rjr-polaroid__caption {
  left: 6%;
  right: 28%;
  bottom: 9%;
  transform: rotate(3.6deg);
}


/* ==========================================================================
   09  TAPE
   A synthetic strip for elements that aren't polaroids. Kept CSS-only so it
   can be dropped on any card without another image request.
   ========================================================================== */

.rjr-tape {
  --rjr-tape-img: url("../img/tape-tilted-right-long.png");
  --rjr-tape-ratio: 360 / 194;
  --rjr-tape-w: clamp(84px, 9vw, 124px);

  position: absolute;
  z-index: 4;
  width: var(--rjr-tape-w);
  aspect-ratio: var(--rjr-tape-ratio);
  background-image: var(--rjr-tape-img);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  pointer-events: none;
}

/* The short, left-leaning piece. */
.rjr-tape--small {
  --rjr-tape-img: url("../img/tape-tilted-left-small.png");
  --rjr-tape-ratio: 234 / 204;
  --rjr-tape-w: clamp(46px, 4.6vw, 64px);
}

/* No rotation here: the tilt is baked into the scans, like the polaroid frames.
   Adding CSS rotation on top would double it. Offsets are a share of the
   tape's own height so it straddles the edge at any size. */
.rjr-tape--tl { top: 0; left: 8%;   transform: translateY(-48%); }
.rjr-tape--tr { top: 0; right: 8%;  transform: translateY(-48%); }
.rjr-tape--bl { bottom: 0; left: 10%;  transform: translateY(48%); }
.rjr-tape--br { bottom: 0; right: 10%; transform: translateY(48%); }


/* ==========================================================================
   10  CALLOUTS & QUOTES
   ========================================================================== */

/* Affiliate disclosure — orange check-square + blue-grey tint */
.rjr-callout {
  display: flex;
  gap: var(--rjr-space-4);
  padding: var(--rjr-space-4) var(--rjr-space-6);
  border-radius: var(--rjr-radius);
  font-size: var(--rjr-text-sm);
  line-height: 1.6;
}
.rjr-callout--affiliate {
  background: var(--rjr-callout-affiliate-bg);
  border-left: 4px solid var(--rjr-accent);
  color: var(--rjr-navy-700);
}
/* markup: <i class="ph-bold ph-check-square rjr-callout__icon" aria-hidden="true"></i> */
.rjr-callout--affiliate .rjr-callout__icon {
  flex: none;
  font-size: 1.5rem;
  color: var(--rjr-accent);
}
.rjr-callout--affiliate a { font-weight: 700; text-decoration: underline; }

/* Pro tip — script label, no icon */
.rjr-callout--tip {
  display: block;
  background: var(--rjr-callout-tip-bg);
  border: 1px dashed var(--rjr-navy-200);
  color: var(--rjr-navy-700);
}
.rjr-callout--tip .rjr-callout__label {
  display: block;
  font-family: var(--rjr-font-script);
  font-size: 1.75rem;
  line-height: 1;
  color: var(--rjr-accent);
  margin-bottom: var(--rjr-space-2);
}

/* --- In-content blocks ---------------------------------------------------
   Two components for the middle of an article, pasted into a Custom HTML block
   by whoever is writing. Documented with copy-and-paste markup in
   proof/styleguide.html.

   Both are built so the article stores as little markup as possible: a wrapper
   class and plain elements, with every appearance decision here. That is what
   lets a design change reach articles that were published months earlier -
   pasted markup is frozen in the post once it is saved, but the class names
   inside it keep resolving to whatever this file says today.

   Rules that follow from that, and that the styleguide repeats:
   - Never add inline styles to a pasted block. They cannot be updated later.
   - Never rename these classes without a search of published content first. */

/*
 * In-content CTA. One offer, mid-article: the newsletter, the guide, the app,
 * the book. Paper stock, so it reads as something laid on the page rather than
 * as an advert dropped into the column.
 */
.rjr-incta {
  margin-block: var(--rjr-space-8);
  padding: var(--rjr-space-6);
  --rjr-card-texture-wash: 0.62;
  background-color: var(--rjr-ctx-card-bg);
  background-image:
    linear-gradient(rgba(255, 255, 253, var(--rjr-card-texture-wash)),
                    rgba(255, 255, 253, var(--rjr-card-texture-wash))),
    url("../img/crumpled-paper-texture.jpg");
  background-size: cover, cover;
  background-position: center, center;
  background-blend-mode: normal, multiply;
  /* The one piece of colour. It marks the block as an offer without a border
     all the way round, which would read as a browser dialog. */
  border-left: 4px solid var(--rjr-accent);
  border-radius: 3px;
  box-shadow:
    1px 2px 3px rgba(5, 39, 49, 0.05),
    3px 7px 18px rgba(5, 39, 49, 0.07);
}
.rjr-incta__eyebrow {
  margin: 0 0 var(--rjr-space-2);
  /* orange-800, not the accent. --rjr-accent is orange-500, which measures
     2.48:1 on this card stock - a clear AA failure at 12px bold, and this is
     the label telling the reader what the block is. orange-800 is 6.96:1 and
     still unmistakably the brand orange. The keyline down the left keeps the
     accent colour where contrast does not apply. */
  color: var(--rjr-orange-800);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
/* Sized under the article's own h2 and h3 on purpose. This is an aside, and a
   heading that outranks the section it interrupts breaks the read. */
.rjr-incta__title {
  margin: 0 0 var(--rjr-space-2);
  font-size: var(--rjr-text-xl);
  line-height: 1.2;
}
.rjr-incta__text {
  margin: 0 0 var(--rjr-space-4);
  color: var(--rjr-navy-600);
  font-size: var(--rjr-text-sm);
  line-height: 1.6;
}
.rjr-incta > :last-child { margin-bottom: 0; }

/*
 * In-content affiliate block. One product or service, why it is here, and where
 * to get it.
 *
 * Deliberately not the same shape as the CTA above: a reader should be able to
 * tell at a glance which blocks are paid and which are the site's own. The
 * teal keyline and the disclosure line under the button are what do that.
 */
.rjr-inaff {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--rjr-space-4);
  margin-block: var(--rjr-space-8);
  /* space-6, not space-5. There is no 5 on the scale (1,2,3,4,6,8,12,16,24) and
     an invalid var() inside a shorthand voids the whole declaration, so the
     block rendered with no padding at all rather than with a wrong value. */
  padding: var(--rjr-space-6);
  background: var(--rjr-paper);
  border: 1px solid var(--rjr-navy-200);
  border-top: 3px solid var(--rjr-teal-500);
  border-radius: var(--rjr-radius);
}
/* Optional. The block is designed to work without one, because a pasted <img>
   carries no srcset and the theme's rule is that library images should. */
.rjr-inaff__logo {
  /* Aligned to the top of the text, not centred against a block whose height
     depends on how much the writer typed. No grid-row span: the body is a
     single item, and spanning two rows only created an empty second row. */
  align-self: start;
  width: 4.5rem;
  height: 4.5rem;
  object-fit: contain;
}
/* No logo: the text spans both columns rather than leaving a gap where one
   would have been. */
.rjr-inaff__body { grid-column: 1 / -1; }
.rjr-inaff__logo ~ .rjr-inaff__body { grid-column: 2; }

.rjr-inaff__label {
  margin: 0 0 var(--rjr-space-1);
  color: var(--rjr-navy-500);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.rjr-inaff__title {
  margin: 0 0 var(--rjr-space-2);
  font-size: var(--rjr-text-lg);
  line-height: 1.2;
}
.rjr-inaff__text {
  margin: 0 0 var(--rjr-space-4);
  color: var(--rjr-navy-600);
  font-size: var(--rjr-text-sm);
  line-height: 1.6;
}
/*
 * The disclosure sits under the button, inside the block, every time.
 *
 * It is part of the component rather than something the writer remembers,
 * because this is the sentence that has to be there on every paid link and the
 * one most easily left out. navy-600 on paper measures 8.71:1: quiet, but not
 * hidden, which is exactly what a disclosure has to be.
 */
.rjr-inaff__note {
  margin: var(--rjr-space-3) 0 0;
  color: var(--rjr-navy-600);
  font-size: var(--rjr-text-xs);
  line-height: 1.5;
}

/* Pull quote */
.rjr-quote {
  position: relative;
  margin: 0;
  padding: clamp(1.5rem, 1.2rem + 1.4vw, 2.5rem);
  background: var(--rjr-ctx-card-bg);
  border: 1px solid var(--rjr-ctx-border);
  border-radius: var(--rjr-radius);
  box-shadow: var(--rjr-shadow-sm);
}
/* Phosphor `quotes` (fill) instead of a typographic " — the glyph varies
   wildly between the display face and its fallbacks, the icon does not. */
.rjr-quote__mark {
  position: absolute;
  top: clamp(-1rem, -0.8rem - 0.6vw, -0.6rem);
  left: clamp(1rem, 0.8rem + 0.8vw, 1.6rem);
  font-size: 2.75rem;
  color: var(--rjr-orange-300);
  pointer-events: none;
}
.rjr-quote p {
  position: relative;
  font-size: var(--rjr-text-lg);
  line-height: 1.5;
  color: var(--rjr-ctx-heading);
  font-weight: 500;
}
.rjr-quote footer,
.rjr-quote__cite {
  display: block;
  margin-top: var(--rjr-space-4);
  font-size: var(--rjr-text-sm);
  font-weight: 600;
  color: var(--rjr-ctx-muted);
  font-style: normal;
}

/* A torn note taped to the page, not a blockquote with a rule down its side.
   On a dark section a light paper scrap reads as a physical object instantly,
   which is the whole point — and it drops the last UI convention in the block. */
.rjr-quote--note {
  --rjr-card-texture-wash: 0.5;
  max-width: 30rem;
  padding: clamp(1.4rem, 1.1rem + 1vw, 2rem) clamp(1.5rem, 1.2rem + 1.2vw, 2.25rem);
  background-color: var(--rjr-paper);
  background-image:
    linear-gradient(rgba(255, 255, 253, var(--rjr-card-texture-wash)),
                    rgba(255, 255, 253, var(--rjr-card-texture-wash))),
    url("../img/crumpled-paper-texture.jpg");
  background-size: cover, cover;
  background-position: center, 30% 40%;
  background-blend-mode: normal, multiply;
  border: 0;
  border-radius: 2px;
  box-shadow: 3px 8px 20px rgba(2, 20, 26, 0.35);
  transform: rotate(-1.6deg);
}
/* The scrap is light even on a navy section, so its type has to flip back. */
/* Unscoped: the scrap is light paper on navy AND on cream, so its type is
   always dark. Previously navy-only, which left it inheriting section colour
   anywhere else. */
.rjr-quote--note p { color: var(--rjr-navy-800); }
.rjr-quote--note footer { color: var(--rjr-navy-600); }
.rjr-quote--note .rjr-quote__mark {
  position: static;
  display: block;
  margin-bottom: var(--rjr-space-2);
  font-size: 1.4rem;
  color: var(--rjr-accent);
}

/* Same mark, sized down and set in flow above the quote. */
.rjr-quote--note p { font-size: var(--rjr-text-base); font-style: italic; }


/* ==========================================================================
   11  BOOK SHOT
   The CSS-built cover mock is gone — this is the real product render.
   ========================================================================== */

.rjr-book-shot {
  display: block;
  width: min(var(--rjr-book-w, 22rem), 100%);
  height: auto;
  /* Pulled up so the book breaks the section's torn top edge and overlaps the
     page above. Overlapping a boundary is the strongest "physical object lying
     on the page" signal available, and it's what stops the section reading as
     a box. */
  position: relative;
  z-index: 3;                /* above the torn strip, which is z-index 2 */
  margin: calc(-1 * var(--rjr-book-rise, 2rem)) auto 0;
  transform: rotate(var(--rjr-book-tilt, -4deg));
  transform-origin: center bottom;
  filter: drop-shadow(10px 20px 26px rgba(2, 20, 26, 0.45));
  transition: transform var(--rjr-dur-slow) var(--rjr-ease-out);
}
.rjr-book-shot:hover { transform: rotate(calc(var(--rjr-book-tilt, -4deg) * 0.35)) translateY(-8px); }

/* ==========================================================================
   12  SCREENSHOT CARD
   A real reader message, presented as the screenshot it actually was.
   ========================================================================== */

.rjr-screenshot {
  position: relative;
  padding: var(--rjr-space-6);
  background: var(--rjr-surface-card);
  border: 1px solid var(--rjr-navy-100);
  border-radius: var(--rjr-radius);
  box-shadow: var(--rjr-shadow-md);
  transform: rotate(1.4deg);
  transition: transform var(--rjr-dur-slow) var(--rjr-ease-out);
}
.rjr-screenshot:hover { transform: rotate(0deg) translateY(-4px); }

.rjr-screenshot__bar {
  display: flex;
  align-items: center;
  gap: var(--rjr-space-3);
  padding-bottom: var(--rjr-space-3);
  margin-bottom: var(--rjr-space-4);
  border-bottom: 1px solid var(--rjr-navy-100);
}
.rjr-screenshot__avatar {
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  flex: none;
  border-radius: 50%;
  background: var(--rjr-teal-500);
  color: #fff;
  font-size: var(--rjr-text-sm);
  font-weight: 600;
}
.rjr-screenshot__to {
  font-size: var(--rjr-text-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--rjr-navy-400);
}
.rjr-screenshot__from {
  font-size: var(--rjr-text-sm);
  font-weight: 600;
  color: var(--rjr-navy-700);
}
.rjr-screenshot__body {
  font-size: var(--rjr-text-base);
  line-height: 1.6;
  color: var(--rjr-navy-700);
}
.rjr-screenshot__meta {
  margin-top: var(--rjr-space-4);
  font-size: var(--rjr-text-xs);
  color: var(--rjr-navy-400);
}


/* ==========================================================================
   13  FORMS
   ========================================================================== */

.rjr-form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--rjr-space-3);
  max-width: 34rem;
}
.rjr-form__field {
  flex: 1 1 16rem;
  min-width: 0;
}
/* The comment form's author, email and url fields are printed by WordPress
   without our class, so they are named here rather than duplicating the rule
   or filtering core's markup to inject a class. */
.rjr-input,
.rjr-comment-form input[type="text"],
.rjr-comment-form input[type="email"],
.rjr-comment-form input[type="url"],
.rjr-comment-form textarea {
  width: 100%;
  padding: 0.9em 1.1em;
  border: 2px solid transparent;
  border-radius: var(--rjr-radius);
  background: var(--rjr-cream);
  color: var(--rjr-navy-800);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-base);
  line-height: 1.2;
  transition: border-color var(--rjr-dur) var(--rjr-ease),
              box-shadow var(--rjr-dur) var(--rjr-ease);
}
.rjr-input::placeholder { color: var(--rjr-navy-300); }
.rjr-input:focus,
.rjr-comment-form input[type="text"]:focus,
.rjr-comment-form input[type="email"]:focus,
.rjr-comment-form input[type="url"]:focus,
.rjr-comment-form textarea:focus {
  outline: none;
  border-color: var(--rjr-accent);
  box-shadow: 0 0 0 4px rgba(244, 131, 31, 0.24);
}
.rjr-form__note {
  width: 100%;
  margin: 0;
  font-size: var(--rjr-text-xs);
  color: var(--rjr-navy-200);
}
.rjr-section--navy .rjr-form__note { color: var(--rjr-navy-200); }


/* ==========================================================================
   14  SITE CHROME
   ========================================================================== */

/*
 * The ticket floats over the page rather than sitting above it.
 *
 * A sticky element stays in normal flow and reserves its height, which pushed
 * the hero down and left an empty band across the top of every page - the
 * ticket looked transparent, but the row it lived in was still the page
 * background. The negative bottom margin removes that reserved height, so the
 * next section starts at the top of the viewport and the ticket lies on top of
 * it. Sticky still works: the offset is measured against the scrollport, not
 * against where the element sits in flow.
 *
 * --rjr-header-h is measured by navigation.js, because the logo is a
 * custom-logo upload and its height is whatever Site Identity says. The
 * fallbacks here are the shipped-logo heights, so the first paint is right
 * even before the script runs.
 */
/*
 * Logged-in offset, in one place.
 *
 * The admin bar is position:fixed, so a sticky header at top:0 slides under it.
 * Core publishes its own height on <html> as --wp-admin--admin-bar--height
 * (32px, 46px at 782px and below), so the number is read from there rather than
 * copied - if core changes it, this follows.
 *
 * At 600px and below core switches the bar to position:absolute: it scrolls
 * away with the page and reserves nothing, so the offset goes back to zero or
 * the header would float a permanent 46px gap below the top of the screen.
 *
 * Everything that pins itself to the top of the viewport reads this one value:
 * the header, the mobile menu overlay, and the two controls fixed inside it.
 */
:root { --rjr-admin-bar-h: 0px; }
body.admin-bar { --rjr-admin-bar-h: var(--wp-admin--admin-bar--height, 32px); }
@media screen and (max-width: 600px) {
  body.admin-bar { --rjr-admin-bar-h: 0px; }
}

.rjr-header {
  position: sticky;
  top: var(--rjr-admin-bar-h);
  z-index: 100;
  margin-bottom: calc(-1 * var(--rjr-header-h));
  padding-block: var(--rjr-space-3) var(--rjr-space-2);
  background: none;
  pointer-events: none;   /* the transparent gutter must not eat hero clicks */
}
.rjr-header > * { pointer-events: auto; }

/*
 * Sections that open a page run underneath the ticket, so each adds the
 * header's height to its own --rjr-pt-base: see .rjr-hero, .rjr-hero--compact
 * and .rjr-article-head. Any new page-opening section has to do the same.
 *
 * This was a `#main > .rjr-section:first-child` rule until Ad Inserter turned
 * up: it injects its own <div> as the first child of <main>, so :first-child
 * matched the ad slot and the article header printed its eyebrow under the
 * ticket. Structural position is not something this theme controls. Folding
 * the offset into --rjr-pt-base also keeps the .rjr-pt-* utilities composing
 * correctly on top of it, which a separate padding-top rule would not.
 */
/*
 * The bar is one long boarding ticket.
 *
 * ticket-style-3 is 433x169 with a rounded left end and a perforated stub on
 * the right. Both ends have to survive at any width, so it is a nine-slice
 * border-image rather than a stretched background: the four corner slices are
 * drawn at fixed size and only the middle is stretched. Slices measured off
 * the alpha channel - the notches at the perforation are 11px deep, so a 34px
 * top and bottom slice clears them, and the 85px right slice takes in the
 * whole stub.
 *
 * --rjr-tk-k converts a slice in source pixels to rendered pixels, so the caps
 * scale with the bar's height instead of being pinned at native size. Dividing
 * a length by the source height gives a length, which is what the calcs below
 * multiply back up.
 */
.rjr-header__inner {
  --rjr-tk-h: 82px;
  --rjr-tk-k: calc(var(--rjr-tk-h) / 169);

  display: flex;
  align-items: center;
  gap: var(--rjr-space-6);
  width: calc(100% - (2 * var(--rjr-section-padding-x)));
  max-width: var(--rjr-container-wide);
  min-height: var(--rjr-tk-h);
  margin-inline: auto;

  border-style: solid;
  border-color: transparent;
  border-width: calc(34 * var(--rjr-tk-k)) calc(85 * var(--rjr-tk-k))
                calc(34 * var(--rjr-tk-k)) calc(60 * var(--rjr-tk-k));
  border-image: url("../img/ticket-style-3.png") 34 85 34 60 fill stretch;

  /* drop-shadow, not box-shadow: border-image leaves the corners and the notch
     cutouts transparent, and box-shadow would draw a rectangle behind them. */
  filter: drop-shadow(0 6px 10px rgba(5, 39, 49, 0.16));
}
/*
 * A filter makes the element a containing block for fixed descendants, and the
 * mobile overlay is a fixed descendant of this one - with the shadow applied it
 * would be trapped inside the ticket instead of covering the viewport. Dropped
 * for as long as the menu is up, when the ticket is hidden behind the panel
 * anyway and the shadow has nothing to cast onto.
 */
body.rjr-menu-open .rjr-header__inner {
  filter: none;

  /* Inside the caps. The right cap is the stub, so content stops short of it
     rather than printing over the perforation. */
  padding: 0 var(--rjr-space-4) 0 var(--rjr-space-2);
}
/* No border-image: the bar keeps the paper surface and a hairline, which is
   what it looked like before the ticket. */
@supports not (border-image: url("../img/ticket-style-3.png") 34 fill stretch) {
  .rjr-header__inner {
    border: 0;
    border-bottom: 1px solid rgba(167, 196, 205, 0.45);
    padding: var(--rjr-space-3) var(--rjr-section-padding-x);
    width: 100%;
  }
}
.rjr-header__logo img {
  width: auto;
  height: clamp(34px, 3vw, 46px);
}
.rjr-header__actions {
  display: flex;
  align-items: center;
  gap: var(--rjr-space-2);
  margin-left: auto;
}

/* Icon-only controls: search, menu, close. */
.rjr-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;            /* 44px minimum touch target */
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: var(--rjr-radius-pill);
  background: transparent;
  color: var(--rjr-navy-700);
  font-size: 1.25rem;
  cursor: pointer;
  transition: background var(--rjr-dur-fast) var(--rjr-ease),
              color var(--rjr-dur-fast) var(--rjr-ease);
}
.rjr-icon-btn:hover {
  background: rgba(8, 66, 82, 0.08);
  color: var(--rjr-navy-900);
}

/* Navy, from the global CTA tokens - the orange was tried and dropped. Only
   the tilt is local. */
.rjr-header__cta {
  transform: rotate(-1.2deg);   /* the pager tickets' tilt */
  white-space: nowrap;
}
.rjr-header__cta:hover { transform: rotate(-1.6deg) translateY(-2px); }

/* The list, not the nav, is the flex row: WordPress emits <ul><li><a> and the
   proof pages use the same markup so the two cannot drift.
   Direct child only - the sub-menu is a ul too, and would inherit the row. */
.rjr-nav {
  margin-left: auto;
}
.rjr-nav > ul {
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 0.3rem + 1.2vw, 1.75rem);
  list-style: none;
  margin: 0;
  padding: 0;
}
.rjr-nav li { position: relative; }
/* Scoped to the list, not the nav. `.rjr-nav a` is (0,1,1) and was beating
   .rjr-btn--primary's colour and display on the CTA that shares this element,
   which printed the mobile button as navy-on-orange. */
.rjr-nav > ul a {
  position: relative;
  display: inline-block;
  color: var(--rjr-navy-700);
  font-size: var(--rjr-text-sm);
  font-weight: 500;
  text-decoration: none;
  padding-block: 0.35em;
}
.rjr-nav > ul a:hover { color: var(--rjr-navy-900); }
/* Hand-drawn stroke rather than the 2px bar it replaces: same wipe, but the
   shape belongs to the brand. */
.rjr-nav > ul a::after {
  content: "";
  position: absolute;
  left: -0.2em; right: -0.2em; bottom: -1px;
  height: 9px;
  background: var(--rjr-accent);
  -webkit-mask-image: var(--rjr-img-paint-orange);
  mask-image: var(--rjr-img-paint-orange);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--rjr-dur) var(--rjr-ease);
}
.rjr-nav > ul a:hover::after,
.rjr-nav .current-menu-item > a::after,
.rjr-nav .current-menu-ancestor > a::after { transform: scaleX(1); }

/* Parent items get a caret. Drawn in CSS so the markup stays whatever
   wp_nav_menu emits. */
.rjr-nav .menu-item-has-children > a { padding-right: 0.9em; }
.rjr-nav .menu-item-has-children > a::before {
  content: "";
  position: absolute;
  right: 0;
  top: 50%;
  width: 0.36em; height: 0.36em;
  margin-top: -0.28em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform var(--rjr-dur-fast) var(--rjr-ease);
}
.rjr-nav .menu-item-has-children:hover > a::before,
.rjr-nav .menu-item-has-children:focus-within > a::before {
  transform: rotate(225deg);
  margin-top: -0.1em;
}

/* The dropdown: a paper card with a piece of tape, opened on hover and on
   focus-within. No script - the CSS cannot fall out of step with the DOM, and
   keyboard users get it for free. */
.rjr-nav .sub-menu {
  position: absolute;
  top: 100%;
  left: 50%;
  z-index: 10;
  display: block;
  min-width: 17rem;
  margin: 0;
  padding: var(--rjr-space-3);
  list-style: none;
  background-color: var(--rjr-ctx-card-bg);
  background-image:
    linear-gradient(rgba(251, 250, 245, 0.93), rgba(251, 250, 245, 0.93)),
    url("../img/crumpled-paper-texture.jpg");
  background-size: auto, 420px auto;
  border-radius: var(--rjr-radius);
  box-shadow: var(--rjr-shadow-lg);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -6px) rotate(-0.5deg);
  transition: opacity var(--rjr-dur) var(--rjr-ease),
              transform var(--rjr-dur) var(--rjr-ease),
              visibility 0s linear var(--rjr-dur);
}
/* Bridges the gap between the item and the card, so the pointer can cross
   without the card closing under it. */
.rjr-nav .sub-menu::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: -0.7rem;
  height: 0.7rem;
}
.rjr-nav .sub-menu::before {
  content: "";
  position: absolute;
  top: -12px;
  left: 1.25rem;
  width: 88px; height: 30px;
  background: url("../img/tape-tilted-left-small.png") no-repeat center / contain;
  pointer-events: none;
}
.rjr-nav .menu-item-has-children:hover > .sub-menu,
.rjr-nav .menu-item-has-children:focus-within > .sub-menu {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0.7rem) rotate(-0.5deg);
  transition-delay: 0s;
}
.rjr-nav .sub-menu a {
  display: block;
  padding: 0.55em 0.7em;
  border-radius: var(--rjr-radius-sm);
  font-size: var(--rjr-text-sm);
  line-height: 1.35;
}
.rjr-nav .sub-menu a:hover { background: rgba(8, 66, 82, 0.06); }
/* The stroke is for the top row. In a stacked card it would sit across the
   text of the row below. */
.rjr-nav .sub-menu a::after { content: none; }

/* Desktop: the panel chrome and the duplicated CTA belong to the mobile shape
   only. */
.rjr-nav__cta,
.rjr-nav__close,
.rjr-nav__logo,
.rjr-nav__plane,
.rjr-header__menu-toggle { display: none; }

/* --- 404 ------------------------------------------------------------------
   The stub, drawn with ticket-style-2 as a nine-slice. Slices measured off the
   alpha channel: the notches at both perforations are 22px deep, so 32px top
   and bottom clears them, and 85/88 takes in each perforated end.

   border-image-repeat is `stretch round`, not the default `stretch`. The first
   value governs the top and bottom edges, the second the left and right - and
   the left and right edges are the perforations. Stretched down a panel this
   tall the dots would smear into streaks; `round` tiles them instead, which is
   what a longer ticket would actually look like. */
.rjr-404-ticket {
  --rjr-tk2-k: 1;

  position: relative;
  display: flex;
  align-items: center;
  max-width: 62rem;
  min-height: 17rem;
  margin-inline: auto;
  padding: var(--rjr-space-6) var(--rjr-space-8);

  border-style: solid;
  border-color: transparent;
  border-width: calc(32px * var(--rjr-tk2-k)) calc(88px * var(--rjr-tk2-k))
                calc(32px * var(--rjr-tk2-k)) calc(85px * var(--rjr-tk2-k));
  border-image: url("../img/ticket-style-2.png") 32 88 32 85 fill stretch round;

  filter: drop-shadow(0 10px 18px rgba(5, 39, 49, 0.18));
}
/* Overprinted large in the top right of the ticket's inner panel, the way a
   flight number is. Positioned against the padding box, so it stays inside the
   perforations rather than sitting over the stub art. */
.rjr-404-ticket__body { position: relative; }
.rjr-404-ticket__code {
  position: absolute;
  top: -0.15em;
  right: 0;
  font-family: var(--rjr-font-display);
  font-size: clamp(3.5rem, 2rem + 7vw, 7rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--rjr-navy-200);
  pointer-events: none;
  user-select: none;
}
.rjr-404-ticket__body > * + * { margin-top: var(--rjr-space-4); }
/* Keeps the copy clear of the overprint on desktop, where the two share a
   line. Below 620px the code sits above the eyebrow instead. */
.rjr-404-ticket__body > .rjr-eyebrow,
.rjr-404-ticket__body > h1 { max-width: calc(100% - 8rem); }
.rjr-404-ticket__body h1 { font-size: var(--rjr-text-3xl); }
.rjr-404-ticket__body .rjr-btn-row { margin-top: var(--rjr-space-6); }

.rjr-404-search {
  max-width: 40rem;
  margin: 0 auto var(--rjr-space-4);
}

/* --- Our Recommendations --------------------------------------------------
   Bands of paper cards, each carrying a partner logo. */

.rjr-rec-disclosure {
  max-width: 44rem;
  margin: var(--rjr-space-6) auto 0;
  text-align: left;
}

/*
 * Hero: copy on the left, the prints on the right. Below 860px the two columns
 * become one, and because the text is first in the source the pile lands under
 * it rather than pushing the h1 off a phone screen.
 */
.rjr-rec-head {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--rjr-space-md);
}

/* Ranged left, not centered. .rjr-page-head centers itself for a full-width
   header; in a half-width column a centered ragged edge on both sides is just
   harder to read, and it leaves the copy floating rather than anchored against
   the prints.
   Both classes are named because .rjr-page-head is also (0,1,0) and sits
   further down the file, so a single-class rule here loses on order. */
.rjr-page-head.rjr-rec-head__text {
  margin-inline: 0;
  text-align: left;
}

/* Two prints dropped on top of each other, not two ornaments in two boxes. The
   overlap is a negative margin on the left print rather than absolute
   positioning, so the pile still claims its own height in the grid. */
.rjr-rec-head__photos {
  display: flex;
  align-items: center;
  justify-content: center;
}
.rjr-rec-head__photo {
  --rjr-rec-photo-tilt: 0deg;
  /* The tilt is baked into each frame scan, so this sits on top of it: both
     frames were shot leaning left, and the right-hand print reads better
     leaning the other way. The reveal composition that actually applies it
     lives with the other .rjr-reveal transform fixes further down. */
  transform: rotate(var(--rjr-rec-photo-tilt));
}
/*
 * Left over right, and larger: the front print of a pile is the one nearest the
 * camera, so matching sizes would read as two photographs pasted side by side.
 *
 * Percentages and max-widths do different jobs here. The caps set the size on a
 * wide screen; the percentages take over once the column is narrower than the
 * caps, and they have to keep 60 + 50 - 12 under 100 or the pile outgrows its
 * column and pushes a scrollbar onto the page.
 *
 * z-index alone settles the stacking: a flex item with a z-index of its own
 * stacks like a positioned box, so position is only here for older engines.
 */
.rjr-rec-head__photo--left {
  position: relative;
  z-index: 2;
  flex: 0 0 60%;
  max-width: 20rem;
  margin-right: -12%;
}
.rjr-rec-head__photo--right {
  position: relative;
  z-index: 1;
  flex: 0 0 50%;
  max-width: 16.5rem;
  /* Dropped a little so the two read as a pile rather than a pair. */
  margin-top: var(--rjr-space-6);
  --rjr-rec-photo-tilt: 8deg;
}

/* No hover on these: they are decoration in a page header, not something to
   click, and a print that lifts under the cursor invites a click that never
   arrives. Both properties have to be restated - .rjr-polaroid:hover is (0,2,0)
   and would otherwise win against the base rule above. */
.rjr-rec-head__photo:hover {
  transform: rotate(var(--rjr-rec-photo-tilt));
  filter: drop-shadow(0 18px 28px rgba(5, 39, 49, 0.22));
}

/*
 * The wall: one cell per recommendation, each holding a card and, for the first
 * card of a band, the band name above it.
 *
 * Every cell reserves the label's height whether it carries one or not, and the
 * label is taken out of flow into that reserved strip. Six of the eight cards
 * start a band, so letting labelled cells push their card down would leave the
 * two unlabelled ones floating a whole label higher - too big a step to read as
 * the deliberate hand-placed stagger the cards already have.
 */
.rjr-rec-wall {
  /* Wider rows than columns. The band name needs air above it, and without the
     extra row gap the label of the row below crowds the card above it. */
  row-gap: clamp(3rem, 2.2rem + 3vw, 5rem);
}
.rjr-rec-cell {
  position: relative;
  display: flex;
  padding-top: 3.25rem;
}
.rjr-rec-cell > .rjr-card {
  flex: 1;
}

/*
 * The band name sits above the paper, not on it, and appears once per band.
 *
 * The padding is measured off the brush, not guessed. blue-swoosh.png is a
 * scan with tapered, ragged ends: alpha is only continuous from 8.9% to 89.8%
 * across and 10.4% to 83.9% down, so anything inside a tighter inset than that
 * sits on the fraying rather than on paint.
 */
.rjr-rec-band__title {
  position: absolute;
  top: 0;
  left: 0.4rem;
  max-width: calc(100% - 0.8rem);
  margin: 0;
  padding: 0.35em 1.5em 0.5em;
  background-image: var(--rjr-img-paint-blue);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  /* 5.93:1 on the swoosh's #006C8F. */
  color: #FFFFFF;
  font-size: var(--rjr-text-base);
  line-height: 1.25;
  transform: rotate(-1.2deg);
}

/* The cards are no longer direct children of the grid, so the tilt, the stagger
   and the numbering all have to reach through the cell. Same values as the
   generic rules, re-pointed - not a restyle. */
.rjr-rec-wall { counter-reset: rjr-card-n; }
.rjr-rec-wall > .rjr-rec-cell > .rjr-card::before {
  counter-increment: rjr-card-n;
  content: counter(rjr-card-n, decimal-leading-zero);
  position: absolute;
  bottom: 0.35rem;
  right: 0.9rem;
  font-family: var(--rjr-font-display);
  font-weight: 700;
  font-size: 2.25rem;
  line-height: 1;
  color: var(--rjr-navy-800);
  opacity: 0.10;
  pointer-events: none;
}
.rjr-rec-wall > .rjr-rec-cell:nth-child(4n + 1) > .rjr-card { --rjr-card-tilt: -0.7deg; background-position: center, 12% 20%; }
.rjr-rec-wall > .rjr-rec-cell:nth-child(4n + 2) > .rjr-card { --rjr-card-tilt:  0.5deg; --rjr-card-offset: 10px; background-position: center, 78% 62%; }
.rjr-rec-wall > .rjr-rec-cell:nth-child(4n + 3) > .rjr-card { --rjr-card-tilt: -0.4deg; --rjr-card-offset: 4px; background-position: center, 40% 85%; }
.rjr-rec-wall > .rjr-rec-cell:nth-child(4n + 4) > .rjr-card { --rjr-card-tilt:  0.8deg; --rjr-card-offset: 14px; background-position: center, 88% 8%; }

/* Bare logos on the card stock. No plate, no padding, no white box: the box
   was doing the unifying work, so a fixed height does it instead - every logo
   sits on the same baseline whatever its own proportions. */
.rjr-rec-card__plates {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--rjr-space-4);
  min-height: 2.5rem;
}
.rjr-rec-card__plate {
  display: inline-flex;
  align-items: center;
  transition: transform var(--rjr-dur) var(--rjr-ease);
}
a.rjr-rec-card__plate:hover { transform: scale(1.05); }
/*
 * The plate is what makes nine unrelated logos sit together. They arrive at
 * every ratio and every background - a wordmark SVG, a square orange tile, a
 * dark banner, a photograph of a credit card - so each is dropped into a fixed
 * box on white and contained rather than sized by its own dimensions. Without
 * this the row reads as a jumble sale.
 */
/* Height, not width: a fixed height is what makes a wordmark and a square
   badge carry the same optical weight. */
.rjr-rec-card__logo {
  width: auto;
  height: 2.25rem;
  max-width: 10rem;
  object-fit: contain;
}

.rjr-rec-card__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--rjr-space-2) var(--rjr-space-5, 1.25rem);
}

/* The search panel drops out of the bar at every width. Transparent, like the
   ticket's row: the field is the only opaque thing in it, so the hero keeps
   running underneath.

   Absolute, so opening it lays the field over the page instead of pushing
   everything below it down by the panel's height. The header is sticky and
   therefore already a positioned ancestor, so this needs nothing else. */
.rjr-search-panel {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  background: none;
}
.rjr-search-panel[hidden] { display: none; }
.rjr-search-panel__inner {
  max-width: var(--rjr-container-wide);
  margin-inline: auto;
  padding: var(--rjr-space-4) var(--rjr-section-padding-x);
}
.rjr-search-form__row {
  display: flex;
  align-items: center;
  gap: var(--rjr-space-2);
  position: relative;
}
.rjr-search-form__icon {
  position: absolute;
  left: 0.9rem;
  color: var(--rjr-navy-400);
  pointer-events: none;
}
.rjr-search-form__field {
  flex: 1 1 auto;
  min-width: 0;
  padding-left: 2.6rem;
  /* White, not cream: the panel is cream, and .rjr-input fills cream. */
  background: #FFFFFF;
  border: 1px solid var(--rjr-border);
}
/* Bright orange with white text, as specified. Noted for the record: this pair
   measures 2.59:1, below the 4.5:1 AA needs at this size. The hover darkens to
   orange-600, which helps a little but does not clear it either. */
.rjr-search-form__submit {
  flex: 0 0 auto;
  --rjr-cta: var(--rjr-orange-500);
  --rjr-cta-hover: var(--rjr-orange-600);
  --rjr-cta-text: #FFFFFF;
}

/* The results page offers the form again under the empty state. */
.rjr-archive-empty .rjr-search-form {
  max-width: 32rem;
  margin: var(--rjr-space-6) auto 0;
}

/* --- Start Here -----------------------------------------------------------
   The link-in-bio landing. One narrow column at every width: essentially all of
   its traffic is a thumb arriving from a social bio, so the phone layout is the
   layout and the desktop one is the same column centered. Nothing here reflows
   into two columns, on purpose - a second column would put half the rows out of
   the thumb's reach for the sake of filling a screen almost nobody uses. */

/*
 * The background covers the viewport and stays put while the rows scroll over
 * it. Written as a fixed element rather than `background-attachment: fixed`,
 * which iOS Safari has never handled properly and which stutters badly on the
 * phones this page is built for.
 *
 * The parallax slack the hero normally carries is removed: nothing is moving
 * this layer any more, and the oversize would just re-zoom the crop.
 *
 * A transform, filter or will-change on any ancestor would make that ancestor
 * the containing block and quietly turn this back into an absolute layer. It is
 * the same trap the mobile menu hit against the header's drop-shadow.
 */
.rjr-start .rjr-hero__bg {
  position: fixed;
  inset: 0;
  height: 100vh;
  /* Beats the URL bar's show and hide on a phone, where 100vh is the tall
     value and leaves a strip of nothing at the bottom mid-scroll. */
  height: 100dvh;
}
.rjr-start .rjr-hero__bg-img {
  inset: 0;
  background-attachment: scroll;
}

/* No header on this page, so the clearance the hero normally reserves for it
   has to go. .rjr-hero--compact adds --rjr-header-h to its own padding-top, and
   with the header absent navigation.js never measures one, leaving the 102px
   default as dead space above the print. */
.rjr-main--no-header { --rjr-header-h: 0px; }

.rjr-start__inner {
  max-width: 34rem;
  margin-inline: auto;
}

.rjr-start__head { text-align: center; }
.rjr-start__head h1 { margin: 0; }

/* Lisa and Matt, in a print. Small: the point is recognition, not a portrait,
   and the frame is already doing the work a plain circle could not. */
.rjr-start__photo {
  width: min(17rem, 68%);
  margin: 0 auto var(--rjr-space-4);
}
/* Decoration in a page header, not something to click. Same reasoning as the
   recommendations prints: a photograph that lifts under the cursor invites a
   click that never arrives. .rjr-polaroid:hover is (0,2,0), so both properties
   have to be restated to beat it. */
.rjr-start__photo:hover {
  transform: none;
  filter: drop-shadow(0 18px 28px rgba(5, 39, 49, 0.22));
}
.rjr-start__tagline {
  margin: var(--rjr-space-3) 0 0;
  color: var(--rjr-navy-600);
  text-wrap: balance;
}
/* The bar is punctuation, not a word. At full strength three of them chop the
   line into three competing pieces. */
.rjr-start__sep { color: var(--rjr-navy-300); }

/* At the foot of the page now. Quieter than the callout is elsewhere, because
   down here it is a footnote closing the page rather than a warning opening
   it. */
.rjr-start__disclosure {
  margin-block: var(--rjr-space-8) 0;
  padding-block: var(--rjr-space-3);
  background: rgba(255, 255, 255, 0.55);
  color: var(--rjr-navy-600);
  font-size: var(--rjr-text-xs);
  text-align: left;
}
.rjr-start__disclosure p { margin: 0; }
.rjr-start__disclosure .rjr-callout__icon { font-size: 1rem; }

.rjr-start__group { margin-top: var(--rjr-space-8); }
.rjr-start__group-title {
  margin: 0 0 var(--rjr-space-2);
  font-size: var(--rjr-text-xl);
}
.rjr-start__group-note {
  margin: 0 0 var(--rjr-space-4);
  color: var(--rjr-navy-600);
  font-size: var(--rjr-text-sm);
}

/*
 * One row, one destination, one tap. Built as a flex row rather than a card
 * stack so the whole thing is the target: at this size a reader should not have
 * to find the link inside the row.
 *
 * The paper is the same stock the cards use, at a flatter wash - a full-strength
 * crumple behind fifteen stacked rows reads as noise rather than texture.
 */
.rjr-start-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--rjr-space-4);
  /* 4.5rem clears the 44px minimum with room to spare, and every row is the
     same height whether or not it carries a note. */
  min-height: 4.5rem;
  padding: var(--rjr-space-3) var(--rjr-space-4);
  margin-top: var(--rjr-space-3);
  --rjr-card-texture-wash: 0.72;
  background-color: var(--rjr-ctx-card-bg);
  background-image:
    linear-gradient(rgba(255, 255, 253, var(--rjr-card-texture-wash)),
                    rgba(255, 255, 253, var(--rjr-card-texture-wash))),
    url("../img/crumpled-paper-texture.jpg");
  background-size: cover, cover;
  background-position: center, center;
  background-blend-mode: normal, multiply;
  border-radius: 3px;
  box-shadow:
    1px 2px 3px rgba(5, 39, 49, 0.05),
    3px 7px 18px rgba(5, 39, 49, 0.07);
  color: var(--rjr-navy-800);
  text-decoration: none;
  transform: translateY(var(--rjr-start-lift, 0px));
  transition: transform var(--rjr-dur) var(--rjr-ease),
              box-shadow var(--rjr-dur) var(--rjr-ease);
}
/* No tilt on these, unlike the cards elsewhere. Fifteen leaning rows in a
   single column reads as a mistake rather than as hand-placement. */
.rjr-start-row:hover,
.rjr-start-row:focus-visible {
  --rjr-start-lift: -3px;
  box-shadow:
    2px 4px 6px rgba(5, 39, 49, 0.06),
    6px 14px 30px rgba(5, 39, 49, 0.12);
}

/* The book. One row is allowed to be louder than the others, and it is the one
   with a price on it. */
.rjr-start-row--featured {
  --rjr-card-texture-wash: 0.4;
  min-height: 5.5rem;
  box-shadow:
    0 0 0 2px var(--rjr-orange-500),
    3px 7px 18px rgba(5, 39, 49, 0.10);
}

/*
 * A fixed square whatever goes in it. The thumbnails arrive as wordmarks,
 * square product shots, a book packshot and portrait photographs, and only a
 * fixed box makes fifteen of them sit together down one column.
 */
.rjr-start-row__mark {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 3.25rem;
  height: 3.25rem;
  border-radius: 4px;
  overflow: hidden;
  font-size: 1.5rem;
  color: var(--rjr-navy-500);
}
.rjr-start-row__img {
  width: 100%;
  height: 100%;
  /* Photographs fill the square. A contained photograph leaves white bars down
     two sides and the column stops looking like a set. */
  object-fit: cover;
}
/* Logos and packshots must not be cropped, and they need room to breathe rather
   than running to the edge of the square. */
.rjr-start-row__mark--contain { overflow: visible; }
.rjr-start-row__mark--contain .rjr-start-row__img {
  object-fit: contain;
  padding: 1px;
}

.rjr-start-row__text {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}
.rjr-start-row__label {
  font-family: var(--rjr-font-display);
  font-weight: 700;
  font-size: var(--rjr-text-base);
  line-height: 1.25;
  color: var(--rjr-navy-900);
}
.rjr-start-row__note {
  color: var(--rjr-navy-600);
  font-size: var(--rjr-text-sm);
  line-height: 1.4;
}

.rjr-start-row__arrow {
  flex: 0 0 auto;
  margin-left: auto;
  color: var(--rjr-navy-400);
  transition: transform var(--rjr-dur) var(--rjr-ease);
}
.rjr-start-row:hover .rjr-start-row__arrow { transform: translateX(3px); }

/* The six categories, as one sheet rather than six separate rows. They are the
   reason this page exists, but they are also the shortest labels on it and six
   full 4.5rem rows would waste most of a phone screen on white space. Same
   paper as the rows so the block still belongs to the page. */
.rjr-start__cats {
  list-style: none;
  margin: 0;
  padding: var(--rjr-space-2) var(--rjr-space-3);
  --rjr-card-texture-wash: 0.72;
  background-color: var(--rjr-ctx-card-bg);
  background-image:
    linear-gradient(rgba(255, 255, 253, var(--rjr-card-texture-wash)),
                    rgba(255, 255, 253, var(--rjr-card-texture-wash))),
    url("../img/crumpled-paper-texture.jpg");
  background-size: cover, cover;
  background-position: center, center;
  background-blend-mode: normal, multiply;
  border-radius: 3px;
  box-shadow:
    1px 2px 3px rgba(5, 39, 49, 0.05),
    3px 7px 18px rgba(5, 39, 49, 0.07);
}
.rjr-start__cats a {
  display: flex;
  align-items: center;
  gap: var(--rjr-space-3);
  min-height: 3.25rem;
  padding: var(--rjr-space-2) var(--rjr-space-3);
  border-bottom: 1px solid rgba(5, 39, 49, 0.10);
  color: var(--rjr-navy-800);
  font-weight: 600;
  text-decoration: none;
}
.rjr-start__cats li:last-child a { border-bottom: 0; }
.rjr-start__cats a:hover { color: var(--rjr-navy-500); }
.rjr-start__cat-label { min-width: 0; }
.rjr-start__cats i {
  margin-left: auto;
  color: var(--rjr-navy-400);
}

/*
 * The library card's drawing, shrunk into a row. .rjr-cat-icon is built for a
 * card corner: it sizes itself from --rjr-cat-icon-w, sits on the bottom edge
 * and leans. In a 3.25rem row all three of those have to be reset, or the
 * plane alone is 7rem wide and pushes the label off the sheet.
 */
.rjr-start__cat-icon {
  flex: 0 0 2.25rem;
  --rjr-cat-icon-w: 2.25rem;
  --rjr-cat-icon-tilt: 0deg;
  height: 2.25rem;
  background-position: center;
  background-size: contain;
}

.rjr-start__foot {
  margin-top: var(--rjr-space-12);
  text-align: center;
}
.rjr-start__social {
  display: flex;
  justify-content: center;
  gap: var(--rjr-space-3);
  list-style: none;
  margin: 0 0 var(--rjr-space-6);
  padding: 0;
}
/*
 * Paper discs, not a dark wash. The footer's version sits on navy where a
 * lightened circle reads correctly; here the page is light and that same trick
 * produced three grey smudges. These match the rows instead - same stock, same
 * shadow - so they read as part of the page rather than as leftover UI.
 *
 * text-decoration: none is not optional. The base `a` rule sets underline
 * thickness and offset but never turns the underline off, so an icon-only link
 * gets a stray rule under the glyph. The footer's .rjr-footer a already covers
 * this; nothing did here.
 */
.rjr-start__social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 3rem, comfortably past the 44px minimum for a thumb. */
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background-color: var(--rjr-ctx-card-bg);
  color: var(--rjr-navy-700);
  font-size: 1.3rem;
  line-height: 1;
  text-decoration: none;
  box-shadow:
    1px 2px 3px rgba(5, 39, 49, 0.05),
    3px 7px 18px rgba(5, 39, 49, 0.07);
  transition: background-color var(--rjr-dur) var(--rjr-ease),
              color var(--rjr-dur) var(--rjr-ease),
              transform var(--rjr-dur) var(--rjr-ease);
}
/* navy-900 on orange-500 is 5.77:1. */
.rjr-start__social a:hover,
.rjr-start__social a:focus-visible {
  background-color: var(--rjr-orange-500);
  color: var(--rjr-navy-900);
  transform: translateY(-3px);
}

.rjr-start__home { text-decoration: none; }

/* --- About Lisa -----------------------------------------------------------
   Copy left, one print right, then the numbers. Built on the components that
   already exist rather than new ones: the print is a .rjr-polaroid, the
   checklist sits on a .rjr-card, and the category tiles reuse .rjr-card--link
   from the front page library. */

/* Its own backdrop rather than the home page's. Two classes, not one: .rjr-hero
   declares --rjr-hero-image too and sits further down the file, so at equal
   weight source order would hand it straight back. Same trap as .rjr-contact. */
.rjr-hero.rjr-about { --rjr-hero-image: url("../img/rjr-bg-2.webp"); }

.rjr-about-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.95fr);
  align-items: center;
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
}
.rjr-about-hero__copy h1 { margin: 0 0 var(--rjr-space-4); }
.rjr-about-hero__copy .rjr-lede { margin: 0; }
.rjr-about-hero__photo { width: min(26rem, 100%); }
/* Decoration in a page header, not something to click. .rjr-polaroid:hover is
   (0,2,0), so both properties have to be restated to beat it. */
.rjr-about-hero__photo:hover {
  transform: none;
  filter: drop-shadow(0 18px 28px rgba(5, 39, 49, 0.22));
}

/* Three facts, already published elsewhere on the site, as three paper tiles.
   The hairline rule that used to separate them from the hero is gone: it was
   the only hairline in the whole design language, which is why it read as
   borrowed from a different site. Paper on a photograph does the separating.

   Layout, tilt and stagger come from .rjr-grid > .rjr-card, so nothing here
   positions anything. Only the tile's own proportions are local. */
.rjr-about-stats {
  list-style: none;
  margin: clamp(2.5rem, 2rem + 2vw, 4rem) 0 0;
  padding: 0;
}
/* Tilt kept, vertical stagger dropped - same call as the category grid below.
   Three parallel facts read against each other, so the numbers want a shared
   line; the drop put "continents" and "countries" 10px apart for no reason. */
.rjr-grid.rjr-about-stats > .rjr-card:nth-child(n) { --rjr-card-offset: 0px; }
.rjr-about-stats__item {
  gap: var(--rjr-space-1);
  /* Shallower than a content card. These hold four words between them, and at
     the card's own padding they were three big empty rectangles. */
  --rjr-pt-base: clamp(1rem, 0.85rem + 0.6vw, 1.35rem);
  text-align: center;
  align-items: center;
}
.rjr-about-stats__n {
  display: block;
  color: var(--rjr-navy-900);
  font-family: var(--rjr-font-display);
  font-weight: 700;
  /* "Carry-on" is eight characters against "7" and "95+", so a fixed size set
     the tile width by the longest string and left the other two looking empty.
     Fluid, and it holds one line in all three tiles down to 360px. */
  font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.25rem);
  line-height: 1.05;
}
/* Spacing only. The look is .rjr-eyebrow--orange, and this rule sits after it
   in the file at the same weight (0,1,0) - so anything declared here would
   silently beat the component. Colour, size, casing and the brush stroke all
   belong to the eyebrow; do not re-declare them here. */
.rjr-about-stats__label {
  margin-top: var(--rjr-space-2);
}

.rjr-about-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* stretch, not start: these are two cards now, and two cards of different
     heights side by side read as one of them having failed to load. */
  align-items: stretch;
  gap: clamp(2rem, 1.5rem + 3vw, 4rem);
}
.rjr-about-who { justify-content: center; }
.rjr-about-who h2 { margin: 0 0 var(--rjr-space-2); }
.rjr-about-who p { margin: 0; line-height: 1.7; }
/* --rjr-ctx-muted is navy-200 inside .rjr-card--navy, which is 7.24:1 there. */
.rjr-about-who__follow { color: var(--rjr-ctx-muted); font-style: italic; }
/* The discs are built for a light surface; on navy they need the light/dark
   relationship inverted or they vanish into the card. */
.rjr-about-who .rjr-about-social a {
  background-color: rgba(240, 240, 234, 0.10);
  color: var(--rjr-cream);
}
/* navy-900 on orange-500 is 5.77:1, the same pairing the light version uses. */
.rjr-about-who .rjr-about-social a:hover,
.rjr-about-who .rjr-about-social a:focus-visible {
  background-color: var(--rjr-orange-500);
  color: var(--rjr-navy-900);
}

/* Same discs as the Start Here foot. Listed together so the two can never
   drift apart; text-decoration: none is not optional, because the base `a`
   rule sets underline thickness without ever turning the underline off. */
.rjr-about-social {
  display: flex;
  gap: var(--rjr-space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}
.rjr-about-social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background-color: var(--rjr-ctx-card-bg);
  color: var(--rjr-navy-700);
  font-size: 1.3rem;
  line-height: 1;
  text-decoration: none;
  box-shadow:
    1px 2px 3px rgba(5, 39, 49, 0.05),
    3px 7px 18px rgba(5, 39, 49, 0.07);
  transition: background-color var(--rjr-dur) var(--rjr-ease),
              color var(--rjr-dur) var(--rjr-ease),
              transform var(--rjr-dur) var(--rjr-ease);
}
/* navy-900 on orange-500 is 5.77:1. */
.rjr-about-social a:hover,
.rjr-about-social a:focus-visible {
  background-color: var(--rjr-orange-500);
  color: var(--rjr-navy-900);
  transform: translateY(-3px);
}

.rjr-about-gets h2 { margin: 0; font-size: var(--rjr-text-xl); }
.rjr-about-gets > p { margin: 0; color: var(--rjr-navy-600); }
.rjr-about-gets__list {
  display: grid;
  gap: var(--rjr-space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}
.rjr-about-gets__list li {
  display: flex;
  align-items: flex-start;
  gap: var(--rjr-space-3);
  line-height: 1.5;
}
/* Orange, matching .rjr-card__icon and .rjr-card__go - the accent is what marks
   an affordance or a marker on card stock throughout the site, and the tick was
   the one place still using teal.

   Decorative and aria-hidden, so 1.4.11's 3:1 does not apply: the list text is
   what carries the meaning, and it stays navy. */
.rjr-about-gets__tick {
  flex: none;
  /* The disc is optically heavier than the bare stroke was, so it sits a touch
     larger and needs less nudging to sit on the first line of text. */
  margin-top: 0.08em;
  color: var(--rjr-accent);
  font-size: 1.15em;
}
.rjr-about-gets__close { margin: 0; color: var(--rjr-navy-600); }

/* No promise line on these cards, so the text block should not reserve the gap
   it leaves for one, and the title carries the card on its own. */
.rjr-about-start__grid .rjr-card__text { gap: 0; }

/* The stagger is zeroed for every .rjr-card--category, in the card section, so
   this grid and the front page library behave the same. Nothing needed here. */

/* --- Contact --------------------------------------------------------------
   Same shape as About Lisa: copy left, one print right, then the page's real
   job underneath. Deliberately its own class set rather than reusing
   .rjr-about-*: the two pages look alike today and will not stay that way, and
   a shared class means a change to one silently moves the other. The one thing
   borrowed on purpose is .rjr-about-social, because the icon row IS the same
   component and two copies of it would drift.

   Everything from .rjr-contact-form down is a plugin restyle, not a component.
   Read the note above it before editing. */

/* Its own backdrop, so Contact does not open on the same photograph as the
   home page. The plane in this shot sits upper-left, over the copy, which is
   where the inherited 95deg scrim is heaviest (0.97 cream) - so it lands
   softened rather than competing with the h1, while still reading as a plane.
   Checked on the rendered page rather than reasoned about: at this crop the
   subject sits mostly ABOVE the headline, so the scrim is doing less work than
   the geometry is. No scrim override needed.

   Written .rjr-hero.rjr-contact, not .rjr-contact. Both classes are on the same
   <section>, .rjr-hero declares --rjr-hero-image too, and it is DOWN the file
   in section 15 while this block is in section 16 - equal weight, and source
   order hands it to .rjr-hero. The page kept rendering the home page shot until
   this was two classes. */
.rjr-hero.rjr-contact { --rjr-hero-image: url("../img/rjr-bg-3.webp"); }

.rjr-contact-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.95fr);
  align-items: center;
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
}
.rjr-contact-hero__copy h1 { margin: 0 0 var(--rjr-space-4); }
.rjr-contact-hero__copy .rjr-lede { margin: 0; }
.rjr-contact-hero__photo { width: min(26rem, 100%); }
/* Decoration in a page header, not something to click. .rjr-polaroid:hover is
   (0,2,0), so both properties have to be restated to beat it. */
.rjr-contact-hero__photo:hover {
  transform: none;
  filter: drop-shadow(0 18px 28px rgba(5, 39, 49, 0.22));
}

/* Everything else these cards need - columns, gap, tilt, stagger, the icon, the
   paper - is .rjr-grid--3 and .rjr-card doing their job, including the
   responsive drop to two columns and then one. All that is left is undoing the
   list bullets, because this set is a <ul> rather than the front page's
   <article>s. Do not add layout here; it belongs to the grid. */
.rjr-contact-help {
  list-style: none;
  margin: 0;
  padding: 0;
}

.rjr-contact-cols {
  display: grid;
  /* The form gets the wider half. Two of its fields sit side by side above
     783px (the plugin's breakpoint, not ours), and at an even split they end up
     narrower than the text they hold. */
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  align-items: start;
  gap: clamp(2rem, 1.5rem + 3vw, 4rem);
}
.rjr-contact-form > h2 { margin: 0 0 var(--rjr-space-6); }

.rjr-contact-aside {
  display: grid;
  gap: var(--rjr-space-8);
}
/* The dark surface itself is .rjr-card--navy, in the card section - the About
   page uses the same one. Only the sizing is local. */
.rjr-contact-aside__card h2 {
  margin: 0;
  font-size: var(--rjr-text-xl);
}
.rjr-contact-aside__card h2 + p { margin: 0; }
.rjr-contact-aside__card p { margin: 0; }
/* Not a heading, not a link: the literal string to type into a subject line.
   Set apart so it reads as a value rather than as emphasis, which the copy
   rules ban in body text. */
.rjr-contact-aside__tag {
  padding: 0.1em 0.45em;
  border-radius: var(--rjr-radius-sm);
  /* Lifted off the navy rather than tinted onto cream, so the chip reads as a
     value on a dark card. Cream on the result stays above 10:1. */
  background: rgba(240, 240, 234, 0.14);
  color: var(--rjr-cream);
  font-family: var(--rjr-font-body);
  font-size: 0.9em;
  letter-spacing: 0.04em;
}
/* A 24-character address inside a pill is a fragile thing, and sizing the pill
   to its text is what makes it fragile: the button then has a natural width of
   ~371px at body size, the aside is 0.85fr of the container, and everything
   between roughly 860px and 1150px leaves it 60px short. It broke mid-domain,
   as "mattwald@ready / jetroam.com", which reads as a typo.

   Measured across 390 / 620 / 800 / 1000 / 1440, chasing it with breakpoints
   just moved the band it broke in. So the button stops having a natural width:
   it fills the card, and the address is set one step down, which is right for
   an address anyway. It also lines up with the full-width Submit opposite it.
   word-break stays as a backstop for a longer address later. */
.rjr-contact-aside__mail {
  width: 100%;
  padding-inline: var(--rjr-space-4);
  font-size: var(--rjr-text-sm);
  word-break: break-word;
}
.rjr-contact-aside__social p {
  margin: 0 0 var(--rjr-space-4);
  color: var(--rjr-navy-600);
  font-style: italic;
}

/* Forminator 17068, restyled ---------------------------------------------
   The plugin ships its own look ("flat"), which is grey, square and nothing
   like the rest of the site. Rather than rebuild the form by hand and lose the
   plugin's validation, spam handling and notifications, the markup stays as
   Forminator prints it and only its skin is replaced here.

   Every override below is prefixed with .rjr-contact-form so it cannot touch
   the newsletter form or anything Matt adds later.

   On specificity, because this is the whole reason these selectors look the
   way they do: the plugin's shared rules are
     .forminator-ui.forminator-custom-form[data-design=flat] .forminator-input
   which is (0,4,0). Adding .rjr-contact-form makes ours (0,5,0), so we win on
   weight and do not depend on load order. That matters: the plugin enqueues its
   CSS from the shortcode, which runs in the body, so its stylesheets land AFTER
   this one. Do not "simplify" these selectors by dropping the [data-design]
   part - it drops us to (0,4,0), ties the plugin, and source order then hands
   the tie to the plugin.

   (0,5,0) is only enough because page-contact.php dequeues the plugin's
   generated per-form stylesheet. That file scopes its rules to the form's ID,
   at (1,3,0), which no class selector can beat. If the form ever renders with
   that file loaded, everything below silently stops applying - so if this page
   suddenly looks like stock Forminator, check that the dequeue is still there
   before touching any of these rules. The reasoning is written out in full in
   page-contact.php, next to the dequeue itself. */

.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-label {
  margin: 0 0 var(--rjr-space-2);
  color: var(--rjr-navy-700);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-sm);
  font-weight: 600;
  line-height: 1.4;
}
/* orange-800 on cream is 6.96:1; orange-500 is 2.48:1 and fails outright. */
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-required {
  color: var(--rjr-orange-800);
}

.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-input,
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-textarea {
  width: 100%;
  padding: 0.9em 1.1em;
  /* The plugin sets border-width:0 and border-style:none, so a border-color
     alone would paint nothing. All three have to be restated. */
  border: 2px solid var(--rjr-border);
  border-radius: var(--rjr-radius);
  background: var(--rjr-surface-card);
  color: var(--rjr-navy-800);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-base);
  line-height: 1.4;
  transition: border-color var(--rjr-dur) var(--rjr-ease),
              box-shadow var(--rjr-dur) var(--rjr-ease);
}
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-input::placeholder,
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-textarea::placeholder {
  color: var(--rjr-navy-300);
}
/* The plugin kills box-shadow on :focus explicitly, and that rule carries the
   same weight as its base one, so the focus ring has to be re-declared at ours
   or it never appears. Same ring as .rjr-input, so a field on this page and a
   field in a comment form behave identically. */
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-input:focus,
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-textarea:focus {
  outline: none;
  border-color: var(--rjr-accent);
  box-shadow: 0 0 0 4px rgba(244, 131, 31, 0.24);
}
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-textarea {
  min-height: 9rem;
  resize: vertical;
}

/* The character counter under the message field. */
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-description {
  margin: var(--rjr-space-2) 0 0;
  color: var(--rjr-navy-600);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-xs);
  text-align: right;
}
/* The counter is printed BEFORE the textarea in the markup. Ordering the field
   as a column and pushing it last puts it under the box, where a counter is
   read, without touching the plugin's HTML. */
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-field-textarea .forminator-field {
  display: flex;
  flex-direction: column;
}
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-field-textarea .forminator-description {
  order: 3;
}

/* Consent checkbox. The plugin hides the real <input> off-screen with
   !important and paints .forminator-checkbox-box in its place, so the box is
   what gets styled and the input is left alone - it still owns focus and the
   keyboard. */
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-checkbox {
  display: flex;
  align-items: flex-start;
  gap: var(--rjr-space-3);
  margin: 0;
  color: var(--rjr-navy-700);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-sm);
  line-height: 1.5;
  cursor: pointer;
}
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-checkbox .forminator-checkbox-box {
  flex: none;
  width: 1.25rem;
  height: 1.25rem;
  margin: 0.15em 0 0;
  border: 2px solid var(--rjr-border);
  border-radius: var(--rjr-radius-sm);
  background: var(--rjr-surface-card);
  color: transparent;
  font-size: 0.75rem;
  transition: background-color var(--rjr-dur) var(--rjr-ease),
              border-color var(--rjr-dur) var(--rjr-ease);
}
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-checkbox input:checked + .forminator-checkbox-box {
  border-color: var(--rjr-cta);
  background: var(--rjr-cta);
  color: var(--rjr-cta-text);
}
/* The input is clipped to 1px, so :focus-visible on it is invisible. The ring
   has to be drawn on the box the reader can actually see. */
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-checkbox input:focus-visible + .forminator-checkbox-box {
  border-color: var(--rjr-accent);
  box-shadow: 0 0 0 4px rgba(244, 131, 31, 0.24);
}

/* Submit. Restated rather than given .rjr-btn, because the class is on markup
   the plugin owns and we cannot add to it. Kept in step with .rjr-btn and
   .rjr-btn--primary by hand - if those change, change this.

   Full width and a pill radius are not the theme's usual button; they are what
   the form's own Custom CSS asked for (`border-radius:50px !important;
   width:100% !important`). That file is dequeued now, so the choice is honored
   here instead, in tokens - 999px and 50px are the same shape at this height.
   If Lisa or Matt would rather it matched the site's other buttons, drop these
   two lines and it inherits --rjr-radius at natural width. */
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-button-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  width: 100%;
  height: auto;
  padding: 0.85em 1.9em;
  border: 2px solid transparent;
  border-radius: var(--rjr-radius-pill);
  /* navy-600 under white is 9.10:1. */
  background: var(--rjr-cta);
  color: var(--rjr-cta-text);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-base);
  font-weight: 600;
  line-height: 1.2;
  text-transform: none;
  cursor: pointer;
  box-shadow: var(--rjr-shadow-md);
  transition: transform var(--rjr-dur) var(--rjr-ease),
              box-shadow var(--rjr-dur) var(--rjr-ease),
              background-color var(--rjr-dur) var(--rjr-ease);
}
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-button-submit:hover,
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-button-submit:focus-visible {
  background: var(--rjr-cta-hover);
  color: var(--rjr-cta-text);
  box-shadow: var(--rjr-shadow-lg);
  transform: translateY(-2px);
}

/* Validation and the post-submit message. The plugin toggles [aria-hidden] on
   these, so an unconditional `display:block` would show an empty red bar on
   page load - hence the attribute selector. */
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-error-message,
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-response-message {
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-sm);
  line-height: 1.5;
}
.rjr-contact-form .forminator-ui.forminator-custom-form[data-design="flat"] .forminator-response-message[aria-hidden="false"] {
  margin: 0 0 var(--rjr-space-4);
  padding: var(--rjr-space-3) var(--rjr-space-4);
  border-radius: var(--rjr-radius-sm);
}

/* --- Shop -----------------------------------------------------------------
   A featured product, then a shelf for the rest. Built so one product and six
   products both look deliberate: the shelf is not rendered while empty, and
   the featured slot never depends on there being a second thing below it. */

/*
 * The featured slot sits on paper rather than straight on the hero photograph.
 *
 * Read over the sand shot, the lede and the orange eyebrow both lost their
 * contrast, and a page asking for money is the wrong place to make the reader
 * work. The paper is the same stock the cards use, so this borrows a device
 * the design system already has instead of introducing another one.
 */
.rjr-shop-hero {
  display: grid;
  /* Cover column sized to the art, not to a fraction: a book cover has a fixed
     ratio and a fluid column would either crop it or leave it swimming. */
  grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
  align-items: start;
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
  margin-top: var(--rjr-space-12);
  padding: clamp(1.75rem, 1rem + 3vw, 3.5rem);
  --rjr-card-texture-wash: 0.66;
  background-color: var(--rjr-ctx-card-bg);
  background-image:
    linear-gradient(rgba(255, 255, 253, var(--rjr-card-texture-wash)),
                    rgba(255, 255, 253, var(--rjr-card-texture-wash))),
    url("../img/crumpled-paper-texture.jpg");
  background-size: cover, cover;
  background-position: center, center;
  background-blend-mode: normal, multiply;
  border-radius: 3px;
  box-shadow:
    1px 2px 3px rgba(5, 39, 49, 0.05),
    6px 14px 34px rgba(5, 39, 49, 0.12);
}
.rjr-shop-hero__media {
  display: flex;
  justify-content: center;
}
/* The same lean the front page spotlight gives it, so the product looks like
   the same object in both places. */
.rjr-shop-hero__cover {
  width: 100%;
  height: auto;
  max-width: 20rem;
  border-radius: 2px;
  box-shadow:
    0 2px 6px rgba(5, 39, 49, 0.10),
    18px 26px 50px rgba(5, 39, 49, 0.20);
  transform: rotate(-3deg);
  transition: transform var(--rjr-dur) var(--rjr-ease);
}
.rjr-shop-hero__cover:hover { transform: rotate(-1deg) translateY(-6px); }

.rjr-shop-hero__title {
  margin: var(--rjr-space-4) 0 var(--rjr-space-3);
}
.rjr-shop-hero__points {
  display: grid;
  gap: var(--rjr-space-2);
  list-style: none;
  margin: var(--rjr-space-6) 0 0;
  padding: 0;
}
.rjr-shop-hero__points li {
  display: flex;
  align-items: flex-start;
  gap: var(--rjr-space-3);
  line-height: 1.5;
}
.rjr-shop-hero__tick {
  flex: none;
  margin-top: 0.15em;
  color: var(--rjr-teal-600, var(--rjr-navy-500));
}
.rjr-shop-hero__points-empty { margin-top: var(--rjr-space-6); }

/* Price beside the button, not under it: the two are one decision, and a price
   that needs looking for reads as a price being hidden. */
.rjr-shop-hero__buy {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--rjr-space-4) var(--rjr-space-6);
  margin-top: var(--rjr-space-8);
}
.rjr-shop-hero__price {
  margin: 0;
  color: var(--rjr-navy-600);
  font-size: var(--rjr-text-sm);
  line-height: 1.3;
}
.rjr-shop-hero__price strong {
  display: block;
  color: var(--rjr-navy-900);
  font-family: var(--rjr-font-display);
  font-size: var(--rjr-text-xl);
  font-weight: 700;
}
.rjr-shop-hero__quote,
.rjr-shop-hero__quote-empty { margin-top: var(--rjr-space-8); }

/* --- The shelf ----------------------------------------------------------- */
.rjr-shop-shelf { margin-top: clamp(4rem, 3rem + 4vw, 7rem); }
.rjr-shop-shelf__title {
  margin: 0 0 var(--rjr-space-6);
  font-size: var(--rjr-text-2xl);
}

.rjr-shop-card__badge {
  align-self: flex-start;
  margin: 0;
  color: var(--rjr-orange-800);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.rjr-shop-card__media {
  display: flex;
  justify-content: center;
  padding-block: var(--rjr-space-2);
}
.rjr-shop-card__cover {
  width: auto;
  height: 11rem;
  border-radius: 2px;
  box-shadow:
    0 1px 4px rgba(5, 39, 49, 0.10),
    8px 12px 24px rgba(5, 39, 49, 0.14);
  transform: rotate(-2deg);
}
.rjr-shop-card__title { margin: 0; font-size: var(--rjr-text-lg); }
.rjr-shop-card__text {
  margin: 0;
  color: var(--rjr-navy-600);
  font-size: var(--rjr-text-sm);
  line-height: 1.55;
}
.rjr-shop-card__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--rjr-space-2) var(--rjr-space-4);
}
.rjr-shop-card__price {
  color: var(--rjr-navy-900);
  font-family: var(--rjr-font-display);
  font-weight: 700;
}

/* --- The Amazon band ----------------------------------------------------- */
.rjr-shop-amazon__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--rjr-space-8);
}
.rjr-shop-amazon__body h2 { margin: 0 0 var(--rjr-space-3); }
.rjr-shop-amazon__body p {
  margin: 0 0 var(--rjr-space-6);
  color: var(--rjr-navy-600);
  max-width: 44ch;
}
.rjr-shop-amazon__note { margin: 0; }

/* --- Embedded app page ----------------------------------------------------
   The Packing App page: one plugin shortcode that draws its own interface. The
   theme supplies the header, the footer and the clearance between them, and
   otherwise leaves the app's own layout alone. */

.rjr-app-page {
  /* The header is sticky and floats over the page, so the space it needs has
     to be reserved here, as every other page-opening component does. */
  padding-top: calc(var(--rjr-header-h) + var(--rjr-space-6));
  padding-bottom: var(--rjr-space-12);
  background: var(--rjr-cream);
}

/* The plugin prints a brand header of its own: the RJR logo linking home,
   directly under the site header that already carries one. Two logos within a
   hundred pixels reads as a mistake, and the site header is the one that also
   carries the navigation, so the app's copy is the one to go.
   Scoped through .rjr-app-page rather than styling `.brand` globally - that is
   a common enough class name to collide with something else later. */
.rjr-app-page .brand { display: none; }


/* --- The default page ----------------------------------------------------
   What page.php renders: any page without a page-<slug>.php of its own, which
   means every page created from the WordPress admin. One measure, ranged left,
   nothing else. It shares the legal pages' proportions on purpose - both are
   "a title and a column of prose", and two different measures for the same
   shape would be a difference with no meaning behind it. */

/* .rjr-container is (0,1,0) and sets max-width too; this wins on source order
   alone, so it must stay below it in the file. */
.rjr-basic__inner {
  max-width: 46rem;
  margin-inline: auto;
}
/* .rjr-section's padding does not know about the fixed header, and this page
   has no hero to reserve the space the way .rjr-hero--compact does. Without
   this the h1 sat 9px under the header bar. */
.rjr-basic {
  --rjr-pt-base: calc(clamp(2rem, 1.5rem + 1.6vw, 3.5rem) + var(--rjr-header-h));
}
/* Ranged left, to match the body underneath: a centred heading over a column of
   left-aligned prose reads as two different pages.
   Both classes named. .rjr-page-head is also (0,1,0) and sits further down the
   file, so a single-class rule here loses on source order - the same trap the
   legal head hit, and the recommendations head before it. */
.rjr-page-head.rjr-basic__head {
  max-width: none;
  margin-inline: 0;
  margin-bottom: var(--rjr-space-8);
  text-align: left;
}
.rjr-basic__head .rjr-page-head__desc { margin-inline: 0; }


/* --- Legal pages ----------------------------------------------------------
   Disclosure, privacy, terms. One narrow column, no decoration and no paper:
   these are read for reference, usually because something has gone wrong or a
   reader wants to check what happens to their data, and every flourish is one
   more thing between them and the sentence they came for. The body reuses
   .rjr-article__content so the prose rules stay in one place.

   NOTE: this comment's opening marker was missing until now, leaving a
   dangling closing marker. Everything after it parsed as a selector, which
   swallowed whichever rule block came next - so .rjr-legal below had been dead
   since it was written. It went unnoticed because .rjr-section--paper fills
   with the same cream, so the page looked right for the wrong reason. Never
   write comment markers inside a comment; that is what caused it. */

/* Plain light ground, no photograph. Stated rather than inherited from body:
   the section is opaque, so what sits behind it is never in question, and the
   hero's overflow clip has nothing left to clip. */
.rjr-legal {
  background: var(--rjr-cream);
  overflow: visible;
}

.rjr-legal__inner {
  max-width: 46rem;
  margin-inline: auto;
}

/* Ranged left, to match the body underneath. A centred heading over a hundred
   lines of left-aligned clauses reads as two different pages.
   Both classes named: .rjr-page-head is also (0,1,0) and sits further down the
   file, so a single-class rule here loses on order. Third time this exact trap
   has bitten; see the specificity notes in docs/PROJECT_STATE.md. */
.rjr-page-head.rjr-legal__head {
  max-width: none;
  margin-inline: 0;
  text-align: left;
}
.rjr-legal__head .rjr-page-head__desc { margin-inline: 0; }

.rjr-legal__updated {
  margin: var(--rjr-space-4) 0 0;
  color: var(--rjr-navy-500);
  font-size: var(--rjr-text-sm);
}

/*
 * The editor-only note. Loud, because it is meant to be acted on and removed,
 * and because it must never be mistaken for something the reader should see.
 * It is already behind a capability check in the template; this makes it
 * obviously not part of the page.
 */
.rjr-legal__note {
  margin-top: var(--rjr-space-8);
  padding: var(--rjr-space-4) var(--rjr-space-6);
  background: var(--rjr-orange-50, #FDF1E4);
  border: 2px dashed var(--rjr-orange-500);
  border-radius: var(--rjr-radius);
  color: var(--rjr-navy-800);
  font-size: var(--rjr-text-sm);
  line-height: 1.6;
}
.rjr-legal__note-title {
  display: flex;
  align-items: center;
  gap: var(--rjr-space-2);
  margin: 0 0 var(--rjr-space-3);
  /* orange-800 is 6.96:1 on this tint; orange-500 would be 2.4:1. */
  color: var(--rjr-orange-800);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.rjr-legal__note p { margin: 0 0 var(--rjr-space-3); }
.rjr-legal__note ul { margin: 0 0 var(--rjr-space-3); padding-left: 1.25rem; }
.rjr-legal__note li { margin-bottom: var(--rjr-space-2); }
.rjr-legal__note > :last-child { margin-bottom: 0; }

/* Contents. Only printed for the long two; see the template. */
.rjr-legal__toc {
  margin-top: var(--rjr-space-8);
  padding: var(--rjr-space-4) var(--rjr-space-6);
  background: var(--rjr-paper);
  border-left: 4px solid var(--rjr-navy-300);
  border-radius: var(--rjr-radius);
}
.rjr-legal__toc-title {
  margin: 0 0 var(--rjr-space-3);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--rjr-navy-500);
}
.rjr-legal__toc ol {
  margin: 0;
  padding-left: 1.25rem;
  columns: 2;
  column-gap: var(--rjr-space-6);
  font-size: var(--rjr-text-sm);
  line-height: 1.5;
}
.rjr-legal__toc li {
  margin-bottom: var(--rjr-space-2);
  /* Keeps a two-line entry from splitting across the column break. */
  break-inside: avoid;
}
.rjr-legal__toc a { color: var(--rjr-navy-700); }

.rjr-legal__body { margin-top: var(--rjr-space-8); }
/* Anchored headings land under nothing when the sticky header is over them. */
.rjr-legal__body h2 { scroll-margin-top: calc(var(--rjr-header-h) + var(--rjr-space-4)); }

.rjr-legal__more {
  margin-top: var(--rjr-space-12);
  padding-top: var(--rjr-space-6);
  border-top: 1px solid var(--rjr-border);
}
.rjr-legal__more-title {
  margin: 0 0 var(--rjr-space-4);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--rjr-navy-500);
}
.rjr-legal__more ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--rjr-space-3) var(--rjr-space-6);
  list-style: none;
  margin: 0;
  padding: 0;
}
.rjr-legal__more a {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--rjr-navy-600);
  font-weight: 600;
  text-decoration: none;
}
.rjr-legal__more a:hover { color: var(--rjr-accent-hover); }

/* --- Footer ---------------------------------------------------------------
   No paper, no ticket. The newsletter section directly above already carries
   the torn edge and the orange swoosh, so a third handmade device in the same
   navy run would cheapen all three. This separates on tone and space instead:
   a deeper navy plus a hairline, which is what stops the footer reading as the
   tail of the newsletter. */

/*
 * The chrome states its own typography rather than inheriting it.
 *
 * A plugin on the Packing App page styles the document, not its own widget:
 * `body { font-family:'Nunito Sans' !important; line-height:1.55 !important }`
 * plus a bare `footer { text-align:center }` and `footer a { font-weight:800 }`,
 * which match the site footer because it is a <footer> element. The result was
 * a centred, bold, wrong-typeface footer on that page only.
 *
 * The !important on body is not the problem it looks like. It only wins against
 * other declarations targeting body; any descendant that declares font-family
 * for itself keeps its own. So the fix is to declare, not to escalate - there
 * is no !important anywhere below.
 *
 * These are cheap and they harden the header and footer against every other
 * plugin that writes CSS as though it owns the page. Do not remove them
 * because they look redundant on a page where no plugin is loaded.
 */
.rjr-header,
.rjr-footer {
  font-family: var(--rjr-font-body);
  /* 1.65 because that is what body sets, so this changes nothing on a page
     with no plugin loaded. It exists only to stop the plugin's 1.55 being
     inherited. Keep the two in step if body's ever changes. */
  line-height: 1.65;
  text-align: left;
}

.rjr-footer {
  padding: var(--rjr-space-12) var(--rjr-section-padding-x) var(--rjr-space-6);
  background: var(--rjr-navy-950);
  color: var(--rjr-navy-100);
  font-size: var(--rjr-text-sm);
  border-top: 1px solid rgba(240, 240, 234, 0.09);
}
/* Weight stated for the same reason: `footer a { font-weight:800 }` otherwise
   applies unopposed, because nothing here used to declare one. */
.rjr-footer a { font-weight: 400; }

/* Brand column wider than the two link columns: it carries a logo, a line of
   copy and three targets, and at an equal third the tagline broke badly. */
.rjr-footer__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--rjr-space-8);
  max-width: var(--rjr-container-wide);
  margin-inline: auto;
}

.rjr-footer__logo { display: inline-block; }
.rjr-footer__logo img {
  display: block;
  height: 46px;
  width: auto;
}
/* The tagline comes from Settings > General and its length is not ours to
   control, so it is balanced rather than left to drop one orphan word. */
.rjr-footer__tagline {
  max-width: 26ch;
  margin: var(--rjr-space-4) 0 0;
  color: var(--rjr-navy-300);
  text-wrap: balance;
}

.rjr-footer__social {
  display: flex;
  gap: var(--rjr-space-3);
  list-style: none;
  margin: var(--rjr-space-6) 0 0;
  padding: 0;
}
/* 2.75rem is 44px: the smallest comfortable touch target, and this is a mobile
   audience. Round, filled, and only faintly lighter than the footer, so three
   icons do not turn into three buttons competing with the newsletter CTA. */
.rjr-footer__social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: rgba(240, 240, 234, 0.07);
  font-size: 1.15rem;
  transition: background-color var(--rjr-dur) var(--rjr-ease),
              color var(--rjr-dur) var(--rjr-ease);
}
/* navy-900 on orange-500 measures 5.77:1. White would have been 2.59:1. */
.rjr-footer__social a:hover,
.rjr-footer__social a:focus-visible {
  background: var(--rjr-orange-500);
  color: var(--rjr-navy-900);
}

.rjr-footer__heading {
  margin: 0 0 var(--rjr-space-4);
  color: #FFFFFF;
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.rjr-footer__col ul {
  display: grid;
  gap: var(--rjr-space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.rjr-footer a { color: var(--rjr-navy-100); text-decoration: none; }
.rjr-footer a:hover { color: var(--rjr-orange-300); }

/* Start Here only: the legal row with no columns above it, so it needs its own
   top spacing and no rule dividing it from something that is not there. */
.rjr-footer--bare { padding-block: var(--rjr-space-6); }
.rjr-footer--bare .rjr-footer__legal {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.rjr-footer__legal {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--rjr-space-4);
  max-width: var(--rjr-container-wide);
  margin: var(--rjr-space-12) auto 0;
  padding-top: var(--rjr-space-6);
  border-top: 1px solid rgba(240, 240, 234, 0.14);
  color: var(--rjr-navy-300);
  font-size: var(--rjr-text-xs);
}
/* The legal links take their own row above the copyright: they are navigation,
   the line below is a statement, and running them together turns both into one
   grey smear at 12px. flex-basis 100% rather than a wrapper, so the copyright
   and Back to top keep sharing the row underneath. */
.rjr-footer__legal-links { flex: 1 0 100%; }
.rjr-footer__legal-links ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--rjr-space-2) var(--rjr-space-6);
  list-style: none;
  margin: 0 0 var(--rjr-space-4);
  padding: 0;
}
/* Quiet, to sit with the copyright line rather than above it. navy-300 on the
   footer navy measures 6.95:1, so dimmed is still comfortably past AA at this
   size; the hover brings them back up. */
.rjr-footer__legal-links a {
  color: var(--rjr-navy-300);
  font-size: var(--rjr-text-xs);
  font-weight: 400;
}
.rjr-footer__legal-links a:hover { color: var(--rjr-orange-300); }

/* Capped measure. Left to run the full 1360px container this is one sentence
   across the whole screen at 12px, which nobody reads. */
.rjr-footer__legal p {
  max-width: 70ch;
  margin: 0;
}
.rjr-footer__top {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex: 0 0 auto;
}


/* ==========================================================================
   15  SECTION COMPOSITION
   ========================================================================== */

/* --- 1 · Hero ------------------------------------------------------------ */
.rjr-hero {
  /* Hero backdrop. WebP: 266KB against the jpeg's 1.28MB for the same image.
     Was driven by the data-hero-bg switcher; now simply the chosen shot. */
  --rjr-hero-image: url("../img/rjr-main-bg.webp");

  /* How far the print is allowed past the container's right edge. Tracks the
     section gutter so it stops on the same line as the page's outer edge.
     Used by .rjr-hero__media in section 16. */
  --rjr-hero-bleed: var(--rjr-section-padding-x);

  /* Tighter than the other sections on purpose: this one also carries
     --rjr-header-h on top of its own padding, so the same clamp that reads as
     comfortable elsewhere put 209px above the eyebrow here. */
  --rjr-pt-base: calc(clamp(2rem, 1.35rem + 2.2vw, 4rem) * var(--rjr-section-rhythm)
                      + var(--rjr-header-h));
  --rjr-pb-base: calc(clamp(3.5rem, 2.4rem + 4.5vw, 8rem) * var(--rjr-section-rhythm));
  padding-block: var(--rjr-pt-base) var(--rjr-pb-base);
  overflow: hidden;
}

/* Photographic hero bed. `cover`, sitting behind the section's content at
   z-index -2, with a scrim on top of it so the navy copy keeps its contrast
   no matter which photo is showing. */
.rjr-hero__bg {
  position: absolute;
  inset: 0;
  z-index: -2;
  overflow: hidden;          /* clips the parallax slack below */
  pointer-events: none;
}

/* The image lives on its own layer so the parallax can move it without
   dragging the scrim along — the scrim's bottom fade has to stay pinned to the
   section edge or the blend into the next section breaks.
   The layer is oversized by --rjr-parallax-slack top and bottom, which is the
   travel budget: move it further than that and a hard edge appears.

   Keep this small. A taller layer flips `cover` from width-driven to
   height-driven and re-zooms the photo — at 14% the crop visibly tightened.
   6% costs about 2% of zoom and still buys ~50px of travel. */
.rjr-hero__bg-img {
  position: absolute;
  inset: calc(-1 * var(--rjr-parallax-slack, 7%)) 0;
  background-image: var(--rjr-hero-image, none);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  will-change: transform;
}

/* All six test shots put their props on the right and their negative space on
   the left, which is exactly where the copy sits — so the scrim is heaviest at
   the left edge and thins out under the polaroid. */
.rjr-hero__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom, rgba(240, 240, 234, 0) 55%, var(--rjr-cream) 100%),
    var(--rjr-hero-scrim,
      linear-gradient(95deg,
        rgba(240, 240, 234, 0.97) 0%,
        rgba(240, 240, 234, 0.30) 80%,
        rgba(240, 240, 234, 0.16) 100%));
}


/* The grid stays inside the container; only the print is allowed out of it.
   -------------------------------------------------------------------------
   This used to opt the whole grid out of the container (max-width: none, plus
   a left pad computed to line the copy back up with where the container would
   have started) so the photo could run to the viewport edge. The side effect
   was that the COLUMNS were a share of the viewport rather than of the
   container, so on a wide screen the copy column grew without limit - measured
   about 1300px of copy at 2537px wide, which is roughly twice a readable
   measure and left the lede running the width of the room.

   Now the grid is an ordinary container and the bleed is the print's alone. */
.rjr-hero__grid {
  display: grid;
  max-width: var(--rjr-container);
  margin-inline: auto;
  /* 55/45 to the copy. It used to be the other way round (1fr / 1.49fr, so
     40/60 to the photo); the headline and the lede were doing the work and
     running out of measure while the print had width to spare.
     The 30rem floor is the width the display headline needs to keep its line
     breaks - below that the photo gives space back rather than letting the type
     break, because the headline outranks the picture. */
  grid-template-columns: minmax(30rem, 55fr) minmax(0, 45fr);
  align-items: center;
  gap: clamp(1.5rem, 0.6rem + 2.4vw, 2.75rem);
}
.rjr-hero h1 { margin-bottom: var(--rjr-space-6); }
.rjr-hero .rjr-eyebrow { margin-bottom: var(--rjr-space-6); }
.rjr-hero .rjr-lede { margin-bottom: var(--rjr-space-8); }
/* The column is a ratio, so without a ceiling the photo keeps growing with the
   viewport — 1056px at 2560px wide, which overpowers the copy. 54rem holds it
   at its ~1920px size; below that the cap never bites and nothing changes.
   `justify-self: end` keeps it pinned to the right edge once the cap applies. */
/* The one element allowed past the container's right edge.
   --rjr-hero-bleed is the single dial for how far. It tracks the section gutter,
   so the print reaches the same line the page's outer edge sits on and no
   further - on a very wide screen that is 4rem past the container, not half the
   room. .rjr-hero has overflow: hidden, so it can never cause a scrollbar.

   width is the full column plus the bleed, pulled back out with a matching
   negative margin, so the print grows outward to the right rather than
   shrinking the copy. It was 80% of the column, which measured 461px against a
   520px column and left 103px of dead space on its left - the column had
   already been narrowed to 45%, so the extra 20% was cutting twice.
   The dial itself is declared on .rjr-hero, in section 15. */
.rjr-hero__media {
  position: relative;
  justify-self: end;
  width: calc(100% + var(--rjr-hero-bleed));
  max-width: none;
  margin-right: calc(-1 * var(--rjr-hero-bleed));
}
.rjr-hero__media .rjr-polaroid {
  width: 100%;
  max-width: none;
}

/* --- 3 · Featured product ------------------------------------------------
   Editorial rather than two even columns: the copy holds the left, the book
   sits high and tilted on the right and breaks the section's top edge. The
   stubs below are hand-placed, not a evenly-spaced row. */
.rjr-feature__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr);
  align-items: center;
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
}

.rjr-feature__media { position: relative; }

/* Passport lying under the book, like something left on the same desk. Reuses
   the decoration asset/size classes but not their corner placement — those
   push an object OUT into the page gutter, and this one belongs inside the
   media column. z-index -1 keeps it behind the book (which sits at 3). */
.rjr-feature__decor {
  --rjr-decor-opacity: 0.3;
  z-index: -1;
  left: -10%;
  bottom: -1rem;
  transform: rotate(-14deg);
}

.rjr-feature__price {
  font-size: var(--rjr-text-sm);
  font-weight: 500;
  color: var(--rjr-navy-200);
}

/* Stubs pinned along a loose line rather than a grid — alternating tilt and a
   small vertical scatter, the same hand-placed logic as the cards. */
.rjr-feature__more {
  position: relative;
  z-index: 1;
  margin-top: clamp(3rem, 2.2rem + 2.6vw, 5rem);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(0.75rem, 0.45rem + 1vw, 1.4rem);
}
.rjr-feature__more > .rjr-ticket:nth-of-type(1) { --rjr-ticket-tilt: -1.6deg; --rjr-ticket-lift: 0px; }
.rjr-feature__more > .rjr-ticket:nth-of-type(2) { --rjr-ticket-tilt:  1.2deg; --rjr-ticket-lift: 9px; }
/* Negative multiplier: this one swings THROUGH straight to the opposite
   angle on hover, instead of just amplifying its resting tilt like the
   other two — otherwise it reads as a repeat of the second stub. */
.rjr-feature__more > .rjr-ticket:nth-of-type(3) {
  --rjr-ticket-tilt: -0.8deg;
  --rjr-ticket-lift: 3px;
  --rjr-ticket-hover-mult: -4;
}
/* No margin-left:auto — it pushed the link onto its own row once the stubs
   filled the width. It reads better trailing the last stub anyway. */
.rjr-feature__more > .rjr-link { flex: none; }

/* --- 4 · Article library ------------------------------------------------- */
.rjr-library__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1024px) {
  /* two columns: a 2-span lead would fill the whole row and lose its emphasis */
  .rjr-card--lead { grid-column: span 2; }
}
@media (max-width: 620px) {
  .rjr-card--lead { grid-column: span 1; }

  /* One column is wide but short — a stacked icon/heading/promise/arrow wastes
     the width and makes the list twice as long to scroll. Every category card
     takes the lead card's row layout, and the lead drops back to the same
     scale so the set reads as one consistent list. */
  .rjr-card--category {
    flex-direction: row;
    align-items: center;
    gap: var(--rjr-space-4);
  }
  .rjr-card--category .rjr-cat-icon {
    --rjr-cat-icon-w: 3.75rem;
    flex: none;
    align-self: center;
    transform-origin: center;
  }
  /* The pinned icon box above exists so titles share a line in the stacked
     layout. In THIS layout it backfires: the row centers a 5.5rem box while the
     drawing inside it still sits on the box's floor (background-position:
     left bottom), so the short drawings - sunglasses, plane - hang visibly
     below the title they belong to. There is no column of titles to align here
     anyway, so the box goes back to its own proportions.
     Later in the file than the rule it undoes, which is what lets a selector of
     equal weight win. */
  .rjr-card--category .rjr-cat-icon {
    height: auto;
    aspect-ratio: var(--rjr-cat-icon-ratio, 1);
  }
  .rjr-card--category .rjr-card__text { flex: 1 1 auto; min-width: 0; }
  .rjr-card--category .rjr-card__go {
    margin: 0 0 0 auto;
    padding-top: 0;
    align-self: center;
  }
  .rjr-card--lead h3,
  .rjr-card--category h3 { font-size: var(--rjr-text-lg); }
  .rjr-card--lead .rjr-card__promise,
  .rjr-card--category .rjr-card__promise { font-size: var(--rjr-text-sm); }
  .rjr-card--lead .rjr-cat-icon { --rjr-cat-icon-w: 3.75rem; }

  .rjr-tools-strip { flex-wrap: wrap; }
  .rjr-tools-strip__go { margin-left: 0; }
}

/* --- 5 · Testimonials ---------------------------------------------------- */
.rjr-praise__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: center;
  gap: clamp(2rem, 1.2rem + 3vw, 4rem);
}
.rjr-praise__left { position: relative; }

/* Two prints dropped on the same desk. The portrait leans anticlockwise and
   the landscape clockwise, so they read as scattered rather than arranged.
   Offset DIAGONALLY rather than side by side: portrait top-left, landscape
   bottom-right, so they meet at one corner only. That's what lets both be
   large and still almost fully visible — stacking them on a single horizontal
   line forces a choice between size and overlap.
   Both are absolutely positioned, so the container would collapse to zero
   height. The aspect-ratio is derived, not guessed: portrait needs 0.81 of the
   width in height, landscape 0.66, and they share ~0.27 of vertical overlap,
   so 1.2 holds the pair. Change either width and this needs recomputing. */
.rjr-polaroid-stack {
  position: relative;
  width: 100%;
  max-width: 36rem;
  margin-inline: auto;
  aspect-ratio: 1 / 0.92;
}
.rjr-polaroid-stack .rjr-polaroid {
  position: absolute;
  max-width: none;
}
.rjr-polaroid-stack__front { width: 52%; left: -42px;  top: 0;     z-index: 2; }
.rjr-polaroid-stack__back  { width: 69%; right: -80px; top: 150px; z-index: 1; }
/* the top print lifts further, so the pair separates instead of moving as one */
.rjr-polaroid-stack__front:hover { transform: translateY(-9px) rotate(1.4deg); }
/* The note now sits under the screenshot in the right column, so the two
   pieces of social proof stack together and the photographs stand alone. */
.rjr-praise__right .rjr-quote--note {
  max-width: none;
  margin-top: clamp(1.75rem, 1.3rem + 1.8vw, 3rem);
}

/* --- 6 · Newsletter ------------------------------------------------------
   A closing CTA, so it centres rather than sitting in the editorial two-column
   rhythm — and with the signup handed off to an external service there is no
   second column left to balance anyway. */
.rjr-newsletter {
  --rjr-pt-base: calc(clamp(5rem, 3.5rem + 6vw, 9rem) * var(--rjr-section-rhythm));
  padding-top: var(--rjr-pt-base);
}
.rjr-newsletter__inner {
  position: relative;
  max-width: 44rem;
  margin-inline: auto;
  text-align: center;
}
.rjr-newsletter__inner .rjr-lede { margin-inline: auto; }
.rjr-newsletter__cta { margin-top: clamp(1.75rem, 1.3rem + 1.8vw, 2.75rem); }
.rjr-newsletter__note {
  margin-top: var(--rjr-space-4);
  font-size: var(--rjr-text-xs);
  color: var(--rjr-navy-200);
}

/* ==========================================================================
   16  MOTION
   Reveal-on-scroll is gated behind .js so content is never hidden if the
   script fails. Port target is AOS in the child theme — the class names and
   stagger variable map 1:1 onto data-aos / data-aos-delay.
   ========================================================================== */

.js .rjr-reveal {
  opacity: 0;
  transform: translateY(20px);
  transition:
    opacity var(--rjr-dur-slow) var(--rjr-ease-out) var(--rjr-reveal-delay, 0ms),
    transform var(--rjr-dur-slow) var(--rjr-ease-out) var(--rjr-reveal-delay, 0ms);
  will-change: opacity, transform;
}
.js .rjr-reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Cards own their transform (tilt + stagger), so `transform: none` above would
   flatten them the moment they revealed — it outranks .rjr-card on specificity.
   Fold the reveal offset into the card's own transform instead of replacing it.
   Must stay after the rule above to win on order. */
.js .rjr-card.rjr-reveal {
  transform:
    rotate(var(--rjr-card-tilt, 0deg))
    translateY(calc(var(--rjr-card-offset, 0px)
                  + var(--rjr-card-lift, 0px)
                  + var(--rjr-reveal-y, 20px)));
}
.js .rjr-card.rjr-reveal.is-visible { --rjr-reveal-y: 0px; }

/* Same trap again: the recommendation prints own a lean, and transform:none
   above would stand them upright the instant they revealed. */
.js .rjr-rec-head__photo.rjr-reveal {
  transform:
    rotate(var(--rjr-rec-photo-tilt, 0deg))
    translateY(var(--rjr-reveal-y, 20px));
}
.js .rjr-rec-head__photo.rjr-reveal.is-visible { --rjr-reveal-y: 0px; }
/* No :hover companion here on purpose - these prints do not react to the
   pointer. The rule above is (0,3,0), so it already holds the transform steady
   against .rjr-polaroid:hover; the hover rule up in the component section only
   has to deal with the shadow and the no-JS case. */

/* Same trap, same fix: archive cards own a vertical offset that
   `transform: none` would flatten on reveal. The lean lives on the polaroid
   inside, which the reveal never touches. */
.js .rjr-post-card.rjr-reveal {
  transform: translateY(calc(var(--rjr-post-offset, 0px) + var(--rjr-reveal-y, 20px)));
}
.js .rjr-post-card.rjr-reveal.is-visible { --rjr-reveal-y: 0px; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .rjr-reveal { opacity: 1; transform: none; transition: none; }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .rjr-polaroid:hover,
  .rjr-book:hover,
  .rjr-screenshot:hover,
  .rjr-btn:hover,
  .rjr-card--link:hover { --rjr-card-lift: 0px; }
}


/* ==========================================================================
   20  ARCHIVE
   --------------------------------------------------------------------------
   The article archive: a compact version of the front page hero, then a wall
   of polaroid prints. Nine to a page, pinned up rather than laid out on a
   grid - the tilt, the vertical drift and the tape all vary per card.
   ========================================================================== */

/* --- Compact hero -------------------------------------------------------- */
/* Same photographic bed as the front page, no media column, so the padding
   comes down and the copy centres. */
.rjr-hero--compact {
  --rjr-pt-base: calc(clamp(2.5rem, 1.8rem + 3.2vw, 5.5rem) * var(--rjr-section-rhythm)
                      + var(--rjr-header-h));
  --rjr-pb-base: calc(clamp(2.5rem, 1.8rem + 3.2vw, 5.5rem) * var(--rjr-section-rhythm));
}

.rjr-archive-head {
  max-width: 48rem;
  margin-inline: auto;
  text-align: center;
}
.rjr-archive-head h1 {
  margin: 0;
}
.rjr-archive-head__desc {
  margin: var(--rjr-space-4) 0 0;
  color: var(--rjr-ctx-muted);
  font-size: var(--rjr-text-lg);
  line-height: 1.5;
  text-wrap: pretty;
}

/* --- Page header ---------------------------------------------------------
   The same shape as the archive header above, deliberately duplicated rather
   than shared. An archive header is generated from a term and a post count; a
   page header is written. They look alike today, but tying a page to the
   archive's rules means every future tweak to one silently lands on the other,
   which is exactly how the recommendations page ended up inheriting an archive
   colour change. Standalone classes here for every ordinary page. */
.rjr-page-head {
  max-width: 48rem;
  margin-inline: auto;
  text-align: center;
}
.rjr-page-head h1 {
  margin: 0;
}
.rjr-page-head__desc {
  margin: var(--rjr-space-4) 0 0;
  color: var(--rjr-ctx-muted);
  font-size: var(--rjr-text-lg);
  line-height: 1.5;
  text-wrap: pretty;
}

/* --- The wall ------------------------------------------------------------ */
.rjr-post-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(2.75rem, 2rem + 3.2vw, 5rem) clamp(1.5rem, 1rem + 2vw, 3rem);
  /* Prints differ in height by design; stretching them would undo the effect. */
  align-items: start;
}

.rjr-post-card {
  --rjr-post-lean: 0deg;
  --rjr-post-offset: 0px;

  position: relative;
  /* Only the vertical drift lives on the card, so the title stays level. */
  transform: translateY(var(--rjr-post-offset));
}

/* Both frame scans were photographed leaning right, by --rjr-pl-tilt. Subtract
   that and --rjr-post-lean becomes the angle you actually see, which is what
   lets prints lean *either* way from right-leaning source images. Rotating the
   card instead would tip the title with it. */
.rjr-post-card .rjr-polaroid {
  transform: rotate(calc(var(--rjr-post-lean) - var(--rjr-pl-tilt, 0deg)));
}

/* The date chip stays at --rjr-pl-tilt and is carried by the rotation above,
   so it lands parallel to the photo's edges whichever way the print leans. */

/* Tape, nudged in for this context only. Scoped by descendant rather than by
   chaining a class to itself: one clean step of specificity over the base
   rule, and it reads as what it is.
   Only these two edges need it. The frames lean right, so the top-right and
   bottom-left scraps sit over the high and low corners of the print and
   straddle the edge differently from the other two. */
.rjr-post-card .rjr-tape--tr { top: 4%; }
.rjr-post-card .rjr-tape--bl { bottom: 10%; }

/* Hand placement, on two cycles of different length: lean alternates every
   card on a four-step cycle, drift runs on five. Both are coprime with the
   three columns, so neither lines up vertically. */
.rjr-post-grid > .rjr-post-card:nth-child(4n + 1) { --rjr-post-lean: -2.4deg; }
.rjr-post-grid > .rjr-post-card:nth-child(4n + 2) { --rjr-post-lean:  1.8deg; }
.rjr-post-grid > .rjr-post-card:nth-child(4n + 3) { --rjr-post-lean: -1.5deg; }
.rjr-post-grid > .rjr-post-card:nth-child(4n + 4) { --rjr-post-lean:  2.6deg; }

.rjr-post-grid > .rjr-post-card:nth-child(5n + 1) { --rjr-post-offset: 0px; }
.rjr-post-grid > .rjr-post-card:nth-child(5n + 2) { --rjr-post-offset: 14px; }
.rjr-post-grid > .rjr-post-card:nth-child(5n + 3) { --rjr-post-offset: 6px; }
.rjr-post-grid > .rjr-post-card:nth-child(5n + 4) { --rjr-post-offset: 18px; }
.rjr-post-grid > .rjr-post-card:nth-child(5n + 5) { --rjr-post-offset: 9px; }

/* One link per card, wrapping print and title, so there is a single tab stop
   and the title is its accessible name. */
.rjr-post-card__link {
  display: block;
  text-decoration: none;
}

/* --- Date, stamped on the matte ------------------------------------------ */
/* A sibling of the frame image rather than a child of the window: inside the
   window the tilted clip-path trimmed it, and the white border below the print
   is where a date belongs on a polaroid.
   z-index 3 puts it above the frame image, which sits at 2.
   --rjr-pl-tilt leans it with the scan, and the polaroid's counter-rotation
   carries it to the print's final lean, so it stays parallel to the matte's
   bottom edge whichever way the print leans. */
.rjr-post-card__date {
  position: absolute;
  z-index: 3;
  bottom: 7%;
  left: 50%;
  padding: 0.4em 1.5em 0.5em;
  background-image: var(--rjr-img-paint-navy);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  color: var(--rjr-text-on-navy);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1.2;
  text-transform: uppercase;
  white-space: nowrap;
  /* Centre first, then lean. --rjr-pl-tilt is the scan's own angle, and the
     polaroid's counter-rotation carries this to the print's final lean, so the
     chip always sits parallel to the bottom edge of the photo. */
  transform: translateX(-50%) rotate(var(--rjr-pl-tilt, -1deg));
  transform-origin: center;
  pointer-events: none;
}

/* Body face, not the display one: at card size the display face's tight
   counters cost more legibility than the character is worth, and these are
   scanned by the dozen. */
.rjr-post-card__title {
  margin: var(--rjr-space-4) 0 0;
  color: var(--rjr-navy-800);
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-base);
  font-weight: 500;
  line-height: 1.45;
  text-align: center;
  text-wrap: balance;
  transition: color var(--rjr-dur) var(--rjr-ease);
}

/* Hover lifts the whole print and shifts the title colour, so the card reads
   as one object. teal-800 rather than teal-700: 5.68:1 on cream against
   4.27:1, and the title sits at body size where AA wants 4.5. */
.rjr-post-card__link:hover .rjr-post-card__title,
.rjr-post-card__link:focus-visible .rjr-post-card__title {
  color: var(--rjr-teal-800);
}
.rjr-post-card__link:hover .rjr-polaroid,
.rjr-post-card__link:focus-visible .rjr-polaroid {
  transform: rotate(calc(var(--rjr-post-lean) - var(--rjr-pl-tilt, 0deg))) translateY(-7px);
  filter: drop-shadow(0 26px 40px rgba(5, 39, 49, 0.28));
}

/* --- Pagination ---------------------------------------------------------- */
.rjr-pagination {
  margin-top: clamp(3rem, 2.2rem + 3vw, 5rem);
}
.rjr-pagination .nav-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--rjr-space-3);
}

/* Torn from the same ticket as the shop links. The stub's one distinguishing
   feature is the dashed perforation at about 70% across, so the numbers crop
   the piece to the right of it: that fragment is already close to square, and
   it keeps the dashed edge and the rounded outer corners. Cropping the middle
   instead gave nine featureless grey squares.
   Prev and Next take the whole ticket, perforation and all. */
.rjr-pagination .page-numbers {
  --rjr-pager-tilt: 0deg;
  --rjr-pager-tilt-hover: -0.9deg;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  min-height: 2.75rem;     /* 44px touch target */
  padding-inline: 0.6em;
  background-image: url("../img/ticket-style-1.png");
  background-repeat: no-repeat;
  background-size: auto 100%;
  background-position: left center;
  /* The crop takes the ticket's left end, so the right edge is a hard cut
     through the paper. Rounding the box finishes that edge and lets the
     background-color on .current sit inside the same silhouette. */
  border-radius: var(--rjr-radius-sm);
  color: var(--rjr-navy-800);
  font-size: var(--rjr-text-sm);
  font-weight: 600;
  text-decoration: none;
  transform: rotate(var(--rjr-pager-tilt));
  /* drop-shadow rather than box-shadow: it follows the stub's punched edges,
     where a box shadow would draw a rectangle around them. */
  filter: drop-shadow(0 3px 5px rgba(5, 39, 49, 0.14));
  transition: transform var(--rjr-dur) var(--rjr-ease),
              filter var(--rjr-dur) var(--rjr-ease);
}

/* The two full tickets lean away from each other, so the row reads as pieces
   set down by hand rather than a button group. */
.rjr-pagination .prev.page-numbers,
.rjr-pagination .next.page-numbers {
  min-width: 7rem;
  padding-inline: 1.1em;
  background-size: 100% 100%;
}
.rjr-pagination .prev.page-numbers { --rjr-pager-tilt: -1.6deg; --rjr-pager-tilt-hover: -2.6deg; }
.rjr-pagination .next.page-numbers { --rjr-pager-tilt:  1.6deg; --rjr-pager-tilt-hover:  2.6deg; }

.rjr-pagination a.page-numbers:hover,
.rjr-pagination a.page-numbers:focus-visible {
  transform: translateY(-2px) rotate(var(--rjr-pager-tilt-hover));
  filter: drop-shadow(0 7px 11px rgba(5, 39, 49, 0.2));
}

/* The current page is stamped: the navy multiplies into the paper rather than
   covering it, so the stub keeps its gradient and its dashed edge.
   This used to carry a mask, to stop background-color squaring off the corners
   the ticket PNG leaves transparent. border-radius on .page-numbers now does
   that job for both the fill and the artwork, so the mask is gone - which also
   removes a second position value that had to be kept in step with
   background-position by hand, and had already drifted out of step. */
.rjr-pagination .page-numbers.current {
  background-color: var(--rjr-navy-700);
  background-blend-mode: multiply;
  color: #FFFFFF;
}

.rjr-pagination .page-numbers.dots {
  min-width: 1.5rem;
  background-image: none;
  color: var(--rjr-navy-400);
}

.rjr-archive-empty {
  max-width: 40rem;
  margin-inline: auto;
  text-align: center;
}


/* ==========================================================================
   21  SINGLE ARTICLE
   ========================================================================== */

/* --- Centred header ------------------------------------------------------ */
/* Tight at the top, nothing at the bottom: the print already carries plenty of
   visual space, and #related supplies the rhythm further down. */
.rjr-article-head {
  /* Roomier than it was: the eyebrow is the first thing on the page and it now
     opens underneath a floating ticket, so it needs the clearance. */
  --rjr-pt-base: calc(clamp(2.25rem, 1.7rem + 2.2vw, 4rem) * var(--rjr-section-rhythm)
                      + var(--rjr-header-h));

  /* Photographic bed, reusing .rjr-hero__bg and its ::after scrim, so the fade
     into the section below is the same one the front page uses. Two things are
     deliberately different here:

     1. The scrim is vertical, not the default 95deg one. That default leans
        heavy on the left because the hero puts copy left and photo right. This
        header is centred, so a left-heavy scrim would leave the right half of
        the h1 sitting on unprotected photo. Strongest at the top where the type
        is, thinning toward the print below it.
     2. No parallax slack. Nothing animates this layer - rjr-motion.js only
        drives .rjr-hero__bg-img when a .rjr-hero trigger is also present - so
        oversizing it by 7% would re-zoom the crop for no reason. */
  --rjr-hero-image: url("../img/rjr-bg-2.webp");
  --rjr-parallax-slack: 0%;
  --rjr-hero-scrim:
    linear-gradient(to bottom,
      rgba(240, 240, 234, 0.94) 0%,
      rgba(240, 240, 234, 0.84) 40%,
      rgba(240, 240, 234, 0.50) 100%);
}
.rjr-article-head__inner {
  max-width: 52rem;
  margin-inline: auto;
  text-align: center;
}
.rjr-article-head__cat {
  margin: 0 0 var(--rjr-space-6);
}
.rjr-article-head__cat a.rjr-eyebrow {
  text-decoration: none;
}
.rjr-article-head__title {
  margin: 0;
  text-wrap: balance;
}
.rjr-article-head__date {
  margin: var(--rjr-space-4) 0 0;
  color: var(--rjr-text-muted);
  font-size: var(--rjr-text-sm);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.rjr-article-head__back {
  margin: var(--rjr-space-3) 0 0;
}
.rjr-link--back i {
  /* The arrow points back, so the nudge on hover has to go back too. */
  transition: transform var(--rjr-dur) var(--rjr-ease);
}
.rjr-link--back:hover i {
  transform: translateX(-3px);
}

/* The print is the widest element on the page and breaks out of the header's
   reading measure. */
.rjr-article-head__media {
  max-width: 46rem;
  margin: var(--rjr-space-12) auto 0;
}
.rjr-article-head__media .rjr-polaroid {
  max-width: none;
}

/* --- Two columns --------------------------------------------------------- */
.rjr-article__grid {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: clamp(2rem, 1.4rem + 2.6vw, 4rem);
  align-items: start;
}

/* --- Article body -------------------------------------------------------- */
/* Post content is core Gutenberg blocks written over a decade, plus whatever
   Ad Inserter drops in, so these rules have to be tolerant: they style
   elements rather than assuming block classes are present. */
.rjr-article__content {
  font-size: var(--rjr-text-base);
  line-height: 1.75;
  color: var(--rjr-navy-800);
}
.rjr-article__content > * + * {
  margin-top: var(--rjr-space-6);
}
.rjr-article__content h2,
.rjr-article__content h3,
.rjr-article__content h4 {
  font-family: var(--rjr-font-display);
  color: var(--rjr-navy-900);
  line-height: 1.2;
  text-wrap: balance;
}
.rjr-article__content h2 { font-size: var(--rjr-text-2xl); margin-top: var(--rjr-space-12); }
.rjr-article__content h3 { font-size: var(--rjr-text-xl);  margin-top: var(--rjr-space-8); }
.rjr-article__content h4 { font-size: var(--rjr-text-lg);  margin-top: var(--rjr-space-8); }

.rjr-article__content a {
  color: var(--rjr-navy-600);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}
.rjr-article__content a:hover { color: var(--rjr-teal-800); }

/* The global reset sets `ul { list-style: none }`, which is right for the menus
   and card grids that make up most of the site's lists - but it also stripped
   the markers off every list inside an article, leaving indented lines with
   nothing to mark them. The `li::marker` colour below was dead as a result.
   Prose lists get their markers back here. */
.rjr-article__content ul,
.rjr-article__content ol {
  padding-left: 1.35em;
}
.rjr-article__content ul { list-style: disc; }
.rjr-article__content ol { list-style: decimal; }
.rjr-article__content li + li { margin-top: var(--rjr-space-2); }
.rjr-article__content li::marker { color: var(--rjr-accent); }

.rjr-article__content img {
  height: auto;
  max-width: 100%;
  border-radius: var(--rjr-radius-sm);
}
.rjr-article__content figure { margin-inline: 0; }
.rjr-article__content figcaption {
  margin-top: var(--rjr-space-2);
  color: var(--rjr-text-muted);
  font-size: var(--rjr-text-sm);
  text-align: center;
}

.rjr-article__content blockquote {
  margin-inline: 0;
  padding: var(--rjr-space-4) var(--rjr-space-6);
  border-left: 4px solid var(--rjr-accent);
  background: var(--rjr-navy-50);
  border-radius: 0 var(--rjr-radius-sm) var(--rjr-radius-sm) 0;
  font-size: var(--rjr-text-lg);
}
.rjr-article__content blockquote p:last-child { margin-bottom: 0; }

.rjr-article__content hr {
  height: 1px;
  border: 0;
  background: var(--rjr-border);
  margin-block: var(--rjr-space-12);
}

/*
 * The in-article blocks, defended against the prose rules above.
 *
 * Everything in this section is written as `.rjr-article__content <element>`,
 * which is (0,1,1) and outranks the components' own single-class rules. Pasted
 * into a post, all three of these blocks would have come out wrong:
 *
 *   - the button. `.rjr-article__content a` sets colour, so a primary button
 *     rendered navy-600 text on a navy fill. Unreadable, and it is the exact
 *     trap the header CTA hit twice. This is the one that matters.
 *   - the block headings, which picked up the article's own h3 size and its
 *     3rem top margin, tearing the title away from the label above it.
 *   - the affiliate logo, which picked up the prose image radius.
 *
 * Two classes each, so they win on specificity rather than on where they sit in
 * the file. Do not "simplify" these to single-class selectors.
 */
.rjr-article__content .rjr-btn { color: var(--rjr-cta-text); }
.rjr-article__content .rjr-btn--secondary { color: var(--rjr-cta-ink); }
.rjr-article__content .rjr-btn:hover { color: var(--rjr-cta-text); }

.rjr-article__content .rjr-incta__title {
  margin-top: 0;
  font-size: var(--rjr-text-xl);
}
.rjr-article__content .rjr-inaff__title {
  margin-top: 0;
  font-size: var(--rjr-text-lg);
}
.rjr-article__content .rjr-inaff__logo { border-radius: 0; }

/* The prose rhythm sets the gap above any top-level child, which would override
   the components' own breathing room. Restated so a block sits apart from the
   paragraphs either side of it rather than in the middle of them. */
.rjr-article__content > .rjr-incta,
.rjr-article__content > .rjr-inaff { margin-top: var(--rjr-space-8); }

/* Legacy content contains wide tables; let them scroll rather than widen the
   column and push the sidebar off. */
.rjr-article__content table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--rjr-text-sm);
}
.rjr-article__content th,
.rjr-article__content td {
  padding: var(--rjr-space-2) var(--rjr-space-3);
  border-bottom: 1px solid var(--rjr-border);
  text-align: left;
}
.rjr-article__content th { color: var(--rjr-navy-900); font-weight: 600; }
.rjr-article__content .wp-block-table { overflow-x: auto; }

.rjr-article__content pre {
  padding: var(--rjr-space-4);
  overflow-x: auto;
  background: var(--rjr-navy-900);
  border-radius: var(--rjr-radius-sm);
  color: var(--rjr-cream);
  font-size: var(--rjr-text-sm);
}

/* Core button blocks, mapped onto the brand button.
   Orange rather than the default navy CTA: in body copy these are the one
   loud element, and the brand's loud button is orange.

   The !important is not laziness. Editors picked colours from the old palette,
   so the markup carries preset classes like `has-vivid-cyan-blue-background-color
   has-background`, and WordPress prints those presets with !important of its
   own. Matching it is the only way to win short of a specificity war, and the
   alternative - stripping the classes with a the_content filter - would rewrite
   published content to fix a styling problem.

   Colours only. Everything else below wins on ordinary specificity. */
.rjr-article__content .wp-block-button__link,
.rjr-article__content .wp-element-button {
  display: inline-block;
  padding: 0.85em 1.6em;
  border: 0;
  border-radius: var(--rjr-radius-pill);
  background-color: var(--rjr-orange-500) !important;
  color: #FFFFFF !important;
  font-family: var(--rjr-font-body);
  font-size: var(--rjr-text-sm);
  font-weight: 600;
  line-height: 1.3;
  text-decoration: none;
  transition: background-color var(--rjr-dur) var(--rjr-ease);
}
.rjr-article__content .wp-block-button__link:hover,
.rjr-article__content .wp-element-button:hover,
.rjr-article__content .wp-block-button__link:focus-visible,
.rjr-article__content .wp-element-button:focus-visible {
  background-color: var(--rjr-orange-600) !important;
  color: #FFFFFF !important;
}

/* Legacy buttons sit inside .wp-block-buttons from the old theme, which shipped
   its own x-btn class. That class now styles nothing, so the wrapper only needs
   to lay them out and give stacked ones room. */
.rjr-article__content .wp-block-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--rjr-space-3);
}

.rjr-article__pages {
  margin-top: var(--rjr-space-8);
  font-size: var(--rjr-text-sm);
}

/* --- Sidebar ------------------------------------------------------------- */
.rjr-article__side > * + * {
  margin-top: var(--rjr-space-8);
}

/* Not sticky, deliberately. It was tried and removed: the sidebar runs to
   roughly 1100px, taller than most desktop viewports, so pinning it puts the
   bottom cards permanently out of reach. It is also a desktop-only refinement
   on a site whose traffic is mostly mobile, where the sidebar sits in normal
   flow below the comments and none of it would apply. */
.rjr-article__side .rjr-callout {
  font-size: var(--rjr-text-xs);
}
.rjr-article__side .rjr-callout p { margin: 0; }

.rjr-side-block__title,
.rjr-side-card__title {
  margin: 0 0 var(--rjr-space-3);
  font-family: var(--rjr-font-display);
  font-size: var(--rjr-text-lg);
  line-height: 1.2;
  color: var(--rjr-navy-900);
}

.rjr-side-card {
  position: relative;
  padding: var(--rjr-space-6);
  background-color: var(--rjr-surface-card);
  background-image: url("../img/crumpled-paper-texture.jpg");
  background-size: cover;
  background-blend-mode: multiply;
  border-radius: var(--rjr-radius-sm);
  box-shadow: 0 10px 22px -14px rgba(5, 39, 49, 0.45);
  text-align: center;
}
.rjr-side-card p { font-size: var(--rjr-text-sm); }
.rjr-side-card__eyebrow {
  margin: 0 0 var(--rjr-space-3);
  color: var(--rjr-accent);
  font-size: var(--rjr-text-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.rjr-side-card__cover {
  width: 8.5rem;
  height: auto;
  margin-inline: auto;
  filter: drop-shadow(0 12px 18px rgba(5, 39, 49, 0.3));
}
.rjr-side-card__price {
  margin: var(--rjr-space-2) 0 var(--rjr-space-4);
  color: var(--rjr-text-muted);
  font-size: var(--rjr-text-xs);
}
.rjr-side-card__icon {
  display: inline-flex;
  font-size: 2rem;
  color: var(--rjr-accent);
}

/* Navy variant of the spotlight card.
   The wash is a near-opaque navy gradient laid *over* the crumple rather than
   multiplied into it, the same way .rjr-section--navy does it: multiply against
   a dark base crushes the texture to black.
   Re-pointing the three CTA tokens flips the button to orange without touching
   the button itself. Teal is 3.6:1 on navy and sinks; orange is 5.1:1. */
.rjr-side-card--navy {
  --rjr-cta: var(--rjr-orange-500);
  --rjr-cta-hover: var(--rjr-orange-600);
  --rjr-cta-text: #FFFFFF;

  background-color: var(--rjr-navy-800);
  background-image:
    linear-gradient(rgba(6, 52, 66, 0.94), rgba(6, 52, 66, 0.94)),
    url("../img/crumpled-paper-texture.jpg");
  background-blend-mode: normal;
  color: var(--rjr-navy-100);
  box-shadow: 0 14px 26px -14px rgba(5, 39, 49, 0.7);
}
.rjr-side-card--navy .rjr-side-card__title { color: var(--rjr-cream); }
.rjr-side-card--navy .rjr-side-card__price { color: var(--rjr-navy-200); }
.rjr-side-card--navy .rjr-side-card__eyebrow { color: var(--rjr-orange-400); }
.rjr-side-card--navy p { color: var(--rjr-navy-100); }

/* Tickets stack full width in a one-third column.
   The stub's own shadow is tuned for a stub sitting loose on a navy field. In
   a narrow column two of them stacked read as heavy, so both the resting and
   the hover shadow come down. */
.rjr-ticket--block {
  display: flex;
  width: 100%;
  justify-content: flex-start;
  padding-inline: 1.4rem;
  filter: drop-shadow(1px 2px 5px rgba(2, 20, 26, 0.16));
}
.rjr-ticket--block:hover {
  filter: drop-shadow(2px 4px 8px rgba(2, 20, 26, 0.22));
}
.rjr-ticket--block .rjr-ticket__arrow { margin-left: auto; }
.rjr-side-block .rjr-ticket + .rjr-ticket { margin-top: var(--rjr-space-4); }

.rjr-side-cats {
  margin: 0;
  padding: 0;
  list-style: none;
}
.rjr-side-cats a {
  display: flex;
  align-items: center;
  gap: var(--rjr-space-3);
  padding: var(--rjr-space-3) var(--rjr-space-2);
  border-bottom: 1px solid var(--rjr-border);
  border-radius: var(--rjr-radius-sm);
  color: var(--rjr-navy-700);
  font-size: var(--rjr-text-sm);
  font-weight: 500;
  text-decoration: none;
  transition: background-color var(--rjr-dur) var(--rjr-ease),
              color var(--rjr-dur) var(--rjr-ease);
}
.rjr-side-cats li:last-child a { border-bottom: 0; }

/* Three things move, none of them far: the row tints, the drawn icon tips and
   grows very slightly as if nudged, and the arrow slides toward its target.
   The icon rotation is what keeps it feeling hand-placed rather than like a
   menu item lighting up. */
.rjr-side-cats a:hover,
.rjr-side-cats a:focus-visible {
  background: var(--rjr-navy-50);
  color: var(--rjr-teal-800);
}
.rjr-side-cats .rjr-cat-icon {
  flex: none;
  width: 2.25rem;
  height: 2.25rem;
  transition: transform var(--rjr-dur-slow) var(--rjr-ease-out);
}
.rjr-side-cats a:hover .rjr-cat-icon,
.rjr-side-cats a:focus-visible .rjr-cat-icon {
  transform: rotate(-6deg) scale(1.1);
}
.rjr-side-cats a > i {
  margin-left: auto;
  color: var(--rjr-accent);
  transition: transform var(--rjr-dur) var(--rjr-ease);
}
.rjr-side-cats a:hover > i,
.rjr-side-cats a:focus-visible > i {
  transform: translateX(4px);
}

/* --- Comments ------------------------------------------------------------ */
.rjr-comments {
  margin-top: clamp(3rem, 2.2rem + 3vw, 5rem);
  padding-top: clamp(2rem, 1.5rem + 2vw, 3.5rem);
  border-top: 1px solid var(--rjr-border);
}
.rjr-comments__title {
  font-family: var(--rjr-font-display);
  font-size: var(--rjr-text-xl);
  color: var(--rjr-navy-900);
}
.rjr-comments__list,
.rjr-comments__list .children {
  margin: 0;
  padding: 0;
  list-style: none;
}
.rjr-comments__list .children {
  margin-top: var(--rjr-space-6);
  padding-left: clamp(1rem, 0.5rem + 2vw, 3rem);
  border-left: 1px solid var(--rjr-border);
}
.rjr-comments__list li.comment { margin-top: var(--rjr-space-8); }
.rjr-comments__list .comment-body {
  font-size: var(--rjr-text-sm);
  line-height: 1.7;
}
.rjr-comments__list .comment-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--rjr-space-2) var(--rjr-space-3);
  margin-bottom: var(--rjr-space-3);
}
/* The avatar and name are inline children of .comment-author, so at 56px the
   name wrapped under the image. Make that element the flex row too. */
.rjr-comments__list .comment-author {
  display: flex;
  align-items: center;
  gap: var(--rjr-space-3);
}
.rjr-comments__list .comment-author img {
  flex: none;
  border-radius: 50%;
}
/* "says:" is filler between the name and the comment. Keep it for screen
   readers, where it carries the relationship, but take it out of the layout. */
.rjr-comments__list .comment-author .says {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.rjr-comments__list .comment-author .fn {
  color: var(--rjr-navy-900);
  font-style: normal;
  font-weight: 600;
}
.rjr-comments__list .comment-metadata,
.rjr-comments__list .comment-metadata a {
  color: var(--rjr-text-muted);
  font-size: var(--rjr-text-xs);
  text-decoration: none;
}
.rjr-comments__list .reply a {
  display: inline-block;
  margin-top: var(--rjr-space-2);
  color: var(--rjr-navy-600);
  font-size: var(--rjr-text-xs);
  font-weight: 600;
  text-decoration: none;
}
.rjr-comments__list .reply a:hover { text-decoration: underline; }
.rjr-comments__closed { color: var(--rjr-text-muted); font-size: var(--rjr-text-sm); }

.rjr-comment-form {
  margin-top: clamp(2.5rem, 2rem + 2vw, 4rem);
}
.rjr-comment-form .comment-reply-title {
  font-family: var(--rjr-font-display);
  font-size: var(--rjr-text-xl);
  color: var(--rjr-navy-900);
}
.rjr-comment-form label {
  display: block;
  margin-bottom: var(--rjr-space-2);
  color: var(--rjr-navy-700);
  font-size: var(--rjr-text-sm);
  font-weight: 600;
}
.rjr-comment-form p { margin-bottom: var(--rjr-space-4); }

/* .rjr-input fills with cream, which reads fine on the navy newsletter but
   disappears completely against a cream article. On this surface the fields
   need to be the lighter thing, with an edge. */
.rjr-comment-form input[type="text"],
.rjr-comment-form input[type="email"],
.rjr-comment-form input[type="url"],
.rjr-comment-form textarea {
  width: 100%;
  background: var(--rjr-surface-card);
  border-color: var(--rjr-border);
}
.rjr-textarea {
  min-height: 9rem;
  resize: vertical;
}
.rjr-comment-form__notes,
.rjr-comment-form .comment-notes {
  color: var(--rjr-text-muted);
  font-size: var(--rjr-text-xs);
}
.rjr-comment-form .comment-form-cookies-consent {
  display: flex;
  align-items: flex-start;
  gap: var(--rjr-space-2);
  font-size: var(--rjr-text-xs);
}
.rjr-comment-form .comment-form-cookies-consent label {
  margin-bottom: 0;
  font-weight: 400;
}


/* ==========================================================================
   17  RESPONSIVE
   ========================================================================== */

@media (max-width: 1024px) {
  .rjr-grid--3,
  .rjr-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rjr-post-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Stacked well before the rest of the page's columns, which go at 620.
     The aside holds a 24-character email address, and the width it needs is
     what sets this number: measured at 390 / 620 / 800 / 900 / 1000 / 1100 /
     1200 / 1440, the address sits on one line everywhere except the 860-950
     band, where the aside lands around 271px. Stacking at 620 or at 860 both
     leave part of that band two-lined, so it stacks here instead - above the
     band entirely. If this number is ever lowered, re-measure at 900. */
  .rjr-contact-cols { grid-template-columns: minmax(0, 1fr); }

  /* One column below this: a third of the remaining width is too narrow for
     the book cover and the ticket stubs. Comments live inside the article
     column, so they stay above the sidebar once this collapses, which is the
     order we want on a phone. */
  .rjr-article__grid { grid-template-columns: minmax(0, 1fr); }
  .rjr-article__side { max-width: 32rem; margin-inline: auto; }
  .rjr-library__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

}

/* The hero stacks earlier than everything else: once the copy column hits its
   30rem floor there's nothing left to give the photo, and a side-by-side hero
   with a shrunken picture is worse than a stacked one. */
@media (max-width: 1100px) {
  .rjr-hero {
    --rjr-hero-scrim: linear-gradient(to bottom,
      rgba(240, 240, 234, 0.52) 0%,
      rgba(240, 240, 234, 0.80) 34%,
      rgba(240, 240, 234, 0.95) 60%);
  }
  .rjr-hero__grid {
    grid-template-columns: minmax(0, 1fr);
    /* The copy's own margins space it out once its children become grid items
       below; leaving the column gap on as well would double every gap. */
    row-gap: 0;
  }

  /* Stacked, the print belongs directly under the headline - not after the
     lede and the buttons, where it was the last thing on a long scroll and the
     first screen was all type.
     .rjr-hero__copy is a wrapper with no styles of its own, so display:contents
     lifts the eyebrow, h1, lede and buttons into the grid alongside the media
     and lets order interleave them. Selectors are unaffected by it; only the
     box goes away. If .rjr-hero__copy ever gains a background, padding or
     border, this stops being free and the markup has to change instead. */
  .rjr-hero__copy { display: contents; }
  .rjr-hero__copy > .rjr-eyebrow { order: 1; }
  .rjr-hero__copy > h1           { order: 2; }
  .rjr-hero__media               { order: 3; }
  .rjr-hero__copy > .rjr-lede    { order: 4; }
  .rjr-hero__copy > .rjr-btn-row { order: 5; }

  /* Centered and capped through the tablet range. The bleed treatment below is
     for phones only: at 1084px wide an uncapped print measured 1049px, which is
     a poster, not a hero image.
     margin-right must be reset explicitly - the desktop rule pulls the print
     out of the container with a negative one, and centering it while that is
     still applied drags it off to the right by the gutter. */
  .rjr-hero__media {
    justify-self: center;
    max-width: 30rem;
    width: 100%;
    margin-right: 0;
    margin-block: var(--rjr-space-6) var(--rjr-space-8);
  }
}

@media (max-width: 860px) {
  .rjr-feature__grid,
  .rjr-praise__grid {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Phones: the print sits right of center and runs off the edge, so it reads
     as a photograph laid on the page rather than a centered box in a column.
     The order set at 1100 already puts it directly under the headline.

     .rjr-hero sets overflow: hidden, so the overhang is clipped at the section
     and never adds a horizontal scrollbar - verified against
     documentElement.scrollWidth at 360, 390 and 620.

     The negative margin has to clear the section's own gutter before it starts
     eating into the viewport at all, hence the padding-x term. */
  .rjr-hero__media {
    justify-self: end;
    width: 106%;
    max-width: none;
    margin-right: calc(-1 * var(--rjr-section-padding-x) - 7%);
  }

  /* Three of these need about 240px each before the detail line stops wrapping
     mid-phrase. Two here, one at 620 below - and that order matters, because
     both rules carry the same weight and the later block is what wins. */
  .rjr-contact-help { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Stacked: nothing above to overlap, so the book sits in flow. */
  .rjr-book-shot { --rjr-book-rise: 0rem; }
  .rjr-feature__media { margin-top: var(--rjr-space-md); }
  .rjr-feature__more > .rjr-link { margin-left: 0; }
  /* The stack's negative offsets are desktop values; in one column they would
     push the frames off-screen. */
.rjr-polaroid-stack__front { width: 49%; left: -58px;}
  .rjr-polaroid-stack__back  { width: 77%; top: 7%; }
  .rjr-polaroid-stack { aspect-ratio: 1 / 1.05; }
  /* Mobile header: logo left, search and menu right. The CTA moves inside the
     panel, where it gets a full-width row instead of competing with two icons
     for the last 90px of a 375px bar. */
  .rjr-header__menu-toggle { display: inline-flex; }
  .rjr-header__actions > .rjr-header__cta { display: none; }
  /* Shorter stub, so the ticket does not eat the top of a phone screen. Narrow
     gutters rather than the desktop ones: enough to read as a ticket lying on
     the page, without spending width the menu and search icons need. */
  .rjr-header__inner {
    --rjr-tk-h: 64px;
    width: calc(100% - (2 * var(--rjr-space-3)));
  }
  /* First-paint fallback to match the shorter bar. navigation.js overwrites it
     with the measured height as soon as it runs. */
  :root { --rjr-header-h: 84px; }

  /* The same nav element, now a full-screen overlay. One menu in the DOM, two
     shapes. Closed state is visibility, not display: the element keeps its box
     so the open transition has something to animate from. */
  .rjr-nav {
    position: fixed;
    inset: var(--rjr-admin-bar-h) 0 0;
    z-index: 200;
    display: flex;
    flex-direction: column;
    gap: var(--rjr-space-2);
    margin-left: 0;
    /* Clears the panel's own logo and close button, which are 44px tall and sit
       at space-3 - not the whole header height, which left a dead band. */
    padding: calc(var(--rjr-header-h) - var(--rjr-space-4))
             var(--rjr-section-padding-x) var(--rjr-space-12);
    overflow-y: auto;
    overscroll-behavior: contain;
    background-color: var(--rjr-cream);
    background-image:
      linear-gradient(rgba(240, 240, 234, 0.93), rgba(240, 240, 234, 0.93)),
      url("../img/crumpled-paper-texture.jpg");
    background-size: auto, 520px auto;
    opacity: 0;
    visibility: hidden;
    /* Opacity only. A transform on the panel would make position:fixed inside
       it resolve against the panel instead of the viewport, and the close
       button would scroll away with the list. */
    transition: opacity var(--rjr-dur) var(--rjr-ease),
                visibility 0s linear var(--rjr-dur);
  }
  .rjr-nav.is-open {
    opacity: 1;
    visibility: visible;
    transition-delay: 0s;
  }
  /* Scroll lock, so the page behind does not travel while the overlay is up. */
  body.rjr-menu-open { overflow: hidden; }
  /*
   * The panel is inside .rjr-header, so it can never paint above anything that
   * outranks the header itself - the cookie banner sits at five figures and
   * was covering the menu. Lifting the whole header for as long as the menu is
   * open moves the panel's entire stacking context over it.
   */
  body.rjr-menu-open .rjr-header { z-index: 100000; }

  /* Fixed, not absolute: both stay put while the list scrolls under them. */
  /* Fixed to the viewport rather than to the panel, so these take the admin bar
     offset themselves instead of inheriting the panel's. */
  .rjr-nav__logo,
  .rjr-nav__close {
    display: inline-flex;
    position: fixed;
    top: calc(var(--rjr-admin-bar-h) + var(--rjr-space-4));
    z-index: 2;
  }
  .rjr-nav__logo {
    left: var(--rjr-section-padding-x);
    align-items: center;
  }
  .rjr-nav__logo img {
    width: auto;
    height: 34px;
  }
  .rjr-nav__close {
    top: calc(var(--rjr-admin-bar-h) + var(--rjr-space-2));
    right: calc(var(--rjr-section-padding-x) - 10px);
    background: rgba(240, 240, 234, 0.9);
  }

  /* Something to land on at the end of the list. margin-top:auto rather than a
     fixed offset, so it sits at the bottom on a tall screen and stays below
     the CTA on a short one instead of overlapping it. */
  /* Sized off the decoration scale rather than by eye: .rjr-decor--plane is
     10.5rem at --sm and .rjr-decor--lg doubles it. aspect-ratio keeps the
     shape, so only the one number has to match the system. */
  .rjr-nav__plane {
    display: block;
    width: calc(10.5rem * 2);
    max-width: 100%;
    aspect-ratio: 500 / 251;
    margin: auto auto 0;
    background: url("../img/decorations/plane.png") no-repeat center / contain;
    opacity: 0.28;
    transform: rotate(-8deg);
    pointer-events: none;
  }

  .rjr-nav > ul {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }
  .rjr-nav > ul > li { border-bottom: 1px solid rgba(167, 196, 205, 0.35); }
  .rjr-nav > ul > li:last-child { border-bottom: 0; }
  .rjr-nav > ul a {
    display: block;
    padding-block: var(--rjr-space-3);
    font-size: var(--rjr-text-base);
  }
  /* The sliding stroke is a pointer affordance; it makes no sense on a stacked
     touch list. */
  .rjr-nav > ul a::after { content: none; }
  .rjr-nav .menu-item-has-children > a::before { content: none; }

  /* One column. The text is already first in the source, so the pile follows
     it without needing an order swap. */
  .rjr-rec-head { grid-template-columns: minmax(0, 1fr); }
  /* Back to centered once it is full width, like every other header on the
     site. The left rag only earns its keep in a narrow column.
     Two classes again, to match the (0,2,0) desktop rule it has to undo. */
  .rjr-page-head.rjr-rec-head__text {
    margin-inline: auto;
    text-align: center;
  }
  /* Caps off, so the percentages govern and the pile scales with the phone
     instead of sitting at its desktop size in the middle of the screen. */
  .rjr-rec-head__photos { max-width: 28rem; margin-inline: auto; }
  .rjr-rec-head__photo--left,
  .rjr-rec-head__photo--right { max-width: none; }

  /* No dropdown on touch: the children sit open and indented under the parent.
     A tap-to-open accordion would put a second job on a link that is already a
     destination, which is the usual way these menus trap people. */
  .rjr-nav .sub-menu,
  /* The open-state rule is (0,4,0) and outranks a plain reset, so touching
     Travel Advice was re-applying the desktop card's translate(-50%) and
     sliding the submenu off to the left. It has to be unset here too. */
  .rjr-nav .menu-item-has-children:hover > .sub-menu,
  .rjr-nav .menu-item-has-children:focus-within > .sub-menu {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    min-width: 0;
    padding: 0 0 var(--rjr-space-2) var(--rjr-space-4);
    background: none;
    border-radius: 0;
    box-shadow: none;
    transition: none;
  }
  .rjr-nav .sub-menu::before,
  .rjr-nav .sub-menu::after { content: none; }
  .rjr-nav .sub-menu a {
    padding-block: var(--rjr-space-2);
    font-size: var(--rjr-text-sm);
    color: var(--rjr-navy-600);
  }

  .rjr-nav__cta {
    display: inline-flex;
    align-self: flex-start;
    margin-top: var(--rjr-space-4);
    /* Keeps the plane off the button when the screen is too short for
       margin-top:auto to leave any slack. */
    margin-bottom: var(--rjr-space-6);
  }

  /* Brand across the top, the two link columns side by side under it. Dropping
     straight to one column here would push the legal line most of a screen
     further down on a tablet for no gain. */
  .rjr-footer__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .rjr-footer__brand { grid-column: 1 / -1; }
}

@media (max-width: 620px) {
  .rjr-grid--3,
  .rjr-grid--4,
  .rjr-library__grid { grid-template-columns: minmax(0, 1fr); }

  /* Smaller caps, or the two perforated ends would take most of a phone's
     width and leave the copy in a gutter. */
  .rjr-404-ticket {
    --rjr-tk2-k: 0.5;
    min-height: 0;
    padding: var(--rjr-space-4) var(--rjr-space-3);
  }
  /* Back into the flow above the eyebrow: at this width an overprint in the
     corner would sit on top of the headline. */
  .rjr-404-ticket__code {
    position: static;
    display: block;
    margin-bottom: var(--rjr-space-2);
  }
  .rjr-404-ticket__body > .rjr-eyebrow,
  .rjr-404-ticket__body > h1 { max-width: none; }

  .rjr-rec-card__logo { height: 2rem; }

  /* One column. Two of these on a phone leaves the detail line breaking after
     every second word. */
  .rjr-contact-help { grid-template-columns: minmax(0, 1fr); }

  /* One column. Two columns of a 328px container leaves about 150px each, and
     every entry longer than two words wraps into a stack of fragments. Taller,
     but a contents list nobody can read is not saving anything. */
  .rjr-legal__toc ol { columns: 1; }

  /* One column, so there is no row for a card to line up with and the reserved
     label strip is just a gap. Written as :not(:has()) rather than a class from
     PHP so that an engine without :has() drops the whole rule and keeps the
     reserved strip - harmless - instead of dropping the padding and letting the
     labels land on the cards. */
  .rjr-rec-cell:not(:has(.rjr-rec-band__title)) { padding-top: 0; }

  /* One column, and the legal line stacks above Back to top rather than trying
     to sit beside it. */
  .rjr-footer__inner { grid-template-columns: minmax(0, 1fr); }
  .rjr-footer__legal { justify-content: flex-start; }

  /* Copy first, print under it. A photograph above the h1 pushes the title
     off a phone screen. */
  .rjr-about-hero { grid-template-columns: minmax(0, 1fr); }
  .rjr-about-hero__photo { width: min(22rem, 100%); margin-inline: auto; }
  .rjr-about-cols { grid-template-columns: minmax(0, 1fr); }

  /* Same reasoning as the About hero above. .rjr-contact-cols is not here with
     it: that one stacks at 860, for a reason written out in that block. */
  .rjr-contact-hero { grid-template-columns: minmax(0, 1fr); }
  .rjr-contact-hero__photo { width: min(22rem, 100%); margin-inline: auto; }

  /* Cover above the copy. It is the thing being sold, so it leads. */
  .rjr-shop-hero { grid-template-columns: minmax(0, 1fr); }
  .rjr-shop-hero__cover { max-width: 15rem; }
  .rjr-shop-amazon__inner { grid-template-columns: minmax(0, 1fr); }

  /* One column: the vertical drift becomes uneven gaps rather than hand
     placement, so it goes. The lean stays, calmed down.
     Written as :nth-child to match the specificity of the desktop rules
     (0,3,0) - a plain .rjr-post-grid > .rjr-post-card reset would lose. */
  .rjr-post-grid { grid-template-columns: minmax(0, 1fr); }
  .rjr-post-grid > .rjr-post-card:nth-child(5n + 1) { --rjr-post-offset: 0px; }
  .rjr-post-grid > .rjr-post-card:nth-child(5n + 2) { --rjr-post-offset: 0px; }
  .rjr-post-grid > .rjr-post-card:nth-child(5n + 3) { --rjr-post-offset: 0px; }
  .rjr-post-grid > .rjr-post-card:nth-child(5n + 4) { --rjr-post-offset: 0px; }
  .rjr-post-grid > .rjr-post-card:nth-child(5n + 5) { --rjr-post-offset: 0px; }
  .rjr-post-grid > .rjr-post-card:nth-child(4n + 1) { --rjr-post-lean: -1.4deg; }
  .rjr-post-grid > .rjr-post-card:nth-child(4n + 2) { --rjr-post-lean:  1.1deg; }
  .rjr-post-grid > .rjr-post-card:nth-child(4n + 3) { --rjr-post-lean: -0.9deg; }
  .rjr-post-grid > .rjr-post-card:nth-child(4n + 4) { --rjr-post-lean:  1.5deg; }
  .rjr-feature__more > .rjr-ticket { width: 100%; --rjr-ticket-lift: 0px; }

  /* Big rotations read as sloppy at phone width — calm everything down. */
  .rjr-book-shot { --rjr-book-tilt: -2deg; --rjr-book-w: 15rem; }
  .rjr-screenshot { transform: rotate(0.8deg); }
  /* A long eyebrow outgrows its brush stroke at phone width. */
  .rjr-eyebrow { transform: rotate(-0.4deg); font-size: var(--rjr-text-xs); }
  .rjr-tape--tr { display: none; }   /* single piece of tape on mobile */
  /* Stacked single column: the stagger reads as uneven gaps rather than as a
     deliberate scatter, and the tilt has no neighbours to play against.
     The offset MUST be reset inside these nth-child rules - a plain
     `.rjr-grid > .rjr-card` reset is a weaker selector than the desktop
     nth-child rules and silently loses to them. */
  .rjr-grid > .rjr-card:nth-child(4n + 1) { --rjr-card-tilt: -0.35deg; --rjr-card-offset: 0px; }
  .rjr-grid > .rjr-card:nth-child(4n + 2) { --rjr-card-tilt:  0.25deg; --rjr-card-offset: 0px; }
  .rjr-grid > .rjr-card:nth-child(4n + 3) { --rjr-card-tilt: -0.2deg;  --rjr-card-offset: 0px; }
  .rjr-grid > .rjr-card:nth-child(4n + 4) { --rjr-card-tilt:  0.4deg;  --rjr-card-offset: 0px; }
  .rjr-btn-row .rjr-btn { flex: 1 1 100%; }
  .rjr-hero h1 { font-size: 2.2rem; }
}


/* --- Stack order ---------------------------------------------------------
   Put .rjr-stack-reverse on a two-child GRID (not on the children) to flip
   which one comes first once the grid stacks. Source order stays the sensible
   reading order for assistive tech; this only changes the visual order.

   Scoped to 1100px because that is the widest point at which anything here
   stacks. On a grid that is still two columns at that width it swaps left for
   right, which is usually what you want anyway - but it is worth knowing. */
@media (max-width: 1100px) {
  .rjr-stack-reverse > :nth-child(1) { order: 2; }
  .rjr-stack-reverse > :nth-child(2) { order: 1; }
}


/* ==========================================================================
   18  SPACING UTILITIES
   --------------------------------------------------------------------------
   These ADJUST the element's own spacing — they do not replace it.

     <section class="rjr-section rjr-pb-xl">   section padding + xl
     <section class="rjr-section rjr-pb-less-md">   section padding - md
     <section class="rjr-section rjr-pb-none">      no bottom padding at all

   How: every component that owns padding publishes it as --rjr-pt-base /
   --rjr-pb-base, and the utility resolves to base ± step. CSS cannot read a
   property's existing value, so the base has to be handed over deliberately.
   Components currently publishing one: .rjr-section (+ --tight, .rjr-hero,
   .rjr-newsletter) and .rjr-card. On anything else the base falls back to 0,
   so the utility simply sets the value.

   Padding is floored at 0 — negative padding is invalid and would drop the
   whole declaration. Margins are NOT floored: pulling a block upward with a
   negative margin is a legitimate move, and `less` is how you ask for it.

   `none` is absolute, not relative: it zeroes the side outright.

     rjr-{pt,pb,mt,mb}-{xs,sm,md,lg,xl}          add
     rjr-{pt,pb,mt,mb}-less-{xs,sm,md,lg,xl}     subtract
     rjr-{pt,pb,mt,mb}-none                      zero
   ========================================================================== */


/* --- padding-top ------------------------------------------------------ */
.rjr-pt-none { padding-top: 0 !important; }
.rjr-pt-xs      { padding-top: calc(var(--rjr-pt-base, 0px) + var(--rjr-space-xs)) !important; }
.rjr-pt-sm      { padding-top: calc(var(--rjr-pt-base, 0px) + var(--rjr-space-sm)) !important; }
.rjr-pt-md      { padding-top: calc(var(--rjr-pt-base, 0px) + var(--rjr-space-md)) !important; }
.rjr-pt-lg      { padding-top: calc(var(--rjr-pt-base, 0px) + var(--rjr-space-lg)) !important; }
.rjr-pt-xl      { padding-top: calc(var(--rjr-pt-base, 0px) + var(--rjr-space-xl)) !important; }
.rjr-pt-less-xs { padding-top: max(0px, calc(var(--rjr-pt-base, 0px) - var(--rjr-space-xs))) !important; }
.rjr-pt-less-sm { padding-top: max(0px, calc(var(--rjr-pt-base, 0px) - var(--rjr-space-sm))) !important; }
.rjr-pt-less-md { padding-top: max(0px, calc(var(--rjr-pt-base, 0px) - var(--rjr-space-md))) !important; }
.rjr-pt-less-lg { padding-top: max(0px, calc(var(--rjr-pt-base, 0px) - var(--rjr-space-lg))) !important; }
.rjr-pt-less-xl { padding-top: max(0px, calc(var(--rjr-pt-base, 0px) - var(--rjr-space-xl))) !important; }

/* --- padding-bottom ------------------------------------------------------ */
.rjr-pb-none { padding-bottom: 0 !important; }
.rjr-pb-xs      { padding-bottom: calc(var(--rjr-pb-base, 0px) + var(--rjr-space-xs)) !important; }
.rjr-pb-sm      { padding-bottom: calc(var(--rjr-pb-base, 0px) + var(--rjr-space-sm)) !important; }
.rjr-pb-md      { padding-bottom: calc(var(--rjr-pb-base, 0px) + var(--rjr-space-md)) !important; }
.rjr-pb-lg      { padding-bottom: calc(var(--rjr-pb-base, 0px) + var(--rjr-space-lg)) !important; }
.rjr-pb-xl      { padding-bottom: calc(var(--rjr-pb-base, 0px) + var(--rjr-space-xl)) !important; }
.rjr-pb-less-xs { padding-bottom: max(0px, calc(var(--rjr-pb-base, 0px) - var(--rjr-space-xs))) !important; }
.rjr-pb-less-sm { padding-bottom: max(0px, calc(var(--rjr-pb-base, 0px) - var(--rjr-space-sm))) !important; }
.rjr-pb-less-md { padding-bottom: max(0px, calc(var(--rjr-pb-base, 0px) - var(--rjr-space-md))) !important; }
.rjr-pb-less-lg { padding-bottom: max(0px, calc(var(--rjr-pb-base, 0px) - var(--rjr-space-lg))) !important; }
.rjr-pb-less-xl { padding-bottom: max(0px, calc(var(--rjr-pb-base, 0px) - var(--rjr-space-xl))) !important; }

/* --- margin-top ------------------------------------------------------- */
.rjr-mt-none { margin-top: 0 !important; }
.rjr-mt-xs      { margin-top: calc(var(--rjr-mt-base, 0px) + var(--rjr-space-xs)) !important; }
.rjr-mt-sm      { margin-top: calc(var(--rjr-mt-base, 0px) + var(--rjr-space-sm)) !important; }
.rjr-mt-md      { margin-top: calc(var(--rjr-mt-base, 0px) + var(--rjr-space-md)) !important; }
.rjr-mt-lg      { margin-top: calc(var(--rjr-mt-base, 0px) + var(--rjr-space-lg)) !important; }
.rjr-mt-xl      { margin-top: calc(var(--rjr-mt-base, 0px) + var(--rjr-space-xl)) !important; }
.rjr-mt-less-xs { margin-top: calc(var(--rjr-mt-base, 0px) - var(--rjr-space-xs)) !important; }
.rjr-mt-less-sm { margin-top: calc(var(--rjr-mt-base, 0px) - var(--rjr-space-sm)) !important; }
.rjr-mt-less-md { margin-top: calc(var(--rjr-mt-base, 0px) - var(--rjr-space-md)) !important; }
.rjr-mt-less-lg { margin-top: calc(var(--rjr-mt-base, 0px) - var(--rjr-space-lg)) !important; }
.rjr-mt-less-xl { margin-top: calc(var(--rjr-mt-base, 0px) - var(--rjr-space-xl)) !important; }

/* --- margin-bottom ------------------------------------------------------- */
.rjr-mb-none { margin-bottom: 0 !important; }
.rjr-mb-xs      { margin-bottom: calc(var(--rjr-mb-base, 0px) + var(--rjr-space-xs)) !important; }
.rjr-mb-sm      { margin-bottom: calc(var(--rjr-mb-base, 0px) + var(--rjr-space-sm)) !important; }
.rjr-mb-md      { margin-bottom: calc(var(--rjr-mb-base, 0px) + var(--rjr-space-md)) !important; }
.rjr-mb-lg      { margin-bottom: calc(var(--rjr-mb-base, 0px) + var(--rjr-space-lg)) !important; }
.rjr-mb-xl      { margin-bottom: calc(var(--rjr-mb-base, 0px) + var(--rjr-space-xl)) !important; }
.rjr-mb-less-xs { margin-bottom: calc(var(--rjr-mb-base, 0px) - var(--rjr-space-xs)) !important; }
.rjr-mb-less-sm { margin-bottom: calc(var(--rjr-mb-base, 0px) - var(--rjr-space-sm)) !important; }
.rjr-mb-less-md { margin-bottom: calc(var(--rjr-mb-base, 0px) - var(--rjr-space-md)) !important; }
.rjr-mb-less-lg { margin-bottom: calc(var(--rjr-mb-base, 0px) - var(--rjr-space-lg)) !important; }
.rjr-mb-less-xl { margin-bottom: calc(var(--rjr-mb-base, 0px) - var(--rjr-space-xl)) !important; }


/* ==========================================================================
   19  PLACEHOLDER MARKER
   --------------------------------------------------------------------------
   Copy that must not reach a live page. The brief's hardest rule is that no
   invented reader quote or invented biography ever ships, and "a marked
   placeholder is always better than a plausible guess" - so placeholder text
   is deliberately made impossible to mistake for finished copy in review.

   Delete this rule and every marker becomes visible as a normal paragraph,
   which is exactly the failure mode it exists to prevent - so remove the
   MARKUP, not this rule.
   ========================================================================== */

.rjr-placeholder {
  outline: 1px dashed rgba(196, 90, 20, 0.55);
  outline-offset: 3px;
  background: rgba(244, 131, 31, 0.07);
  font-style: italic !important;
}
