/*
Theme Name: Roastberry (Build)
Description: Specialty coffee storefront for Roastberry, Lisbon. WooCommerce-driven catalog with category navigation, quick view and per-lot brewing data.
Author: Roastberry
Version: 4.0.0
Requires at least: 6.4
Requires PHP: 8.0
Text Domain: roastberry
WC requires at least: 8.0
*/

/* ---------------------------------------------------------------
   0. Fonts — self-hosted

   Alexandria is a variable font: one file per subset covers 300–700.
   IBM Plex Mono ships static instances, so 400 and 500 are separate files.
   Served from the theme rather than fonts.gstatic.com, so no visitor IP
   leaves the site to load type.

   These rules are the only place the browser learns the fonts exist, and it
   cannot read them until it has fetched and parsed this file — so with
   font-display: swap the first paint is drawn in whatever is installed. On a
   phone that window is long enough to read, which is how a site set in the
   right type got reported as being set in the wrong one (review 2026-09-19).

   Two answers to that, and they are different answers. Shorten the window:
   rb_preload_fonts() in inc/rb-setup.php asks for the two Latin faces from
   wp_head, before this file has been parsed. And hold the frame still while it
   lasts: 'Alexandria Fallback' below is the installed grotesque scaled to
   Alexandria's metrics, so the text that appears first occupies the space the
   text that replaces it will need.

   A browser without size-adjust (before Chrome 92, Firefox 92, Safari 17)
   ignores those four descriptors and matches the face unscaled — which is what
   every browser did before this was added, so the old behaviour is the floor,
   not a break.
   --------------------------------------------------------------- */

@font-face {
  font-family: 'Alexandria';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('assets/fonts/alexandria-var-arabic.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
@font-face {
  font-family: 'Alexandria';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('assets/fonts/alexandria-var-latin-ext.woff2') format('woff2');
  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: 'Alexandria';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('assets/fonts/alexandria-var-latin.woff2') format('woff2');
  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: 'Alexandria';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('assets/fonts/alexandria-var-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* The stand-in, for the moment before Alexandria arrives.

   Alexandria is 8.31% wider than Arial for the same string at the same size,
   and the fallback frame therefore rewraps when the real font lands: measured
   over eight of this shop's coffee names at five card widths, 6 of 40 changed
   line count — a card's name on one line and then on two, which is a jump you
   see. This face is the installed grotesque, scaled to occupy Alexandria's
   space, so most of that stops moving: the same 40 cases give 2.

   Two, not none, and it cannot be none: size-adjust matches the average
   advance, not each glyph's. It is a smaller wrong than 6.

   The numbers are measured, not guessed — canvas TextMetrics on both faces at
   1000px (advance 17339.055 vs 16008.301 → 108.31%; Alexandria's ascent 0.968
   and descent 0.251 em, each divided by that factor for the overrides). To
   re-measure after a font change, do the same and put the three numbers here.

   local() only: no download, so this costs nothing and, where none of the three
   is installed, the face simply does not match and the stack falls through to
   the next family exactly as it did before. Arial and Liberation Sans are metric
   twins, so the adjustment is exact on Windows, macOS and most Linux; Android
   has Roboto, a little narrower, where it is close rather than exact.

   IBM Plex Mono gets no such stand-in on purpose: it sets short labels that do
   not wrap, so a width difference there moves nothing. */
@font-face {
  font-family: 'Alexandria Fallback';
  src: local('Arial'), local('Liberation Sans'), local('Roboto');
  size-adjust: 108.31%;
  ascent-override: 89.37%;
  descent-override: 23.17%;
  line-gap-override: 0%;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-400-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-400-greek.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-400-latin-ext.woff2') format('woff2');
  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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-400-latin.woff2') format('woff2');
  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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-400-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-500-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-500-greek.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-500-latin-ext.woff2') format('woff2');
  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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-500-latin.woff2') format('woff2');
  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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-500-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

/* ---------------------------------------------------------------
   1. Tokens
   --------------------------------------------------------------- */

:root {
  /* The widest a catalog page grows on a wide monitor (2026-09-23). */
  --rb-page-max: 1440px;
  --rb-ink: #141414;
  --rb-paper: #FDFDFD;
  --rb-body: #3A3A3A;
  --rb-grey: #6E6E6E;
  --rb-muted: #8A8A8A;
  --rb-label-ink: #5F5F5F; /* the concept's small-caps labels on product and brew screens */
  --rb-line: #DADADA;
  --rb-rule: #E6E6E6;
  --rb-wash: #F2F2F2;
  --rb-wash-2: #F7F7F7;

  --rb-lime: #A2E144;
  --rb-berry: #B659F5;
  --rb-caramel: #D7873D;
  --rb-cherry: #F84B4D;
  /* Modern has no single colour: its bags stand on an iridescent ground, so its
     dot and pill are a soft version of that gradient. Ink text still reads on it.
     The stops are sampled from the swatch the roastery sent on 2026-09-19 —
     dusty rose into lilac, aqua, blue, and back to periwinkle. To re-sample from
     a new swatch: render this gradient at the image's width beside it and compare
     across it. These stops hold every channel within 11 of 255 of the original,
     which is as close as a linear gradient gets to the mesh one they drew.
     Review 2026-09-21: "a tad brighter, same colours". Same stops at the same
     positions, each hue unchanged, saturation raised by about a fifth and
     lightness by 1.5%. Ink text on the darkest stop is still 6.5:1.
     Same day, second round: "more colourful — more orange and green", the two
     the Modern bag's own ground has and the swatch did not. The dusty rose and
     lilac third became a peach-orange into a soft green; the blue half is as it
     was, so the darkest stop, and the contrast on it, did not move. */
  --rb-modern: linear-gradient(120deg, #FFA36E 0%, #FFC48A 14%, #D8EA7E 28%, #A4ECB4 38%, #9FE8FF 48%, #88CAFF 58%, #74A7FF 72%, #8F8EFF 86%, #B3AEFC 100%);
  /* One colour out of that gradient's blue half, for the places a gradient cannot
     go: --rb-accent is used as a text colour as well as a background, and
     `color: linear-gradient(…)` is simply invalid. The periwinkle rather than the
     aqua, because as text on paper the aqua is barely there. See
     rb_flavour_accents(). */
  --rb-modern-solid: #8795F7;
  /* The rotating accent. rb_accent() overrides this in wp_head, one step of the
     palette per page, the way the concept advances it per navigation — except on
     a flavour group's own pages, which wear the group's colour and do not rotate
     (rb_fixed_accent()). The value here is the fallback if that <style> never
     lands. */
  --rb-accent: var(--rb-lime);

  /* Alexandria, then the stand-in sized to its metrics (§0), then the ordinary
     chain for a browser too old for size-adjust or a machine with none of its
     three locals installed. */
  --rb-sans: Alexandria, "Alexandria Fallback", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --rb-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --rb-gutter: 40px;
  --rb-header-h: 82px; /* 17px + a 48px logo + 17px. Every sticky offset reads this. */
  --rb-radius: 999px;
}

@media (max-width: 900px) {
  :root { --rb-gutter: 20px; }
}

/* ---------------------------------------------------------------
   2. Reset and base
   --------------------------------------------------------------- */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--rb-paper);
  color: var(--rb-ink);
  font-family: var(--rb-sans);
  font-size: 15px;
  font-weight: 300;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

a { color: var(--rb-ink); text-decoration: none; }
a:hover { color: var(--rb-accent); }

::selection { background: var(--rb-accent); color: var(--rb-ink); }

h1, h2, h3, h4, p, ul, ol, dl, dd, figure { margin: 0; }

button { font-family: inherit; }

/* Ink, at the concept's 3px offset — the ring stays one colour while the accent
   moves, so it reads as the same affordance on every page. Fields sit tighter,
   because a 3px ring around a bordered box double-outlines it. */
:where(a, button, input, textarea, select):focus-visible {
  outline: 2px solid var(--rb-ink);
  outline-offset: 3px;
}
input:focus-visible,
textarea:focus-visible,
select:focus-visible { outline-offset: 1px; }

.rb-skip-link {
  position: absolute;
  left: -9999px;
}
.rb-skip-link:focus {
  left: var(--rb-gutter);
  top: 12px;
  z-index: 200;
  background: var(--rb-ink);
  color: var(--rb-paper);
  padding: 10px 18px;
  border-radius: var(--rb-radius);
}

.screen-reader-text {
  position: absolute !important;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(1px, 1px, 1px, 1px);
  white-space: nowrap;
}

/* ---------------------------------------------------------------
   3. Type utilities
   --------------------------------------------------------------- */

/* Small uppercase label used for eyebrows, meta rows and nav */
.rb-label {
  font-family: var(--rb-sans);
  font-weight: 400;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rb-muted);
}
.rb-label--nav {
  font-size: 11px;
  letter-spacing: 0.12em;
  color: inherit;
}
.rb-label--tight {
  font-size: 11px;
  letter-spacing: 0.1em;
}

.rb-display {
  font-weight: 500;
  font-size: clamp(30px, 4.2vw, 42px);
  line-height: 1.05;
  letter-spacing: -0.035em;
  text-wrap: pretty;
}
.rb-display--hero {
  font-size: clamp(38px, 4.6vw, 66px);
  line-height: 1.02;
}

.rb-lede {
  font-size: clamp(18px, 2vw, 22px);
  font-weight: 300;
  line-height: 1.4;
  letter-spacing: -0.02em;
  text-wrap: pretty;
}

.rb-prose {
  font-size: 13px;
  line-height: 1.7;
  color: var(--rb-body);
  max-width: 58ch;
}
.rb-prose p + p { margin-top: 14px; }
.rb-prose--mono { font-family: var(--rb-mono); }

/* Narrative copy — the product description and the lot story — reads as
   prose, so it takes Alexandria and a slightly larger, looser setting.
   Numbers to follow (brew figures, steps, timeline) are IBM Plex Mono via
   .rb-figure, .rb-steps and .rb-timeline. */
.rb-prose--sans {
  font-family: var(--rb-sans);
  font-size: 14px;
  font-weight: 300;
  line-height: 1.75;
}
.rb-prose--sans p + p { margin-top: 16px; }

.rb-figure {
  font-family: var(--rb-mono);
  font-size: 18px;
  letter-spacing: -0.01em;
}

/* ---------------------------------------------------------------
   4. Buttons, chips, pills
   --------------------------------------------------------------- */

.rb-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--rb-sans);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 15px 30px;
  border: 0;
  border-radius: var(--rb-radius);
  background: var(--rb-ink);
  color: var(--rb-paper);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.rb-btn:hover { background: var(--rb-accent); color: var(--rb-ink); }

.rb-btn--paper { background: var(--rb-paper); color: var(--rb-ink); }
.rb-btn--paper:hover { background: var(--rb-accent); color: var(--rb-ink); }

/* Filled with the page's accent — see rb_accent(), which steps the palette one
   place per page. Ink text on all four: they are light enough to carry it,
   which is the point of the palette. */
.rb-btn--accent {
  background: var(--rb-accent);
  color: var(--rb-ink);
  padding: 18px 44px;
}
.rb-btn--accent:hover { background: var(--rb-ink); color: var(--rb-paper); }

.rb-btn--ghost {
  background: none;
  color: var(--rb-ink);
  border: 1px solid var(--rb-line);
  padding: 14px 26px;
}
.rb-btn--ghost:hover { background: var(--rb-accent); border-color: var(--rb-accent); color: var(--rb-ink); }

.rb-btn--small { padding: 9px 16px; font-size: 11px; letter-spacing: 0.12em; }

/* Every hover above is undone on a touch screen — in §15, at the end of this
   file, together with the chips, pills, tags and links that had the same bug. */
.rb-btn--block { width: 100%; }
.rb-btn[disabled], .rb-btn.is-busy { opacity: 0.55; pointer-events: none; }

.rb-link-underline {
  display: inline-block;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-bottom: 1px solid currentColor;
  padding-bottom: 3px;
}

/* Filter chips and option buttons.
   .rb-product-option / .is-selected are kept from the existing theme. */
.rb-chip,
.rb-product-option {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--rb-sans);
  font-weight: 400;
  font-size: 12px;
  line-height: 1;
  padding: 11px 20px;
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  background: transparent;
  color: var(--rb-ink);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.rb-chip:hover,
.rb-product-option:hover { border-color: var(--rb-ink); color: var(--rb-ink); }

.rb-chip.is-selected,
.rb-chip[aria-current="true"],
.rb-product-option.is-selected {
  background: var(--rb-ink);
  border-color: var(--rb-ink);
  color: var(--rb-paper);
}

/* Radio-backed options (enquiry forms) select themselves, no JS needed */
.rb-product-option:has(input:checked) {
  background: var(--rb-ink);
  border-color: var(--rb-ink);
  color: var(--rb-paper);
}
.rb-product-option:has(input:focus-visible) {
  outline: 2px solid var(--rb-ink);
  outline-offset: 3px;
}

.rb-note-dot {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--rb-ink);
}
/* The dot takes its colour from an ancestor carrying the note key — a menu link,
   a catalog link — or from the key on the dot itself, as on the catalog title. */
[data-rb-note="floral"] .rb-note-dot,   .rb-note-dot[data-rb-note="floral"]   { background: var(--rb-lime); }
[data-rb-note="berry"] .rb-note-dot,    .rb-note-dot[data-rb-note="berry"]    { background: var(--rb-berry); }
[data-rb-note="chocolaty"] .rb-note-dot,.rb-note-dot[data-rb-note="chocolaty"]{ background: var(--rb-caramel); }
[data-rb-note="fruity"] .rb-note-dot,   .rb-note-dot[data-rb-note="fruity"]   { background: var(--rb-cherry); }
[data-rb-note="modern"] .rb-note-dot,   .rb-note-dot[data-rb-note="modern"]   { background: var(--rb-modern); }

.rb-tag {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 8px 15px;
  border-radius: var(--rb-radius);
  background: var(--rb-wash);
  color: var(--rb-ink);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.rb-tag[data-rb-note="floral"] { background: var(--rb-lime); }
.rb-tag[data-rb-note="berry"] { background: var(--rb-berry); }
.rb-tag[data-rb-note="chocolaty"] { background: var(--rb-caramel); }
.rb-tag[data-rb-note="fruity"] { background: var(--rb-cherry); }
.rb-tag[data-rb-note="modern"] { background: var(--rb-modern); }
/* In quick view the tag is a link into its flavour group: the colour is the
   information, so the hover draws a ring rather than recolouring the text. */
a.rb-tag:hover { color: var(--rb-ink); box-shadow: inset 0 0 0 1px var(--rb-ink); }
/* Two of them fit across a catalog card at two columns on a phone — because the
   flavour one says the group in a single word there (rb_flavour_short_label()),
   not because this size makes a sentence fit. Same colours and the same links as
   the full-size tag, one register down. */
.rb-tag--small {
  gap: 6px;
  padding: 5px 10px;
  font-size: 10px;
  letter-spacing: 0.08em;
}

/* The outline pill: paper inside, a ring in a flavour group's colour. The group's
   name at the head of its page, and the descriptors off the bag on a coffee's
   own page (review 2026-09-21). The ring is a border drawn by the background —
   paper clipped to the padding box over the colour on the border box — because
   Modern's colour is a gradient, and border-color cannot be one. 3px, and 5px
   at headline size, since the second round of that day: "bolder". */
.rb-outline-pill {
  --rb-ring: var(--rb-ink);
  display: inline-flex;
  align-items: center;
  padding: 7px 16px;
  border: 3px solid transparent;
  border-radius: var(--rb-radius);
  background: linear-gradient(var(--rb-paper), var(--rb-paper)) padding-box, var(--rb-ring) border-box;
  color: var(--rb-ink);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.25;
  white-space: nowrap;
}
.rb-outline-pill[data-rb-note="floral"]    { --rb-ring: var(--rb-lime); }
.rb-outline-pill[data-rb-note="berry"]     { --rb-ring: var(--rb-berry); }
.rb-outline-pill[data-rb-note="chocolaty"] { --rb-ring: var(--rb-caramel); }
.rb-outline-pill[data-rb-note="fruity"]    { --rb-ring: var(--rb-cherry); }
.rb-outline-pill[data-rb-note="modern"]    { --rb-ring: var(--rb-modern); }
/* At headline size the ring is thicker. The type is the headline's face a step
   smaller than a plain headline, and on one line where there is room: at full
   size a two-word Portuguese name ("Frutados e ousados") broke over two lines
   inside a ring half the width of the column (third round of 2026-09-21). It
   still wraps inside the pill, not out of it, on a narrow screen. */
.rb-outline-pill--title {
  border-width: 5px;
  padding: 0.12em 0.6em 0.16em;
  font: inherit;
  font-size: clamp(24px, 2.6vw, 32px);
  line-height: 1.1;
  white-space: normal;
}
@media (min-width: 901px) {
  .rb-outline-pill--title { white-space: nowrap; }
}

/* ---------------------------------------------------------------
   5. Form controls
   --------------------------------------------------------------- */

.rb-field,
.woocommerce form .form-row input.input-text,
.woocommerce form .form-row textarea,
.woocommerce form .form-row select,
.woocommerce-page form .form-row input.input-text,
.woocommerce-page form .form-row textarea {
  font-family: var(--rb-sans);
  font-weight: 300;
  font-size: 13px;
  line-height: 1.4;
  padding: 13px 16px;
  width: 100%;
  color: var(--rb-ink);
  background: var(--rb-paper);
  border: 1px solid var(--rb-line);
  border-radius: 4px;
}
.rb-field::placeholder { color: var(--rb-muted); }
.rb-field:focus { outline: none; border-color: var(--rb-ink); }
textarea.rb-field { resize: vertical; }

/* A labelled group of chips in a form. The fieldset stays a block — a <legend>
   is not a flex item, and a flex fieldset can lay its first row over the top of
   it — and the legend keeps a line of its own above the row, rather than
   sitting 3px off the field before it (review 2026-09-19). */
.rb-fieldset {
  border: 0;
  padding: 0;
  margin: 0 0 22px;
  /* Fieldsets carry an intrinsic minimum width; without this one a long chip
     row would push the form wider than its column instead of wrapping. */
  min-width: 0;
}
.rb-fieldset > legend { padding: 0; margin-bottom: 12px; }

.rb-field-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.rb-field-stack {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
@media (max-width: 700px) {
  .rb-field-grid { grid-template-columns: 1fr; }
}

.rb-qty {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
}
.rb-qty button {
  background: none;
  border: 0;
  padding: 12px 16px;
  font-size: 15px;
  line-height: 1;
  color: var(--rb-ink);
  cursor: pointer;
}
/* An explicit width, not only a minimum. A bare <input type="number"> with no
   width is as wide as a text field — about 170px in Chrome — which made the
   stepper 255px long: in the quick view it ran the width of the panel next to
   the price, and on a catalog card it pushed Add to cart out of the card
   (2026-09-19). The WooCommerce-shaped steppers in the cart and on the product
   page never showed it: their inputs carry WooCommerce's own .qty class and are
   sized further down (§10 and §13), so three of the five looked right. */
.rb-qty output,
.rb-qty input {
  width: 34px;
  min-width: 0;
  text-align: center;
  font-size: 14px;
  border: 0;
  background: none;
  color: var(--rb-ink);
  -moz-appearance: textfield;
}
.rb-qty input::-webkit-outer-spin-button,
.rb-qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ---------------------------------------------------------------
   6. Layout scaffolding
   --------------------------------------------------------------- */

.rb-pad { padding-inline: var(--rb-gutter); }
.rb-section { padding: 56px var(--rb-gutter) 72px; }

.rb-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.rb-split--wide { grid-template-columns: 1.4fr 1fr; }
/* One column, when the second half has nothing to show — a page with no
   featured image should not render half a screen of empty grey. */
.rb-split--single { grid-template-columns: 1fr; }
.rb-split > * + * { border-left: 1px solid var(--rb-rule); }

/* Square image that holds its place while the column beside it scrolls.
   Declared here rather than inline: the inline version set position twice,
   sticky then relative, and relative won — so it never stuck. */
.rb-sticky-square {
  display: block;
  align-self: start;
  position: sticky;
  top: var(--rb-header-h);
  aspect-ratio: 1 / 1;
  background: var(--rb-wash);
  overflow: hidden;
}
.rb-sticky-square img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Subscription: one photograph per plan, stacked and swapped by the coffee
   selector. All of them are absolutely positioned so the square keeps its size
   whichever is showing. */
.rb-sub-photo {
  position: absolute;
  inset: 0;
  margin: 0;
}
.rb-sub-photo[hidden] { display: none; }
.rb-sub-photo__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 24px 26px;
  color: var(--rb-paper);
  background: linear-gradient(to top, rgba(24, 16, 10, 0.78) 0%, rgba(24, 16, 10, 0) 100%);
}
.rb-sub-photo__caption .rb-label { color: var(--rb-paper); opacity: 0.72; }
.rb-sub-photo__caption a { color: var(--rb-paper); }
.rb-sub-photo__caption a:hover { color: var(--rb-accent); }

/* Subscription: the per-delivery total, under the selectors and above the
   button, exactly where the concept puts it. */
.rb-sub-price {
  display: flex;
  align-items: center;
  gap: 18px;
  padding-top: 6px;
  flex-wrap: wrap;
}
.rb-sub-price__amount {
  font-size: 22px;
  font-weight: 400;
  line-height: 1.2;
}
.rb-sub-price__amount .woocommerce-Price-amount { font-size: inherit; font-weight: inherit; }
.rb-sub-price__note {
  font-family: var(--rb-sans);
  font-size: 13px;
  color: var(--rb-muted);
}

/* A pack row for a size that is not selected. */
.rb-row--chips[hidden] { display: none; }

/* Hairline grid: children sit on a rule-coloured bed */
.rb-grid-rules {
  display: grid;
  gap: 1px;
  background: var(--rb-rule);
}
.rb-grid-rules > * { background: var(--rb-paper); }
.rb-grid-rules--2 { grid-template-columns: 1fr 1fr; }
.rb-grid-rules--3 { grid-template-columns: repeat(3, 1fr); }

.rb-rule-top { border-top: 1px solid var(--rb-rule); }
.rb-rule-bottom { border-bottom: 1px solid var(--rb-rule); }

.rb-stack { display: flex; flex-direction: column; }
.rb-stack--6 { gap: 6px; }
.rb-stack--10 { gap: 10px; }
.rb-stack--14 { gap: 14px; }
.rb-stack--26 { gap: 26px; }
.rb-stack--40 { gap: 40px; }

.rb-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 18px;
}
.rb-row--chips { gap: 8px; }
.rb-row--between {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
}

@media (max-width: 900px) {
  .rb-split,
  .rb-split--wide,
  .rb-grid-rules--2,
  .rb-grid-rules--3 { grid-template-columns: 1fr; }
  .rb-split > * + * { border-left: 0; border-top: 1px solid var(--rb-rule); }
  .rb-section { padding: 40px var(--rb-gutter) 48px; }
}

/* ---------------------------------------------------------------
   7. Header and mega-menu
   --------------------------------------------------------------- */

.rb-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--rb-paper);
  border-bottom: 1px solid var(--rb-rule);
}

.rb-header__bar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
  padding: 17px 32px;
}

.rb-logo { display: block; width: fit-content; }
/* The height sets the size and the width follows the file's own proportions.
   max-width: none, because the global img rule let the grid squeeze the width
   on a phone while the height stayed put: a squashed wordmark. */
.rb-logo img,
.rb-logo svg { display: block; height: 48px; width: auto; max-width: none; }
/* The theme's own wordmark (rb_logo_svg()) is drawn in the text colour, and
   stays ink under a pointer rather than taking the link hover's accent. */
.rb-logo--mark,
.rb-logo--mark:hover,
.rb-footer__logo--mark,
.rb-footer__logo--mark:hover { color: var(--rb-ink); }

/* Concept v2 splits the two registers apart: the nav is a real 15px word set
   in caps, the utility rail stays the 11px tracked-out label. */
.rb-nav,
.rb-utility {
  display: flex;
  align-items: center;
  font-weight: 400;
  text-transform: uppercase;
}
.rb-nav {
  gap: 24px;
  font-size: 15px;
  letter-spacing: 0.04em;
}
.rb-utility {
  gap: 20px;
  font-size: 11px;
  letter-spacing: 0.12em;
  justify-content: flex-end;
  white-space: nowrap;
}

/* The page you are on is underlined. The rule is always there and only changes
   colour, so nothing shifts by 2px when it lights up. */
.rb-nav > a,
.rb-nav__toggle {
  padding-bottom: 4px;
  border-bottom: 2px solid transparent;
}
.rb-nav > a.is-current,
.rb-nav__toggle.is-current { border-bottom-color: var(--rb-ink); }

.rb-nav__toggle {
  display: flex;
  align-items: center;
  gap: 7px;
  font: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  background: none;
  border: 0;
  /* Put back what `border: 0; padding: 0` would otherwise strip from the shared
     rule above: without the 4px and the 2px rule, Shop's box was 6px shorter
     than its neighbours, centred lower, and could never show its underline. */
  border-bottom: 2px solid transparent;
  padding: 0 0 4px;
  color: inherit;
  cursor: pointer;
}
.rb-nav__toggle:hover { color: var(--rb-accent); }
.rb-caret {
  display: inline-block;
  width: 5px;
  height: 5px;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: rotate(45deg);
  margin-bottom: 3px;
  transition: transform 160ms ease;
}
.rb-nav__toggle[aria-expanded="true"] .rb-caret {
  transform: rotate(-135deg);
  margin-bottom: 0;
}

/* An open panel is paper laid on paper. Over the home page's photograph, or a
   lot's, it reads as a layer and you can see where it ends; over the catalog's
   white grid the roastery opened it on a phone and it merged with the page
   (review 2026-09-19). So it casts a shadow, which costs nothing where there
   was contrast already and is the whole of the panel's edge where there was
   not. The rule under it is the header's own border-bottom: the panel is
   inside the header. */
.rb-mega {
  border-top: 1px solid var(--rb-rule);
  background: var(--rb-paper);
  max-height: calc(100vh - var(--rb-header-h));
  overflow: auto;
  box-shadow: 0 18px 30px -22px rgba(20, 20, 20, 0.42);
}
.rb-mega[hidden] { display: none; }
.rb-mega__inner {
  display: grid;
  grid-template-columns: repeat(3, minmax(120px, 1fr)) minmax(150px, 220px);
  gap: 28px;
  padding: 40px var(--rb-gutter) 48px;
}
.rb-mega__col {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.rb-mega__col--feature {
  border-left: 1px solid var(--rb-rule);
  padding-left: 34px;
  gap: 13px;
}
.rb-mega__heading {
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--rb-muted);
  margin-bottom: 3px;
}
.rb-mega__heading + .rb-mega__heading,
.rb-mega__link + .rb-mega__heading { margin-top: 22px; }
.rb-mega__link {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  font-size: 17px;
  font-weight: 400;
  letter-spacing: -0.015em;
  white-space: nowrap;
}
.rb-mega__link .rb-note-dot { margin-top: 7px; }

.rb-mega__thumb {
  display: block;
  aspect-ratio: 1 / 1;
  background: var(--rb-wash);
  position: relative;
  overflow: hidden;
}
.rb-mega__thumb img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.rb-nav__burger { display: none; }

@media (max-width: 1080px) {
  .rb-header__bar { grid-template-columns: auto 1fr auto; gap: 16px; }
  .rb-nav {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
    padding: 24px var(--rb-gutter) 28px;
    background: var(--rb-paper);
    border-top: 1px solid var(--rb-rule);
    /* Nothing closes this one — it hangs off the header over the page — so it
       carries its own bottom edge as well as the shadow. */
    border-bottom: 1px solid var(--rb-line);
    box-shadow: 0 18px 30px -22px rgba(20, 20, 20, 0.42);
  }
  .rb-nav[hidden] { display: none; }
  .rb-nav__burger {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 34px;
    height: 34px;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
  }
  .rb-nav__burger span {
    display: block;
    height: 1px;
    background: var(--rb-ink);
  }
  .rb-mega__inner { grid-template-columns: 1fr 1fr; }
  .rb-mega__col--feature { display: none; }
}

@media (max-width: 640px) {
  /* A 48px logo is too much of a phone screen. Shrinking it changes the header
     height, so --rb-header-h moves with it and every sticky offset and
     100vh-minus-header follows. */
  :root { --rb-header-h: 70px; }
  /* The logo no longer gives way, so the bar does: the page gutter instead of
     32px, and closer gaps. Measured at 390px and 360px. */
  .rb-header__bar { gap: 12px; padding-inline: var(--rb-gutter); }
  .rb-logo img,
  .rb-logo svg { height: 36px; }
  .rb-mega__inner { grid-template-columns: 1fr; gap: 24px; }
  .rb-utility { gap: 14px; font-size: 10px; }
  .rb-nav { font-size: 14px; }
}

@media (max-width: 380px) {
  :root { --rb-header-h: 64px; }
  .rb-header__bar { gap: 10px; }
  .rb-logo img,
  .rb-logo svg { height: 30px; }
  .rb-utility { gap: 10px; }
  .rb-lang { gap: 6px; padding-right: 10px; }
}

/* Language switcher — PT / EN as text, never flags. Built by rb_language_switcher(),
   which reads qTranslate-XT (what this store runs), then Polylang, then WPML. */
.rb-lang {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-right: 12px;
  border-right: 1px solid var(--rb-rule);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.rb-lang a { color: var(--rb-body); }

/* Every item on the utility rail carries the same box as the language codes —
   3px and a 2px rule under the text — or Account and Cart sit 2.5px below PT/EN.
   The 1px on top lands the rail's baseline exactly on the 15px nav's across the
   bar: both groups are centred in it, and without it the 11px rail sat half a
   pixel high. Measured in Chrome, not estimated. */
.rb-lang a,
.rb-utility > a {
  padding: 1px 0 3px;
  border-bottom: 2px solid transparent;
}
.rb-lang a:hover { color: var(--rb-ink); }
/* The cart is a basket with its count beside it (review 2026-09-21). The icon
   sits on the rail's centre line, the figure on the label's baseline. */
.rb-cart-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.rb-utility > a.rb-cart-link { padding: 0; border-bottom: 0; }
.rb-cart-link__icon { display: block; flex: none; }
.rb-cart-link .cart-contents-count {
  min-width: 1ch;
  font-variant-numeric: tabular-nums;
}
.rb-lang .is-current {
  color: var(--rb-ink);
  font-weight: 500;
  border-bottom-color: var(--rb-ink);
}
/* The two codes are separated by the rule under the current one, not a slash. */
.rb-lang__sep { display: none; }

/* ---------------------------------------------------------------
   8. Home
   --------------------------------------------------------------- */

/* Concept v2 centres the hero: headline and CTA stacked in the lower middle
   rather than pinned to opposite corners. The ground is a warm roast brown, not
   the neutral wash, so the photograph has something to sit on while it loads. */
.rb-hero {
  position: relative;
  height: calc(100vh - var(--rb-header-h));
  min-height: 600px;
  background: #2A1F16;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
/* No featured image: no scrim renders, so the warm ground carries the paper-white
   title on its own. */
.rb-hero--flat { background: #2A1F16; }
.rb-hero__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.rb-hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(24,16,10,0.78) 0%, rgba(24,16,10,0.42) 55%, rgba(24,16,10,0.28) 100%);
}
.rb-hero__inner {
  position: relative;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 32px;
  /* Raised off the bottom edge (review 2026-09-21): a fifth of the hero's
     height, so it sits in the lower middle on a laptop and still clears the
     fold on a short one. */
  padding: 0 var(--rb-gutter) clamp(64px, 20vh, 200px);
}
.rb-hero__title {
  color: var(--rb-paper);
  max-width: 20ch;
}
.rb-hero__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
}
.rb-hero .rb-btn { flex: none; }

@media (max-width: 700px) {
  .rb-hero__inner { align-items: flex-start; text-align: left; gap: 24px; padding-bottom: clamp(48px, 14vh, 120px); }
  .rb-hero__title { max-width: none; }
  /* Two across and the last under both, filling the column edge to edge, so the
     group sits square under the left-set headline rather than trailing off to
     one side of it (second round of 2026-09-21: "not centred on mobile"). A
     lone last button — three here, or one if a Selection is missing — spans the
     row. */
  .rb-hero__actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; width: 100%; }
  .rb-hero__actions > :last-child:nth-child(odd) { grid-column: 1 / -1; }
  .rb-hero .rb-btn { justify-content: center; text-align: center; padding-inline: 12px; }
}

.rb-meta-strip {
  display: flex;
  justify-content: space-between;
  gap: 24px;
  padding: 22px var(--rb-gutter);
  border-bottom: 1px solid var(--rb-rule);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--rb-grey);
}
@media (max-width: 700px) {
  .rb-meta-strip { flex-direction: column; gap: 8px; }
}

/* ---------------------------------------------------------------
   9. Catalog
   --------------------------------------------------------------- */

/* The catalog is a standing sidebar of every way in, with the grid beside it.
   The sidebar sticks under the header so the routes stay reachable however far
   the grid scrolls. Below 900px it unsticks, stacks above the grid, and becomes
   a disclosure: the button (.rb-catalog__toggle) and the routes behind it
   (.rb-catalog__groups), so a flavour group opens on its name and its first
   card rather than on every way into the catalog. */
/* The routes column. Concept v2 draws it at 232px, which is a width measured
   against English: Chocolatey & Nutty is the longest label there at 139.5px and
   fits with room to spare. In Portuguese — the language this shop opens in —
   Frutos vermelhos e florais is 184.7px and Chocolate e frutos secos 175.9px,
   against 157px of text space once the 28px padding and the dot's 19px are
   taken out, so two of the five profiles wrapped onto a second line (review
   2026-09-20).

   276px, not the 260px that first went in. 260 was the widest label plus that
   furniture rounded up, which left it 0.3px of room — and a text measurement is
   not exact to a third of a pixel across platforms, so it still wrapped on the
   roastery's own screen while passing every measurement here. This is the same
   number with about 8% of headroom, which is what the variance between one
   renderer and another actually is. It costs 44px of grid — around 15px off
   each card in a three-column row.

   A label longer than either language has now still wraps rather than
   overflowing, which is the right failure. The probe in tools/local-wp/probes/
   filters.js measures the Portuguese five against this number and fails if the
   longest one comes within 8px of it. */
.rb-catalog { display: grid; grid-template-columns: 276px 1fr; }
/* Past this the cards stopped growing into bars on a wide monitor; the page
   stands in the middle with paper either side (2026-09-23). The rest of the
   site stops at the same width in §18. */
.rb-catalog { max-width: var(--rb-page-max); margin-inline: auto; }
.rb-catalog__aside {
  border-right: 1px solid var(--rb-rule);
  padding: 44px 28px 64px;
  position: sticky;
  top: var(--rb-header-h);
  align-self: start;
  display: flex;
  flex-direction: column;
  gap: 30px;
}
/* The routes themselves. They used to be the aside's own flex children; they
   sit in a box of their own now so the aside can also hold the button that
   opens them on a phone (review 2026-09-19). */
.rb-catalog__groups {
  display: flex;
  flex-direction: column;
  gap: 30px;
}
.rb-catalog__groups[hidden] { display: none; }
/* Above 900px there is nothing to disclose: the sidebar stands open. */
.rb-catalog__toggle { display: none; }
.rb-catalog__group { display: flex; flex-direction: column; gap: 11px; }
.rb-catalog__link {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  font-size: 15px;
  font-weight: 300;
  color: var(--rb-ink);
  transition: color 140ms ease;
}
.rb-catalog__link .rb-note-dot { flex: none; margin-top: 6px; }
/* The flavour profile's introduction, straight under the group's name, and the
   filter, the count and Sort under it (review 2026-09-22). */
.rb-catalog__intro { margin: 18px 0 0; max-width: 68ch; }
/* On a desktop the group's introduction is the page's lede: larger, and set
   from the group's name down its left edge, the line ending at the middle of
   the second photo below (review 2026-09-24; it was centred on the page from
   2026-09-21). With three cards a row that middle is the head's own middle, so
   50% of its content box; with two, three quarters of the head's full width,
   which is 75% of the content box and half a gutter. A phone keeps the column's
   left edge and its 68ch. */
@media (min-width: 901px) {
  .rb-catalog__intro {
    width: 50%;
    max-width: none;
    margin: 30px 0 0;
    font-size: 17px;
    line-height: 1.6;
    text-wrap: pretty;
  }
}
@media (min-width: 901px) and (max-width: 1180px) {
  .rb-catalog__intro { width: calc(75% + var(--rb-gutter) / 2); }
}
.rb-catalog__link.is-selected { font-weight: 500; }
.rb-catalog__link:hover { color: var(--rb-accent); }
.rb-catalog__main { min-width: 0; }

.rb-catalog__head { padding: 44px var(--rb-gutter) 0; }
.rb-catalog__title { display: flex; align-items: center; gap: 14px; }
.rb-note-dot--title { width: 14px; height: 14px; }
.rb-catalog__tools {
  display: flex;
  align-items: center;
  gap: 20px;
  white-space: nowrap;
}
.rb-sort { display: flex; align-items: center; gap: 10px; }
.rb-sort__select {
  font-family: inherit;
  font-size: 13px;
  font-weight: 300;
  color: var(--rb-ink);
  background: var(--rb-paper);
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  padding: 9px 14px;
  cursor: pointer;
}
/* The filter on the left, the count and Sort on the right, one line above the
   grid; without a filter the tools keep the right. */
.rb-catalog__bar {
  margin-top: 28px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px 24px;
}
.rb-catalog__bar .rb-catalog__tools { margin-left: auto; }
.rb-catalog__active {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
}
/* Clearing a filter is a link back to the unfiltered view, styled as the chip
   that is currently on — with the × that says what tapping it does. */
.rb-chip--clear {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  background: var(--rb-paper);
  border: 1px solid var(--rb-ink);
  color: var(--rb-ink);
}
.rb-chip--clear:hover { background: var(--rb-ink); color: var(--rb-paper); }
.rb-chip__x { font-size: 15px; line-height: 1; }


.rb-products {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--rb-rule);
  margin-top: 36px;
  border-top: 1px solid var(--rb-rule);
}
/* Team favourites: the months, as chips under the intro, and whose pick a coffee
   is, above its country on the card. */
.rb-team-months {
  margin-top: 22px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.rb-team-badge {
  align-self: flex-start;
  padding: 4px 10px;
  border-radius: var(--rb-radius);
  background: var(--rb-ink);
  color: var(--rb-paper);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* The rest of a short last row: paper, not the rule bed, spanning what the cards
   left — at three columns, and at two below 1180px. A remainder of 0 is printed
   as the data attribute and hides the cell — except inside a flavour group, where
   the cell is the way on to the next group and a full last row gets it as a band
   under the row instead (the inline span is then the column count). How big the
   way on is inside this cell, on a desktop, is set with the column count further
   down (after the 1180px breakpoint). */
.rb-products__rest {
  grid-column: span var(--rb-rest-3);
  display: flex;
  background: var(--rb-paper);
}
/* Three columns only. Unscoped, this also hid the cell at two columns, where a
   count that fills rows of three (Bestsellers' three) leaves one empty: the grey
   bed showed through (third round of 2026-09-21). */
@media (min-width: 1181px) {
  .rb-products__rest[data-rb-rest-3="0"]:not(.rb-products__rest--next) { display: none; }
}
/* Inside a flavour group it is the next group, as a pale gradient from this
   group's colour into its, the size of a card's photo in the photo's place at
   every width (review 2026-09-23; phones and tablets too since that evening).
   The flex, min-height, justify-content and padding here are the old cell's,
   as tall as the card beside it: the rules in §9 after the 1180px breakpoint
   override all four at every width, and they are kept only as the backup the
   roastery may ask for (session file of 2026-09-23, item 5). */
.rb-next-group {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 12px;
  min-height: 220px;
  padding: 26px 26px 30px;
  color: var(--rb-ink);
}
.rb-next-group .rb-label { color: var(--rb-ink); }
.rb-next-group__name {
  font-size: clamp(20px, 2.2vw, 28px);
  font-weight: 500;
  letter-spacing: -0.025em;
  line-height: 1.15;
}
/* The solid fallback, where the gradient is not drawn. It has to come before the
   gradient rule: after it, at the same specificity, it won (2026-09-23). */
.rb-next-group[data-rb-note="floral"]    { background: var(--rb-ng-lime); }
.rb-next-group[data-rb-note="berry"]     { background: var(--rb-ng-berry); }
.rb-next-group[data-rb-note="chocolaty"] { background: var(--rb-ng-caramel); }
.rb-next-group[data-rb-note="fruity"]    { background: var(--rb-ng-cherry); }
/* From the colour of the group you are in into the colour of the one it leads
   to (review 2026-09-22; kept, paler, 2026-09-23). --rb-grad-from and
   --rb-grad-to are stop lists: a group's colour twice, so it holds a band at its
   end rather than only touching the corner, and Modern three of its own. */
.rb-next-group[data-rb-from] { background: linear-gradient(120deg, var(--rb-grad-from), var(--rb-grad-to)); }
/* Each colour mixed into paper, to the strength the photos' own backgrounds
   have (review 2026-09-23: "the same intensity as on the product photo"). The
   four percentages are the quick view's grounds (.rb-quickview__media), so the
   two pale versions of a group's colour are one; Modern's three stops at 55%,
   set by eye against its photos. */
.rb-next-group {
  --rb-ng-lime:    color-mix(in srgb, var(--rb-lime) 45%, var(--rb-paper));
  --rb-ng-berry:   color-mix(in srgb, var(--rb-berry) 35%, var(--rb-paper));
  --rb-ng-caramel: color-mix(in srgb, var(--rb-caramel) 40%, var(--rb-paper));
  --rb-ng-cherry:  color-mix(in srgb, var(--rb-cherry) 35%, var(--rb-paper));
  --rb-ng-modern:  color-mix(in srgb, #FFA36E 55%, var(--rb-paper)), color-mix(in srgb, #9FE8FF 55%, var(--rb-paper)), color-mix(in srgb, #8F8EFF 55%, var(--rb-paper));
}
.rb-next-group[data-rb-from="floral"]    { --rb-grad-from: var(--rb-ng-lime), var(--rb-ng-lime); }
.rb-next-group[data-rb-from="berry"]     { --rb-grad-from: var(--rb-ng-berry), var(--rb-ng-berry); }
.rb-next-group[data-rb-from="chocolaty"] { --rb-grad-from: var(--rb-ng-caramel), var(--rb-ng-caramel); }
.rb-next-group[data-rb-from="fruity"]    { --rb-grad-from: var(--rb-ng-cherry), var(--rb-ng-cherry); }
.rb-next-group[data-rb-from="modern"]    { --rb-grad-from: var(--rb-ng-modern); }
.rb-next-group[data-rb-note="floral"]    { --rb-grad-to: var(--rb-ng-lime), var(--rb-ng-lime); }
.rb-next-group[data-rb-note="berry"]     { --rb-grad-to: var(--rb-ng-berry), var(--rb-ng-berry); }
.rb-next-group[data-rb-note="chocolaty"] { --rb-grad-to: var(--rb-ng-caramel), var(--rb-ng-caramel); }
.rb-next-group[data-rb-note="fruity"]    { --rb-grad-to: var(--rb-ng-cherry), var(--rb-ng-cherry); }
.rb-next-group[data-rb-note="modern"]    { --rb-grad-to: var(--rb-ng-modern); }
.rb-next-group:hover { color: var(--rb-ink); box-shadow: inset 0 0 0 1px var(--rb-ink); }

/* A column, because the grid stretches every card in a row to the tallest of
   them and the buttons have to land on one line across that row whatever the
   name above them did. The foot takes the leftover height and hands it to the
   gap above the buttons, which are pinned to the bottom of it at every width
   (.rb-product-card__actions, 2026-09-22). Until 2026-09-20 that was done
   accidentally, by a
   flex-basis meant for the horizontal layout being read as a height, and it
   held only while the name and the tags stayed under 150px. */
.rb-product-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--rb-paper);
  padding: 26px 26px 30px;
}
.rb-product-card__media {
  /* Never squeezed by the column above: the photograph keeps its square. */
  flex: none;
  /* An <a>, so it must be blockified: aspect-ratio is ignored on an inline box,
     and the absolutely-positioned img inside then sizes to an empty inline
     containing block — thumbnails present in the markup, nothing on screen. */
  display: block;
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--rb-wash);
  overflow: hidden;
}
.rb-product-card__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 400ms ease;
  /* The table under the bag fades into the card, from 82% of the photo's
     height: the owner's point between too much over the image and none
     (review 2026-09-22). */
  -webkit-mask-image: linear-gradient(to bottom, #000 82%, transparent);
  mask-image: linear-gradient(to bottom, #000 82%, transparent);
}
.rb-product-card:hover .rb-product-card__media img { transform: scale(1.03); }
/* Grey only as the stand-in for a missing photo: under a photo it would show
   through the fade. */
.rb-product-card__media:has(img) { background: var(--rb-paper); }

/* Sold out. The lot stays in the grid — what a roaster has poured through the
   year is part of the shop — but it steps back: the photograph goes quiet and
   the pill says why. The pill sits on the card, not inside the media link,
   which is aria-hidden. */
.rb-stock-pill {
  position: absolute;
  top: 26px;
  left: 26px;
  z-index: 1;
  padding: 6px 12px;
  background: var(--rb-ink);
  color: var(--rb-paper);
  font-family: var(--rb-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.rb-product-card.is-soldout .rb-product-card__media img { opacity: 0.42; }
.rb-product-card.is-soldout:hover .rb-product-card__media img { transform: none; }
.rb-product-card.is-soldout .rb-product-card__price { opacity: 0.55; }
/* Quick view and Add to cart, as the concept's card carries both — laid out
   one way at every width (2026-09-22: "make button positioning and alignment
   more uniform"). They used to sit beside the name on a wide card, right-
   aligned under it on a narrower one, and stacked across the card on a phone:
   three layouts of the same two buttons. Now always the phone's: under the
   name, the width of the card, on its bottom edge — so a row of cards has its
   buttons on one line too — with Quick view first and the buy row under it. */
.rb-product-card__actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  margin-top: auto;
}
/* The width of the photo above them at every size, so both edges line up. They
   were capped at 320px after "too wide on PC?" (review 2026-09-22), which on a
   wide monitor left them ending under nothing (2026-09-23); the catalog's own
   cap (.rb-catalog, --rb-page-max) now keeps the card from growing instead. */
/* How many, then Add to cart, on one line; the button takes what the stepper
   leaves. The stepper starts at one, so the common case is still a single tap
   (review 2026-09-19). Where the two do not fit — a card at half a phone is
   about 160px inside — the button wraps under, and the stepper, which grows
   only once it is alone on its line (1 against the button's 999), spans the
   card with − and + at its ends: still two rows edge to edge, not a small pill
   stranded on the left. */
.rb-product-card__buy {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
/* The button's floor, at every width: it wraps rather than hanging over the
   card's edge. A card at half a 360px phone is 152px and “Adicionar ao
   carrinho” sets at 208px, so a nowrap put the black pill of every Portuguese
   card across the rule and into the next column (review 2026-09-20). The
   card's own label is a short word now; this is for whatever is translated
   next. */
.rb-product-card__buy .rb-btn { flex: 999 1 auto; white-space: normal; overflow-wrap: anywhere; }

/* There is deliberately no .is-in-cart rule. The state is a label, not a
   colour: the button is ink whether the cart holds this bag or not, because a
   button that changed colour under the thumb is the thing the roastery asked us
   to stop doing (2026-09-19, and see @media (hover: none) in §15). The class is
   a state hook only — product-card.php prints it on load, main.js adds it after
   an add, and the probes read it. */

/* The stepper, sized down for a card. */
.rb-qty--card { flex: 1 1 auto; justify-content: space-between; }
.rb-qty--card button { padding: 7px 11px; font-size: 14px; }
.rb-qty--card input { width: 30px; font-size: 13px; }
/* The sticker, then the buttons under it, the foot taking the card's leftover
   height so the buttons (margin-top: auto) land on its bottom edge — the same
   line for every card in a row, because the grid stretches them all. */
.rb-product-card__foot {
  display: flex;
  flex-direction: column;
  gap: 18px;
  margin-top: 20px;
  flex: 1 1 auto;
  min-width: 0;
}

/* The bag's sticker, in the order the roastery reads it (review 2026-09-19):
   the country, the lot, the two tags that file it, then the price. Four lines
   where there used to be two, so they are a column with one rhythm rather than
   four margins. */
.rb-product-card__lot {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 7px;
}
/* Small, tracked out and in the mono face — the register the labels on the bag
   are set in, and quiet enough that the lot stays the card's heading. */
.rb-product-card__origin {
  font-family: var(--rb-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rb-grey);
}
.rb-product-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.rb-product-card__name {
  font-size: 17px;
  font-weight: 500;
  letter-spacing: -0.015em;
}
.rb-product-card__price {
  font-size: 12px;
  color: var(--rb-grey);
}
.rb-product-card__price del { color: var(--rb-muted); margin-right: 6px; }
.rb-product-card__price ins { text-decoration: none; }

.rb-catalog__more { padding: 56px var(--rb-gutter) 88px; text-align: center; }

.rb-empty {
  padding: 80px var(--rb-gutter) 100px;
  text-align: center;
  color: var(--rb-grey);
}

@media (max-width: 1180px) {
  .rb-products { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rb-products__rest { grid-column: span var(--rb-rest-2); display: flex; }
  .rb-products__rest[data-rb-rest-2="0"]:not(.rb-products__rest--next) { display: none; }
}
/* The next-group cell reads from its top left corner, with the invitation one
   photo wide (review 2026-09-22), at every width since the evening of 2026-09-23: until then a
   phone and a tablet under 901px kept the older cell, as tall as the card beside
   it, though the grid there is two columns just as it is at 901-1180px.
   --rb-col is one column of the grid, measured off the grid itself (a container,
   so 100cqi is its width), and --rb-col-text a photo's width in it: the column
   less the card's side padding, --rb-card-pad, twice. --rb-card-pad and
   --rb-card-pad-top follow the card's own padding at each size (the card rules
   in the 900px and 640px blocks below set them beside it). The rules after this
   size the cell from them. */
.rb-products {
  container-type: inline-size;
  --rb-card-pad: 26px;
  --rb-card-pad-top: 26px;
}
.rb-next-group,
.rb-products__rest[data-rb-rest-3="0"] .rb-next-group,
.rb-products__rest[data-rb-rest-2="0"] .rb-next-group {
  --rb-col: calc((100cqi - 2px) / 3);
  --rb-col-text: calc(var(--rb-col) - 2 * var(--rb-card-pad));
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: flex-start;
  justify-content: flex-start;
  gap: 0.5em;
  padding: var(--rb-card-pad);
}
.rb-next-group .rb-label,
.rb-next-group__name { max-width: var(--rb-col-text); }
.rb-next-group .rb-label {
  /* A sixteenth of the photo, and never under the site's smallest label: on a
     360px phone a sixteenth is 9.5px. */
  font-size: max(11px, var(--rb-col-text) / 16);
  font-weight: 400;
  letter-spacing: -0.01em;
  line-height: 1.25;
  text-transform: none;
}
.rb-next-group__name {
  font-size: calc(var(--rb-col-text) / 7.5);
  line-height: 1.05;
  letter-spacing: -0.035em;
  text-wrap: balance;
}
@media (max-width: 1180px) {
  .rb-next-group,
  .rb-products__rest[data-rb-rest-2="0"] .rb-next-group { --rb-col: calc((100cqi - 1px) / 2); }
}
/* The size of a card's photograph, where the photograph would be (review
   2026-09-23): the rest of the row keeps a card's padding and the cell is one
   photo square (--rb-col-text) in its top left. So one coffee in a row of three
   puts it in position 2 with position 3 plain paper, and two put it in position
   3; at two columns, one coffee puts it in position 2. Under a full row, a band
   half a photo tall from the first photo's left edge, --rb-band-cols positions
   long: 2, chosen over 3 from screenshots. At two columns the band runs across
   both.
   Kept as a backup, if the roastery asks: the cell as tall as the card beside
   it (the photo's width, top of the photo to the Add to cart line) is
   align-items: stretch on .rb-products__rest--next and aspect-ratio: auto on
   .rb-next-group, before the band rules below. The exact rule is in
   docs/feedback/2026-09-23-session-of-23-september.md, item 5. */
.rb-products__rest--next {
  padding: var(--rb-card-pad-top) var(--rb-card-pad) 30px;
  align-items: flex-start;
}
.rb-next-group,
.rb-products__rest[data-rb-rest-3="0"] .rb-next-group,
.rb-products__rest[data-rb-rest-2="0"] .rb-next-group {
  display: flex;
  flex: none;
  width: var(--rb-col-text);
  aspect-ratio: 1 / 1;
  min-height: 0;
  overflow: hidden;
}
@media (min-width: 1181px) {
  .rb-products__rest[data-rb-rest-3="0"] .rb-next-group {
    --rb-band-cols: 2;
    width: calc(var(--rb-band-cols) * var(--rb-col) + var(--rb-band-cols) * 1px - 1px - 2 * var(--rb-card-pad));
    height: calc(var(--rb-col-text) / 2);
    display: flex;
    aspect-ratio: auto;
  }
  /* Both lines run the band's length: held to a photo's width, the Portuguese
     label wrapped, and the name's foot was cut off by the overflow (audit
     2026-09-23). */
  .rb-products__rest[data-rb-rest-3="0"] .rb-next-group .rb-label,
  .rb-products__rest[data-rb-rest-3="0"] .rb-next-group__name { max-width: none; }
}
@media (max-width: 1180px) {
  .rb-products__rest[data-rb-rest-2="0"] .rb-next-group {
    width: calc(100cqi - 2 * var(--rb-card-pad));
    height: calc(var(--rb-col-text) / 2);
    display: flex;
    aspect-ratio: auto;
  }
  /* Both lines the band's length here too (audit 2026-09-23, see above). */
  .rb-products__rest[data-rb-rest-2="0"] .rb-next-group .rb-label,
  .rb-products__rest[data-rb-rest-2="0"] .rb-next-group__name { max-width: none; }
}
@media (max-width: 900px) {
  .rb-catalog { grid-template-columns: 1fr; }
  .rb-catalog__aside {
    position: static;
    border-right: 0;
    border-bottom: 1px solid var(--rb-rule);
    padding: 0 var(--rb-gutter) 0;
    gap: 0;
  }
  /* Stacked above the grid, the whole sidebar was the first thing a visitor
     met on opening a flavour group. Here it is a disclosure: a full-width row
     that reads like the product page's tabs, and the routes behind it. */
  .rb-catalog__toggle {
    display: flex;
    width: 100%;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    padding: 18px 0;
    background: none;
    border: 0;
    cursor: pointer;
    color: inherit;
    text-align: left;
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 0.12em;
    text-transform: uppercase;
  }
  .rb-catalog__toggle[hidden] { display: none; }
  .rb-catalog__groups {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 28px 40px;
    padding-bottom: 32px;
  }
  .rb-catalog__head { padding-top: 32px; }
  .rb-catalog__tools { flex-wrap: wrap; white-space: normal; }
  /* Two coffees across on a phone, three on a desktop, as the roastery asked
     (review 2026-09-19). It used to drop to one here, which made scrolling the
     catalog a much longer job than it needed to be. A card at half a phone is
     about 160px of content, so it loses the wide padding, and its name and its
     buttons stop sharing a line. */
  .rb-products { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rb-product-card { padding: 20px 16px 26px; }
  /* The same padding, for the next-group cell to size itself by (above). */
  .rb-products { --rb-card-pad: 16px; --rb-card-pad-top: 20px; }
  .rb-stock-pill { top: 20px; left: 16px; padding: 5px 9px; font-size: 9px; }
  .rb-product-card__foot { gap: 16px; }
}
@media (max-width: 640px) {
  .rb-catalog__title { font-size: 30px; }
  .rb-catalog__bar { flex-direction: column; align-items: flex-start; }
  .rb-catalog__bar .rb-catalog__tools { margin-left: 0; }
  .rb-product-card { padding: 16px 14px 22px; }
  .rb-products { --rb-card-pad: 14px; --rb-card-pad-top: 16px; }
  .rb-stock-pill { top: 16px; left: 14px; }
  .rb-product-card__name { font-size: 15px; }
  .rb-qty--card button { padding: 6px 9px; font-size: 13px; }
  .rb-qty--card input { width: 26px; }
  /* The card's flavour tag needs no phone size of its own: it says one word
     (rb_flavour_short_label()), and the widest of those in Portuguese is a
     77.9px pill in the 152px a card gets at half a 360px phone. Shrinking the
     type was the answer while the tag carried the full profile name, and it was
     never a good one — it bought one of the two long names a single line and
     left the other wrapping. */
}

/* ---------------------------------------------------------------
   10. Product page
   --------------------------------------------------------------- */

.rb-pdp { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
/* No wider than the catalog on a wide monitor (review 2026-09-23: "too much
   gradient, too little photo" — at 1920px each half was 960px and the photo a
   560px square in the left one). */
.rb-pdp { max-width: var(--rb-page-max); margin-inline: auto; }
.rb-pdp__media {
  border-right: 1px solid var(--rb-rule);
}
/* The photograph is a square in its column, not the column itself (review
   2026-09-21: "smaller, not stretched across the screen"), never cropped. It
   takes the column's width, and is never taller than the screen under the
   header less its padding, so the whole bag is always in view while the column
   scrolls. The page's cap (--rb-page-max) is what stops it growing on a wide
   monitor; until 2026-09-23 a 560px ceiling did, and left the column around it
   empty. Centred on the screen's middle line under the header (review
   2026-09-22). */
.rb-pdp__media-inner {
  position: sticky;
  top: var(--rb-header-h);
  min-height: calc(100vh - var(--rb-header-h));
  /* No side padding: the photo takes the column's whole width (2026-09-23,
     "why is the photo not full width?"). Above and below, the room that keeps it
     clear of the header and the bottom of the screen. */
  padding: 48px 0;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  isolation: isolate;
  /* Two colours off the photo, one a side (--rb-edge-l, --rb-edge-r, set by
     edgeFill() in assets/main.js from the background clear of the bag), run
     across the column; the ::after below fades it to paper at the bottom.
     Paper until the script has run. Since the photo takes the column's whole
     width (2026-09-23) they show only above and below it: on a wide monitor
     they had filled half the screen ("too much gradient, too little photo").
     Chosen over plain paper from screenshots of both (the owner, 2026-09-23);
     paper is the fallback if the roastery prefers it. Tried and dropped on
     2026-09-22: a blurred copy of the photo, which bled the bag's pink into
     the ground, and its edges carried on row by row, which striped. */
  background: linear-gradient(to right, var(--rb-edge-l, var(--rb-paper)), var(--rb-edge-r, var(--rb-paper)));
}
.rb-pdp__photo {
  position: relative;
  z-index: 2;
  width: 100%;
  display: flex;
  justify-content: center;
}
.rb-pdp__media-inner img {
  display: block;
  width: min(100%, calc(100vh - var(--rb-header-h) - 96px));
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  /* Little over the photograph (the owner, 2026-09-22, between a soft wide
     edge and none): a thin 5% edge on the sides and top so its square does
     not show against the ground, and the bottom fading from 82%. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent), linear-gradient(to bottom, transparent, #000 4%, #000 82%, transparent);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent), linear-gradient(to bottom, transparent, #000 4%, #000 82%, transparent);
  mask-composite: intersect;
}
.rb-pdp__media-inner::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to bottom, transparent 58%, var(--rb-paper) 92%);
  pointer-events: none;
}
/* The concept's labels on this page are 11px and a darker grey (#5F5F5F)
   than the site-wide .rb-label: tasting notes, why try it, weight, grind,
   the recipe and the green-coffee panel all read at this size. */
.rb-pdp .rb-label,
.rb-pdp .rb-product-choice > label {
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--rb-label-ink);
}
.rb-pdp__body {
  padding: 56px 48px 100px;
  max-width: 640px;
  min-width: 0;
}
/* Ink, not grey: in the concept the way back is a link you are meant to see. */
.rb-pdp__back {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rb-ink);
}
.rb-pdp__title { margin: 20px 0 14px; font-size: clamp(30px, 3.4vw, 40px); overflow-wrap: break-word; }
.rb-pdp__meta {
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--rb-grey);
}
.rb-pdp__notes { max-width: 32ch; }

/* Origin · variety · roast · flavour, as links back into the catalog: liked
   this lot, here is more of the same. The flavour pill fills with its accent,
   the rest stay outlined so the colour still means one thing. */
.rb-pdp__pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.rb-pill {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--rb-ink);
  padding: 8px 14px;
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  transition: background 140ms ease, border-color 140ms ease;
}
.rb-pill:hover { background: var(--rb-accent); border-color: var(--rb-accent); color: var(--rb-ink); }
.rb-pill--fill { border-color: transparent; padding: 8px 15px; background: var(--rb-wash); }
.rb-pill--fill[data-rb-note="floral"] { background: var(--rb-lime); }
.rb-pill--fill[data-rb-note="berry"] { background: var(--rb-berry); }
.rb-pill--fill[data-rb-note="chocolaty"] { background: var(--rb-caramel); }
.rb-pill--fill[data-rb-note="fruity"] { background: var(--rb-cherry); }
.rb-pill--fill[data-rb-note="modern"] { background: var(--rb-modern); }
/* A filled pill already carries its note colour, which is information — the
   accent must not overwrite it. Only the border answers the hover. */
.rb-pill--fill:hover { border-color: var(--rb-ink); }

/* Tasting notes and Why try it, each labelled and separated by a hairline —
   the cup in two registers: what it tastes of, and who it is for. */
.rb-pdp__cup {
  margin-top: 28px;
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--rb-rule);
}
.rb-pdp__cup-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 20px 0;
}
.rb-pdp__cup-row + .rb-pdp__cup-row { border-top: 1px solid var(--rb-rule); }
.rb-pdp__hook { max-width: 52ch; }

/* The lot's intro — tagline, then the paragraph on the cup — above Weight on the
   product page and under the flavour pill in quick view (rb_lot_story()). */
.rb-lot-intro { margin-top: 24px; max-width: 52ch; }
.rb-lot-intro em { font-style: italic; letter-spacing: 0.02em; }
.rb-pdp__pills + .rb-lot-intro { margin-top: 28px; }

/* The descriptors off the bag, after the intro: outlined pills (review
   2026-09-21). One line where they fit, which on a desktop they do; where they
   do not, a second row rather than a line to scroll sideways, and bolder
   (review 2026-09-22). Printed on the product page and in the quick view. */
.rb-descriptors {
  margin: 24px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.rb-descriptors .rb-outline-pill { font-size: 14px; font-weight: 500; }

/* The Discovery index, under the descriptors: the sticker's label and its six
   dots, filled in the group's colour (review 2026-09-22). Modern's filled dots
   are each a slice of its gradient, left to right, as its bag prints them.
   The label, underlined in dots, opens what the index means: on hover, and on
   a tap, which focuses the button (no asterisk: the owner, 2026-09-22). */
.rb-discovery {
  --rb-dot-on: var(--rb-ink);
  margin-top: 18px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 16px;
}
.rb-discovery[data-rb-note="floral"]    { --rb-dot-on: var(--rb-lime); }
.rb-discovery[data-rb-note="berry"]     { --rb-dot-on: var(--rb-berry); }
.rb-discovery[data-rb-note="chocolaty"] { --rb-dot-on: var(--rb-caramel); }
.rb-discovery[data-rb-note="fruity"]    { --rb-dot-on: var(--rb-cherry); }
.rb-discovery__term { position: relative; display: inline-flex; }
.rb-discovery__label {
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--rb-sans);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #5F5F5F;
}
button.rb-discovery__label {
  cursor: help;
  text-decoration: underline dotted;
  text-underline-offset: 4px;
}
button.rb-discovery__label:hover,
button.rb-discovery__label:focus-visible { color: var(--rb-ink); }
.rb-discovery__tip {
  position: absolute;
  z-index: 5;
  left: 0;
  bottom: calc(100% + 10px);
  width: min(320px, 80vw);
  padding: 14px 16px;
  border: 1px solid var(--rb-ink);
  border-radius: 14px;
  background: var(--rb-paper);
  color: var(--rb-ink);
  font-size: 13px;
  font-weight: 300;
  line-height: 1.5;
  letter-spacing: 0;
  text-transform: none;
  box-shadow: 0 8px 24px rgba(20, 20, 20, 0.12);
  opacity: 0;
  visibility: hidden;
  transition: opacity 120ms ease, visibility 0s linear 120ms;
}
.rb-discovery__term:hover .rb-discovery__tip,
.rb-discovery__label:focus + .rb-discovery__tip {
  opacity: 1;
  visibility: visible;
  transition: opacity 120ms ease;
}
.rb-discovery__dots { display: inline-flex; gap: 6px; }
.rb-discovery__dot {
  width: 14px;
  height: 14px;
  border: 2px solid var(--rb-ink);
  border-radius: 50%;
  background: var(--rb-paper);
}
.rb-discovery__dot.is-on { background: var(--rb-dot-on); }
.rb-discovery[data-rb-note="modern"] .rb-discovery__dot.is-on {
  background: var(--rb-modern);
  background-size: 600% 100%;
  background-position: calc(var(--rb-dot) * 20%) 50%;
}

/* A bag switched to that cannot be bought: said beside the disabled button. */
.rb-size-soldout {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--rb-grey);
}
.rb-size-soldout[hidden] { display: none; }
/* WooCommerce paints a disabled Add to cart in its own lilac; here it is the
   same ink button, faded. */
.woocommerce .rb-pdp__purchase button.button:disabled,
.woocommerce .rb-pdp__purchase button.button:disabled[disabled],
.woocommerce .rb-pdp__purchase button.button.alt:disabled,
.woocommerce .rb-pdp__purchase button.button.alt:disabled:hover {
  background: var(--rb-ink);
  color: var(--rb-paper);
  opacity: 0.35;
  cursor: not-allowed;
}

/* Adding over AJAX: the same faded button the disabled state uses, since a
   WooCommerce button does not answer .rb-btn.is-busy. */
.woocommerce .rb-pdp__purchase button.button.is-busy,
.woocommerce .rb-pdp__purchase button.button.alt.is-busy {
  opacity: 0.55;
  pointer-events: none;
}

.rb-pdp__buy { margin-top: 36px; display: flex; flex-direction: column; gap: 20px; }
.rb-pdp__price { font-size: 22px; font-weight: 400; }

/* The add's answer, under the button inside the form (review 2026-09-19).
   It has no height of its own until there is something to say, so nothing
   moves on the page until the add has happened. */
.rb-pdp__feedback {
  margin-top: 14px;
  font-size: 12px;
  color: var(--rb-grey);
}
.rb-pdp__feedback:empty { display: none; }
.rb-pdp__feedback.is-error { color: var(--rb-ink); }

/* On green coffee: closes the buying column on the lighter grey, as in the
   concept, rather than running full width under the page. */
.rb-pdp__green {
  margin-top: 40px;
  padding: 28px 30px;
  background: var(--rb-wash-2);
}
.rb-pdp__green p {
  margin: 14px 0 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--rb-body);
  max-width: 56ch;
  text-wrap: pretty;
}

.rb-product-choice { display: flex; flex-direction: column; gap: 10px; }
.rb-product-choice > label {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--rb-muted);
}
.rb-product-choice-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
/* Grind rolls out rather than spreading: five chips took two lines of a phone,
   between the price and the button (review 2026-09-19). A native select is one
   line and opens the platform's own list, which is the list the roastery meant.
   The arrow is the browser's, because it is the arrow the visitor's phone uses
   everywhere else. */
.rb-select {
  font-family: var(--rb-sans);
  font-weight: 400;
  font-size: 12px;
  line-height: 1.2;
  color: var(--rb-ink);
  background: var(--rb-paper);
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  padding: 10px 14px;
  width: fit-content;
  max-width: 100%;
  cursor: pointer;
}
.rb-select:focus { outline: none; border-color: var(--rb-ink); }

/* WooCommerce's form is a plain block, so Weight, Grind and the buy row sat
   flush against each other. The same 20px rhythm as .rb-pdp__buy, inside it. */
.rb-pdp__buy form.cart {
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin: 0;
}

/* − 1 + · price · Add to cart — built around WooCommerce's own quantity field
   and button by the rb_product_page_buy_row_*() hooks in functions.php. */
.rb-pdp__purchase {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px 18px;
  padding-top: 6px;
}
.rb-pdp__purchase .quantity { display: flex; }
/* WooCommerce's stylesheet gives .qty a 3.6em box; inside the pill the number
   only needs its own width. */
.rb-pdp__purchase .rb-qty .qty { width: 28px; padding: 0; font-size: 15px; }
.rb-pdp__purchase .single_add_to_cart_button { padding: 15px 32px; }

/* Accordion */
.rb-accordion { margin-top: 44px; border-top: 1px solid var(--rb-rule); }
.rb-accordion__item { border-bottom: 1px solid var(--rb-rule); }
.rb-accordion__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 19px 0;
  background: none;
  border: 0;
  cursor: pointer;
  color: inherit;
  text-align: left;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.rb-accordion__sign {
  font-size: 19px;
  line-height: 1;
  color: var(--rb-label-ink);
}
.rb-accordion__panel { padding: 0 0 28px; }
.rb-accordion__panel[hidden] { display: none; }

/* Process, Producer, Shipping, Storage, Certifications: the concept's panel
   text — Alexandria at 15px, the same size as Why try it above. */
.rb-prose--panel {
  font-family: var(--rb-sans);
  font-size: 15px;
  line-height: 1.6;
}
.rb-prose--panel p + p { margin-top: 12px; }

/* Brew recipe. "As pourover, V60" above the figures. */
.rb-recipe__method { display: block; }

/* Brew figures: Alexandria labels over IBM Plex Mono values (.rb-figure).
   Shared with the Brewing page. */
.rb-figures {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px 18px;
}
.rb-figures__item { display: flex; flex-direction: column; gap: 5px; }
.rb-figures__label {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--rb-label-ink);
}

.rb-timeline { display: flex; flex-direction: column; gap: 10px; }
.rb-timeline__row {
  display: grid;
  grid-template-columns: 92px 12px 1fr;
  align-items: center;
  gap: 14px;
  font-family: var(--rb-mono);
  font-size: 13px;
}
.rb-timeline__time { color: var(--rb-muted); font-size: 12px; }
.rb-timeline__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--rb-berry);
}

.rb-steps {
  margin: 0;
  padding-left: 18px;
  font-family: var(--rb-mono);
  font-size: 13px;
  line-height: 1.7;
  color: var(--rb-body);
}
.rb-steps li { margin-bottom: 8px; }

/* Flavour profile bars */
.rb-profile { display: flex; flex-direction: column; gap: 14px; }
.rb-profile__row {
  display: grid;
  grid-template-columns: 108px 1fr;
  align-items: center;
  gap: 16px;
}
.rb-profile__label {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rb-muted);
}
.rb-profile__track { height: 4px; background: #EDEDED; }
.rb-profile__fill { height: 100%; background: var(--rb-ink); }
.rb-profile__row--acidity .rb-profile__fill { background: var(--rb-lime); }
.rb-profile__row--sweetness .rb-profile__fill { background: var(--rb-caramel); }
.rb-profile__row--body .rb-profile__fill { background: var(--rb-berry); }
.rb-profile__row--bitterness .rb-profile__fill { background: var(--rb-cherry); }

.rb-spec {
  margin: 24px 0 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 9px 22px;
  font-size: 13px;
}
.rb-spec dt {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rb-muted);
}
.rb-spec dd { margin: 0; }

@media (max-width: 900px) {
  /* minmax(0, …), not 1fr: a bare 1fr track is never narrower than its widest
     unbreakable content, and the descriptors row is a single line by design — it
     widened the column past the screen, and the whole page with it, so the
     header sat in the left three-quarters and the title ran off the right
     (review 2026-09-21, second round). The row scrolls inside the column now. */
  .rb-pdp { grid-template-columns: minmax(0, 1fr); }
  .rb-pdp__media { border-right: 0; border-bottom: 1px solid var(--rb-rule); }
  /* Cut a little short of a square: its last rows are faded to white anyway,
     and left in they were a blank strip between the bag and the title. And
     never more than 60% of the screen's height: on an upright tablet or an
     open Fold the full-width square filled the screen, and the price and Add
     to cart were below the fold (2026-09-23). A phone held upright is always
     under that, so it keeps the full width. */
  .rb-pdp__media-inner { position: relative; top: auto; display: block; min-height: 0; padding: 0; background: var(--rb-paper); height: min(95vw, 60vh); overflow: hidden; }
  /* Paper under 900px: the photo is the full width, or nearly, so there is no
     column around it to colour. */
  .rb-pdp__media-inner::after { display: none; }
  .rb-pdp__photo { display: block; height: 100%; }
  /* Full width on a phone: only its bottom fades into the page, as a card's
     does. */
  .rb-pdp__media-inner img {
    /* The square whose top 95% fills the box: the full width on a phone,
       narrower and centred where the 60% cap is the smaller. */
    width: min(100%, min(95vw, 60vh) / 0.95);
    height: auto;
    margin-inline: auto;
    -webkit-mask-image: linear-gradient(to bottom, #000 82%, transparent);
    mask-image: linear-gradient(to bottom, #000 82%, transparent);
  }
  .rb-pdp__body { padding: 36px var(--rb-gutter) 64px; max-width: none; }
  .rb-figures { grid-template-columns: repeat(2, 1fr); }
}
/* Where 60% of the height is less than the width (a tablet, an open Fold, a
   phone on its side) the photo is narrower than the screen, so its sides soften
   into the paper as they do on a desktop. */
@media (max-width: 900px) and (min-aspect-ratio: 60 / 95) {
  .rb-pdp__media-inner img {
    -webkit-mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent), linear-gradient(to bottom, #000 82%, transparent);
    -webkit-mask-composite: source-in;
    mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent), linear-gradient(to bottom, #000 82%, transparent);
    mask-composite: intersect;
  }
}

/* ---------------------------------------------------------------
   11. Quick view
   --------------------------------------------------------------- */

.rb-quickview {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px;
  background: rgba(20, 20, 20, 0.34);
}
.rb-quickview[hidden] { display: none; }
.rb-quickview__scrim { position: absolute; inset: 0; border: 0; background: none; cursor: default; }
.rb-quickview__panel {
  position: relative;
  width: min(1020px, 100%);
  max-height: 100%;
  background: var(--rb-paper);
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: minmax(0, 1fr);
  overflow: hidden;
}
/* The photograph is a square in its column, never cropped (review 2026-09-21:
   "way too close, zoomed in"). It used to cover the whole column, which is as
   tall as the text beside it — so a square bag shot was cut to a portrait, and
   the narrower the window the harder: at 960px only the middle half of the bag
   was left. Now it keeps its square, centred in the column; where the column is
   shorter than it is wide, `contain` shrinks it rather than cutting it.
   The room it leaves is not grey. assets/main.js (edgeFill()) reads the photo's
   edges into properties, and each band is the photo carried on past its edge:
   --rb-edge-top, its top row, fills the band above; --rb-edge-left and
   --rb-edge-right, each side's edge row by row, lie beside it at its drawn
   height (--rb-edge-h), down to where the photo starts to fade and then into
   paper. The band below is paper: since 2026-09-22 the photo fades to white
   where the table was (see ::after below), and the band used to be the table
   carried on. Until they are set, or where the photo cannot be read,
   the ground is the group's colour. It was first stood on the bottom of the
   column, which the roastery found low on a tablet; centred since the same day. */
.rb-quickview__media {
  --rb-qv-ground: var(--rb-wash);
  --rb-qv-band: calc(50% - var(--rb-edge-h, 100%) / 2);
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    var(--rb-edge-left, none) left center / 50% var(--rb-edge-h, 100%) no-repeat,
    var(--rb-edge-right, none) right center / 50% var(--rb-edge-h, 100%) no-repeat,
    var(--rb-edge-top, none) top / 100% var(--rb-qv-band) no-repeat,
    linear-gradient(var(--rb-paper), var(--rb-paper)) bottom / 100% var(--rb-qv-band) no-repeat,
    var(--rb-qv-ground);
  min-height: 0;
  overflow: hidden;
}
.rb-quickview__media[data-rb-note="floral"]    { --rb-qv-ground: color-mix(in srgb, var(--rb-lime) 45%, var(--rb-paper)); }
.rb-quickview__media[data-rb-note="berry"]     { --rb-qv-ground: color-mix(in srgb, var(--rb-berry) 35%, var(--rb-paper)); }
.rb-quickview__media[data-rb-note="chocolaty"] { --rb-qv-ground: color-mix(in srgb, var(--rb-caramel) 40%, var(--rb-paper)); }
.rb-quickview__media[data-rb-note="fruity"]    { --rb-qv-ground: color-mix(in srgb, var(--rb-cherry) 35%, var(--rb-paper)); }
.rb-quickview__media[data-rb-note="modern"]    { --rb-qv-ground: var(--rb-modern); }
.rb-quickview__media img {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: auto;
  max-height: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  -webkit-mask-image: linear-gradient(to bottom, #000 82%, transparent);
  mask-image: linear-gradient(to bottom, #000 82%, transparent);
}
/* The photo fades from 82% of its height, where the table is, and the column
   under it fades to white over its lower part, as the product page does
   (review 2026-09-22; the owner settled the 82% after trying 64%, 76% and
   88%). The ::after sits under the photo, never over it. */
.rb-quickview__media { position: relative; }
.rb-quickview__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, transparent 60%, var(--rb-paper) 95%);
  pointer-events: none;
}
.rb-quickview__body {
  padding: 38px 38px 34px;
  min-height: 0;
  overflow: auto;
}
.rb-quickview__title {
  margin: 0 0 7px;
  font-size: clamp(24px, 2.6vw, 30px);
  font-weight: 500;
  line-height: 1.08;
  letter-spacing: -0.03em;
}
/* On the panel, in its top right corner, over whatever scrolls under it — the
   text beside the photo, or on a phone the whole stacked view (review
   2026-09-22). A paper disc, so it reads over the photograph too. */
.rb-quickview__close {
  position: absolute;
  z-index: 3;
  top: 12px;
  right: 12px;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--rb-paper);
  border: 1px solid var(--rb-rule);
  border-radius: 50%;
  padding: 0;
  font-size: 22px;
  line-height: 1;
  color: var(--rb-ink);
  cursor: pointer;
}
.rb-quickview__close:hover { border-color: var(--rb-ink); }
/* The title keeps clear of it. */
.rb-quickview__title { padding-right: 36px; }
.rb-quickview__feedback {
  margin-top: 14px;
  font-size: 12px;
  color: var(--rb-grey);
}
.rb-quickview__feedback.is-error { color: var(--rb-cherry); }

.rb-quickview__loading {
  padding: 60px;
  text-align: center;
  color: var(--rb-muted);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Stacked only on a screen that is taller than it is wide. A phone on its side
   (852×393) is under 860px too, and stacking there left a strip of photo over a
   panel of text a thumb's height tall (second round of 2026-09-21). */
@media (max-width: 860px) and (orientation: portrait) {
  .rb-quickview { padding: 16px; }
  .rb-quickview__panel { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr); }
  /* Stacked, the square would push the buying column off a phone: it is held
     under 42% of the screen, shrunk rather than cropped to a 4:3 as it was. */
  .rb-quickview__media img { width: auto; max-width: 100%; max-height: 42vh; }
  .rb-quickview__body { padding: 24px; }
}

/* A short landscape screen: side by side, the photo's column as wide as the
   panel is tall so the square fills it, and the text scrolling beside it. */
@media (max-height: 560px) and (orientation: landscape) {
  .rb-quickview { padding: 12px; }
  .rb-quickview__panel { grid-template-columns: minmax(0, calc(100dvh - 24px)) minmax(0, 1fr); }
  .rb-quickview__body { padding: 22px 24px; }
}

/* ---------------------------------------------------------------
   12. Editorial pages
   --------------------------------------------------------------- */

.rb-page-hero {
  position: relative;
  height: 58vh;
  min-height: 380px;
  background: var(--rb-wash);
  overflow: hidden;
}
.rb-page-hero img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.rb-page-hero__scrim {
  position: absolute;
  inset: 0;
  /* Warm black, matching the home hero and the concept — a neutral grey scrim
     over a roastery photograph reads as a print that was left in the fixer. */
  background: linear-gradient(to top, rgba(24,16,10,0.78) 0%, rgba(24,16,10,0.35) 60%, rgba(24,16,10,0.25) 100%);
}
.rb-page-hero__inner {
  position: absolute;
  left: var(--rb-gutter);
  right: var(--rb-gutter);
  bottom: 48px;
}
.rb-page-hero__inner .rb-label { color: var(--rb-line); }
.rb-page-hero__inner .rb-display { color: var(--rb-paper); max-width: 24ch; }

.rb-page-head { padding: 64px var(--rb-gutter) 0; }
.rb-page-head .rb-display { margin-top: 18px; max-width: 24ch; }
.rb-page-head .rb-prose { margin-top: 22px; max-width: 60ch; }

.rb-panel { padding: 52px 48px 64px; }
.rb-panel--wash { background: var(--rb-wash-2); }
.rb-list {
  margin: 8px 0 0;
  padding-left: 18px;
  font-size: 13px;
  line-height: 1.7;
  color: var(--rb-body);
}
.rb-list li { margin-bottom: 7px; }

/* A hero with no featured image renders no scrim, so the paper-white title
   would sit on the light wash. Paint a solid ground instead. */
.rb-page-hero--flat { background: var(--rb-ink); }
.rb-page-hero__lede {
  margin-top: 20px;
  max-width: 62ch;
  font-size: 13px;
  line-height: 1.65;
  color: #EDEDED;
  text-wrap: pretty;
}

/* Section heading — one step below .rb-display, for a headline inside a panel
   rather than at the top of a page. */
.rb-heading {
  font-weight: 500;
  font-size: 26px;
  letter-spacing: -0.025em;
  text-wrap: pretty;
}

/* Stockists — where the coffee is served. A hairline list, name in sans over
   the address in mono: the address is data, and reads better set that way. */
.rb-stockists {
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
  border-bottom: 1px solid var(--rb-rule);
}
.rb-stockist {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 15px 0;
  border-top: 1px solid var(--rb-rule);
}
.rb-stockist__name { font-size: 15px; font-weight: 400; }
/* With a map link the name is the link, and the arrow says it leaves the site. */
a.rb-stockist__name { display: inline-flex; align-items: baseline; gap: 6px; align-self: flex-start; }
.rb-stockist__map { font-size: 12px; color: var(--rb-grey); transition: color 140ms ease; }
a.rb-stockist__name:hover .rb-stockist__map { color: inherit; }
.rb-stockist__address {
  font-family: var(--rb-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--rb-grey);
}

/* About's brief: the opening line and the paragraph under it. Framed in 5px of
   ink on 2026-09-23 ("the same as Espresso and Filter"); the frame read as a
   mourning border (2026-09-24), and the roastery asked for the brief to stand
   out, or be structural, by its type instead: before the frame it "looked like
   just text in different font sizes". Three ways, one class each, the template
   choosing one:
   --rule       a bar of the page's accent down its left edge, the line larger;
   --wash       on the pale wash, rounded, no line round it;
   --statement  the line set as a statement in the heading's weight, a short ink
                bar above it.
   The line is always larger and heavier than the lede elsewhere, so the brief
   leads the column by its type whichever the ground. The owner chose
   --statement (2026-09-24); --rule and --wash stay as the alternatives. */
.rb-brief .rb-lede {
  font-size: clamp(21px, 2.3vw, 27px);
  font-weight: 400;
  line-height: 1.3;
  color: var(--rb-ink);
}
.rb-brief .rb-prose { margin-top: 20px; }

.rb-brief--rule {
  padding: 4px 0 6px 28px;
  border-left: 5px solid var(--rb-accent);
}

.rb-brief--wash {
  padding: 34px 36px 36px;
  border-radius: 28px;
  background: var(--rb-wash);
}

.rb-brief--statement::before {
  content: "";
  display: block;
  width: 48px;
  height: 5px;
  margin-bottom: 22px;
  border-radius: 3px;
  background: var(--rb-ink);
}
.rb-brief--statement .rb-lede {
  font-size: clamp(24px, 2.8vw, 32px);
  font-weight: 500;
  line-height: 1.18;
  letter-spacing: -0.03em;
}

@media (max-width: 900px) {
  .rb-brief--rule { padding-left: 18px; border-left-width: 4px; }
  .rb-brief--wash { padding: 24px 22px 26px; border-radius: 22px; }
}

/* The title of one way of working on the wholesale page. */
.rb-service__title {
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.015em;
}
/* The wholesale form, under the sentence that leads into it in the left half
   (review 2026-09-24): a little more room above it than the label and the
   sentence have between them, so the form reads as what follows them. */
.rb-ws-form { margin-top: 18px; }

/* Everything in this recipe — the coffee and the gear a brewing guide asks for,
   boxed beside the method so the reading and the buying sit together. */
.rb-bundle {
  border: 1px solid var(--rb-rule);
  padding: 26px 26px 28px;
}
.rb-bundle__items { margin-top: 18px; display: flex; flex-direction: column; }
.rb-bundle__item {
  display: grid;
  grid-template-columns: 64px 1fr auto;
  gap: 16px;
  align-items: center;
  padding: 14px 0;
  border-top: 1px solid var(--rb-rule);
  color: var(--rb-ink);
}
.rb-bundle__item:hover .rb-bundle__name { color: var(--rb-accent); }
.rb-bundle__media {
  display: block;
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--rb-wash);
  overflow: hidden;
}
.rb-bundle__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.rb-bundle__text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.rb-bundle__name { font-size: 15px; font-weight: 500; letter-spacing: -0.01em; }
.rb-bundle__kind {
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--rb-muted);
}
.rb-bundle__price { font-size: 13px; }
.rb-bundle__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-top: 4px;
  padding-top: 16px;
  border-top: 1px solid var(--rb-ink);
}
.rb-bundle__sum { font-size: 19px; }
.rb-bundle__cta { width: 100%; margin-top: 20px; text-align: center; }
.rb-bundle__note { margin-top: 14px; color: var(--rb-grey); }

@media (max-width: 900px) {
  .rb-panel { padding: 36px var(--rb-gutter) 44px; }
}

/* ---------------------------------------------------------------
   13. Cart, checkout, account (WooCommerce)

   Two kinds of rule live here. Rules for the theme's own templates
   (woocommerce/cart/cart.php, myaccount/form-login.php, my-account.php,
   dashboard.php) style markup the theme wrote. Rules for WooCommerce's
   templates — cart totals and shipping, notices, Orders, Addresses, Account
   details — restyle markup the theme deliberately does not replace, and so
   must outrank WooCommerce's own stylesheet; several need its context in the
   selector, and a few need !important where WooCommerce uses it.
   The map of which is which is in the theme README, "Cart and account".
   --------------------------------------------------------------- */

.rb-cart-line {
  display: grid;
  grid-template-columns: 104px 1fr auto;
  gap: 22px;
  align-items: start;
  padding: 26px 0;
  border-bottom: 1px solid var(--rb-rule);
}
.rb-cart-line__media {
  display: block;
  aspect-ratio: 1 / 1;
  background: var(--rb-wash);
  position: relative;
  overflow: hidden;
}
.rb-cart-line__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.rb-cart-line__name {
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.015em;
}
/* "1 kg · Em grão" — rb_cart_item_meta_line() in functions.php. */
.rb-cart-line__meta {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--rb-grey);
}
/* A unit in a line set in capitals keeps SI's lower case: "250 g", not
   "250 G" (2026-09-23). */
.rb-unit { text-transform: none; }
.rb-cart__lines { margin-top: 36px; border-top: 1px solid var(--rb-rule); }

/* Remove. WooCommerce's stylesheet draws every a.remove as a big red bold
   glyph, colours marked !important — it is meant for its own table's × link —
   which turned REMOVER into a headline. The concept's is a small ink label, so
   the override needs the class, the context and !important on colour. */
.woocommerce .rb-cart-line a.remove,
.woocommerce .rb-cart-line a.remove:hover {
  display: inline;
  width: auto;
  height: auto;
  font-size: 11px;
  font-weight: 400;
  line-height: inherit;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rb-ink) !important;
  background: none;
  border-radius: 0;
}
.woocommerce .rb-cart-line a.remove:hover { text-decoration: underline; text-underline-offset: 4px; }

/* − 1 + around WooCommerce's quantity field; the concept's cart pill is a
   size smaller than the product page's. WooCommerce gives .qty a 3.6em box. */
.rb-qty .quantity { display: flex; }
.rb-qty .quantity .qty { width: 28px; padding: 0; font-size: 13px; }
.rb-qty--small button { padding: 8px 13px; font-size: 13px; }

/* Once initCart() (assets/main.js) updates on change, the button is not needed.
   Without JavaScript .is-live never arrives and the button stays. */
.rb-cart.is-live .rb-cart__update { display: none; }
.rb-cart.is-live .rb-cart__actions:not(:has(#coupon_code)) { padding-top: 0 !important; }

/* WooCommerce's totals (cart/cart-totals.php, not overridden) as the concept's
   Subtotal / Shipping / Total rows. Its stylesheet floats the block right at
   48% — the empty half on staging — and under 768px hides each th and relabels
   the cells from data-title. All three are undone here. The heading "Cart
   totals" is hidden: the column's Summary label already names it. */
.woocommerce .rb-cart .cart-collaterals .cart_totals { float: none; width: auto; }
/* Towards free shipping: a 6px track that fills in the page's accent, with what
   is left to spend over it (rb_render_free_shipping_bar()). */
.rb-freeship {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 26px;
}
.rb-freeship__text { margin: 0; font-size: 14px; font-weight: 300; color: var(--rb-ink); }
.rb-freeship__text strong { font-weight: 500; }
.rb-freeship__track {
  height: 6px;
  border-radius: var(--rb-radius);
  background: #E2E2E2;
  overflow: hidden;
}
.rb-freeship__fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--rb-accent);
}
.rb-freeship.is-reached .rb-freeship__fill { background: var(--rb-ink); }
.rb-cart .cart_totals > h2 { display: none; }
.woocommerce .rb-cart .cart_totals table.shop_table {
  display: block;
  margin: 0;
  border: 0;
}
.rb-cart .cart_totals tbody { display: flex; flex-direction: column; gap: 12px; }
.woocommerce .rb-cart .cart_totals table.shop_table tr {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 20px;
}
.woocommerce .rb-cart .cart_totals table.shop_table th,
.woocommerce .rb-cart .cart_totals table.shop_table td {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  font-size: 15px;
  font-weight: 300;
  letter-spacing: 0;
  text-transform: none;
  color: var(--rb-ink);
  text-align: right !important;
}
.woocommerce .rb-cart .cart_totals table.shop_table th { color: var(--rb-grey); text-align: left !important; }
.woocommerce .rb-cart .cart_totals table.shop_table td::before { content: none; }
.woocommerce .rb-cart .cart_totals table.shop_table tr.order-total {
  padding-top: 12px;
  border-top: 1px solid var(--rb-rule);
}
.woocommerce .rb-cart .cart_totals table.shop_table tr.order-total th,
.woocommerce .rb-cart .cart_totals table.shop_table tr.order-total td { color: var(--rb-ink); font-weight: 500; }
.rb-cart .cart_totals tr.order-total strong { font-weight: inherit; }
.rb-cart .woocommerce-shipping-methods { list-style: none; margin: 0; padding: 0; }
.woocommerce .rb-cart ul#shipping_method li { margin: 0; }
.rb-cart .woocommerce-shipping-destination { margin: 4px 0 0; font-size: 12px; color: var(--rb-grey); }
.woocommerce .rb-cart .wc-proceed-to-checkout { padding: 0; margin-top: 28px; }
.woocommerce .rb-cart .wc-proceed-to-checkout a.checkout-button {
  display: block;
  width: 100%;
  margin: 0;
  padding: 15px 30px;
  font-size: 11px;
  text-align: center;
}
.rb-summary {
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-size: 13px;
  margin-top: 22px;
}
.rb-summary__row {
  display: flex;
  justify-content: space-between;
  gap: 20px;
}
.rb-summary__row dt { color: var(--rb-grey); }
.rb-summary__row dd { margin: 0; }
.rb-summary__row--total {
  padding-top: 12px;
  border-top: 1px solid var(--rb-rule);
  font-size: 16px;
}
.rb-summary__row--total dt { color: var(--rb-ink); }

@media (max-width: 600px) {
  .rb-cart-line { grid-template-columns: 72px 1fr; }
  .rb-cart-line__total { grid-column: 2; }
}

/* Tame WooCommerce defaults so its own markup inherits the design */
.woocommerce .button,
.woocommerce a.button,
.woocommerce button.button,
.woocommerce input.button,
.woocommerce #respond input#submit,
.woocommerce a.button.alt,
.woocommerce button.button.alt {
  font-family: var(--rb-sans);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 15px 30px;
  border: 0;
  border-radius: var(--rb-radius);
  background: var(--rb-ink);
  color: var(--rb-paper);
  line-height: 1;
  transition: background 140ms ease;
}
.woocommerce .button:hover,
.woocommerce a.button:hover,
.woocommerce button.button:hover,
.woocommerce input.button:hover,
.woocommerce a.button.alt:hover,
.woocommerce button.button.alt:hover {
  background: var(--rb-accent);
  color: var(--rb-ink);
}

.woocommerce-message,
.woocommerce-info,
.woocommerce-error {
  list-style: none;
  margin: 0 0 24px;
  padding: 16px 20px;
  border: 0;
  border-left: 0;
  background: var(--rb-wash-2);
  font-size: 13px;
  color: var(--rb-ink);
}
.woocommerce-error { background: rgba(248, 75, 77, 0.12); }
/* WooCommerce draws an icon-font glyph at left: 1.5em and expects 3.5em of
   padding; with the padding above the glyph sat on the first letter ("•razil
   Jaguara…" on the cart). The theme's notices carry no icon. */
.woocommerce-message::before,
.woocommerce-info::before,
.woocommerce-error::before { content: none; }
/* A page-level notice (cart, checkout, account) runs edge to edge above the
   split, so its text takes the page gutter instead of sitting at 20px. */
#rb-main > .woocommerce > .woocommerce-notices-wrapper > * {
  margin: 0;
  padding-inline: var(--rb-gutter);
  /* WooCommerce's 3px coloured top rule gives way to a hairline under the
     band. The id outranks WooCommerce's own selector. */
  border: 0;
  border-bottom: 1px solid var(--rb-rule);
}
/* The dark frame round the notice on staging was not a border: WooCommerce's
   script (focus_populate_live_region) gives a role="alert" notice tabindex -1
   and focuses it 500ms after load, so screen readers announce it, and the
   browser draws its focus ring. The focus and the announcement stay; the ring
   goes, on these notices only — nothing here can be operated by keyboard. */
.woocommerce-message[role="alert"][tabindex="-1"]:focus,
.woocommerce-error[role="alert"][tabindex="-1"]:focus { outline: none; }
/* "View cart" on the cart page leads to the page you are already on. */
.woocommerce-cart .woocommerce-message .wc-forward { display: none; }
.woocommerce-message a, .woocommerce-info a { text-decoration: underline; }

.woocommerce table.shop_table {
  border: 0;
  border-radius: 0;
  font-size: 13px;
}
.woocommerce table.shop_table th,
.woocommerce table.shop_table td {
  border-color: var(--rb-rule);
  padding: 16px 12px;
}
.woocommerce table.shop_table th {
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rb-muted);
}

/* Account, signed in — woocommerce/myaccount/my-account.php. The concept's
   split carried past sign-in: heading and menu in a narrow column, the screen
   on the right. WooCommerce's layout stylesheet floats the two at 30% / 68%;
   the floats are cancelled so the grid decides. */
.rb-account {
  grid-template-columns: minmax(280px, 1fr) 2fr;
  min-height: calc(100vh - var(--rb-header-h));
}
.rb-account__side,
.rb-account__content { padding-top: 72px; padding-bottom: 80px; }
/* Block, so its line is its own 10px and not the column's 15px strut: inline,
   it sat 5px lower than the flex-item label across the page. */
.rb-account__side > .rb-label { display: block; }
.rb-account__title { margin-top: 18px; overflow-wrap: anywhere; }
.woocommerce-account .rb-account .woocommerce-MyAccount-navigation,
.woocommerce-account .rb-account .woocommerce-MyAccount-content { float: none; width: auto; }
/* .rb-account sets its own columns after §6's collapse, so it collapses itself. */
@media (max-width: 900px) {
  .rb-account { grid-template-columns: 1fr; min-height: 0; }
  .rb-account__side,
  .rb-account__content { padding-top: 40px; padding-bottom: 48px; }
}

.woocommerce-MyAccount-navigation { margin-top: 36px; }
.woocommerce-MyAccount-navigation ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--rb-rule);
}
.woocommerce-MyAccount-navigation li a {
  display: block;
  padding: 14px 0;
  border-bottom: 1px solid var(--rb-rule);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--rb-grey);
}
.woocommerce-MyAccount-navigation li a:hover { color: var(--rb-ink); }
/* Ink and weight, not the accent: the accent rotates through lime, and lime
   text on white cannot be read. */
.woocommerce-MyAccount-navigation li.is-active a { color: var(--rb-ink); font-weight: 500; }

/* Dashboard rows — woocommerce/myaccount/dashboard.php, the device of the
   sign-in screen's "Once signed in" list, made into links. */
.rb-account__rows {
  display: flex;
  flex-direction: column;
  max-width: 560px;
  border-top: 1px solid var(--rb-rule);
}
.rb-account__row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 20px 0;
  border-bottom: 1px solid var(--rb-rule);
}
.rb-account__row-title { font-size: 15px; font-weight: 500; }
.rb-account__row:hover .rb-account__row-title { text-decoration: underline; text-underline-offset: 4px; }
.rb-account__signout { margin: 0; font-size: 13px; color: var(--rb-grey); }
.rb-account__signout a { color: var(--rb-ink); border-bottom: 1px solid currentColor; }

/* Sign-in fields with their label above, as the concept sets them. */
.rb-labelled { display: flex; flex-direction: column; gap: 8px; }

/* Registration waits behind "Create account" (form-login.php). The summary is
   the button; once open it steps aside for the form's own submit. */
.rb-register > summary { list-style: none; }
.rb-register > summary::-webkit-details-marker { display: none; }
.rb-register[open] > summary { display: none; }

/* WooCommerce's stylesheet boxes form.login and form.register in a rounded
   1px border with 20px of padding. The concept has no box, and the padding
   left Sign in at 327px instead of the column's 380. */
.woocommerce form.login,
.woocommerce form.register { border: 0; padding: 0; border-radius: 0; }
.woocommerce form.register { margin: 18px 0 0; }

/* ---- Inside a signed-in screen ------------------------------------------
   Orders, Addresses and Account details are WooCommerce's own templates,
   styled here rather than overridden, so WooCommerce keeps its markup, its
   hooks and its words. */

/* Notices with a button ("Ver produtos", "Confirm email address"): text and
   button on one line, the button pushed right. WooCommerce floats it. */
.rb-account__content .woocommerce-info,
.rb-account__content .woocommerce-message,
.rb-account__content .woocommerce-error {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px 24px;
  margin: 0 0 16px;
  padding: 16px 20px;
}
.rb-account__content .woocommerce-info .button,
.rb-account__content .woocommerce-message .button,
.rb-account__content .woocommerce-error .button { order: 2; float: none; margin-left: auto; }

/* Addresses: WooCommerce's 48% floats with a large h2 and an "Add …" link
   floated against it. Here a grid of cards in the page's typography: the
   title as a row title, the link as an underlined label, the address as prose. */
.rb-account__content > p:first-child { margin: 0; max-width: 60ch; font-size: 13px; color: var(--rb-grey); }
.rb-account__content .woocommerce-Addresses {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 32px;
  margin-top: 28px;
}
/* col2-set's clearfix pseudo-elements would become empty grid cells. */
.rb-account__content .woocommerce-Addresses::before,
.rb-account__content .woocommerce-Addresses::after { content: none; }
.woocommerce .rb-account__content .woocommerce-Address,
.woocommerce-account .rb-account__content .addresses .woocommerce-Address { float: none; width: auto; max-width: none; }
.rb-account__content .woocommerce-Address { margin-top: 28px; padding-top: 20px; border-top: 1px solid var(--rb-rule); }
.rb-account__content .woocommerce-Addresses .woocommerce-Address { margin-top: 0; }
.woocommerce-account .rb-account__content .woocommerce-Address-title {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
.woocommerce-account .rb-account__content .woocommerce-Address-title::after { content: none; }
.woocommerce-account .rb-account__content .woocommerce-Address-title h2 {
  float: none;
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.015em;
}
.woocommerce-account .rb-account__content .woocommerce-Address-title .edit {
  float: none;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-bottom: 1px solid currentColor;
  padding-bottom: 3px;
}
.rb-account__content address {
  margin-top: 14px;
  font-style: normal;
  font-size: 13px;
  line-height: 1.6;
  color: var(--rb-body);
}

/* Account details: the password fieldset loses the browser's box and its
   legend becomes a heading; the display-name hint is a note, not italics. */
.rb-account__content fieldset { margin: 32px 0 0; padding: 0; border: 0; min-width: 0; }
.rb-account__content legend { margin-bottom: 14px; padding: 0; font-size: 15px; font-weight: 500; }
.rb-account__content .form-row em { display: block; margin-top: 6px; font-style: normal; font-size: 12px; color: var(--rb-grey); }

/* A checkbox or radio label is a sentence ("Receive order updates"), not a
   field name: §13's uppercase field-label rule must not reach it. Covers a
   label wrapping its input and a label following it (Mailchimp's markup). */
.woocommerce form .form-row label:has(> input[type="checkbox"]),
.woocommerce form .form-row label:has(> input[type="radio"]),
.woocommerce form .form-row input[type="checkbox"] + label,
.woocommerce form .form-row input[type="radio"] + label {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 0 6px;
  font-size: 13px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--rb-body);
}
.woocommerce form .form-row label:has(> input[type="checkbox"]),
.woocommerce form .form-row label:has(> input[type="radio"]) { margin-left: 0; }

.woocommerce form .form-row { padding: 0; margin: 0 0 14px; }
.woocommerce form .form-row label {
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rb-muted);
  margin-bottom: 6px;
  display: block;
}
.woocommerce .select2-container--default .select2-selection--single {
  border: 1px solid var(--rb-line);
  border-radius: 4px;
  height: 46px;
  padding: 7px 8px;
}

/* Checkout — WooCommerce's own template (checkout/form-checkout.php), not
   overridden, laid out as the cart is: details on the left, the order on the
   wash to the right. Unstyled, the form had no gutter at all and WooCommerce's
   48% floats put the billing fields against the left edge of the window and
   the order note against the right; with a browser zoomed in, both ran off the
   page (review 2026-09-16).

   Floats, not a grid, on purpose. WooCommerce's checkout script prepends its
   error notices into form.checkout — sometimes two groups at once. As grid
   items they fell into a cell under the form, and the script scrolled the
   customer to the bottom of the page to read them. As blocks before a float
   they run full width above both columns. The wash and the rule between the
   columns are the form's background, so the right column reaches the bottom
   however short the order is.

   The price of the float is below, on #order_review, and it is not optional:
   a float takes the whole form's formatting context with it, so every clear in
   every plugin's markup on the other side reaches for it. Read that comment
   before changing either column.

   WooCommerce 11 also puts <wc-order-attribution-inputs> straight inside the
   form; they hold hidden inputs only. */
.woocommerce-checkout form.checkout {
  --rb-checkout-split: 58.333%; /* 1.4fr : 1fr, the cart's split */
  display: flow-root;
  min-height: calc(100vh - var(--rb-header-h));
  margin: 0;
  background: linear-gradient(90deg,
    transparent calc(var(--rb-checkout-split) - 1px),
    var(--rb-rule) calc(var(--rb-checkout-split) - 1px) var(--rb-checkout-split),
    var(--rb-wash-2) var(--rb-checkout-split));
}
.woocommerce-checkout form.checkout > wc-order-attribution-inputs { display: none; }
.woocommerce-checkout form.checkout > #customer_details {
  float: left;
  width: calc(var(--rb-checkout-split) - 1px);
  padding: 56px 48px 72px var(--rb-gutter);
}
.woocommerce-checkout form.checkout > #order_review_heading,
.woocommerce-checkout form.checkout > #order_review {
  margin-left: var(--rb-checkout-split);
  padding-inline: 48px var(--rb-gutter);
}
.woocommerce-checkout form.checkout > #order_review_heading { margin-top: 0; margin-bottom: 0; padding-top: 56px; }
/* flow-root, and it is load-bearing. The order column sits beside a float, in
   the same formatting context, and WooCommerce clearfixes nearly everything it
   prints: every ul.payment_methods and every li.wc_payment_method carries an
   ::after with clear: both. A clear inside this column reaches for the details
   column's float and lands at the bottom of it — on staging that stretched the
   first payment method to 883px, leaving a page-long blank between MBWAY and
   the method under it, and the taller the billing form the bigger the hole
   (review 2026-09-19). A block formatting context keeps those clears in here,
   where there is nothing to clear. It changes nothing visually: the column
   already begins past the float's right edge, so it is not narrowed. */
.woocommerce-checkout form.checkout > #order_review {
  display: flow-root;
  padding-top: 22px;
  padding-bottom: 72px;
}
/* The script's notices, above both columns, in the page gutter. */
.woocommerce-checkout form.checkout > .woocommerce-NoticeGroup:empty { display: none; }
/* On paper, not on the split background: the error tint is translucent. */
.woocommerce-checkout form.checkout > .woocommerce-NoticeGroup { background: var(--rb-paper); }
.woocommerce-checkout form.checkout > .woocommerce-error {
  background: linear-gradient(rgba(248, 75, 77, 0.12), rgba(248, 75, 77, 0.12)), var(--rb-paper);
}
/* WooCommerce 11's checkout script wraps its errors one level deeper than its
   templates do — <div role="alert"><ul class="woocommerce-error" tabindex="-1">
   — so the band is the notice itself, wherever inside the group it sits. */
.woocommerce-checkout form.checkout > .woocommerce-NoticeGroup .woocommerce-error,
.woocommerce-checkout form.checkout > .woocommerce-NoticeGroup .woocommerce-message,
.woocommerce-checkout form.checkout > .woocommerce-NoticeGroup .woocommerce-info,
.woocommerce-checkout form.checkout > .woocommerce-error,
.woocommerce-checkout form.checkout > .woocommerce-message,
.woocommerce-checkout form.checkout > .woocommerce-info {
  margin: 0;
  padding-inline: var(--rb-gutter);
  border: 0;
  border-bottom: 1px solid var(--rb-rule);
}
/* The script focuses the list so a screen reader reads it: keep the focus, lose
   the ring, as for the page notices above. */
.woocommerce-checkout form.checkout .woocommerce-NoticeGroup [tabindex="-1"]:focus { outline: none; }

/* Billing, then shipping and the note, one under the other rather than in
   two 48% floats inside a column that is already half the page. */
.woocommerce-checkout #customer_details.col2-set { display: flex; flex-direction: column; gap: 36px; }
.woocommerce-checkout #customer_details.col2-set::before,
.woocommerce-checkout #customer_details.col2-set::after { content: none; }
.woocommerce .woocommerce-checkout #customer_details .col-1,
.woocommerce .woocommerce-checkout #customer_details .col-2,
.woocommerce-checkout #customer_details .col-1,
.woocommerce-checkout #customer_details .col-2 { float: none; width: auto; max-width: 720px; }

.woocommerce-checkout #customer_details h3,
.woocommerce-checkout #order_review_heading {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rb-label-ink);
  margin: 0 0 22px;
}
/* "Ship to a different address?" is a checkbox sentence, not a label. */
.woocommerce-checkout #ship-to-different-address label {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--rb-ink);
  cursor: pointer;
}
.woocommerce-checkout #ship-to-different-address input { margin: 0; }

/* Half-width rows stay side by side, with a real gap instead of 47% + 47%. */
.woocommerce-checkout .woocommerce-billing-fields__field-wrapper,
.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: 16px;
}
.woocommerce-checkout .woocommerce-billing-fields__field-wrapper > *,
.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper > * { grid-column: 1 / -1; }
.woocommerce-checkout .woocommerce-billing-fields__field-wrapper > .form-row-first,
.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper > .form-row-first { grid-column: 1; }
.woocommerce-checkout .woocommerce-billing-fields__field-wrapper > .form-row-last,
.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper > .form-row-last { grid-column: 2; }
.woocommerce form.checkout .form-row-first,
.woocommerce form.checkout .form-row-last { float: none; width: auto; }
.woocommerce-checkout .woocommerce-billing-fields__field-wrapper::before,
.woocommerce-checkout .woocommerce-billing-fields__field-wrapper::after,
.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper::before,
.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper::after { content: none; }
/* A required field's mark is WooCommerce's own: bold, #a00, from
   .woocommerce form .form-row .required. The theme had a grey rule for it that
   named a weaker selector and never applied; the owner chose the red
   (2026-09-24), so it is gone. */
/* The terms checkbox's sentence and its mark, one run of text. The checkbox
   label is a flex row (§13), which made the sentence, the &nbsp; after it and
   the mark three items 9px apart: the mark stood alone, 22px past the sentence
   (the owner, 2026-09-24: "weird looking asterisk"). Here the label is a block
   with the checkbox inline, hung in the left padding so a wrapped second line
   starts under the first word, not under the box. The box is 13px: 22px is that,
   and the 9px after it. */
.woocommerce form .woocommerce-terms-and-conditions-wrapper .form-row label.woocommerce-form__label-for-checkbox {
  display: block;
  padding-left: 22px;
  line-height: 1.6;
}
.woocommerce-terms-and-conditions-wrapper .woocommerce-form__label-for-checkbox > input[type="checkbox"] {
  /* 5px, and the space WooCommerce's markup leaves before the sentence, make
     the 9px the other checkbox labels have. */
  margin: 0 5px 0 -22px;
  vertical-align: -2px;
}
.woocommerce-checkout .woocommerce-additional-fields textarea { min-height: 96px; }
/* A field's hint — "For an invoice with your tax number on it." under NIF / VAT
   (rb_vat_billing_field()) — reads like the account form's hints, not like a
   label. WooCommerce prints it as a bare span inside the row. */
.woocommerce form .form-row span.description {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  font-weight: 300;
  color: var(--rb-grey);
}
/* The same number back under the billing address, on the order-received page
   and in My account -> Orders (rb_vat_order_details()). */
.rb-order-vat { margin-top: 12px; display: flex; align-items: baseline; gap: 10px; }

/* The order: WooCommerce's review table on the wash, and its payment box
   without the lilac panel and rounded corners of WooCommerce's stylesheet. */
.woocommerce-checkout #order_review table.shop_table { margin: 0 0 28px; background: none; }
.woocommerce-checkout #order_review table.shop_table th,
.woocommerce-checkout #order_review table.shop_table td { padding: 14px 0; }
.woocommerce-checkout #order_review table.shop_table td.product-total,
.woocommerce-checkout #order_review table.shop_table tfoot td { text-align: right; }
.woocommerce-checkout #order_review .order-total th,
.woocommerce-checkout #order_review .order-total td { font-size: 15px; }
.woocommerce-checkout #order_review .woocommerce-shipping-methods { list-style: none; margin: 0; padding: 0; }
.woocommerce-checkout #payment,
.woocommerce-checkout #payment div.payment_box { background: none; border-radius: 0; }
.woocommerce-checkout #payment ul.payment_methods { padding: 0 0 20px; border-bottom: 1px solid var(--rb-rule); }
.woocommerce-checkout #payment ul.payment_methods li { font-size: 14px; line-height: 1.6; margin-bottom: 10px; }
.woocommerce-checkout #payment div.payment_box {
  margin: 8px 0 4px;
  padding: 0 0 0 24px;
  font-size: 13px;
  color: var(--rb-body);
}
.woocommerce-checkout #payment div.payment_box::before { content: none; }
.woocommerce-checkout #payment div.form-row { padding: 20px 0 0; }
.woocommerce-checkout #payment .woocommerce-privacy-policy-text { font-size: 12px; line-height: 1.6; color: var(--rb-grey); }
.woocommerce #payment #place_order,
.woocommerce-page #payment #place_order { float: none; width: 100%; margin-top: 18px; padding: 18px 30px; }

/* The "Returning customer?" and coupon bands take the page gutter, like the
   notices above them; the login form they open does too. */
.woocommerce-checkout #rb-main > .woocommerce > .woocommerce-form-login-toggle > .woocommerce-info,
.woocommerce-checkout #rb-main > .woocommerce > .woocommerce-form-coupon-toggle > .woocommerce-info {
  margin: 0;
  padding-inline: var(--rb-gutter);
  border-bottom: 1px solid var(--rb-rule);
}
.woocommerce-checkout #rb-main > .woocommerce > form.login,
.woocommerce-checkout #rb-main > .woocommerce > form.checkout_coupon {
  margin: 24px var(--rb-gutter);
  max-width: 520px;
}

@media (max-width: 900px) {
  .woocommerce-checkout form.checkout { background: none; min-height: 0; }
  .woocommerce-checkout form.checkout > #customer_details { float: none; width: auto; padding: 36px var(--rb-gutter) 44px; }
  .woocommerce-checkout form.checkout > #order_review_heading,
  .woocommerce-checkout form.checkout > #order_review { margin-left: 0; padding-inline: var(--rb-gutter); background: var(--rb-wash-2); }
  .woocommerce-checkout form.checkout > #order_review_heading { padding-top: 36px; border-top: 1px solid var(--rb-rule); }
  .woocommerce-checkout form.checkout > #order_review { padding-bottom: 48px; }
}
@media (max-width: 520px) {
  .woocommerce-checkout .woocommerce-billing-fields__field-wrapper > .form-row-first,
  .woocommerce-checkout .woocommerce-billing-fields__field-wrapper > .form-row-last,
  .woocommerce-checkout .woocommerce-shipping-fields__field-wrapper > .form-row-first,
  .woocommerce-checkout .woocommerce-shipping-fields__field-wrapper > .form-row-last { grid-column: 1 / -1; }
}

/* ---------------------------------------------------------------
   14. Footer
   --------------------------------------------------------------- */

.rb-footer {
  border-top: 1px solid var(--rb-rule);
  background: var(--rb-paper);
  display: grid;
  grid-template-columns: 1.2fr 1fr 1fr;
  gap: 40px;
  padding: 56px var(--rb-gutter) 44px;
}
.rb-footer__col {
  display: flex;
  flex-direction: column;
  gap: 11px;
}
.rb-footer__col--address { gap: 16px; }
/* The wordmark, at the concept's 128px cap. A logo wider than it is tall would
   otherwise fill the whole column. */
.rb-footer__logo {
  display: block;
  width: 100%;
  max-width: 128px;
}
.rb-footer__logo img,
.rb-footer__logo svg { display: block; width: 100%; height: auto; }
/* The address block sits under the wordmark and its shipping line, so it needs
   its own space rather than the column's 16px rhythm. */
.rb-footer__col--address .rb-label + p { margin-top: 8px; }
.rb-footer a,
.rb-footer p {
  font-size: 13px;
  line-height: 1.7;
  color: var(--rb-body);
}
.rb-footer a:hover { color: var(--rb-accent); }
/* Search, at the foot of the Follow column: under Instagram but a clear 40px below
   it, so it does not read as one more social link. Space rather than a rule — the
   toggle already carries its own underline. */
.rb-footer__search {
  margin-top: 40px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: min(260px, 100%);
}
.rb-footer__search .rb-search-toggle,
.rb-footer__search .rb-search-form { margin-top: 0; }
.rb-footer__search .rb-search-form { width: 100%; }

/* Cart note: the concept's open, labelled field in the summary column, under
   Roast day. It used to be a closed toggle under the line items. */
.rb-cart-note { margin-top: 24px; }
.rb-cart-note .rb-field { font-size: 15px; resize: vertical; }

.rb-search-toggle {
  margin-top: 9px;
  width: fit-content;
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--rb-ink);
  background: none;
  border: 0;
  padding: 0 0 7px;
  border-bottom: 1px solid var(--rb-rule);
  cursor: pointer;
}
.rb-search-toggle:hover { border-bottom-color: var(--rb-ink); }
.rb-search-glass {
  display: inline-block;
  width: 11px;
  height: 11px;
  border: 1px solid currentColor;
  border-radius: 50%;
  position: relative;
}
.rb-search-glass::after {
  content: "";
  position: absolute;
  top: 10px;
  left: 9px;
  width: 5px;
  height: 1px;
  background: currentColor;
  transform: rotate(45deg);
  transform-origin: left center;
}

.rb-search-form {
  margin-top: 9px;
  display: flex;
  align-items: center;
  gap: 10px;
  border-bottom: 1px solid var(--rb-ink);
  padding-bottom: 7px;
}
.rb-search-form[hidden] { display: none; }
.rb-search-form input {
  flex: 1;
  min-width: 0;
  font-family: var(--rb-sans);
  font-weight: 300;
  font-size: 13px;
  color: var(--rb-ink);
  background: none;
  border: 0;
  outline: none;
  padding: 0;
}
.rb-search-form button {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rb-muted);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

@media (max-width: 900px) {
  .rb-footer { grid-template-columns: 1fr; gap: 32px; padding: 40px var(--rb-gutter) 36px; }
}

/* ---------------------------------------------------------------
   15. Touch screens

   A phone has no pointer to hover with, but a tap still applies :hover, and
   leaves it applied until something else is tapped — and the back button
   restores a page with that state still on it, so the wrong colour outlives the
   navigation that should have cleared it.

   The roastery has now met it three times, and the first two fixes were both
   too small. The card's Add to cart kept the accent it was painted on the way
   down (2026-09-19); that fix named four button selectors. A lot's ESPRESSO
   pill was still purple after a tap, a navigation and a back (2026-09-19); that
   fix named every hover that paints a background or a border, and left the ones
   that only change text colour, on the reasoning that you navigate away from a
   link and so never see it. You come back, though — and on 2026-09-20 the
   roastery sent a grid with a chocolatey coffee whose name was lime, because
   `a:hover` had painted it on the way to the product page.

   So: every hover, without an argument about which ones matter. `a:hover` is
   the general rule and comes first; the ones under it are the links whose
   resting colour is not ink, which a reset to ink would otherwise flatten.

   One block rather than a guard beside each rule: a hover is only ever wrong in
   this one context, the resting value has to be spelled out either way, and at
   the end of the file these win at equal specificity. A hover that only
   underlines is left alone — it says the same thing whenever it is read — and
   so is .rb-btn--accent, where the accent is the resting colour and the hover
   is the ink.

   The :not()s are not decoration. A selected chip, a checked option, a filled
   flavour pill and the current account link all carry their state as the same
   property the hover paints, and a reset at equal specificity would strip it.
   --------------------------------------------------------------- */

@media (hover: none) {
  /* Buttons — §4, and WooCommerce's own in §13. */
  .rb-btn:hover { background: var(--rb-ink); color: var(--rb-paper); }
  .rb-btn--paper:hover { background: var(--rb-paper); color: var(--rb-ink); }
  .rb-btn--ghost:hover { background: none; border-color: var(--rb-line); color: var(--rb-ink); }
  .rb-btn--accent:hover { background: var(--rb-accent); color: var(--rb-ink); }
  .woocommerce .button:hover,
  .woocommerce a.button:hover,
  .woocommerce button.button:hover,
  .woocommerce input.button:hover,
  .woocommerce a.button.alt:hover,
  .woocommerce button.button.alt:hover { background: var(--rb-ink); color: var(--rb-paper); }

  /* Chips, options, tags and pills — §4, §9, §10. The pill is the one the
     roastery tapped. */
  .rb-chip:not(.is-selected):not([aria-current="true"]):hover,
  .rb-product-option:not(.is-selected):not(:has(input:checked)):hover { border-color: var(--rb-line); }
  .rb-chip--clear:hover { background: var(--rb-paper); border-color: var(--rb-ink); color: var(--rb-ink); }
  a.rb-tag:hover { box-shadow: none; }
  .rb-pill:not(.rb-pill--fill):hover { background: none; border-color: var(--rb-line); }
  .rb-pill--fill:hover { border-color: transparent; }

  /* Every link, from the base rule in §2 — this is the one that painted a
     coffee's name lime on the grid after a tap (review 2026-09-20). */
  a:hover { color: var(--rb-ink); }
  /* And the links whose resting colour is not ink, which the line above would
     otherwise flatten. Each of these already outranks it on specificity; they
     are listed together so the exceptions can be counted. */
  .rb-lang a:hover { color: var(--rb-body); }
  .rb-footer a:hover { color: var(--rb-body); }
  .rb-sub-photo__caption a:hover { color: var(--rb-paper); }
  .woocommerce-MyAccount-navigation li:not(.is-active) a:hover { color: var(--rb-grey); }
  /* Not links, or not coloured by the rule above: a <button>, a hover that
     reads through a child, and one drawn as a border. */
  .rb-nav__toggle:hover { color: inherit; }
  .rb-catalog__link:hover { color: var(--rb-ink); }
  .rb-quickview__close:hover { border-color: var(--rb-rule); }
  /* A tap leaves :hover on the label; its box answers to focus alone here,
     so tapping anywhere else closes it. */
  button.rb-discovery__label:hover { color: #5F5F5F; }
  .rb-discovery__term:hover .rb-discovery__tip { opacity: 0; visibility: hidden; }
  .rb-discovery__label:focus + .rb-discovery__tip { opacity: 1; visibility: visible; }
  .rb-bundle__item:hover .rb-bundle__name { color: inherit; }
  .rb-search-toggle:hover { border-bottom-color: transparent; }
  .rb-next-group:hover { box-shadow: none; }
  a.rb-stockist__name:hover .rb-stockist__map { color: var(--rb-grey); }

  /* The card's photograph grows under a pointer. A tap left it grown. */
  .rb-product-card:hover .rb-product-card__media img { transform: none; }
}

/* ---------------------------------------------------------------
   16. Motion
   --------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------------------------------------------------------------
   17. Print
   --------------------------------------------------------------- */

@media print {
  .rb-header, .rb-footer, .rb-quickview, .rb-btn { display: none !important; }
  body { color: #000; }
}

/* ---------------------------------------------------------------
   18. Wide screens
   --------------------------------------------------------------- */

/* The whole site at one width (2026-09-23, §8.29 item 17). The catalog and the
   product page stop at --rb-page-max on their own rules (.rb-catalog, .rb-pdp);
   this brings every other page, the checkout included, to the same edges, so
   on a wide monitor the logo, the grid, the text and the footer line up instead
   of the header running to the window's edges round a page in the middle. Only
   above the cap, so nothing at 1440px or below moves. Two kinds of box:
   - content blocks stop at the cap and stand centred, their lines with them
     (as the catalog's do);
   - boxes whose background or rule is meant to run edge to edge — the page
     heroes' photo, the footer's and the home strip's lines, WooCommerce's
     page-level notices and the checkout's two bands (each a band with a line
     under it) — stay full width and pad their inside by --rb-page-pad, so the
     photo or the line still reaches the window's edges while the words line
     up with everything else.
   --rb-page-pad is half the room either side of the cap, plus the gutter; the
   max() keeps the gutter where that is less (it never is above 1440px, but a
   rule copied out of this block stays safe). A new block that should stop at
   the cap goes in the first list below; one that should bleed, in the second. */
@media (min-width: 1441px) {
  :root { --rb-page-pad: calc((100% - var(--rb-page-max)) / 2 + var(--rb-gutter)); }

  .rb-header__bar,
  .rb-mega__inner,
  .rb-split,
  .rb-grid-rules,
  .rb-page-head,
  .rb-section,
  .rb-catalog__head,
  #rb-main > .rb-rule-top,
  #rb-main > .rb-products,
  .woocommerce-checkout form.checkout {
    max-width: var(--rb-page-max);
    margin-inline: auto;
  }

  .rb-footer,
  .rb-meta-strip,
  #rb-main > .woocommerce > .woocommerce-notices-wrapper > *,
  .woocommerce-checkout #rb-main > .woocommerce > .woocommerce-form-login-toggle > .woocommerce-info,
  .woocommerce-checkout #rb-main > .woocommerce > .woocommerce-form-coupon-toggle > .woocommerce-info { padding-inline: max(var(--rb-gutter), var(--rb-page-pad)); }
  /* The narrow forms those two bands open keep their 520px, at the page's left
     edge rather than the window's. */
  .woocommerce-checkout #rb-main > .woocommerce > form.login,
  .woocommerce-checkout #rb-main > .woocommerce > form.checkout_coupon { margin-inline-start: max(var(--rb-gutter), var(--rb-page-pad)); }
  .rb-page-hero__inner {
    left: max(var(--rb-gutter), var(--rb-page-pad));
    right: max(var(--rb-gutter), var(--rb-page-pad));
  }
}
