/** Shopify CDN: Minification failed

Line 87:2 Unexpected "{"
Line 87:3 Expected identifier but found "%"
Line 87:59 Expected identifier but found "%"
Line 114:2 Unexpected "{"
Line 114:3 Expected identifier but found "%"
Line 115:79 Expected identifier but found whitespace
Line 121:3 Expected identifier but found "%"
Line 122:2 Unexpected "{"
Line 122:3 Expected identifier but found "%"
Line 209:2 Unexpected "{"
... and 4 more hidden warnings

**/
/* SHOPIFY_STYLESHEETS_VERSION: 1.0 */


/* CSS from section stylesheet tags */
/* START_SECTION:main-cart-footer (INDEX:28) */
.royal-cart-delivery {
    grid-column: 1 / -1;
    margin: 1.6rem 0 0;
    padding-top: 1.6rem;
    border-top: 1px solid var(--r-rule, rgba(var(--color-foreground), 0.14));
    font-size: 1.4rem;
    line-height: 1.55;
    letter-spacing: 0.01em;
    color: var(--r-muted, rgba(var(--color-foreground), 0.68));
    text-align: left;
  }

  @media screen and (min-width: 750px) {
    .royal-cart-delivery {
      text-align: right;
      max-width: 46rem;
      margin-inline-start: auto;
    }
  }
/* END_SECTION:main-cart-footer */

/* START_SECTION:main-cart-items (INDEX:29) */
.royal-empty__heading {
    margin: 0 0 1.2rem;
  }

  .royal-empty__line {
    margin: 0 0 2.4rem;
    font-size: var(--r-body, 1.6rem);
    line-height: 1.5;
    color: var(--r-muted, rgba(var(--color-foreground), 0.68));
  }

  .royal-empty__links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.8rem 2.4rem;
    margin: 3.2rem 0 0;
    padding: 2.4rem 0 0;
    list-style: none;
    border-top: 1px solid var(--r-rule, rgba(var(--color-foreground), 0.14));
  }

  .royal-empty__link {
    display: inline-flex;
    align-items: center;
    min-height: var(--r-tap, 4.8rem);
    font-size: 1.5rem;
    letter-spacing: 0.02em;
    text-underline-offset: 0.4rem;
    color: rgb(var(--color-foreground));
  }
/* END_SECTION:main-cart-items */

/* START_SECTION:main-collection-banner (INDEX:30) */
.rcb {
    padding-block: var(--r-section-y);
  }

  .rcb__inner {
    display: grid;
    gap: 2.8rem;
    align-items: center;
  }

  {%- comment -%} styles below only apply in overlay mode {%- endcomment -%}

  /* OVERLAY MODE: full-bleed photograph with the title on it, which is what every reference
     collection page does. The band is edge to edge, so the section's own page padding is
     removed and the text carries its own inset instead.

     The media is NOT a sibling column here, it is the ground: one grid cell holds both, and
     the text is stacked on top with grid-area rather than position:absolute, so the band's
     height is still driven by the image and nothing overlaps if the copy runs long. */
  .rcb--overlay {
    padding-block: 0;
  }
  .rcb--overlay .rcb__inner {
    display: grid;
    grid-template-areas: "stack";
    gap: 0;
    max-width: none;
    padding-inline: 0;
  }
  .rcb--overlay .rcb__media,
  .rcb--overlay .rcb__text {
    grid-area: stack;
  }
  .rcb--overlay .rcb__media {
    aspect-ratio: 3 / 2;
    position: relative;
  }
  {%- comment -%}
    THE SCRIM IS PER COLLECTION, because a single value cannot serve both ends. Measuring the
    lower left of each banner, the zone the headline sits in, white type landed at 12:1 over
    Solids and 2.9:1 over Prints. One global strength either fails the pale photographs or
    drowns the dark ones. royal.banner_scrim carries the measured figure per collection and
    falls back to 45 for anything unmeasured; the floor of 30 in the data exists so type
    always sits on something rather than floating on bare cloth.
  {%- endcomment -%}
  {%- liquid
    assign scrim_pct = collection.metafields.royal.banner_scrim.value | default: 45
    assign scrim_a = scrim_pct | divided_by: 100.0
    assign scrim_mid = scrim_pct | times: 0.38 | divided_by: 100.0
  -%}
  /* rises from the lower edge, so the top of the picture is never veiled */
  .rcb--overlay .rcb__media::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(0,0,0,{{ scrim_a }}) 0%, rgba(0,0,0,{{ scrim_mid }}) 38%, rgba(0,0,0,0) 68%);
  }
  .rcb--overlay .rcb__text {
    position: relative;
    z-index: 1;
    align-self: end;
    padding: 0 var(--r-pad-x) 3.2rem;
    max-width: 62rem;
  }
  /* named explicitly: base.css sets color directly on h1, and a direct declaration beats
     any colour inherited from an ancestor however specific that ancestor is */
  .rcb--overlay .rcb__title,
  .rcb--overlay .rcb__eyebrow,
  .rcb--overlay .rcb__desc,
  .rcb--overlay .rcb__desc > p {
    color: #fff;
  }
  .rcb--overlay .rcb__desc { max-width: 46ch; }

  @media screen and (min-width: 990px) {
    .rcb--overlay .rcb__media { aspect-ratio: 21 / 9; }
    .rcb--overlay .rcb__text { padding-bottom: 4.8rem; }
    /* the two column rule below must not win here */
    .rcb--overlay .rcb__inner { grid-template-columns: minmax(0, 1fr); }
  }

  .rcb__eyebrow {
    margin: 0 0 1.2rem;
  }

  .rcb__title {
    margin: 0;
    font-size: var(--r-h2);
    line-height: 1.12;
    letter-spacing: -0.01em;
    max-width: 18ch;
    text-wrap: balance;
  }

  .rcb__desc {
    margin-top: 1.8rem;
    max-width: 54ch;
    font-size: var(--r-body);
    line-height: 1.6;
    color: var(--r-muted);
  }

  .rcb__desc > p {
    margin: 0;
  }

  .rcb__desc > p + p {
    margin-top: 1.2rem;
  }

  /* the ratio is reserved before a byte of image arrives, so the grid below never jumps */
  .rcb__media {
    aspect-ratio: 4 / 3;
  }

  @media screen and (min-width: 990px) {
    .rcb__inner {
      grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
      gap: 6.4rem;
    }

    .rcb__media {
      aspect-ratio: 5 / 4;
    }
  }

  /* No image: do not leave a two-column layout with one empty side. Collapse to a
     centered block with a tighter measure and more air. It has to look chosen. */
  .rcb--plain {
    padding-block: calc(var(--r-section-y) * 1.25);
  }

  {%- comment -%}
    `justify-items: center` makes the text block shrink to fit, so its width becomes whatever
    the longest line of the description happens to be. Everything inside then centres against
    THAT box rather than against the page, and the heading drifts off centre by however much
    the description's last wrap left over. Giving the block the full width of its container
    fixes the reference frame: the box is stable, so the heading and the paragraph share one
    centre line. margin-inline is explicit so the 68rem box cannot sit against the gutter if
    a later change removes .page-width.
  {%- endcomment -%}
  .rcb--plain .rcb__inner {
    grid-template-columns: minmax(0, 1fr);
    max-width: 68rem;
    margin-inline: auto;
    text-align: center;
  }

  .rcb--plain .rcb__text {
    width: 100%;
  }

  .rcb--plain .rcb__title {
    max-width: 22ch;
  }

  .rcb--plain .rcb__desc {
    margin-inline: auto;
    max-width: 58ch;
  }

  .rcb--plain .r-eyebrow::after {
    margin-inline: auto;
  }
/* END_SECTION:main-collection-banner */

/* START_SECTION:main-collection-product-grid (INDEX:31) */
/* ---------- refine bar ---------- */
  .rf {
    border-top: 1px solid var(--r-rule);
    border-bottom: 1px solid var(--r-rule);
    margin-bottom: 3.2rem;
  }

  /* MOBILE: one line, never a stacked block.
     A two-column grid of accordions put three rows of controls above the first product and
     cost most of a phone screen before anything was for sale. This is the shape the
     reference brands use: a single row of labels that scrolls sideways, and the panel for
     whichever one you tap drops underneath the bar rather than pushing it around.
     Still zero JS: <details name> gives the single-open behaviour natively. */
  /* The panel anchors to the LABEL ROW, not to .rf. .rf also wraps the product count and
     the sort control, so top:100% of it dropped the panel underneath "Sort by" and
     "Apply", several rows away from the label that opened it. */
  .rf__groups { position: relative; }

  /* NO overflow on this row, deliberately.
     It was `overflow-x: auto` to let the labels scroll. That silently broke every filter:
     the open panel is positioned against .rf, and an ancestor with a non-visible overflow
     sitting between the two clips it, so the panel opened and was cut off with nothing
     visible. Setting overflow-x also makes overflow-y compute to auto, so it clipped
     downward as well. Three or four short labels fit one line on a 390px screen; if a
     collection ever carries more they wrap to a second line, which is ugly for a moment
     rather than broken forever. */
  .rf__groups {
    display: flex;
    flex-wrap: wrap;
    gap: 0 2rem;
    overflow: visible;
  }

  .rf__group {
    flex: 0 0 auto;
    min-width: 0;
    border-bottom: 0;
  }

  /* scoped to mobile on purpose: this selector is more specific than the desktop
     `.rf__panel` dropdown rule below, so unscoped it would win at every width and break
     the desktop flyout */
  @media screen and (max-width: 749px) {
    .rf__group[open] > .rf__panel {
      position: absolute;
      left: 0;
      right: 0;
      top: calc(100% + 0.8rem);
      z-index: 5;
      /* the shell tone, not the page white. On a white ground an unfilled panel read as
         loose text floating under the bar rather than as a thing that had opened. */
      background: var(--r-shell);
      border: 1px solid var(--r-rule);
      box-shadow: 0 14px 24px -18px rgba(var(--color-foreground), 0.45);
      padding: 1.6rem;
      max-height: 60vh;
      overflow-y: auto;
    }
  }

  .rf__summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    min-height: var(--r-tap);
    font-size: var(--r-small);
    letter-spacing: 0.02em;
    cursor: pointer;
    list-style: none;
  }

  .rf__summary::-webkit-details-marker {
    display: none;
  }

  .rf__summary::after {
    content: '';
    flex: 0 0 auto;
    width: 0.7rem;
    height: 0.7rem;
    margin-inline-start: auto;
    border-right: 1px solid currentColor;
    border-bottom: 1px solid currentColor;
    transform: translateY(-0.2rem) rotate(45deg);
  }

  .rf__group[open] > .rf__summary::after {
    transform: translateY(0.1rem) rotate(225deg);
  }

  .rf__on {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.8rem;
    height: 1.8rem;
    border-radius: 999px;
    background: var(--r-sage-deep);
    color: #fff;
    font-size: 1.1rem;
    line-height: 1;
  }

  .rf__panel {
    padding-bottom: 1.2rem;
  }

  .rf__values {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    column-gap: 2rem;
  }

  .rf__val {
    display: flex;
    align-items: center;
    gap: 1rem;
    min-height: var(--r-tap);
    font-size: var(--r-small);
    color: inherit;
    text-decoration: none;
  }

  .rf__val .r-sw {
    flex: 0 0 auto;
    width: 2rem;
    height: 2rem;
  }

  .rf__val[aria-current='true'] {
    text-decoration: underline;
    text-underline-offset: 0.4rem;
  }

  .rf__n {
    margin-inline-start: auto;
    font-size: 1.2rem;
    color: var(--r-muted);
  }

  /* A swatch-only panel is a grid of dots, not a stacked list of six lone circles.
     4.4rem cells keep the tap target at the 44px floor even though the dot is 2.8rem. */
  .rf__values--sw {
    display: flex;
    flex-wrap: nowrap;
    justify-content: space-between;
    gap: 0.8rem;
  }
  .rf__values--sw .rf__val {
    display: grid;
    place-items: center;
    width: 4.4rem;
    height: 4.4rem;
    padding: 0;
    min-height: 0;
  }
  .rf__values--sw .rf__val[aria-current='true'] .r-sw {
    box-shadow:
      inset 0 0 0 1px rgba(var(--color-foreground), 0.22),
      0 0 0 2px rgb(var(--color-background)),
      0 0 0 3px rgb(var(--color-foreground));
  }

  .rf__count {
    margin: 0;
    display: flex;
    align-items: center;
    min-height: var(--r-tap);
    font-size: 1.3rem;
    letter-spacing: 0.02em;
    color: var(--r-muted);
  }

  .rf__sort {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 1rem;
    padding-bottom: 1.2rem;
  }

  .rf__sort-label {
    font-size: 1.3rem;
    letter-spacing: 0.02em;
    color: var(--r-muted);
  }

  .rf__select,
  .rf__apply {
    min-height: var(--r-tap);
    border: 1px solid var(--r-rule-strong);
    border-radius: 0;
    background: transparent;
    color: inherit;
    font-family: inherit;
    font-size: var(--r-small);
    padding: 0 1.4rem;
  }

  .rf__apply {
    letter-spacing: 0.04em;
    cursor: pointer;
  }

  .rf__active {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.8rem;
    padding-block: 1.2rem;
    border-top: 1px solid var(--r-rule);
  }

  .rf__pill {
    display: inline-flex;
    align-items: center;
    gap: 0.8rem;
    min-height: var(--r-tap);
    padding: 0 1.4rem;
    border: 1px solid var(--r-rule-strong);
    font-size: 1.3rem;
    color: inherit;
    text-decoration: none;
  }

  .rf__pill::after {
    content: '\00d7';
    font-size: 1.6rem;
    line-height: 1;
  }

  .rf__clear {
    display: inline-flex;
    align-items: center;
    min-height: var(--r-tap);
    padding-inline: 0.4rem;
    font-size: 1.3rem;
    color: var(--r-muted);
  }

  @media screen and (min-width: 750px) {
    .rf__bar {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 0 2.4rem;
      min-height: 6.4rem;
    }

    .rf__groups {
      display: flex;
      flex-wrap: wrap;
      gap: 0 2.4rem;
    }

    .rf__group {
      position: relative;
      border-bottom: 0;
    }

    .rf__panel {
      position: absolute;
      top: 100%;
      inset-inline-start: 0;
      z-index: 6;
      min-width: 30rem;
      max-width: 46rem;
      max-height: 40rem;
      overflow-y: auto;
      padding: 1.2rem 2rem 1.6rem;
      background: rgb(var(--color-background));
      border: 1px solid var(--r-rule-strong);
      box-shadow: 0 1.2rem 3.2rem rgba(0, 0, 0, 0.10);
    }

    .rf__values {
      grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
    }

    .rf__count {
      margin-inline-start: auto;
      order: 2;
    }

    .rf__sort {
      order: 3;
      padding-bottom: 0;
    }
  }

  /* ---------- empty state ---------- */
  .rf__empty {
    padding-block: calc(var(--r-section-y) * 1.2);
    text-align: center;
  }

  .rf__empty-title {
    margin: 0;
    font-size: clamp(2rem, 4vw, 2.6rem);
    line-height: 1.2;
  }

  .rf__empty-body {
    margin: 1.2rem 0 0;
    font-size: var(--r-body);
    color: var(--r-muted);
  }
/* END_SECTION:main-collection-product-grid */

/* START_SECTION:royal-404 (INDEX:51) */
.royal-404__inner {
    max-width: 62rem;
    margin-inline: auto;
  }

  .royal-404__h {
    margin: 0 0 1.2rem;
  }

  .royal-404__line {
    margin: 0;
    max-width: 46rem;
  }

  .royal-404__links {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 3.2rem;
    margin: 3.2rem 0 0;
    padding: 2.4rem 0 0;
    list-style: none;
    border-top: 1px solid var(--r-rule, rgba(var(--color-foreground), 0.14));
  }

  .royal-404__link {
    display: inline-flex;
    align-items: center;
    min-height: var(--r-tap, 4.8rem);
    font-size: 1.6rem;
    letter-spacing: 0.02em;
    text-underline-offset: 0.4rem;
    color: rgb(var(--color-foreground));
  }

  .royal-404__search {
    margin-top: 2.4rem;
    padding-top: 2.4rem;
    border-top: 1px solid var(--r-rule, rgba(var(--color-foreground), 0.14));
  }

  .royal-404__search-label {
    display: block;
    margin-bottom: 0.8rem;
    font-size: 1.2rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--r-muted, rgba(var(--color-foreground), 0.68));
  }

  .royal-404__search-row {
    display: flex;
    gap: 0.8rem;
    flex-wrap: wrap;
  }

  .royal-404__search-input {
    flex: 1 1 22rem;
    min-height: var(--r-tap, 4.8rem);
    padding: 0 1.4rem;
    font-size: 1.6rem;
    color: rgb(var(--color-foreground));
    background: transparent;
    border: 1px solid var(--r-rule-strong, rgba(var(--color-foreground), 0.5));
    border-radius: 0;
  }

  .royal-404__search-button {
    min-height: var(--r-tap, 4.8rem);
    padding: 0 2.4rem;
    font-size: 1.5rem;
    letter-spacing: 0.02em;
    color: rgb(var(--color-background));
    background: rgb(var(--color-foreground));
    border: 1px solid rgb(var(--color-foreground));
    border-radius: 0;
    cursor: pointer;
  }
/* END_SECTION:royal-404 */

/* START_SECTION:royal-bestsellers (INDEX:52) */
.rbs__intro { margin-top: 0.8rem; }

  /* one rule underneath the whole strip, each tab carrying its own segment of it, so the
     active tab reads as a selection rather than as a floating underline */
  .rbs__tabs {
    display: flex;
    gap: 2.4rem;
    margin-top: 2.4rem;
    overflow-x: auto;
    scrollbar-width: none;
    border-bottom: 1px solid var(--r-rule);
  }
  .rbs__tabs::-webkit-scrollbar { display: none; }

  /* The active segment is 2px against the strip's 1px hairline, so it reads as a marker
     rather than as a slightly darker piece of the same rule. `margin-bottom: -1px` with a
     2px border lands the segment's bottom edge exactly on the hairline's bottom edge: it
     covers the rule and stands 1px proud of it. A -2px margin would hang the bar below the
     line instead. The 0.2rem of side padding lets the segment run just past the label so it
     underlines the word rather than butting up against its first and last letters. */
  .rbs__tab {
    position: relative;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    min-height: var(--r-tap);
    padding: 0 0.2rem;
    margin-bottom: -1px;
    border-bottom: 2px solid transparent;
    font-size: var(--r-small);
    letter-spacing: 0.02em;
    color: var(--r-muted);
    cursor: pointer;
  }

  /* off-screen, never display:none: a display:none control cannot be focused and the tabs
     would stop working for anyone on a keyboard */
  .rbs__r {
    position: absolute;
    inset: 0;
    margin: 0;
    opacity: 0;
    cursor: pointer;
  }
  .rbs__tab:focus-within {
    outline: 2px solid rgb(var(--color-foreground));
    outline-offset: 3px;
  }

  .rbs__row { margin-top: 2.4rem; }
  .rbs__p {
    flex: 0 0 58%;
    width: 58%;
    min-width: 0;
    scroll-snap-align: start;
  }
  @media screen and (min-width: 750px) {
    .rbs__p { flex-basis: 23%; width: 23%; }
  }

  .rbs__cta { margin-top: 2.8rem; text-align: center; }
/* END_SECTION:royal-bestsellers */

/* START_SECTION:royal-brand (INDEX:53) */
.rbm {
    /* Defensive, not decorative. The tone is supposed to come from the `.color-<scheme>`
       class, but that rule lives in an inline <style> in the head and a future Dawn upgrade
       could move or rename it. --color-background is set by the same class, so this resolves
       to the identical value and the band can never silently go white. */
    background-color: rgb(var(--color-background));
    padding-block: calc(var(--r-section-y) * 1.4);
  }

  .rbm__wrap {
    display: grid;
    gap: 2.4rem;
    align-items: center;
  }

  .rbm__media {
    aspect-ratio: 4 / 5;
    overflow: hidden;
    background: rgba(var(--color-foreground), 0.05);
  }
  .rbm__media > img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .rbm__text {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1.2rem;
  }

  /* .r-h2 ships max-width: 20ch, which is right for section headings and wrong for a
     three-word motto: at desktop width it can break "Royal." onto its own line. */
  .rbm__h { margin: 0; max-width: 24ch; }

  /* Prose to be read, not a caption to be scanned, but still a measure: past roughly 50
     characters the eye loses its place returning to the next line. */
  .rbm__b { margin: 0; max-width: 50ch; }

  .rbm__cta { margin-top: 0.8rem; }

  @media screen and (min-width: 750px) {
    .rbm__wrap {
      grid-template-columns: 46fr 54fr;
      gap: 6.4rem;
    }
    /* Order, not direction. `direction: rtl` would flip the text inside the column as well,
       and the source stays in reading order for a screen reader either way. */
    .rbm--flip .rbm__media { order: 2; }
  }
/* END_SECTION:royal-brand */

/* START_SECTION:royal-cart-extras (INDEX:54) */
.royal-xsell {
    border-top: 1px solid var(--r-rule, rgba(var(--color-foreground), 0.14));
  }

  .royal-xsell__h {
    margin: 0 0 0.8rem;
  }

  .royal-xsell__row {
    margin-top: 2.4rem;
  }

  .royal-xsell__shade {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.4rem;
    font-size: 1.3rem;
    letter-spacing: 0.02em;
    color: var(--r-muted, rgba(var(--color-foreground), 0.68));
  }

  .royal-xsell__shade .r-sw {
    width: 1.4rem;
    height: 1.4rem;
    flex: 0 0 auto;
  }
/* END_SECTION:royal-cart-extras */

/* START_SECTION:royal-collections (INDEX:56) */
.rcx__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.8rem;
    margin-top: 2.4rem;
  }

  .rcx__t {
    position: relative;
    display: block;
    overflow: hidden;
    text-decoration: none;
    color: #fff;
    background: rgba(var(--color-foreground), 0.05);
  }

  /* mobile: lead tile full width and portrait, the rest pair off as squares */
  .rcx__t { grid-column: span 1; }
  .rcx__t:first-child { grid-column: span 2; }
  .rcx__t:nth-child(4):last-child { grid-column: span 2; }

  .rcx__media { aspect-ratio: 1 / 1; }
  .rcx__t:first-child .rcx__media { aspect-ratio: 4 / 5; }

  .rcx__media,
  .rcx__media > img,
  .rcx__media > picture,
  .rcx__media > picture > img,
  .rcx__media > svg {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* Rising from the tile's own lower edge rather than veiling the whole picture: a flat
     scrim greys the two thirds of the image no text ever reaches. Measured for white type
     against the palest of these photographs. */
  .rcx__t::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
      to top,
      rgba(0, 0, 0, 0.62) 0%,
      rgba(0, 0, 0, 0.28) 34%,
      rgba(0, 0, 0, 0) 62%
    );
    pointer-events: none;
  }

  .rcx__body {
    position: absolute;
    inset: auto 0 0 0;
    z-index: 1;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding: 1.6rem;
  }

  .rcx__eyebrow {
    font-size: 1rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    opacity: 0.85;
  }

  .rcx__title {
    font-size: 1.8rem;
    line-height: 1.15;
    letter-spacing: 0.01em;
  }

  .rcx__note {
    font-size: 1.2rem;
    line-height: 1.4;
    opacity: 0.88;
  }

  /* the whole tile is one link, so the focus ring belongs on the tile */
  .rcx__t:focus-visible {
    outline: 2px solid rgb(var(--color-foreground));
    outline-offset: 3px;
  }

  /* A still photograph under a still caption gives no sign the tile is a link. The lift is
     on the image only, so the caption stays put and the type never blurs. */
  .rcx__media > img,
  .rcx__media > picture > img {
    transition: transform 0.6s cubic-bezier(0.2, 0.7, 0.3, 1);
  }
  .rcx__t:hover .rcx__media > img,
  .rcx__t:hover .rcx__media > picture > img {
    transform: scale(1.04);
  }
  @media (prefers-reduced-motion: reduce) {
    .rcx__media > img,
    .rcx__media > picture > img { transition: none; }
    .rcx__t:hover .rcx__media > img,
    .rcx__t:hover .rcx__media > picture > img { transform: none; }
  }

  @media screen and (min-width: 750px) {
    .rcx__grid {
      grid-template-columns: repeat(4, 1fr);
      gap: 1.2rem;
    }

    /* every tile two columns wide, the lead one also two rows tall: auto-placement then
       lays them out as big-left, two-stacked-right, band underneath */
    .rcx__t { grid-column: span 2; }
    .rcx__t:first-child { grid-column: span 2; grid-row: span 2; }
    /* four tiles leave a hole in the last corner, so the fourth runs the full width */
    .rcx__t:nth-child(4):last-child { grid-column: span 4; }

    .rcx__media { aspect-ratio: 16 / 9; }
    /* no ratio on the lead tile: it stretches to two rows plus the gap, which is exactly
       what makes its bottom edge line up with the tile beside it */
    .rcx__t:first-child,
    .rcx__t:first-child .rcx__media { aspect-ratio: auto; height: 100%; }

    .rcx__body { padding: 2.4rem; }
    .rcx__title { font-size: 2.2rem; }
    .rcx__t:first-child .rcx__title { font-size: 2.8rem; }
  }
/* END_SECTION:royal-collections */

/* START_SECTION:royal-complete-set (INDEX:57) */
/* Complete the set.
   The product card's own rules live in snippets/royal-card.liquid, not here. Shopify compiles
   every stylesheet block into compiled_assets/styles.css but only links that file on pages
   where the owning file actually renders, verified on the storefront rather than assumed, so
   card CSS parked in this section would go missing on any page using the card without it. */

/* the pair */

.r-cs__pairs {
  display: grid;
  gap: 4rem;
  margin-top: 2.8rem;
  padding-inline: var(--r-pad-x);
}
.r-cs__head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem 1rem;
  margin-bottom: 1.2rem;
}
.r-cs__shade {
  font-size: var(--r-body);
  letter-spacing: 0.02em;
}
.r-cs__note {
  font-size: 1.3rem;
  color: var(--r-muted);
}

/* no gap between the two halves: the point of the block is that the bed is one thing, and a
   seam down the middle of the picture argues the opposite */
.r-cs__duo {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
}
/* the titles would otherwise collide at the join */
.r-cs__duo > * + * .r-card__name,
.r-cs__duo > * + * .r-card__meta {
  padding-inline-start: 1.2rem;
}

.r-cs__total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.6rem;
  margin: 1.6rem 0 0;
  padding-top: 1.2rem;
  border-top: 1px solid var(--r-rule);
  font-size: var(--r-small);
}
.r-cs__total b {
  font-weight: 500;
  letter-spacing: 0.02em;
}

.r-cs__cta { margin-top: 2.4rem; }

@media screen and (min-width: 750px) {
  .r-cs__pairs {
    grid-template-columns: repeat(2, 1fr);
    gap: 5.6rem 4.8rem;
  }
}
/* END_SECTION:royal-complete-set */

/* START_SECTION:royal-design-row (INDEX:58) */
/* Shop by Design. The scroller itself is the shared .r-shades primitive from royal.css,
     so the two browse rows scroll, snap and gutter identically. Only the tile is new: a
     square of the product's own photograph where the shade row puts a circle. */
  .royal-design-row .r-body {
    margin-top: 1rem;
  }
  .royal-design-row .rdr-row {
    align-items: flex-start;
    gap: 1.2rem;
  }
  .royal-design-row .rdr-tile {
    flex: 0 0 auto;
    display: block;
    width: 9.6rem;
    scroll-snap-align: start;
    text-decoration: none;
    color: inherit;
  }
  @media screen and (min-width: 750px) {
    .royal-design-row .rdr-row {
      gap: 1.6rem;
    }
    .royal-design-row .rdr-tile {
      width: 12.8rem;
    }
  }
  /* .r-media reserves the 1:1 box before the image arrives; this only stops a long press
     turning the swipe into a native image drag. */
  .royal-design-row .rdr-tile img {
    -webkit-user-drag: none;
    user-select: none;
  }
  .royal-design-row .rdr-name {
    display: block;
    margin-top: 0.8rem;
    font-size: 1.2rem;
    letter-spacing: 0.02em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .royal-design-row .rdr-tile:focus-visible {
    outline: 2px solid rgb(var(--color-foreground));
    outline-offset: 3px;
  }
/* END_SECTION:royal-design-row */

/* START_SECTION:royal-pdp-extras (INDEX:63) */
/* ---------------------------------------------------------------------------
   Royal PDP. 1rem = 10px (Dawn sets html { font-size: 62.5% }).
   --------------------------------------------------------------------------- */

/* One-size callout. Duvet covers have a single variant, so the dimension is the
   one fact a customer needs and it gets the largest type in the buy column. */
.rp-onesize {
  display: grid;
  gap: 0.4rem;
  margin: 1.6rem 0 0;
  padding: 1.6rem 1.8rem;
  background: rgba(var(--color-foreground), 0.04);
  border-left: 2px solid rgba(var(--color-foreground), 0.55);
}

.rp-onesize__k {
  font-size: 1.2rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(var(--color-foreground), 0.68);
}

.rp-onesize__v {
  font-size: clamp(2.4rem, 6vw, 3.2rem);
  line-height: 1.1;
  font-weight: 500;
  letter-spacing: 0.01em;
}

.rp-onesize__n {
  font-size: 1.4rem;
  line-height: 1.5;
  color: rgba(var(--color-foreground), 0.68);
}

/* Shared <details> treatment: size guide and the four detail blocks. */
.rp-guide,
.rp-acc details {
  border-top: 1px solid rgba(var(--color-foreground), 0.14);
}

.rp-acc {
  margin: 2.4rem 0 0;
  border-bottom: 1px solid rgba(var(--color-foreground), 0.14);
}

.rp-guide {
  margin: 1.6rem 0 0;
  border-bottom: 1px solid rgba(var(--color-foreground), 0.14);
}

.rp-guide > summary,
.rp-acc summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.6rem;
  min-height: 4.8rem;
  padding: 1.2rem 0;
  cursor: pointer;
  list-style: none;
  font-size: 1.5rem;
  letter-spacing: 0.02em;
}

.rp-guide > summary::-webkit-details-marker,
.rp-acc summary::-webkit-details-marker {
  display: none;
}

.rp-guide > summary::after,
.rp-acc summary::after {
  content: '+';
  font-size: 1.8rem;
  line-height: 1;
  color: rgba(var(--color-foreground), 0.68);
}

.rp-guide[open] > summary::after,
.rp-acc details[open] summary::after {
  content: '\2212';
}

.rp-guide__b,
.rp-acc__b {
  padding: 0 0 2rem;
  font-size: 1.5rem;
  line-height: 1.6;
  color: rgba(var(--color-foreground), 0.82);
}

.rp-guide__b > p,
.rp-acc__b > p {
  margin: 0 0 1.2rem;
}

.rp-guide__b > p:last-child,
.rp-acc__b > p:last-child {
  margin-bottom: 0;
}

.rp-acc__b ul {
  margin: 0 0 1.2rem;
  padding-left: 1.8rem;
}

.rp-acc__b li {
  margin: 0 0 0.8rem;
}

/* Tables scroll on their own so the page body never scrolls sideways. */
.rp-guide__t {
  overflow-x: auto;
  margin: 0 0 1.6rem;
  -webkit-overflow-scrolling: touch;
}

.rp-guide__b table {
  width: 100%;
  min-width: 28rem;
  border-collapse: collapse;
  font-size: 1.4rem;
}

.rp-guide__b caption {
  text-align: left;
  padding: 0 0 0.8rem;
  font-size: 1.2rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(var(--color-foreground), 0.68);
}

.rp-guide__b th,
.rp-guide__b td {
  text-align: left;
  padding: 0.9rem 1.2rem 0.9rem 0;
  border-bottom: 1px solid rgba(var(--color-foreground), 0.14);
  vertical-align: top;
}

.rp-guide__b th {
  font-weight: 500;
  white-space: nowrap;
}

.rp-guide__note {
  font-size: 1.4rem;
  line-height: 1.55;
  color: rgba(var(--color-foreground), 0.68);
}

/* Three reassurance lines, directly above the add-to-cart. About the cloth only. */
.rp-reassure {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 2rem;
  margin: 1.6rem 0 0.4rem;
  padding: 0;
  list-style: none;
}

.rp-reassure li {
  position: relative;
  padding-left: 1.6rem;
  font-size: 1.4rem;
  line-height: 1.5;
  color: rgba(var(--color-foreground), 0.82);
}

.rp-reassure li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.75em;
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  background: rgba(var(--color-foreground), 0.42);
}

/* Dawn prints "Shipping calculated at checkout" straight under the price. It goes:
   it is the first thing said about the product after the number, it says the opposite
   of the free-delivery line under the button, and shipping is not something this brand
   leads with. The shipping policy is still linked from the footer and stated in the
   Delivery accordion, so nothing is hidden from a buyer who wants it. */
.product__info-container .product__tax { display: none; }

/* An empty block wrapper still occupies a row of the info grid. royal_size renders
   nothing on a sheet set, since its sizes are the buttons, which left a visible hole
   between the last pill and the button. :empty will not match here because Liquid
   leaves whitespace inside the div, so the test is "contains no element". */
.product__info-container > div:not(:has(*)) { display: none; }

/* One-line summary under the price. */
.rp-sub {
  margin: 0.8rem 0 2rem;
  font-size: 1.5rem;
  line-height: 1.55;
  color: rgba(var(--color-foreground), 0.82);
}

/* ---------------------------------------------------------------------------
   Shade row. Every dot is a link to a different product, so this is not a variant
   swatch and cannot reuse Dawn's swatch component.
   --------------------------------------------------------------------------- */
.rp-shade { margin: 0 0 1.6rem; }

.rp-shade__h {
  display: flex;
  align-items: baseline;
  gap: 0.8rem;
  margin: 0 0 0.4rem;
}

.rp-shade__k {
  font-size: 1.2rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(var(--color-foreground), 0.68);
}

.rp-shade__v {
  font-size: 1.4rem;
  letter-spacing: 0.02em;
}

/* Fifteen shades is 66rem of dots against a 39rem phone, so the row swipes rather
   than wrapping to three ragged lines. Same behaviour as the homepage shade row.
   The negative inline margin lets the first and last dot reach the page gutter so
   the row reads as continuing off-screen instead of stopping short of the edge. */
.rp-shade__row {
  display: flex;
  gap: 0.2rem;
  margin: 0 -0.8rem;
  padding: 0 0.8rem;
  list-style: none;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.rp-shade__row::-webkit-scrollbar { display: none; }

/* The box is 4.4rem for the tap target; the colour is a 2.8rem circle drawn inside
   it. Sizing the dot itself to 4.4rem would look like a row of buttons. */
.rp-shade__d {
  position: relative;
  display: block;
  flex: 0 0 auto;
  width: 4.4rem;
  height: 4.4rem;
  border-radius: 50%;
  text-decoration: none;
}

.rp-shade__d::before {
  content: '';
  position: absolute;
  inset: 0.8rem;
  border-radius: 50%;
  /* two-tone for prints, flat when the accent equals the base, which is what a
     tonal stripe stores */
  background: linear-gradient(135deg, var(--rp-a) 0 50%, var(--rp-b) 50% 100%);
  /* a Pearl or Frost dot is nearly white and would otherwise vanish on the page */
  box-shadow: inset 0 0 0 1px rgba(var(--color-foreground), 0.18);
}

.rp-shade__d.is-on::after {
  content: '';
  position: absolute;
  inset: 0.1rem;
  border-radius: 50%;
  border: 1px solid rgb(var(--color-foreground));
}

.rp-shade__d:hover::after {
  content: '';
  position: absolute;
  inset: 0.3rem;
  border-radius: 50%;
  border: 1px solid rgba(var(--color-foreground), 0.4);
}

.rp-shade__d:focus-visible {
  outline: 2px solid rgb(var(--color-foreground));
  outline-offset: 2px;
}

/* Sold-out shades stay in the row so the range does not look thinner than it is.
   The rule has to be drawn by ::after, not by the anchor's own background: ::before
   carries the colour circle and a pseudo-element always paints above its element's
   background, so a background rule here would sit underneath the dot and never show.
   Declared after the hover ring because both selectors weigh the same and the strike
   has to survive being hovered. */
.rp-shade__d.is-out::before { opacity: 0.5; }

.rp-shade__d.is-out::after,
.rp-shade__d.is-out:hover::after {
  content: '';
  position: absolute;
  inset: 0.8rem;
  border: 0;
  border-radius: 50%;
  background: linear-gradient(
    to top right,
    transparent calc(50% - 0.5px),
    rgba(var(--color-foreground), 0.6) calc(50% - 0.5px),
    rgba(var(--color-foreground), 0.6) calc(50% + 0.5px),
    transparent calc(50% + 0.5px)
  );
}

/* ---------------------------------------------------------------------------
   Size label row and the duvet one-size line.

   Both share the reference's shape: the label on the left, "Size guide" as a quiet
   link pushed to the right edge of the same row. .rp-optrow is the sheet version,
   rendered inside the picker's fieldset; .rp-size__h is the duvet version, which
   has no picker to sit inside.
   --------------------------------------------------------------------------- */
.rp-optrow,
.rp-size__h {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.6rem;
}

/* the trailing margin matters: with no picker under it this line runs straight into the
   divider above the buy button, which reads as the sentence being cut off */
.rp-size { margin: 0 0 1.6rem; }

.rp-size__k {
  font-size: 1.2rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(var(--color-foreground), 0.68);
}

.rp-optrow__g,
.rp-size__g {
  flex: 0 0 auto;
  font-size: 1.3rem;
  line-height: 1.4;
  color: rgba(var(--color-foreground), 0.68);
  text-decoration: underline;
  text-underline-offset: 0.3rem;
}

.rp-optrow__g:hover,
.rp-size__g:hover { color: rgb(var(--color-foreground)); }

.rp-size__v {
  margin: 0.6rem 0 0;
  font-size: 1.5rem;
  line-height: 1.55;
  color: rgba(var(--color-foreground), 0.82);
}

/* ---------------------------------------------------------------------------
   The one factual line under the add-to-cart button.
   --------------------------------------------------------------------------- */
.rp-note {
  margin: 1rem 0 0;
  font-size: 1.3rem;
  line-height: 1.5;
  color: rgba(var(--color-foreground), 0.68);
}

/* ---------------------------------------------------------------------------
   Extra pillowcases. A card with an Add button, backed by the third variant option.

   The two radios are visually hidden but still focusable and still where Dawn's
   <variant-selects> expects them. The card shows Add or Added depending on which is
   checked, tested with :has() so the toggle needs no script. Focus is forwarded from
   the hidden input to whichever button is currently visible, otherwise tabbing to the
   control would leave no visible focus at all.
   --------------------------------------------------------------------------- */
.rp-pc {
  margin: 0 0 0.4rem;
  padding: 0;
  border: 0;
}

.rp-pc__in {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* The thumbnail is optional and usually absent. Standalone pillowcases are unpublished
   from the Online Store, since Royal does not sell them on their own, and a metafield
   reference to an unpublished product stops resolving on the storefront. The price does
   not care, because it is worked out from this product's own variants, so the card simply
   loses its picture and reads as a line of text with a button, which is what Omar asked
   for. The vertical padding steps up when there is no image to fill the height. */
/* The whole card is the label for the checkbox, so anywhere on it is a valid target
   rather than just the button on the right. */
.rp-pc__card {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  padding: 1rem;
  border: 1px solid rgba(var(--color-foreground), 0.14);
  cursor: pointer;
}

.rp-pc__card:hover { border-color: rgba(var(--color-foreground), 0.4); }

/* no thumbnail to fill the height, so the box needs the padding instead */
.rp-pc__card:not(:has(.rp-pc__m)) { padding: 1.4rem; }

.rp-pc__m {
  flex: 0 0 auto;
  width: 5.6rem;
  height: 5.6rem;
  overflow: hidden;
  background: rgba(var(--color-foreground), 0.04);
}

.rp-pc__m img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.rp-pc__t {
  display: grid;
  gap: 0.2rem;
  flex: 1 1 auto;
  min-width: 0;
}

.rp-pc__n {
  font-size: 1.4rem;
  line-height: 1.3;
  letter-spacing: 0.01em;
}

.rp-pc__s {
  font-size: 1.2rem;
  line-height: 1.4;
  color: rgba(var(--color-foreground), 0.68);
}

.rp-pc__btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 4rem;
  padding: 0 1.4rem;
  border: 1px solid rgba(var(--color-foreground), 0.28);
  border-radius: 999px;
  font-size: 1.3rem;
  letter-spacing: 0.01em;
  cursor: pointer;
  white-space: nowrap;
}

.rp-pc__btn:hover { border-color: rgba(var(--color-foreground), 0.6); }

.rp-pc__p { color: rgba(var(--color-foreground), 0.68); }

/* Only one of the two buttons is ever shown. Default is Add; once the 4-pillowcase
   radio is checked the card flips to Added and the same click target becomes the way
   back out. */
.rp-pc__btn--rm { display: none; }

.rp-pc:has(.rp-pc__in:checked) .rp-pc__btn--add { display: none; }
.rp-pc:has(.rp-pc__in:checked) .rp-pc__btn--rm { display: inline-flex; }

.rp-pc:has(.rp-pc__in:checked) .rp-pc__card {
  border-color: rgb(var(--color-foreground));
  box-shadow: inset 0 0 0 1px rgb(var(--color-foreground));
}

.rp-pc__in:focus-visible ~ .rp-pc__card .rp-pc__btn--add,
.rp-pc__in:focus-visible ~ .rp-pc__card .rp-pc__btn--rm {
  outline: 2px solid rgb(var(--color-foreground));
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   Pillowcase upsell inside the cart and the cart drawer.

   Deliberately quieter than the product-page card: it sits next to a checkout button,
   so it has to read as part of the order rather than as an interruption. Hairline rules
   instead of a filled panel, and the Add button is an outline, never the sage fill,
   which stays reserved for Checkout.
   --------------------------------------------------------------------------- */
.rp-up {
  margin: 0 0 1.6rem;
  padding: 1.6rem 0 0;
  border-top: 1px solid rgba(var(--color-foreground), 0.14);
}

.rp-up__h {
  margin: 0 0 1rem;
  font-size: 1.2rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(var(--color-foreground), 0.68);
}

.rp-up__list {
  display: grid;
  gap: 0.8rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.rp-up__i {
  display: flex;
  align-items: center;
  gap: 1.2rem;
}

.rp-up__m {
  flex: 0 0 auto;
  width: 4.8rem;
  height: 4.8rem;
  overflow: hidden;
  background: rgba(var(--color-foreground), 0.04);
}

.rp-up__m img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.rp-up__t {
  display: grid;
  gap: 0.1rem;
  flex: 1 1 auto;
  min-width: 0;
}

.rp-up__n {
  font-size: 1.4rem;
  line-height: 1.3;
  letter-spacing: 0.01em;
}

.rp-up__s {
  font-size: 1.3rem;
  color: rgba(var(--color-foreground), 0.68);
}

.rp-up__f { flex: 0 0 auto; margin: 0; }

.rp-up__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 4rem;
  padding: 0 1.6rem;
  background: transparent;
  border: 1px solid rgba(var(--color-foreground), 0.28);
  border-radius: 999px;
  color: rgb(var(--color-foreground));
  font-size: 1.3rem;
  letter-spacing: 0.01em;
  cursor: pointer;
}

.rp-up__btn:hover { border-color: rgba(var(--color-foreground), 0.6); }

.rp-up__btn[aria-disabled='true'] { opacity: 0.5; }

.rp-up__btn:focus-visible {
  outline: 2px solid rgb(var(--color-foreground));
  outline-offset: 2px;
}

/* The drawer version is inside the drawer's own padding, so it supplies its own gutter
   and loses the thumbnail below 380px where the row would otherwise crush. */
.rp-up--compact {
  margin: 0;
  padding: 1.6rem;
}

@media screen and (max-width: 379px) {
  .rp-up--compact .rp-up__m { display: none; }
}

/* ---------------------------------------------------------------------------
   Matching pillowcases. A row, not a card grid: there is exactly one match and a
   full-size card here would compete with the product being looked at.
   --------------------------------------------------------------------------- */
.rp-match { margin: 2.4rem 0 0; }

.rp-match__k {
  display: block;
  margin: 0 0 0.8rem;
  font-size: 1.2rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(var(--color-foreground), 0.68);
}

.rp-match__c {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  min-height: 4.8rem;
  padding: 1rem;
  border: 1px solid rgba(var(--color-foreground), 0.14);
  text-decoration: none;
  color: inherit;
}

.rp-match__c:hover { border-color: rgba(var(--color-foreground), 0.4); }

.rp-match__c:focus-visible {
  outline: 2px solid rgb(var(--color-foreground));
  outline-offset: 2px;
}

.rp-match__m {
  flex: 0 0 auto;
  width: 6.4rem;
  height: 6.4rem;
  overflow: hidden;
  background: rgba(var(--color-foreground), 0.04);
}

.rp-match__m img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.rp-match__t {
  display: grid;
  gap: 0.2rem;
  flex: 1 1 auto;
  min-width: 0;
}

.rp-match__n {
  font-size: 1.4rem;
  line-height: 1.3;
  letter-spacing: 0.01em;
}

.rp-match__s,
.rp-match__p {
  font-size: 1.3rem;
  color: rgba(var(--color-foreground), 0.68);
}

.rp-match__go {
  flex: 0 0 auto;
  padding-right: 0.6rem;
  font-size: 1.6rem;
  color: rgba(var(--color-foreground), 0.68);
}

/* ---------------------------------------------------------------------------
   Variant pills.

   Dawn ships these as sharp rectangles with 0.1rem tracking, which is a different
   object from the format chips already running on every collection page. Same
   control, same shape: fully rounded, hairline border, ink fill when chosen.
   The border sits at 0.28 rather than Dawn's token because an interactive boundary
   needs 3:1 under WCAG 1.4.11 and the theme default resolves to 1.32:1.
   --------------------------------------------------------------------------- */
/* EVERY SELECTOR HERE IS SCOPED TO variant-selects ON PURPOSE. Dawn's own rule in
   component-product-variant-picker.css weighs exactly the same as an unscoped copy of
   it, so which one wins would come down to which file the browser applies last, and
   Shopify decides that for section {% stylesheet %} bundles, not this file. Naming the
   custom element as an ancestor makes these rules heavier and takes load order out of
   the question. */
variant-selects .product-form__input--pill input[type='radio'] + label {
  min-height: 4.8rem;
  display: inline-flex;
  align-items: center;
  margin: 0.8rem 0.6rem 0 0;
  padding: 0 2rem;
  border: 1px solid rgba(var(--color-foreground), 0.28);
  border-radius: 999px;
  font-size: 1.4rem;
  letter-spacing: 0.01em;
}

variant-selects .product-form__input--pill input[type='radio'] + label:before { box-shadow: none; }

variant-selects .product-form__input--pill input[type='radio']:hover + label,
variant-selects .product-form__input--pill input[type='radio'] + label:hover {
  border-color: rgba(var(--color-foreground), 0.6);
}

/* SELECTION IS AN OUTLINE, NOT A FILL. Filled black made the chosen pill the highest
   contrast element on the page, ahead of the buy button, which put the loudest thing on
   a choice already made rather than on the action still to take. A 2px border plus a
   slightly heavier weight reads as clearly chosen while leaving the sage button the only
   filled object in the column.

   Not colour alone: the border weight and the type weight both change, so the state is
   still perceivable without relying on hue. The border is drawn inset via box-shadow
   rather than by thickening the real border, because growing a 1px border to 2px would
   reflow the pill by a pixel on each side and nudge the whole row. */
variant-selects .product-form__input--pill input[type='radio']:checked + label {
  background-color: rgb(var(--color-background));
  border-color: rgb(var(--color-foreground));
  box-shadow: inset 0 0 0 1px rgb(var(--color-foreground));
  color: rgb(var(--color-foreground));
  font-weight: 500;
}

/* The option name above the pills, matched to the shade label so the buy column
   has one label style rather than three. */
variant-selects .product-form__input .form__label,
.product-form__input .form__label {
  font-size: 1.2rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(var(--color-foreground), 0.68);
  margin-bottom: 0;
}

/* ---------------------------------------------------------------------------
   Buy buttons.

   Dawn gives Add to cart button--secondary whenever dynamic checkout is on, so the
   real action renders as an outline and Shopify's Buy it now takes the filled
   treatment. That reads as the wrong button being primary. Inverted here: Add to
   cart is the one filled element in the column, in the brand's action colour, and
   Buy it now steps back to a quiet outline.
   --------------------------------------------------------------------------- */
/* .button--secondary remaps --color-button to the page background, which is the whole
   reason Add to cart renders as an outline. Setting the two variables to `inherit`
   hands back the scheme's real button colour from the ancestor that declared it,
   so this follows whatever Omar picks in theme settings instead of pinning a hex. */
.product-form__submit.button--secondary {
  --color-button: inherit;
  --color-button-text: inherit;
}

.product-form__submit {
  min-height: 5.2rem;
  font-size: 1.4rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.product-form__submit[disabled] { opacity: 0.45; }

/* Shopify injects its own styling for this button at runtime, so the override is
   scoped to the form wrapper to outweigh it rather than trade blows at equal weight. */
.product-form__buttons .shopify-payment-button__button--unbranded {
  min-height: 5.2rem;
  background: transparent;
  border: 1px solid rgba(var(--color-foreground), 0.28);
  color: rgb(var(--color-foreground));
  font-size: 1.4rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  box-shadow: none;
}

.product-form__buttons .shopify-payment-button__button--unbranded:hover:not([disabled]) {
  background: transparent;
  border-color: rgba(var(--color-foreground), 0.6);
}

/* Cross-sell band. One card, because there is exactly one matching partner. */
.rp-cross {
  padding-top: 4.8rem;
  padding-bottom: 4.8rem;
  border-top: 1px solid rgba(var(--color-foreground), 0.14);
}

.rp-cross__h {
  margin: 0 0 2.4rem;
  font-size: clamp(2.1rem, 4.4vw, 2.8rem);
  line-height: 1.15;
  letter-spacing: 0.01em;
  font-weight: 400;
}

/* The row swipes on a phone and lays out as five columns on desktop. Full-bleed on
   mobile via negative margin plus matching scroll padding, so the first card starts on
   the page gutter and the last one can still clear it. */
.rp-row {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 62vw;
  gap: 1.2rem;
  margin-inline: calc(var(--r-pad-x, 2rem) * -1);
  padding-inline: var(--r-pad-x, 2rem);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.rp-row::-webkit-scrollbar { display: none; }

.rp-row > * { scroll-snap-align: start; }

@media screen and (min-width: 750px) {
  .rp-cross {
    padding-top: 7.2rem;
    padding-bottom: 7.2rem;
  }

  /* THE ITEM COUNT VARIES FROM TWO TO FIVE, so neither a fixed column count nor equal
     1fr tracks works: repeat(5, 1fr) leaves a hole when a family has four members, and
     1fr auto-columns stretch a two-card row into two half-page cards. Flex with a grow
     basis and a hard cap gives both, cards sized sensibly and packed from the left
     whatever arrives. The kids sheet family has exactly two members, which is the case
     that exposed it. */
  .rp-row {
    display: flex;
    flex-wrap: wrap;
    gap: 1.6rem;
    margin-inline: 0;
    padding-inline: 0;
    overflow: visible;
  }

  .rp-row > * {
    flex: 1 1 20rem;
    max-width: 28rem;
  }
}

/* ---------------------------------------------------------------------------
   Mobile media, full bleed.

   The gallery sat inside the page gutter at 356px on a 390px screen, which is what
   made the product look small: a 2048px square shot rendered into a box smaller than
   the phone. Cancelling the gutter on the media column only takes it edge to edge and
   leaves the buy column indented, which is how every premium mobile PDP reads. The
   info column keeps its own padding, so nothing else moves.
   --------------------------------------------------------------------------- */
@media screen and (max-width: 749px) {
  /* DO NOT full-bleed the media wrapper. Dawn already does it one level down:
     slider-component.slider-mobile-gutter carries margin-inline: -1.5rem, so the slider
     is edge to edge on its own. Adding a negative margin on the wrapper as well applies
     the gutter twice and pushes the whole gallery 15px off the left of the screen.

     What actually kept the picture small is the slide, not the wrapper. Dawn gives each
     .product__media-item a 15px left margin and sizes it short of the track so the next
     image peeks in from the right, which on a 390px screen spends 34px of a 2048px square
     photograph on an affordance the thumbnails underneath already provide. Squaring the
     slide to the full track takes the image from 356 to 390 across.

     Four classes deep because section-main-product.css sets
     `.product__media-list .product__media-item { width: calc(100% - 3rem - gutter) }` at the
     same weight as a two-class copy of it and loads after this bundle, so an equal-specificity
     override silently loses. Same reason the first-child margin is named explicitly: the
     mobile slider indents the leading slide, and that indent is the other half of the 34px. */
  .product .product__media-wrapper .product__media-list .product__media-item,
  .product .product__media-wrapper .product__media-list .product__media-item:first-child {
    width: 100%;
    max-width: 100%;
    margin-left: 0;
    scroll-margin-left: 0;
  }

  /* the thumbnail strip is not the picture, so it stays on the text grid. 1.5rem to
     match .page-width, not the Royal gutter, so it lines up with the title below. */
  .product__media-wrapper .thumbnail-list {
    padding-inline: 1.5rem;
  }
}

/* ---------------------------------------------------------------------------
   Mobile sticky add-to-cart. CSS only.

   The sticky element is the buy-buttons block wrapper, a direct child of
   .product__info-container, so its travel is bounded by that container: it
   releases at the end of the buy column and can never reach, overlap or trap
   the footer. The container gets matching bottom padding so the final line of
   the last accordion is never sitting underneath the bar.
   --------------------------------------------------------------------------- */
@media screen and (max-width: 749px) {
  .product__info-wrapper,
  .product__info-container {
    overflow: visible;
  }

  /* A sticky box can travel to the bottom of its containing block's padding box and
     no further. Reserving 15rem, more than the bar can be (two stacked buttons plus
     its own padding), guarantees the bar comes to rest inside the reserved space
     rather than on top of the last line of the final accordion. */
  .product__info-container {
    padding-bottom: 15rem;
  }

  /* The reserved space reads as the gap before the next section, so the cross-sell
     band tightens up on mobile instead of stacking a second gap on top of it. */
  .rp-cross {
    padding-top: 2.4rem;
  }

  .product__info-container > div:has(> product-form) {
    position: sticky;
    bottom: 0;
    z-index: 3;
    padding: 1.2rem 0 calc(1.2rem + env(safe-area-inset-bottom, 0px));
    background: rgb(var(--color-background));
    box-shadow: 0 -1px 0 rgba(var(--color-foreground), 0.14);
  }

  .product__info-container > div:has(> product-form) .product-form__submit {
    min-height: 4.8rem;
  }
}
/* END_SECTION:royal-pdp-extras */

/* START_SECTION:royal-reviews (INDEX:64) */
/* The whole block, heading included, stays out of the layout until there are reviews
     to read.

     TESTING FOR "HAS CHILDREN" IS NOT ENOUGH, which is what the first version did. With
     no reviews Judge.me still injects a full widget, headed "Customer Reviews" over "Be
     the first to write a review", and that measured 376px of empty state on every product
     page. The real signal is the attribute it stamps on its own root:
     data-number-of-reviews="0". So the section is hidden by default and only revealed by a
     widget whose count is something other than zero.

     To invite reviews instead of hiding, drop the :not(...) and the section will show the
     "Be the first" panel. */
  .royal-reviews {
    display: none;
  }

  .royal-reviews:has(.jdgm-rev-widg:not([data-number-of-reviews='0'])) {
    display: block;
  }

  .royal-reviews__h {
    margin: 0 0 2.4rem;
  }
/* END_SECTION:royal-reviews */

/* START_SECTION:royal-sale-spotlight (INDEX:65) */
.rsp { padding-block: 4.8rem; }

  .rsp__head { display: block; }
  .rsp__ctawrap { margin-top: 2.4rem; }
  .rsp__eyebrow {
    margin: 0 0 0.8rem;
    font-size: 1.2rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
  }
  .rsp__h { margin: 0; }
  .rsp__b { margin: 0.8rem 0 0; max-width: 46ch; }
  .rsp__cta { flex: 0 0 auto; }

  .rsp__row { margin-top: 2.8rem; }

  /* the wrapper is the positioning context for the badge, which has to sit on the card's
     image without the card itself knowing anything about sales */
  .rsp__p {
    position: relative;
    flex: 0 0 58%;
    width: 58%;
    min-width: 0;
    scroll-snap-align: start;
  }


  .rsp__left,
  .rsp__gone {
    margin: 0.6rem 0 0;
    font-size: 1.2rem;
    letter-spacing: 0.04em;
  }

  @media screen and (min-width: 750px) {
    .rsp { padding-block: 6.4rem; }

    /* Grid areas put the button back beside the heading WITHOUT moving it in the markup, so
       the phone keeps it under the carousel and there is still only one link in the DOM.
       `align-items: end` sits it on the heading's baseline rather than centred on the block,
       so a two line heading does not drag it down. */
    .rsp {
      display: grid;
      grid-template-columns: 1fr auto;
      grid-template-areas:
        "head cta"
        "row  row";
      align-items: end;
      column-gap: 3.2rem;
    }
    .rsp__head { grid-area: head; }
    .rsp__ctawrap { grid-area: cta; margin-top: 0; }
    .rsp__row { grid-area: row; }

    .rsp__p { flex-basis: 23%; width: 23%; }
  }
/* END_SECTION:royal-sale-spotlight */

/* START_SECTION:royal-shade-row (INDEX:67) */
/* The radios ARE the state. Kept in the DOM and reachable by keyboard, moved to opacity 0
     rather than display:none, because a display:none control cannot be focused and the whole
     filter would stop working for anyone on a keyboard. */
  .rsx { position: relative; }
  .rsx__r {
    position: absolute;
    inset: 0;
    margin: 0;
    opacity: 0;
    cursor: pointer;
  }
  .rsx .r-shade:focus-within .r-sw {
    outline: 2px solid rgb(var(--color-foreground));
    outline-offset: 3px;
  }

  /* ONE LINE, SWIPED. Twelve circles do not fit a 390px screen, and the seventh sitting cut
     off at the edge is the affordance that says there is more: a row that visibly stops mid
     circle reads as scrollable in a way a tidy wrapped block does not.

     The rule stays on the ITEMS rather than the container. On a scroll container the border
     is painted on the border box, so it would hang at the viewport edge while the circles
     travelled underneath it; carried by the items, with the column gap at zero, the segments
     meet into one continuous line that moves with them and stops where they stop. */
  .rsx .r-shades {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    column-gap: 0;
    align-items: flex-start;
    padding-bottom: 0;
    scroll-snap-type: x proximity;
  }

  /* min-width 0 so a long label cannot push its column wider than its grid track and drag the
     whole line out of alignment. The border stays 2px selected or not and only its COLOR
     changes: swapping 1px for 2px on selection would shift every label on the line by a pixel
     as you tapped along the row. NO fixed height anywhere; a capped height once clipped a
     label to "NEU". */
  .rsx .r-shade {
    position: relative;
    cursor: pointer;
    flex: 0 0 auto;
    /* wide enough for the longest label, "Charcoal", so no item is narrower than its own
       text and the underline segments stay an even rhythm rather than a ragged one */
    min-width: 5.6rem;
    gap: 0.6rem;
    justify-content: flex-start;
    align-items: center;
    text-align: center;
    padding: 0.5rem 0.4rem 0.9rem;
    border-bottom: 2px solid var(--r-rule);
  }
  .rsx .r-shade > .r-sw { flex: 0 0 auto; }
  .rsx .r-shade .r-sw { width: 2.8rem; height: 2.8rem; }

  /* muted until selected, so the row reads as one active label and eleven options rather than
     twelve equal shouts; the generated rule above restores full foreground */
  .rsx .r-shade__name {
    max-width: none;
    overflow: visible;
    white-space: normal;
    font-size: 1.1rem;
    line-height: 1.25;
    color: var(--r-muted);
  }

  .rsx__grid { margin-top: 2.4rem; }

  /* .r-row sizes its direct children, so this wrapper has to be the flex item itself */
  .rsx__p {
    display: block;
    flex: 0 0 58%;
    width: 58%;
    min-width: 0;
    scroll-snap-align: start;
  }
  @media screen and (min-width: 750px) {
    .rsx__p { flex-basis: 23%; width: 23%; }
  }

  .royal-shade-row .r-body { margin-top: 1rem; }
/* END_SECTION:royal-shade-row */

/* CSS from snippet stylesheet tags */
/* START_SNIPPET:royal-card (INDEX:107) */
/* Royal product card.
   Base classes are in assets/royal.css, which this file does not own and does not repeat.
   These are the rules that file does not carry. */

.r-cardw { display: block; }

/* Live price on top, previous price under it on its own line. --r-muted is 0.68 alpha on the
   foreground, which still clears 4.5:1; going paler would fail. No red: the strike, the word
   "Was" and the badge are already three independent signals, so nothing here depends on
   colour alone. */
.r-card__meta { font-variant-numeric: tabular-nums; }
.r-card__now { display: block; }
.r-card__was {
  display: block;
  margin-top: 0.4rem;
  font-size: 1.2rem;
  color: var(--r-muted);
  text-decoration: line-through;
}

/* Markdown badge. .r-media is already position:relative in royal.css, so it is the
   positioning context and no wrapper is needed. Neutral by design: it sits on collection
   grids and white rows as well as inside the clay sale band, so it takes the page's own
   ink rather than a sale colour, and the sale section overrides it locally. */
.r-badge {
  position: absolute;
  z-index: 1;
  top: 0.8rem;
  left: 0.8rem;
  padding: 0.5rem 0.9rem;
  background: rgb(var(--color-background));
  color: rgb(var(--color-foreground));
  border: 1px solid rgba(var(--color-foreground), 0.5);
  font-size: 1.1rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
}

/* The swatch row is a row of real links, so every dot needs a hit box. A full 4.8rem per dot
   would be 28.8rem across six, wider than a 58vw card on a 390px phone, so the target settles
   at 3rem by 3.2rem and the row absorbs the gap instead of adding to it. This is the one spot
   the 4.8rem floor is not met, and it is a geometric limit rather than a preference. */
.r-cardw .r-swatches { gap: 0; }
.r-sw__hit {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 3rem;
  min-height: 3.2rem;
  text-decoration: none;
  border-radius: 50%;
}
.r-sw__hit:focus-visible {
  outline: 2px solid rgb(var(--color-foreground));
  outline-offset: -2px;
}
/* the shade the shopper is already on, marked so the row reads as a position and not a menu */
.r-sw__hit[aria-current] .r-sw {
  outline: 1px solid rgba(var(--color-foreground), 0.55);
  outline-offset: 2px;
}
/* END_SNIPPET:royal-card */