/* ════════════════════════════════════════════════════════════════════
   Rosewood B2C Sign-Up — brand restyle
   Tokens mirror rwhg-web-head (apps/web/src/app/globals.css). Typography per
   docs/internal/brand-typography-reference.md, extracted from the live AEM
   clientlib. (The design study these were first drafted in,
   frontend-mockup/, is retired — it stood on Google Fonts placeholders for
   Austin and Engravers and this file supersedes it.)
   ════════════════════════════════════════════════════════════════════ */

/* ── Brand faces, self-hosted ──
   Hotlinking the AEM clientlib fonts is impossible, not merely undesirable:
   @font-face always fetches in CORS mode and those URLs serve no
   Access-Control-Allow-Origin, so the browser discards the bytes and the
   page silently renders in a fallback forever. */
@font-face {
  font-family: "Austin Light Web";
  src: url("fonts/Austin-Light-Web.woff2") format("woff2");
  font-weight: 300; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Austin Light Web";
  src: url("fonts/Austin-LightItalic-Web.woff2") format("woff2");
  font-weight: 300; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "Engravers Gothic Bold";
  src: url("fonts/EngraversGothicBold.otf") format("opentype");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "NotoKufiArabicBold";
  src: url("fonts/NotoKufiArabic-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

/* ── Tokens ── */
:root {
  --green: #013d2d;
  --green-hover: #012a20;
  --cream: #fafbf5;
  --brass: #9a7b4a;
  --ink: #1c2620;                /* 14.98:1 on the cream ground */
  --ink-soft: #54605a;           /*  6.31:1 */
  /* 9.72:1 — for small secondary type. Austin Light has hairline strokes, and
     at 13-14px the 6.31:1 tone was the least legible thing on the page even
     though it passes 1.4.3 (which measures luminance only, never stroke
     weight). Still clearly recessive against --ink. */
  --ink-mid: #3a443e;
  --hairline: #e0d9c6;          /* decorative dividers only — see --control-line */
  --secondary: #ece6d5;
  /* WCAG 1.4.11 wants 3:1 against the page for anything that identifies a
     control. A field here has no box — the underline IS the control's
     boundary, and the checkbox square IS the checkbox — so both are held to
     that bar. The old #c9c2ae underline measured 1.71:1 and #b3ac93 checkbox
     borders 2.18:1; #8f8871 measures 3.40:1 on the cream ground. */
  --control-line: #8f8871;
  --field-line: var(--control-line);
  --field-line-hover: var(--ink-soft);   /* 6.31:1 — a clear step up on hover */
  --destructive: #a2452f;

  /* ONE text face. Austin sets everything on the page; the Submit button is
     the single exception and uses --font-button below. */
  --font-serif: "Austin Light Web", Georgia, "Times New Roman", serif;
  --font-button: "Engravers Gothic Bold", "Arial Narrow", system-ui, sans-serif;

  /* Austin ships in exactly one weight. The AEM clientlib publishes
     Austin-Light-Web and Austin-LightItalic-Web and nothing else — there is
     no Austin Regular, Medium or Bold to load. So weight cannot separate the
     label tier from the value tier here: requesting 400 or 500 selects the
     300 face and renders identically, and requesting 600+ makes the browser
     SYNTHESISE a bold, which on a high-contrast serif thickens the stems
     without touching the hairlines and reads as a rendering fault.

     The distinction is carried by the axes that remain:
        size     14px label      vs 16px value
        case     UPPERCASE       vs sentence case
        tracking 0.10em          vs 0
     Tracking went 0.14 -> 0.16em to compensate for the lost face, then back to
     0.10em on client review (2026-08-04): the caps read as too loosely spaced
     at 0.16em. Do NOT "restore" 0.16em — the tightening is deliberate. Size and
     case still carry the label/value split on their own, and 0.10em is a long
     way from the 0 the value tier sits at, so the axis survives the cut. What
     was true before is still true: Engravers is a wide monoline gothic that
     reads as a label at any tracking, while Austin's caps are narrow and
     high-contrast, so if these two ever need different tracking, split this
     into --label-track and --button-track rather than reopening the value.

     Colour is deliberately NOT one of these axes any more. The label register
     inside a field is a ::placeholder, which is never on screen at the same
     time as that field's value, so a colour step between them separated two
     things nobody can see together — see .rw-input::placeholder. Colour still
     separates the two secondary blocks (--ink-mid, 9.72:1) from body copy
     (--ink, 14.98:1), which is a distinction you CAN see at once. */
  --serif-weight: 300;
  --button-weight: 700;
  --label-track: 0.10em;
  /* 14px. Was 11px, then 13px for scale against the value tier; 14px is for
     legibility, because Austin Light has no heavier cut to reach for and its
     hairlines are what carry the caps at this size. Compared against 13px
     side by side in every locale — one extra pixel is the difference between
     delicate and faint, and it costs 3-4px of page height.

     Note this is the LABEL register only. --button-size keeps the Submit
     label where it was, so changing labels never silently resizes the CTA. */
  --label-size: 0.875rem;
  --button-size: 0.8125rem;
  /* What the guest types, and the interest-tile text. 16px is the floor:
     mobile Safari zooms the viewport on focus for any field below it. */
  --value-size: 1rem;
  --lede-em-style: italic;

  --ease: cubic-bezier(0.32, 0.72, 0, 1);
}

/* ── Per-script typography ──
   The live site swaps .austin/.engravers per script with duplicated rules.
   We override the TOKENS instead: one declaration point, every consumer
   adapts, and no extra class has to reach the markup. :lang() matches on
   language-subtag prefix, so these hit the documentElement.lang values
   app.js:52 already sets (zh-cn, zh-hk, ja-jp, ar-ae, ar-sa).

   Austin covers no CJK and no Arabic. Chinese + Japanese webfonts total
   28.5 MB, so this page names the same typographic ROLES using faces
   already on every CJK device — mirroring the brand's own
   `.austin:lang(ar) -> Times New Roman` precedent. Zero bytes.

   These blocks used to raise --label-size to 13px because 11px is unreadable
   in Han glyphs. The Latin default has since passed that on its own (13px for
   scale, then 14px for legibility), so the override became a no-op and was
   dropped — one label size, every script.

   THE BRAND FACE STAYS FIRST IN EVERY STACK. Font fallback is resolved per
   character, not per element, so "Austin, Times" means Latin characters
   render in Austin and only the characters Austin has no glyph for fall
   through. Naming the script font alone dragged every Latin run into it as
   well — the country list is English in all twelve locales, so on Arabic
   "United Arab Emirates" was rendering in Times while the same string on
   every other locale rendered in Austin. Verified per-glyph with
   CSS.getPlatformFontsForNode, not by eye.

   The label tier no longer names a face of its own, so these blocks only set
   --font-serif (every text role) and --font-button (the Submit button). CJK
   labels are now Mincho/Songti like everything else rather than the sans they
   used to borrow; with the face axis gone they lean on size, colour and the
   small tracking below, exactly as the Latin labels do. */
html:lang(zh) {
  --font-serif: "Austin Light Web", "Songti SC", STSong, SimSun, "Noto Serif CJK SC", serif;
  --font-button: "Engravers Gothic Bold", "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
  /* 300 maps to Songti Light (too thin); 400 is the readable weight here. */
  --serif-weight: 400;
  --label-track: 0.04em;        /* tracked caps do not suit CJK */
  --lede-em-style: normal;      /* synthetic oblique looks wrong on CJK */
}
html:lang(ja) {
  --font-serif: "Austin Light Web", "Hiragino Mincho ProN", "Yu Mincho", YuMincho, "Noto Serif CJK JP", serif;
  --font-button: "Engravers Gothic Bold", "Hiragino Sans", "Yu Gothic", YuGothic, "Noto Sans CJK JP", sans-serif;
  --serif-weight: 400;
  --label-track: 0.04em;
  --lede-em-style: normal;
}
html:lang(ar) {
  /* Times New Roman is the brand's own Arabic fallback; it sits behind
     Austin rather than replacing it. */
  --font-serif: "Austin Light Web", "Times New Roman", Times, serif;
  /* Engravers has no Arabic glyphs, so the Kufi behind it is what actually
     renders the Submit label on these locales — this is the only remaining
     consumer of NotoKufiArabic-Bold.woff2 now that labels are Austin. */
  --font-button: "Engravers Gothic Bold", "NotoKufiArabicBold", "Noto Kufi Arabic", Tahoma, sans-serif;
  --label-track: 0;             /* tracking breaks Arabic cursive joins outright */
  --lede-em-style: normal;
}

/* ── Base ──
   box-sizing and the body margin reset are declared HERE, not inherited from
   Bootstrap Reboot: mun-confirmed.html loads this stylesheet WITHOUT
   Bootstrap, and .signup-container's max-width + padding needs border-box. */
*, *::before, *::after { box-sizing: border-box; }

/* min-height:100vh + column flex pins the green base rule to the bottom of
   the modal instead of leaving bare cream beneath it — the success view is
   only ~200px tall, so without this the rule floats a quarter of the way
   down the page.

   This costs no test signal. documentElement.scrollHeight already floors at
   the viewport height, and every locale's content now fits inside 885px, so
   T6's height metric reads exactly 885 across the board with or without
   this rule. Content TALLER than the viewport still reports its true height
   either way — min-height only raises a floor that already exists.
   #submitBtn's rect bottom, which T6 also records, is the metric carrying
   the real signal now. */
body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: var(--cream);
  color: var(--ink);
  font-family: var(--font-serif);
  font-weight: var(--serif-weight);
  font-size: 1rem;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ── Page frame ──
   72rem, not 46rem. Inside the 689px fancybox modal the cap never binds —
   the container is viewport-width there and the padding sets the 609px
   content width. It only decides the STANDALONE page, and 46rem pinched
   that to 608px of content against the pre-restyle 1027px. 72rem restores
   it: 1152px of container, 1024px of content, within a few pixels of what
   subscription.rosewoodhotelgroup.com rendered before.

   Nothing here has to hold a long line at that width — .rw-lede caps at
   34rem and .rw-foot-text at 26rem, so the prose keeps a readable measure
   while the FIELDS get the room back. */
.signup-container {
  flex: 1 0 auto;               /* grow to push the base rule to the bottom */
  width: 100%;
  max-width: 72rem;
  margin: 0 auto;              /* auto cross-axis margins still centre it */
  padding: 3rem 2.5rem 2.5rem;
}

@media (min-width: 800px) {
  .signup-container { padding: 5rem 4rem 4rem; }
}

.rw-base-rule { flex: none; height: 15px; background: var(--green); }

/* ── Masthead ──
   No decorative rule above the heading: the display face is the page's
   opening statement and a hairline above it reads as a second, competing
   one. The 15px forest-green base rule at the foot carries the motif. */
.rw-masthead { margin-bottom: 2rem; }

/* 0.5rem below, not 0.875rem — tightened on client review (2026-08-04) to pull
   the lede up under the display line. line-height:1.08 leaves almost no
   internal leading on a 40px face, so this margin IS the visible gap; the
   heading and lede now read as one masthead block rather than two. The lede
   keeps its own 0.875rem below, which is what separates the block from the
   mandatory note. */
.rw-display {
  font-family: var(--font-serif);
  font-weight: var(--serif-weight);
  font-size: 2.5rem;
  line-height: 1.08;
  color: var(--ink);
  margin: 0 0 0.5rem;
}

.rw-lede {
  font-size: 1.25rem;
  line-height: 1.4;
  color: var(--ink);
  max-width: 34rem;
  margin: 0 0 0.875rem;
}

.rw-lede-em { font-style: var(--lede-em-style); }

.rw-note {
  font-family: var(--font-serif);
  font-weight: var(--serif-weight);
  font-size: var(--label-size);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  color: var(--ink-mid);        /* secondary to the lede, but not faint */
  margin: 0;
}

/* ── Fields ──
   Six equal columns, count set by the width available rather than fixed at
   three. `auto-fit` with a 16rem floor lands on 2-up inside the 689px modal
   (292px each) and 3-up on the standalone desktop (326px each), which is
   within 4px of the 322px the pre-restyle desktop gave — same grid, no
   breakpoint, and every field the same width at every size.

   The 16rem floor is what does the work. Three equal columns in the modal
   would be 187px each, and a 26-character email already needs 200px; 292px
   holds roughly 43 characters, so the widest field the form has to render
   fits without the layout having to play favourites.

   Stacking all six full-width — what the pre-restyle page did below its
   992px Bootstrap breakpoint — is what put the submit button 217px below
   the fold in the modal. Two rows of three cost ~100px against ~430px for
   six, and that trade is most of the restyle's height saving. */
.rw-field-grid {
  display: grid;
  /* min(16rem, 100%), not a bare 16rem: a rem-based track minimum grows with
     text-only zoom, and once it exceeds the container the track cannot shrink
     to fit and the grid overflows sideways. min() caps the floor at the
     container's own width, so the grid degrades to one column instead. */
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  column-gap: 1.5rem;
  row-gap: 1.625rem;
  margin-bottom: 2.125rem;
}

/* min-width:0 lets a long country name ellipsize inside its column instead
   of forcing the grid wider than the modal (T7 asserts no h-overflow). */
.rw-field { min-width: 0; }

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

.rw-input {
  width: 100%;
  appearance: none;
  -webkit-appearance: none;
  border: none;
  border-bottom: 1px solid var(--field-line);
  border-radius: 0;
  background: transparent;
  font-family: var(--font-serif);
  font-weight: var(--serif-weight);
  font-size: var(--value-size);
  line-height: 1.2;
  color: var(--ink);
  padding: 0.375rem 0 0.5rem;
  transition: border-color 300ms ease-out;
}

/* The caps label idiom lives here: app.js writes the uppercased field name
   into the placeholder, so styling it in the Engravers voice gives the
   mockup's label look without the raw-xlsx asterisk problem.
   opacity:1 is required — Firefox dims placeholders by default. */
/* Full --ink, not the recessive tone. A ::placeholder and the field's value
   are never visible at the same time — the placeholder goes the moment the
   guest types — so the colour step between them was doing no work at all.
   What tells a reader "this is a prompt, not something I typed" is the tracked
   uppercase, which no entered value ever resembles. That leaves colour free to
   buy legibility instead: 6.31:1 -> 14.98:1.
   opacity:1 is required — Firefox dims placeholders by default. */
.rw-input::placeholder {
  font-family: var(--font-serif);
  font-weight: var(--serif-weight);
  font-size: var(--label-size);
  letter-spacing: var(--label-track);
  color: var(--ink);
  opacity: 1;
}

.rw-input:hover { border-bottom-color: var(--field-line-hover); }

.rw-input:focus {
  outline: none;
  border-bottom-color: var(--brass);
  box-shadow: 0 1px 0 0 var(--brass);
}

/* app.js:260 adds .error-class to the input itself; for the two select2
   fields it also decorates the widget container (app.js:261-262). */
.rw-input.error-class {
  border-bottom-color: var(--destructive);
  box-shadow: 0 1px 0 0 var(--destructive);
}

/* ── select2 ──
   Kept, not replaced: app.js:223 reads the country via $('#country').val()
   and app.js:261 decorates errors through $(el).data('select2').$container.
   styles.css is the last stylesheet in <head>, so matching select2's own
   selector specificity is enough — no !important needed. */
.select2-container--default .select2-selection--single {
  height: auto;
  border: none;
  border-bottom: 1px solid var(--field-line);
  border-radius: 0;
  background: transparent;
  padding: 0.375rem 0 0.5rem;
  transition: border-color 300ms ease-out;
}

.select2-container--default .select2-selection--single .select2-selection__rendered {
  padding: 0 1.25rem 0 0;       /* room for the chevron, which is absolute */
  line-height: 1.2;
  font-family: var(--font-serif);
  font-weight: var(--serif-weight);
  font-size: var(--value-size);
  color: var(--ink);
}

.select2-container--default .select2-selection--single .select2-selection__placeholder {
  font-family: var(--font-serif);
  font-weight: var(--serif-weight);
  font-size: var(--label-size);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  color: var(--ink);   /* see .rw-input::placeholder */
}

.select2-container--default .select2-selection--single .select2-selection__clear {
  color: var(--ink-soft);
  margin-right: 1.25rem;
  font-size: 1.1rem;
  line-height: 1;
}

/* Focus and open share the brass underline. Without the --focus half, a
   keyboard user tabbing through the form gets a brass underline on the four
   text inputs and the browser's default blue ring on the two select2 widgets
   — two different focus languages in one row of fields. select2's span
   carries a tabindex, so the UA outline has to be suppressed explicitly. */
.select2-container--default.select2-container--open .select2-selection--single,
.select2-container--default.select2-container--focus .select2-selection--single {
  border-bottom-color: var(--brass);
  box-shadow: 0 1px 0 0 var(--brass);
}

.select2-container--default .select2-selection--single:focus,
.select2-container--default .select2-selection--single { outline: none; }

/* select2 renders the arrow as a CSS triangle (b with transparent borders),
   so the width/height and both visible border edges have to be re-declared
   to turn it into a chevron. */
.select2-container--default .select2-selection--single .select2-selection__arrow {
  height: 100%;
  right: 0;
  width: 20px;
}

.select2-container--default .select2-selection--single .select2-selection__arrow b {
  width: 7px;
  height: 7px;
  margin: -5px 0 0 -5px;
  border-style: solid;
  border-width: 0 1px 1px 0;
  border-color: var(--ink-soft);
  transform: rotate(45deg);
  transition: transform 300ms ease-out;
}

/* select2's own --open rule carries one extra class, so it outranks the base
   chevron above and restores its triangle borders unless they are
   re-declared here. Verified visually: without these two lines the closed
   state is a chevron and the open state silently reverts to a filled
   triangle. */
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
  border-width: 0 1px 1px 0;
  border-color: var(--ink-soft);
  margin-top: -2px;
  transform: rotate(225deg);
}

/* select2 mirrors ITSELF when <html dir="rtl">: it reads the document dir at
   init and stamps dir="rtl" on its own container, which its stylesheet keys
   off with [dir="rtl"] attribute selectors. Our body{direction:ltr} override
   cannot reach those, so on Arabic the chevron jumps to the left edge and
   two of the six fields end up mirrored while the other four do not.
   Pre-existing — visible in the MR-A baseline screenshots — but §3.4's
   decision is that this page does not mirror for Arabic, so close it here. */
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
  left: auto;
  right: 0;
}

.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__rendered {
  padding: 0 1.25rem 0 0;
}

/* The clear "x" is the FIRST child of .select2-selection__rendered, so its
   position is decided entirely by the float. select2 floats it right in LTR
   and left in RTL. `float: none` was the wrong correction: it drops the span
   back into the inline flow, which puts it at the start of the line — still
   the left edge, just by a different route. It has to float RIGHT, the same
   side as the chevron it sits beside, with the margin flipped to match. */
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
  float: right;
  margin-right: 1.25rem;
  margin-left: 0;
}

/* The country list is English in all twelve locales, so its search box is
   typed into in English. Leaving it RTL parks the caret on the right and
   pushes the query away from the text it filters.

   Keyed off .select2-dropdown, NOT .select2-container: select2 detaches the
   open dropdown to a container appended to <body> and stamps dir="rtl" on
   the .select2-dropdown span itself — the wrapping container carries no dir
   attribute at all, so a .select2-container[dir="rtl"] descendant selector
   matches nothing here. Confirmed by walking the live ancestor chain. */
.select2-dropdown[dir="rtl"] .select2-search__field {
  direction: ltr;
  text-align: left;
}

.select2-container--default .select2-dropdown {
  border: 1px solid var(--hairline);
  border-radius: 0;
  background: #fff;
  box-shadow: 0 12px 28px rgba(28, 38, 32, 0.10);
}

.select2-container--default .select2-search--dropdown .select2-search__field {
  border: none;
  border-bottom: 1px solid var(--hairline);
  border-radius: 0;
  background: transparent;
  padding: 0.625rem 0.875rem;
  font-family: var(--font-serif);
  font-weight: var(--serif-weight);
  font-size: 1rem;
  color: var(--ink);
}

.select2-container--default .select2-search--dropdown .select2-search__field:focus {
  outline: none;
  border-bottom-color: var(--brass);
}

.select2-container--default .select2-results > .select2-results__options {
  max-height: 15rem;
}

.select2-container--default .select2-results__option {
  font-family: var(--font-serif);
  font-weight: var(--serif-weight);
  font-size: 1rem;
  line-height: 1.3;
  color: var(--ink);
  padding: 0.5rem 0.875rem;
}

.select2-container--default .select2-results__option--highlighted[aria-selected] {
  background: var(--secondary);
  color: var(--ink);
}

.select2-container--default .select2-results__option[aria-selected="true"] {
  background: rgba(1, 61, 45, 0.06);
  color: var(--ink);
}

/* ── Error box ──
   app.js:246 joins messages with <br>, so seven validation errors added
   198px (measured) — 158px over budget at exactly the moment the submit
   button most needs to be reachable. Collapsing to a middot-separated run
   recovers ~150px without touching JS. */
.error-box {
  display: none;                /* app.js flips this to block */
  border-left: 2px solid var(--destructive);
  background: rgba(162, 69, 47, 0.06);
  color: var(--destructive);
  font-size: 0.9375rem;
  line-height: 1.45;
  padding: 0.75rem 1rem;
  margin: 0 0 1.5rem;
}

/* GENERAL sibling (~), not adjacent (+): app.js:246 joins the messages with
   <br>, so consecutive spans are never adjacent siblings and `span + span`
   silently matches nothing — the messages run together with no separator at
   all. display:none hides the <br> visually but it stays in the DOM as a
   sibling. `span ~ span` matches the 2nd..nth span, which is exactly the
   set that needs a separator; the first must not have one, because T3
   asserts #errorBox innerText equals exactly one message per single-field
   case. (The spec's §3.6 snippet uses + and is wrong.) */
.error-box br { display: none; }
.error-box span { display: inline; }
.error-box span ~ span::before { content: " · "; opacity: 0.55; }

.select2-container.error-class .select2-selection--single {
  border-bottom-color: var(--destructive);
  box-shadow: 0 1px 0 0 var(--destructive);
}

/* ── Interests ──
   auto-fit rather than a fixed column count: each locale takes the tallest
   layout that still fits instead of a compromise tuned for English. At the
   609px content width this resolves to 3 columns (4 would need 734px). */
.rw-interests { margin: 0; }

.rw-section-label {
  font-family: var(--font-serif);
  font-weight: var(--serif-weight);
  font-size: var(--label-size);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 1rem;
}

.rw-interest-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  gap: 0.625rem;
  margin: 0;
}

/* Eight tiles. Left to auto-fit, the widened standalone page resolves to
   five columns and leaves a 5 + 3 ragged block; four gives two even rows.
   1000px is where it starts to matter — auto-fit only reaches five columns
   at ~1048px of viewport, so this takes over just before that and no
   five-column state is ever reachable. */
@media (min-width: 1000px) {
  .rw-interest-grid { grid-template-columns: repeat(4, 1fr); }
}

.rw-tile { display: block; margin: 0; cursor: pointer; }

.rw-tile input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.rw-tile-face {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  height: 100%;                 /* equal-height tiles when one label wraps */
  border: 1px solid var(--hairline);
  background: transparent;
  padding: 0.8125rem 0.875rem;
  transition: border-color 300ms ease-out, background-color 300ms ease-out;
}

.rw-tile:hover .rw-tile-face { border-color: var(--brass); }

.rw-tile-mark {
  flex: none;
  width: 15px;
  height: 15px;
  border: 1px solid var(--control-line);
  position: relative;
  transition: background-color 250ms ease-out, border-color 250ms ease-out;
}

.rw-tile-mark::after {
  content: "";
  position: absolute;
  left: 4px; top: 1px;
  width: 5px; height: 9px;
  border-right: 1.5px solid #fff;
  border-bottom: 1.5px solid #fff;
  transform: rotate(45deg) scale(0);
  transition: transform 200ms var(--ease);
}

.rw-tile-text {
  font-size: var(--value-size);
  line-height: 1.2;
  color: var(--ink);
}

.rw-tile input:checked + .rw-tile-face {
  border-color: var(--green);
  background: rgba(1, 61, 45, 0.045);
}

.rw-tile input:checked + .rw-tile-face .rw-tile-mark {
  background: var(--green);
  border-color: var(--green);
}

.rw-tile input:checked + .rw-tile-face .rw-tile-mark::after {
  transform: rotate(45deg) scale(1);
}

.rw-tile input:focus-visible + .rw-tile-face {
  outline: 2px solid var(--brass);
  outline-offset: 2px;
}

#interestsRow.error-class .rw-tile-face { border-color: rgba(162, 69, 47, 0.55); }

/* ── Consent ── */
.rw-consent { margin-top: 1.75rem; }

.rw-consent input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

/* The label is the whole click target for the consent checkbox, and at one
   line of 15px text it measured 22px tall — under the 24px floor WCAG 2.2
   SC 2.5.8 sets for pointer targets. The padding buys the 2px and then some;
   it applies to the box and the text alike, so their alignment is unchanged.
   (The privacy link inside is exempt — 2.5.8 excuses targets sized by the
   line-height of the sentence they sit in.) */
.rw-consent-label {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  min-height: 1.5rem;
  padding: 0.25rem 0;
  margin: 0;                    /* Bootstrap Reboot sets label margin-bottom */
  cursor: pointer;
}

.rw-box {
  flex: none;
  width: 15px;
  height: 15px;
  margin-top: 4px;
  border: 1px solid var(--control-line);
  position: relative;
  transition: background-color 250ms ease-out, border-color 250ms ease-out;
}

.rw-box::after {
  content: "";
  position: absolute;
  left: 4px; top: 1px;
  width: 5px; height: 9px;
  border-right: 1.5px solid #fff;
  border-bottom: 1.5px solid #fff;
  transform: rotate(45deg) scale(0);
  transition: transform 200ms var(--ease);
}

.rw-consent input:checked + .rw-consent-label .rw-box {
  background: var(--green);
  border-color: var(--green);
}

.rw-consent input:checked + .rw-consent-label .rw-box::after {
  transform: rotate(45deg) scale(1);
}

.rw-consent input:focus-visible + .rw-consent-label .rw-box {
  outline: 2px solid var(--brass);
  outline-offset: 2px;
}

.rw-consent-text {
  font-size: 0.9375rem;
  line-height: 1.45;
  color: var(--ink);
}

/* Links are --ink (#1c2620), not --green, per the rwhg design system
   (/design-system/#carousels). That makes them the same colour as the body copy
   around them, so the UNDERLINE is now the only thing marking them as links —
   WCAG 1.4.1 (Use of Colour) is satisfied by the underline alone and it must
   not be removed here or in .rw-foot-text a. Green moves to :hover, which
   keeps a colour affordance on pointer without using colour to identify. */
.rw-consent-text a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color 300ms ease-out;
}

.rw-consent-text a:hover { color: var(--green); }

/* ── Footer text + CTA ──
   flex-wrap is load-bearing, not tidiness. .rw-cta is flex:none and the copy
   beside it cannot shrink below its own longest word — which is
   privacy@rosewoodhotelgroup.com, one 30-character unbreakable token. Set
   nowrap, the two together simply exceed the row and the button is pushed
   past the container's right edge: at 200% text in the 689px modal it landed
   at x=710 (English) and x=775 (German) against a 689px viewport, a WCAG
   1.4.4 failure. Wrapping drops the button to its own line instead.

   Deliberately not solved with a width media query. The failing case is
   TEXT-only zoom, where the viewport stays 689px and only the type grows, so
   no viewport breakpoint can see it — the layout has to respond to its own
   content. */
.rw-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.5rem;
  margin-top: 1.75rem;
  padding-top: 1.375rem;
  border-top: 1px solid var(--hairline);
}

/* 14px, not 13px. This block and .rw-note are the two smallest things on the
   page, and they are the two that suffer most at the standalone desktop
   width, where the same type sits surrounded by cream and reads smaller than
   it does inside the 689px modal.

   34rem, matching .rw-lede — one prose measure for the page. 26rem was set
   against the old 46rem container and did not move when the container went
   to 72rem, so on the standalone page the copy wrapped inside 416px with
   885px of room beside it, and the 174px mailto address — one unbreakable
   token — could not fit in what was left of the second line and dropped
   onto a third by itself. At 34rem the address rejoins the text on every
   locale.

   text-wrap: pretty is the belt to that braces: it re-flows the last line
   to avoid exactly this kind of orphan. It fixes pt-br at the desktop width,
   where 34rem alone still stranded the address, and is ignored by browsers
   that do not implement it.

   min-width:0 + overflow-wrap are the last resort under the same criterion.
   Once the container is narrower than the mailto address itself — a 320px
   viewport at 200% text leaves ~224px against a ~398px address — wrapping the
   button away no longer helps and the address alone overflows.

   BOTH declarations are needed and neither is sufficient. A flex item's
   min-width defaults to auto, i.e. its min-content width, and overflow-wrap
   is explicitly ignored when computing min-content — so break-word alone
   leaves the item still demanding 398px and still overflowing. min-width:0
   lets it shrink to the container; overflow-wrap then breaks the address to
   fit the narrower box. (overflow-wrap:anywhere WOULD shrink min-content on
   its own, but it also lets flex crush this column to a character wide under
   ordinary pressure, so it is the wrong tool.)

   Both are inert at normal sizes: nothing shrinks until the row runs out of
   room, and the button wraps before that happens. */
.rw-foot-text {
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--ink-mid);        /* same hairline-at-small-size argument */
  /* flex-basis 20rem, NOT auto. Flex breaks lines on the basis before it
     shrinks anything, so with basis:auto this column asked for its 34rem
     max-width — 544px against 609px of modal, which together with the button
     and the gap overran the row and wrapped the button onto its own line at
     ORDINARY size. That cost ~73px and pushed es-es and ja-jp past the 885px
     fold; the e2e height guard caught it. A 320px basis leaves the row
     comfortable (320 + 115 + 24), flex-grow then fills the space up to the
     34rem cap, so the rendered width is unchanged — and at 200% text the
     basis doubles past the row and the button still wraps as intended. */
  flex: 1 1 20rem;
  max-width: 34rem;
  min-width: 0;
  text-wrap: pretty;
  overflow-wrap: break-word;
  margin: 0;
}

/* Same treatment as .rw-consent-text a — see the note there on why the
   underline is load-bearing now that links share the body colour. */
.rw-foot-text a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color 300ms ease-out;
}

.rw-foot-text a:hover { color: var(--green); }

/* rwhg-web CTA: square forest green, engraved caps, brass focus ring. */
.rw-cta {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--green);
  color: #fff;
  border: none;
  border-radius: 0;
  padding: 0.9375rem 2rem;
  font-family: var(--font-button);
  font-weight: var(--button-weight);
  font-size: var(--button-size);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 300ms ease-out;
}

.rw-cta:hover { background: var(--green-hover); }
.rw-cta:focus { outline: none; }

.rw-cta:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--cream), 0 0 0 4px var(--brass);
}

.rw-cta:disabled { background: #8b9490; cursor: not-allowed; }

/* ── Success view ──
   No `display` here: app.js toggles it inline, and a CSS display would be a
   trap for anyone who later removes the inline style. */
#successView { padding-top: 0.5rem; }

/* ── mun-confirmed.html body copy ──
   That page loads styles.css but NOT Bootstrap, which is why the base block
   declares its own box-sizing and body margin reset. */
.rw-body {
  font-size: 1.0625rem;
  line-height: 1.55;
  color: var(--ink);
  max-width: 34rem;
  margin: 0 0 1rem;
}

/* ── RTL (Arabic locales) ──
   app.js:51 sets <html dir="rtl"> for ar-ae/ar-sa, but the LAYOUT stays
   left-aligned — only Arabic text runs flip, via the Unicode bidi
   algorithm. Verified in-browser: computed direction is rtl on <html> and
   ltr on <body>, and the display heading's text is flush with its box's
   left edge. Dropping this rule silently mirrors the page for Arabic
   users, which nobody signed off on. */
html[dir="rtl"] body {
  direction: ltr;
  text-align: left;
}

/* ── Reduced motion ──
   Every transition here is a 200-300ms ease on a colour, a chevron rotation
   or a checkmark scale — nothing that would trigger vestibular symptoms, so
   this is not what makes the page conformant. It is honoured because a guest
   who has asked the OS for less motion has asked, and the cost is four
   lines. Colour still changes on hover, focus and check; only the tween
   between the two states goes. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* ── Phone ──
   Above 560px the layout is width-stable (609px of content at the 689px
   modal, 608px at desktop), so this is the only other tier needed. */
@media (max-width: 560px) {
  .signup-container { padding: 2.5rem 1.5rem 2rem; }

  .rw-display { font-size: 2rem; }
  .rw-lede { font-size: 1.125rem; }

  /* auto-fit would already collapse to one column below ~34rem of content;
     this pins it so the 560px tier does not depend on that arithmetic. */
  .rw-field-grid {
    grid-template-columns: 1fr;
    row-gap: 1.375rem;
  }

  .rw-interest-grid { grid-template-columns: 1fr; }

  .rw-foot {
    flex-direction: column;
    align-items: stretch;
    gap: 1.25rem;
  }

  .rw-cta { width: 100%; }
}
