/*
 * Shared motion and button system, sitewide. docs/PAGE-TEMPLATE-LAWS.md
 * sections 3 (motion law), 6 (cards) and 7 (buttons). Enqueued on every
 * page from functions.php, filemtime-versioned, never duplicated per page.
 *
 * Only the card-hover and button rules ship here in T07b. `.myth-reveal` is
 * applied to elements now (the T07b grid cards) but carries no rule in this
 * file yet, so the class is inert: it hides nothing on its own. T07c adds
 * both the hidden-until-visible CSS and js/myth-motion.js together, in one
 * change, so a page is never caught with the class but no script to clear
 * it. Parallax and live counters are also T07c's addition.
 *
 * Palette hexes only: White #FFFFFF, Charcoal #231F1E, Ink #1C1310,
 * Muted Grey #767676, Gold #D4A94A, Maroon #6E0C01.
 */

/* =======================================================================
 * THE ELEVATION SCALE, R05 (review 2026-09-08 items A3, A4, A11, B4, B5,
 * G3, J3; question 15 answer (a), SUBTLE). One scale, three steps, applied
 * by the classes that already exist. No page and no band declares a shadow
 * of its own from here on.
 *
 * OPTION (a) ONLY, and the three things it rules out are as binding as the
 * three it asks for: no gradient fill, no animated border sweep, no
 * resting glow. The gold bloom appears on hover and nowhere else.
 *
 *   --myth-elev-1     resting card, resting filter pill.
 *   --myth-elev-2     resting PRIMARY button, and a card lifted on hover.
 *   --myth-elev-glow  the gold bloom, hover only, never at rest.
 *
 * ALPHAS ARE SET PER GROUND, which is why each step has an `-on-dark`
 * twin. This site paints on six grounds: parchment and White (the reading
 * bands), Ink #1C1310 (the atmospheric bands), Charcoal #231F1E (the games
 * band and the carousel region), a poster image under a scrim, and the
 * hero video. An Ink shadow at 0.10 is a clean soft edge on parchment and
 * is literally invisible on Ink or Charcoal: measured composites, ground
 * Charcoal rgb(35, 31, 30), shadow Ink at 0.10 gives rgb(34, 30, 29), a
 * luminance step of 1.006:1, which no eye reads. The same shadow at 0.70
 * gives rgb(30, 23, 20), a step of 1.091:1, which does read as a soft
 * anchor under the card. The on-dark alphas are those numbers, not a
 * guess, and they are the reason the scale is written twice.
 *
 * NO NEW COLOUR. Every value below is one of the six hexes from
 * docs/BRAND-DIRECTION.md section 1 at an alpha: Ink #1C1310 for the
 * shadow, Gold #D4A94A for the bloom and the hairline, Maroon #6E0C01 for
 * the PRIMARY fill's own hue-matched instance of the bloom (MANUAL-12's
 * measured value, folded in here rather than duplicated).
 *
 * The near layer of --myth-elev-2 is `0 2px 6px rgba(28, 19, 16, 0.30)`,
 * byte for byte the PRIMARY resting shadow MANUAL-12 measured, so no
 * button's contact shadow changes when it moves onto the token; the step
 * gains one wider, fainter ambient layer so that the same token can also
 * carry a lifted card.
 * ===================================================================== */
:root {
  /* Step 1, light grounds: parchment, White, and a White card face. */
  --myth-elev-1: 0 1px 2px rgba(28, 19, 16, 0.06), 0 4px 10px rgba(28, 19, 16, 0.10);
  /* Step 2, light grounds. */
  --myth-elev-2: 0 2px 6px rgba(28, 19, 16, 0.30), 0 4px 8px rgba(28, 19, 16, 0.16);
  /* Step 1 and 2 on Ink, Charcoal, a poster scrim or the hero video. */
  --myth-elev-1-on-dark: 0 1px 2px rgba(28, 19, 16, 0.40), 0 4px 10px rgba(28, 19, 16, 0.55);
  --myth-elev-2-on-dark: 0 2px 6px rgba(28, 19, 16, 0.55), 0 4px 8px rgba(28, 19, 16, 0.70);
  /* THE BLOOM. Gold #D4A94A, HOVER ONLY, on cards, filter pills and the
     unfilled (ghost) buttons.

     THE RADIUS IS NOT A TASTE DECISION, IT IS A MEASUREMENT. A bloom is
     only subtle if it stops before it reaches the thing beside it, so it
     is sized to the TIGHTEST GAP ON THE SITE in the state it fires in.
     That gap was measured, not guessed (loop/work/REVIEW-05/prove.mjs,
     599 elements, five routes, three widths): the two buttons in a card's
     actions row stack 8px apart at 390, and the hovered one rises 2px
     toward its neighbour, leaving 6.00px. Every other pair is wider: 8px
     side by side at 768 and 1440, 12px between filter pills, 16px between
     room cards at 390, 24px between grid and group cards.

     A shadow paints (offset + blur / 2 + spread) beyond its border box, so
     `0 2px 8px` reaches 6.00px below, 2.00px above and 4.00px to each
     side: it arrives exactly at the tightest neighbour's edge, where its
     own alpha is zero, and enters no box on the site at any width. */
  --myth-elev-glow: 0 2px 8px rgba(212, 169, 74, 0.32);
  --myth-elev-glow-on-dark: 0 2px 8px rgba(212, 169, 74, 0.42);
  /* THE FILLED PRIMARY'S OWN HUE-MATCHED GLOW, law section 7.3: the glow
     matches the fill's hue, the hairline is the opposite accent. These are
     MANUAL-12's measured values, UNCHANGED by R05 and moved here only so
     that no rule repeats a literal shadow. They are wider than the bloom
     above on purpose and are the one pair on the site that can reach a
     neighbour's box (18px against the 6px stacked gap at 390). That was
     true before this task, it was reviewed at MANUAL-12, and narrowing the
     site's most visible interaction is the user's call, not this task's. */
  --myth-elev-glow-primary: 0 8px 20px rgba(110, 12, 1, 0.42);
  --myth-elev-glow-primary-gold: 0 8px 20px rgba(212, 169, 74, 0.34);
  /* The card hover hairline: Gold at 24 percent on a light ground. On a
     dark ground the resting hairline is ALREADY Gold at 20 percent (law
     section 6), so 24 percent would be a 4-point change nobody can see;
     the on-dark twin carries the same visible step instead of the same
     number. Colour only: the 1px border WIDTH never changes, in any
     state, on any surface, so the hairline can move no layout. */
  --myth-hairline-gold: rgba(212, 169, 74, 0.24);
  --myth-hairline-gold-on-dark: rgba(212, 169, 74, 0.44);
}

/* THE CARD CONTRACT, one rule for every card on the site (.myth-card is on
   the homepage grid cards, the four group cards and both location pages'
   room cards; the frozen carousel card is handled separately and is not
   given a hover at all).

   Resting step 1. On hover: a 2px lift, step 2 with the bloom, and the
   hairline to Gold. 320ms, the same duration and curve as the button
   system, so a card and the button inside it move as one thing. Pointer
   devices only, so a tap can never leave a card stuck lifted.

   R05 SUPERSEDES the 4px / 400ms figures in law section 3 item 2. Two
   reasons, both measurable: the button system settled on 2px and 320ms in
   MANUAL-12, and a card that lifts twice as far as the button inside it
   reads as two systems; and 4px of travel on a card whose shadow was
   0.18 alpha was the largest movement on the site, which is the opposite
   of the answer to question 15.

   ZERO LAYOUT SHIFT BY INSPECTION: transform, box-shadow and border-color
   only. The border WIDTH never changes, only its colour, so no card ever
   reflows its own contents or its neighbours. */
.myth-card {
  box-shadow: var(--myth-elev-1);
  transition:
    transform 320ms cubic-bezier(0.2, 0.7, 0.3, 1),
    box-shadow 320ms cubic-bezier(0.2, 0.7, 0.3, 1),
    border-color 320ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.myth-card .myth-carousel__image {
  transition: transform 400ms ease;
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .myth-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--myth-elev-2), var(--myth-elev-glow);
    border-color: var(--myth-hairline-gold);
  }

  .myth-card:hover .myth-carousel__image {
    transform: scale(1.04);
  }
}

@media (prefers-reduced-motion: reduce) {
  .myth-card,
  .myth-card .myth-carousel__image {
    transition: none;
  }
}

/* =======================================================================
 * Button system, MANUAL-12 (user, 2026-09-07; spec loop/work/MANUAL-12/
 * vesper.md sections 1 to 3). This block replaces the two-level system
 * T07b shipped. Three rules govern it and nothing else does:
 *
 *   1. LEVEL IS FILL WEIGHT, NEVER HUE. Primary is the only filled button
 *      on the site. Secondary is an outline with no fill and no shadow.
 *      Ghost (tertiary) is text with a drawn underline. Because nothing
 *      else is filled, a Gold fill can only ever read as a primary, which
 *      is what lets brand.config.json palette.locationAccents keep Gold
 *      for Maidstone without Gold also meaning "second action".
 *   2. TONE IS A SECOND, INDEPENDENT AXIS, only ever on --primary:
 *      no class is Maroon (the default, and the Gillingham accent),
 *      .myth-btn--gold is Gold (a Maidstone action).
 *   3. SURFACE IS A THIRD AXIS, required on --secondary and --ghost,
 *      ignored on --primary: .myth-btn--on-light (the default when the
 *      class is absent) and .myth-btn--on-dark. The light variant is the
 *      default on purpose, so a forgotten class ships Maroon on parchment
 *      at 12.23:1 rather than Gold on parchment at 2.19:1, which
 *      palette.contrastLaws forbids outright.
 *
 * Six documented hexes only (docs/BRAND-DIRECTION.md section 1), plain or
 * at a stated alpha: White #FFFFFF, Charcoal #231F1E, Ink #1C1310, Muted
 * Grey #767676, Gold #D4A94A, Maroon #6E0C01. No new colour is introduced;
 * the "Maroon lift" a brief offered was rejected because it is in neither
 * brand.config.json nor BRAND-DIRECTION, and the hover carries itself on a
 * 2px lift, a coloured glow and a 1px hairline in the opposite accent.
 *
 * EVERY RULE HERE IS CLASS-ONLY. The `a.` / `button.` qualifiers T07b used
 * are gone, so the highest hover specificity is 0,3,0 and the focus block
 * at the end of the section wins on source order and specificity both. Do
 * not reintroduce a type qualifier on any rule in this section: it would
 * silently outrank the focus ring.
 *
 * Global box-sizing: border-box (Divi's own reset) means the stated
 * min-height holds the same outer box whether or not a variant carries a
 * border, so no padding compensation is needed between a filled and an
 * outlined variant, and the 1px border is present at rest in every level,
 * so a hover that only changes its colour cannot move a single pixel.
 *
 * FOLD P1 FIX, 2026-09-05 (T07c responsive pass), kept: `white-space:
 * nowrap`. A button label is a short call to action, never a sentence;
 * letting it wrap mid-word inside a narrow flex parent is always a defect.
 * The one exception is --ghost, which is a text link and may wrap.
 * ===================================================================== */

/* Base atom. */
.myth-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
  padding: 0 24px;
  border-radius: 4px;
  border: 1px solid transparent;
  font-family: Karma, serif;
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    transform 320ms cubic-bezier(0.2, 0.7, 0.3, 1),
    box-shadow 320ms cubic-bezier(0.2, 0.7, 0.3, 1),
    background-color 320ms cubic-bezier(0.2, 0.7, 0.3, 1),
    border-color 320ms cubic-bezier(0.2, 0.7, 0.3, 1),
    color 320ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* Size, two variants and no more. The card size exists because a card
   actions row carrying two buttons is the narrowest two-button case on
   the site: at the band size "LEARN MORE" alone needs 178.2px and two of
   them 364.4px, which no card provides. The rail size is the sitewide
   44px touch floor (PAGE-TEMPLATE-LAWS section 9). */
.myth-btn--card {
  font-size: 13px;
  letter-spacing: 0.09em;
  padding: 0 12px;
}

.myth-btn--rail {
  font-size: 14px;
  letter-spacing: 0.08em;
  padding: 0 16px;
  min-height: 44px;
}

/* PRIMARY, Maroon tone. Identical on both surfaces; the fill carries it.
   White on Maroon is 12.23:1. */
.myth-btn--primary {
  background-color: #6E0C01;
  border-color: #6E0C01;
  color: #FFFFFF;
  /* R05: the elevation scale's step 2. The near layer is the value
     MANUAL-12 measured (0 2px 6px at 0.30); the token adds the one wider
     ambient layer that makes a button and a lifted card the same step. */
  box-shadow: var(--myth-elev-2);
}

/* PRIMARY, Gold tone. Maidstone actions only, per
   brand.config.json palette.locationAccents. Ink on Gold is 8.33:1. */
.myth-btn--primary.myth-btn--gold {
  background-color: #D4A94A;
  border-color: #D4A94A;
  color: #1C1310;
}

/* SECONDARY, on light (the default). Maroon on White is 12.23:1 and the
   border on White is 3.62:1. The 0.55 alpha is the point at which the
   border clears the 3:1 floor for a component boundary. Do not lower it. */
.myth-btn--secondary {
  background-color: transparent;
  border-color: rgba(110, 12, 1, 0.55);
  color: #6E0C01;
}

/* SECONDARY, on dark. White on Ink is 18.26:1, border on Ink 3.37:1. */
.myth-btn--secondary.myth-btn--on-dark {
  border-color: rgba(212, 169, 74, 0.55);
  color: #FFFFFF;
}

/* TERTIARY (ghost): a text link with a drawn underline, using the same
   gradient mechanic already shipping on .myth-gill-link, so the underline
   grows from background-size and can never affect layout. No border, no
   fill, no shadow, in any state. `white-space: normal` and `padding: 0`
   are what let "Directions to Canterbury Street" (283.9px at 14px) sit on
   one line inside a 326px column at 390. */
.myth-btn--ghost {
  background-color: transparent;
  border-color: transparent;
  padding: 0;
  font-size: 14px;
  letter-spacing: 0.08em;
  white-space: normal;
  color: #6E0C01;
  background-image: linear-gradient(#6E0C01, #6E0C01);
  background-repeat: no-repeat;
  /* The atom's 48px min-height is kept (section 2.1 states it for every
     size variant, and the sitewide touch floor depends on it), so the
     spec's `background-position: 0 100%` would draw the rule at the bottom
     of a 48px box, roughly 16px clear of a 14px label. Centring the
     underline on the text instead keeps both: the box stays a 48px target
     and the rule sits 2px under the label. The offset is in `em`, so it
     holds at the 52px min-height css/myth-room.css sets under 768 and at
     any font size. Paint only; nothing here can move layout. */
  background-position: 0 calc(50% + 0.75em);
  background-size: 0% 2px;
  transition:
    background-size 320ms cubic-bezier(0.2, 0.7, 0.3, 1),
    color 320ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.myth-btn--ghost.myth-btn--on-dark {
  color: #FFFFFF;
  background-image: linear-gradient(#D4A94A, #D4A94A);
}

/* The arrow. An inline SVG chevron in a reserved 14px slot, replacing the
   &rarr; text glyph T07b shipped: U+2192 is absent from Karma, so that
   glyph was drawn by an unspecified platform serif at a full em (measured
   16.5px at a 16px font size) and its width was not ours to control. The
   glyph is 10px and the slot is 14px, so the 4px hover slide happens
   entirely inside the reserved box: the arrow never reaches the right
   padding and the button never changes width. `currentColor` inherits
   every level and surface colour above with no further rules. This is one
   path in one atom, not a second icon system beside ETmodules
   (docs/CODE-CONVENTIONS.md section 2b). */
.myth-btn__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  flex: 0 0 14px;
  width: 14px;
  height: 10px;
  /* R04 (review A1, B1, F10): THE OPTICAL CORRECTION. The chevron read as
     sitting low on every button on the site, and it measured low on every
     one: 2.48px at the band size, 1.95px at the card size, 2.25px on the
     events card CTA and 2.80px on the ghost, in Chromium and WebKit, at
     390, 768 and 1440 (loop/work/REVIEW-04/before.json).

     The rule behind the number, not a nudge. `align-items: center` centres
     both flex items on the button's cross axis, so the arrow's 10px box
     centres on the LABEL'S LINE BOX. A line box is centred on the font's
     ascent and descent, and the label is uppercase, so its optical band is
     baseline to cap height. For any face the gap between those two centres
     is (cap - (ascent - descent)) / 2. Karma Bold measures ascent 1.0em,
     descent 0.625em and cap 0.685em, which is 0.155em, or 2.48px at 16px:
     the measured number falls out of the metrics exactly.

     0.17em, not 0.155em, and that is the second half of the measurement.
     Chromium FLOORS the half-leading to whole pixels and WebKit keeps the
     fraction, so wherever (line-height - (ascent + descent)) is not an even
     number of pixels the painted label sits up to 1px higher in Chromium.
     At the 14px ghost that difference is exactly 0.5px (2.80 against 2.29),
     so no single correction can put both engines on zero. 0.17em is the
     value that keeps EVERY size variant inside 0.5px in BOTH engines:
     residuals -0.24 / -0.26 / +0.42 / +0.04 at 16, 13, 14 and the events
     13px, worst case 0.42px. One declaration, every instance sitewide.

     Paint only, by inspection: a transform on a flex item cannot change
     its own box or its siblings', so no button changes width or height,
     the reserved 14px slot is untouched, and the 4px hover slide still
     happens on the child svg inside that slot. */
  transform: translateY(-0.17em);
}

.myth-btn__arrow svg {
  transition: transform 320ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* Hover, pointer devices only, so a touch tap can never get stuck holding
   a hover state. 320ms on the button and the arrow alike. The only
   animated properties are transform, box-shadow, background-color,
   border-color, color and background-size on a gradient: no width, no
   height, no padding, no margin, no font-size, no border-width, so the
   contract is zero-layout-shift by inspection.

   The hover border is the one place the opposite accent appears: a 1px
   hairline of Gold arrives around a Maroon button and a 1px hairline of
   Maroon around a Gold one. */
@media (hover: hover) and (pointer: fine) {
  .myth-btn--primary:hover {
    transform: translateY(-2px);
    border-color: #D4A94A;
    box-shadow: var(--myth-elev-glow-primary);
  }

  .myth-btn--primary.myth-btn--gold:hover {
    border-color: #6E0C01;
    box-shadow: var(--myth-elev-glow-primary-gold);
  }

  /* R05 (review A3, A4). SECONDARY is the site's ghost: an outline, no
     fill and NO SHADOW AT REST, on every surface. What it gains here is
     the one thing question 15 answer (a) asks for, a faint gold bloom on
     hover and only on hover. It is the same token the cards use, at the
     ground's own alpha, so a ghost button on a White card and a ghost
     button on an Ink band bloom by the same amount to the eye rather than
     by the same number on paper. This supersedes law section 7.2's
     "SECONDARY and TERTIARY carry no shadow at rest and none on hover":
     at rest is still absolute, on hover is now the bloom. */
  .myth-btn--secondary:hover {
    transform: translateY(-2px);
    background-color: rgba(110, 12, 1, 0.10);
    border-color: #6E0C01;
    box-shadow: var(--myth-elev-glow);
  }

  .myth-btn--secondary.myth-btn--on-dark:hover {
    background-color: rgba(212, 169, 74, 0.14);
    border-color: #D4A94A;
    box-shadow: var(--myth-elev-glow-on-dark);
  }

  .myth-btn--ghost:hover {
    background-size: 100% 2px;
  }

  .myth-btn:hover .myth-btn__arrow svg {
    transform: translateX(4px);
  }
}

/* Pressed. Applies on touch as well, because a tap must confirm itself,
   and drops to 120ms because press feedback has to feel immediate. */
.myth-btn--primary:active,
.myth-btn--secondary:active {
  transition-duration: 120ms;
  transform: translateY(0);
}

.myth-btn--primary:active {
  border-color: #6E0C01;
  box-shadow: inset 0 2px 4px rgba(28, 19, 16, 0.38);
}

.myth-btn--primary.myth-btn--gold:active {
  border-color: #D4A94A;
}

.myth-btn--secondary:active {
  background-color: rgba(110, 12, 1, 0.16);
  /* R05: the bloom belongs to hover. A pressed button is going down, so
     the glow comes off in the same 120ms the lift does. */
  box-shadow: none;
}

.myth-btn--secondary.myth-btn--on-dark:active {
  background-color: rgba(212, 169, 74, 0.22);
}

.myth-btn--ghost:active {
  transition-duration: 120ms;
  background-size: 100% 3px;
}

/* Reduced motion: the state change stays, the movement goes. The colour,
   border and shadow changes still fire on hover; only the 2px lift and
   the 4px arrow slide are suppressed. Removing the whole hover would
   leave a reduced-motion visitor with no pointer affordance at all, which
   is worse than the motion. */
@media (prefers-reduced-motion: reduce) {
  .myth-btn,
  .myth-btn__arrow svg {
    transition: none;
  }

  .myth-btn--primary:hover,
  .myth-btn--secondary:hover {
    transform: none;
  }

  .myth-btn:hover .myth-btn__arrow svg {
    transform: none;
  }

  .myth-btn--ghost {
    transition: none;
  }
}

/* The "Coming soon" chip. NOT a disabled button, and that is a decision,
   not an omission: a disabled button is an action that failed, an undated
   room is a status. Static text, never a link, never a <button>, never
   aria-disabled, and never a month, a season or a year. One class
   sitewide, replacing .myth-games__soon and .myth-maid-soon__status. It
   matches a card button's 48px min-height and 4px radius exactly, so a row
   of mixed cards keeps one action baseline. On dark 6.15:1, on light
   5.10:1; inactive-component text is exempt from the 4.5:1 floor anyway.
   No arrow, no shadow, no transition, no hover, no focus ring, and not in
   the tab order. */
.myth-chip--soon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 24px;
  margin: 0;
  border-radius: 4px;
  border: 1px solid;
  font-family: Karma, serif;
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-align: center;
  cursor: default;
  color: rgba(28, 19, 16, 0.62);
  border-color: rgba(28, 19, 16, 0.24);
  background-color: rgba(28, 19, 16, 0.03);
}

.myth-chip--soon.myth-btn--card,
.myth-chip--soon.myth-chip--card {
  font-size: 13px;
  letter-spacing: 0.09em;
  padding: 0 12px;
}

.myth-chip--soon.myth-btn--on-dark,
.myth-chip--soon.myth-chip--on-dark {
  color: rgba(255, 255, 255, 0.55);
  border-color: rgba(255, 255, 255, 0.28);
  background-color: rgba(255, 255, 255, 0.04);
}

/* Moved here from css/myth-games.css in MANUAL-23, because myth_btn_suffix()
   prints this span in every card button and css/myth-games.css only loads
   where the carousel renders, so on a route without a carousel the suffix
   was painting as visible text and wrapping the buttons. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* The card actions row, MANUAL-12 spec section 5.2. One row, every room
   card renderer: the homepage grid, the carousel, the Gillingham venue
   cards and the Maidstone coming-soon cards.

   Side by side or stacked is decided by MEASUREMENT, not by viewport, and
   no breakpoint is written for it anywhere. The 138px basis is the
   min-content width of the widest card action: 12px padding + 91.8px
   "LEARN MORE" + 8px gap + 14px arrow slot + 12px padding = 137.8px. Two
   of those plus the 8px gap need 284px, so a row wider than 284px lays
   out side by side with the width shared equally and a narrower row wraps
   and each item fills its own line. Equal height is automatic: both items
   are min-height 48px and flex items stretch.

   `margin-top: auto` is what pins the row to the bottom of a flex card so
   every card's buttons land on one baseline whatever the blurb length. */
.myth-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: auto;
  /* MANUAL-12 FIX ROUND (Fold F3). `margin-top: auto` resolves to 0 on the
     TALLEST card in a row, because there is no free space left for it to
     absorb, so that one card's buttons sat flush against its blurb while
     every neighbour got 25px to 134px. The padding is a floor the auto
     margin cannot eat: it guarantees at least the row's own 16px step
     between the text and the actions on every card, and because it is
     applied to every card equally it moves no baseline. Measured after:
     rowLevel and the row bottom spread are both still 0. */
  padding-top: 16px;
}

.myth-card-actions > * {
  flex: 1 1 138px;
}

/* THE SHARED FOCUS RING, and it is deliberately the LAST block in the
   button section, after the hover block. One rule for every level, tone
   and surface; it retires the four different rings that used to live in
   this file, css/myth-games.css and css/myth-room.css, each of which was a
   correct local fix for a ring that failed on its own surface.

   A 6px halo painted entirely outside the border box: Ink from 0 to 2px,
   White from 2 to 4px, Ink from 4 to 6px. The White band's adjacent colour
   is Ink on both sides, 17.4:1, so the indicator is self-contained and
   passes on every possible background: a Gold fill, a Maroon fill,
   parchment, Ink, or a Poster band under its scrim.

   The ring replaces PRIMARY's resting box-shadow while focused. That is
   intended: focus outranks elevation. */
.myth-btn.myth-btn--primary:focus-visible,
.myth-btn.myth-btn--primary.myth-btn--gold:focus-visible,
.myth-btn.myth-btn--secondary:focus-visible,
.myth-btn.myth-btn--ghost:focus-visible {
  outline: 2px solid #FFFFFF;
  outline-offset: 2px;
  box-shadow: 0 0 0 6px #1C1310;
}

/* =======================================================================
 * THE FORM SHELL, R05, for R12 (/events/) and R15 (/contact/).
 * Review items A11, G3 and J3: "Forms get a max width, a border, padding
 * and a drop shadow so they read as lifted, not flat."
 *
 * DECLARED HERE, APPLIED THERE. Nothing on the site carries this class
 * yet, so this block changes no rendered pixel today; it exists so that
 * two later tasks apply one agreed container instead of inventing two.
 * Relay wires the form and owns everything inside it; this rule owns the
 * box around it and nothing else. It sets no colour on a label, no height
 * on a field and no style on a button: the shared button system already
 * styles the submit.
 *
 * Usage: wrap the form's own markup in
 *     <div class="myth-form-shell">            (parchment or White band)
 *     <div class="myth-form-shell myth-form-shell--on-dark">   (Ink,
 *                                     Charcoal, or a poster under a scrim)
 *
 * 640px is the measure, not a round number: a form's longest natural line
 * is its intro sentence, and law section 4 caps a reading measure at 68
 * characters, which at the 16px Karma this site sets is 596px of text
 * inside 24px of padding each side.
 * ===================================================================== */
.myth-form-shell {
  box-sizing: border-box;
  width: 100%;
  max-width: 640px;
  margin-inline: auto;
  padding: 24px;
  border: 1px solid rgba(28, 19, 16, 0.12);
  border-radius: 8px;
  background-color: #FFFFFF;
  box-shadow: var(--myth-elev-2);
}

@media (min-width: 768px) {
  .myth-form-shell {
    padding: 32px;
  }
}

.myth-form-shell--on-dark {
  border-color: rgba(212, 169, 74, 0.20);
  background-color: rgba(28, 19, 16, 0.92);
  box-shadow: var(--myth-elev-2-on-dark);
}

/* MANUAL-49 (2026-09-12), /contact/'s merged hero: the FIRST route to
   actually apply `--on-dark`, which surfaced a real gap this block's own
   original comment had knowingly left open ("It sets no colour on a
   label... Relay wires the form and owns everything inside it"): Relay
   never shipped, so nothing ever set a label colour for a form sitting
   on this dark a surface. Gravity Forms' own default label colour,
   `rgb(51, 51, 51)`, measured 1.45:1 against this shell's own
   `rgba(28, 19, 16, 0.92)` fill (WCAG formula); the "(Required)" marker,
   `rgb(192, 43, 10)`, measured 3.12:1, both failing the 4.5:1 floor
   badly. Scoped to `--on-dark` only, so /events/'s own light
   `.myth-form-shell` (no modifier) is untouched: White for the label
   text, the same on-dark convention every other component in this file
   already uses, not a new colour. The input and select boxes themselves
   need no change: Gravity Forms renders them as their own White boxes
   with dark text regardless of the shell's own background, already
   legible by construction. */
.myth-form-shell--on-dark label,
.myth-form-shell--on-dark .gfield_label {
  color: #FFFFFF;
}

/* Gravity Forms' own theme.min.css sets `.gform_wrapper.gravity-theme
   .gfield_required` at (0,3,0) (two chained classes on one element, one
   more on its ancestor); a first attempt restating `.gform_wrapper` here
   also landed at (0,3,0), an exact tie confirmed live via
   `document.styleSheets`, so it kept losing regardless of this file's own
   later position. `!important` settles it, scoped tightly: only the RGB
   colour of a third-party plugin's own selector is overridden, on the
   one page that ever pairs it with `--on-dark`. */
.myth-form-shell--on-dark .gform_wrapper .gfield_required {
  color: #FFFFFF !important;
}

/* No hover and no lift: a form is a place to work, not a control to press.
   The shell is elevation only, which is exactly what A11 asked for. */

/* T07c: shared motion contract, docs/PAGE-TEMPLATE-LAWS.md section 3 and
   loop/work/T07c/spec.md section 1. Reveal, parallax and live counters,
   all driven by js/myth-motion.js and guarded twice: once by that file's
   own `matchMedia` check, once by the `@media (prefers-reduced-motion:
   reduce)` blocks below. The hidden start state below only ever applies
   once js/myth-motion.js has added `myth-js` to <html> (its first line,
   run synchronously), so a visitor whose JavaScript never runs at all sees
   every `.myth-reveal` element in its final, visible state by default. */

.myth-js .myth-reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 500ms ease-out, transform 500ms ease-out;
  transition-delay: calc(var(--i, 0) * 60ms);
}

.myth-js .myth-reveal.myth-reveal--visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .myth-reveal,
  .myth-js .myth-reveal {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* Parallax: js/myth-motion.js sets `transform: translateY(Npx)` directly on
   `.myth-parallax` elements, capped at 10% of the element's own measured
   height, rAF-throttled, off under 768px and under reduced motion (no
   scroll listener attached in either case). This rule only hints the
   browser the property will change; it never animates on its own. */
.myth-parallax {
  will-change: transform;
}

@media (max-width: 767px), (prefers-reduced-motion: reduce) {
  .myth-parallax {
    transform: none !important;
    will-change: auto;
  }
}

/* Live counters: js/myth-motion.js rewrites the text node of any
   `.myth-count[data-target]` element from 0 to `data-target` once it is
   15% visible. No CSS is required for the count itself (it is a text
   change, not a transform); the element still participates in
   `.myth-reveal` on its own card wrapper for the card's own fade/rise. */

/* FAQ accordion panels ([myth_faq_accordion], inc/bands-home.php): the
   server HTML renders every answer in full so a visitor without
   JavaScript can read all five; the collapse behaviour only ever applies
   once `.myth-js` is present, guaranteeing js/myth-motion.js's click
   handler exists whenever a panel is actually collapsed. */
.myth-band-faq__panel {
  overflow: hidden;
}

.myth-js .myth-band-faq__panel {
  max-height: 0;
  transition: max-height 300ms ease;
}

@media (prefers-reduced-motion: reduce) {
  .myth-js .myth-band-faq__panel {
    transition: none;
  }
}

/* T08 SIGNATURE: myth-wipe
   ------------------------------------------------------------------------
   Appended block, loop/work/T08/spec.md section 15. Registered in
   loop/DECISIONS.md as /gillingham/'s signature: a clip-path text reveal
   with a 2px gold edge travelling the width of the line. Nothing above
   this comment is edited; every rule below is new and scoped to
   `.myth-wipe`, a class printed only by inc/bands-gillingham.php.

   Properties: clip-path, transform, opacity. None triggers layout. The
   edge is a full-size overlay whose only painted pixels are the rightmost
   2px, so at translateX(-100%) they sit on the line's left edge and at
   translateX(0) on its right: a 2px gold rule travels exactly the width
   of the line, in lockstep with the clip opening, and fades on arrival.

   The hidden start state is scoped to `.myth-js`, so without JavaScript
   the lines render fully visible and no gold edge is ever drawn. */

.myth-js .myth-wipe {
  position: relative;
  display: inline-block;
  max-width: 100%;
}

.myth-js .myth-wipe__text {
  display: inline-block;
  clip-path: inset(0 100% 0 0);
  opacity: 0;
}

.myth-js .myth-wipe--visible .myth-wipe__text {
  clip-path: inset(0 0 0 0);
  opacity: 1;
  transition: clip-path 620ms cubic-bezier(0.22, 0.61, 0.36, 1), opacity 240ms linear;
}

.myth-js .myth-wipe__edge {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: linear-gradient(
    90deg,
    rgba(212, 169, 74, 0) 0%,
    rgba(212, 169, 74, 0) calc(100% - 2px),
    #D4A94A calc(100% - 2px)
  );
  transform: translateX(-100%);
  opacity: 0;
}

@media (prefers-reduced-motion: no-preference) {
  .myth-js .myth-wipe--visible .myth-wipe__edge {
    animation: myth-wipe-edge 620ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
  }

  @keyframes myth-wipe-edge {
    0% {
      transform: translateX(-100%);
      opacity: 0;
    }

    12% {
      opacity: 1;
    }

    86% {
      opacity: 1;
    }

    100% {
      transform: translateX(0);
      opacity: 0;
    }
  }
}

/* 390 tuning: shorter, same easing. The clip distance is a percentage, so
   it needs no shortening. It runs at 390 exactly as it runs at 1440; it is
   not a parallax and not a video, so law section 9's under-768 shutoff
   does not apply to it. */
@media (max-width: 767px) and (prefers-reduced-motion: no-preference) {
  .myth-js .myth-wipe--visible .myth-wipe__text {
    transition: clip-path 460ms cubic-bezier(0.22, 0.61, 0.36, 1), opacity 200ms linear;
  }

  .myth-js .myth-wipe--visible .myth-wipe__edge {
    animation-duration: 460ms;
  }
}

/* Reduced-motion end state, rendered: the line is fully visible with no
   transition and the edge is never drawn. Mirrored in js/myth-motion.js's
   initWipe(), so the guard holds even if one file is cached without the
   other. */
@media (prefers-reduced-motion: reduce) {
  .myth-js .myth-wipe__text {
    clip-path: none !important;
    opacity: 1 !important;
    transition: none !important;
  }

  .myth-wipe__edge {
    display: none !important;
  }
}

/* T09 SIGNATURE: myth-fan. Appended block, loop/work/T09/spec.md section 15.
 * APPEND ONLY: no rule above this line is edited, and the file's previous
 * 10162 bytes are an exact byte prefix of it.
 *
 * Card fan-out, the registered signature of /maidstone/: the three
 * coming-soon cards arrive stacked and spread into the grid as their band
 * enters. At 1024 and up the outer two start pulled 96px toward the centre
 * card and rotated 3 degrees apart, the middle card starts 32px low and
 * flat, and all three settle to zero on a 90ms stagger, which reads as a
 * stack opening into a hand. The middle card leads because it is the one
 * that never moves horizontally, so the spread reads outward rather than
 * as a slide.
 *
 * Transform and opacity only. Neither triggers layout, so the cards' boxes
 * (reserved by aspect-ratio) never move and CLS stays at zero. The hidden
 * start state is scoped to `.myth-js`, so without JS the three cards render
 * in place, fully visible and untransformed.
 *
 * DEPART from Atlas Q2 under the three-up breakpoint: no rotation. Below
 * it the cards
 * live in a horizontally scrollable snap rail, and a rotated 600px card
 * transiently widens the container's scrollWidth by about 31px for the
 * length of the animation, which nudges the snap positions and can leave a
 * phone visitor mid-swipe against a moving target. The phone variant is a
 * translate-only deal-in, 20px in and 24px up over 420ms on a 70ms stagger,
 * which still runs, still staggers and still reads as cards arriving.
 */
.myth-js .myth-fan__card {
  opacity: 0;
  transform: translate3d(var(--fan-x, 0), var(--fan-y, 24px), 0) rotate(var(--fan-r, 0deg));
  will-change: transform, opacity;
}

.myth-js .myth-fan--visible .myth-fan__card {
  opacity: 1;
  transform: translate3d(0, 0, 0) rotate(0deg);
  transition:
    transform 560ms cubic-bezier(0.22, 0.61, 0.36, 1) calc(var(--i, 0) * 90ms),
    opacity 320ms linear calc(var(--i, 0) * 90ms);
}

@media (min-width: 1200px) {
  .myth-js .myth-fan__card:nth-child(1) {
    --fan-x: 96px;
    --fan-y: 20px;
    --fan-r: -3deg;
  }

  .myth-js .myth-fan__card:nth-child(2) {
    --fan-x: 0;
    --fan-y: 32px;
    --fan-r: 0deg;
  }

  .myth-js .myth-fan__card:nth-child(3) {
    --fan-x: -96px;
    --fan-y: 20px;
    --fan-r: 3deg;
  }
}

@media (max-width: 1199px) {
  .myth-js .myth-fan__card {
    --fan-x: 20px;
    --fan-y: 24px;
    --fan-r: 0deg;
  }

  .myth-js .myth-fan--visible .myth-fan__card {
    transition:
      transform 420ms cubic-bezier(0.22, 0.61, 0.36, 1) calc(var(--i, 0) * 70ms),
      opacity 260ms linear calc(var(--i, 0) * 70ms);
  }
}

/* T09 FOLD, 2026-09-06: both media queries moved from 1024 to 1200, in
   step with the same move in css/myth-maidstone.css section 10 F3. The
   three-up grid only fits from 1200 up (three 378px cards plus two 32px
   gutters is 1198px inside a 1200px cap), so between 1024 and 1199 the
   band is the scroll-snap rail. The no-rotation rule is written against
   the rail, not against a number, so it has to follow the rail: a
   rotated 700px card transiently widens the container's scrollWidth by
   about 37px and nudges the snap positions mid-swipe. Measured after the
   move, at 1100: rotation 0deg, translate only, page overflow 0. */

/* Reduced-motion end state, rendered: the cards sit in place, fully
   visible, with no transition and no will-change. Mirrored in
   js/myth-motion.js's initFan(), so the guard holds even if one file is
   cached without the other. */
@media (prefers-reduced-motion: reduce) {
  .myth-js .myth-fan__card {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    will-change: auto;
  }
}

/* T10 ROOM TEMPLATE: myth-room-kb, myth-room-torch, myth-room-rail
   ------------------------------------------------------------------------
   ONE appended block, loop/work/T10/spec.md sections 14, 15 and 3.
   Registered in loop/DECISIONS.md as the room template's hero style (full
   bleed poster with a slow Ken Burns drift) and its signature (spotlight
   band). Nothing above this comment is edited; every rule below is new and
   scoped to a class printed only by inc/bands-room.php, so the twelve room
   routes are the only pages that pay a byte for it.

   Properties: transform and opacity only. Neither triggers layout. No
   filter, no blend mode, no background-attachment, no box-shadow and no
   background-position is animated anywhere below. Nothing loops at any
   width except the hero drift, which is a composited transform on an
   element that is already painted.

   Both behaviours are guarded twice under prefers-reduced-motion, once
   here and once in js/myth-motion.js, so the guard holds even if one file
   is cached without the other. Both hidden start states are scoped to
   `.myth-js`, which that file adds synchronously as its first line, so a
   visitor with no JavaScript sees the plate static at its rest scale and
   the band with no beam at all, with the copy fully legible either way. */

/* --- 1. myth-room-kb: the hero's Ken Burns drift ---------------------- */

/* The plate rests at scale(1.005) and never at exactly 1, so no hairline
   edge is ever exposed at a fractional device pixel ratio. Edge budget: at
   1440 a 0.055 scale gives 39.6px of overflow each side against 8px of
   translate; at 390 a 0.030 scale gives 9px against 0px of translate. No
   edge can appear at any width. The band root carries overflow: hidden.

   MANUAL-16, 2026-09-08: transform-origin was 58% 45%. It is the hero's
   object-position written a second time, so the zoom holds the same source
   point still, and css/myth-room.css section 16.1 moved that crop to
   50% 40% for the new aspect 1.5 plate. One value, edited in place,
   because it exists only to match the one above it and two disagreeing
   values would make the drift pull the crop off its focal. Nothing else
   about the drift changes: same scales, same durations, same keyframes,
   and the edge budget above still holds on the new plate (280px of
   surplus painted plate vertically at 1440). */
.myth-js .myth-room-kb__img {
  transform: scale(1.005) translate3d(0, 0, 0);
  transform-origin: 50% 40%;
}

.myth-js .myth-room-kb--drift .myth-room-kb__img {
  animation: myth-room-kb-drift 24s ease-in-out infinite alternate;
  will-change: transform;
}

@keyframes myth-room-kb-drift {
  from {
    transform: scale(1.005) translate3d(0, 0, 0);
  }

  to {
    transform: scale(1.055) translate3d(-8px, -6px, 0);
  }
}

/* 390 tuning: the drift STILL RUNS, at 18s and scale 1.030 with no
   translate. S6(f) requires the page to be bold at every width and allows
   shorter distances on phones; this is a composited transform, not
   parallax and not video, so the battery rules of law section 9 do not
   switch it off. */
@media (max-width: 767px) {
  .myth-js .myth-room-kb--drift .myth-room-kb__img {
    animation-duration: 18s;
    animation-name: myth-room-kb-drift-narrow;
  }
}

@keyframes myth-room-kb-drift-narrow {
  from {
    transform: scale(1.005) translate3d(0, 0, 0);
  }

  to {
    transform: scale(1.030) translate3d(0, 0, 0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .myth-js .myth-room-kb__img,
  .myth-js .myth-room-kb--drift .myth-room-kb__img {
    animation: none !important;
    transform: scale(1.005) !important;
    will-change: auto;
  }
}

/* --- 2. myth-room-torch: the signature, band 1 ------------------------ */

/* The beam sits UNDER the band's ink overlay in the layer stack, so the
   lightest composite it can produce is the one where the plate is already
   pure White. The worst pixel table in loop/work/T10/spec.md section 0 is
   computed on pure White, so the signature cannot move a single measured
   ratio on the band it sits on. That is a proof, not an estimate, and it
   is why a light is safe on a band nobody may shorten to fix a contrast
   problem.

   MANUAL-16, 2026-09-08: the host band changed from band 3, the story
   band, to band 1, the hero, because the user's review deleted band 3.
   Not one declaration in this block changes with it: the hero's two ink
   scrims are computed against the same pure White bound the story band's
   overlay was, and the beam is the same layer in the same position in the
   same stack. Re-measured on both built rooms at 390, 768, 1024 and 1440,
   at both ends of the drift, with the beam at its full 0.16
   (loop/work/MANUAL-16/cortex.md item 4).

   will-change now sits on the LIT rule and not on the resting beam, so no
   compositor layer is promoted inside the LCP band before the sweep
   starts. On band 3 that cost nothing; on the hero it would be a layer
   promoted on the largest paint element of twelve routes. */
.myth-js .myth-room-torch__beam {
  --torch-x: -38%;
  --torch-y: 6%;
  position: absolute;
  top: -25%;
  left: -10%;
  width: 120%;
  height: 150%;
  pointer-events: none;
  opacity: 0;
  transform: translate3d(var(--torch-x), var(--torch-y), 0);
  background: radial-gradient(
    circle at 50% 50%,
    rgba(255, 255, 255, 0.16) 0%,
    rgba(255, 255, 255, 0.10) 30%,
    rgba(255, 255, 255, 0) 62%
  );
}

/* One sweep on entry, then it stops. Nothing loops, at any width. */
.myth-js .myth-room-torch--lit .myth-room-torch__beam {
  --torch-x: 0%;
  --torch-y: 0%;
  opacity: 1;
  will-change: transform, opacity;
  transition:
    transform 900ms cubic-bezier(0.22, 0.61, 0.36, 1),
    opacity 700ms linear;
}

/* Steering is added on the first pointermove AFTER the sweep, never during
   it, so the entry sweep is never interrupted mid flight by a cursor that
   happens to be over the band. */
.myth-js .myth-room-torch--steer .myth-room-torch__beam {
  transition:
    transform 260ms linear,
    opacity 700ms linear;
}

/* 390: the SAME behaviour at a shorter duration, not a different one. The
   menu's touch fallback for spotlight is a looping gold vignette pulse;
   a permanently pulsing surface under a 326 word paragraph competes with
   reading, and a wholly different behaviour on phones is a weak reading of
   S6(f), which allows shorter distances and durations, not substitution. */
@media (max-width: 767px) {
  .myth-js .myth-room-torch__beam {
    --torch-x: -24%;
    --torch-y: 4%;
  }

  .myth-js .myth-room-torch--lit .myth-room-torch__beam {
    transition:
      transform 700ms cubic-bezier(0.22, 0.61, 0.36, 1),
      opacity 520ms linear;
  }
}

/* The lit selector is carried here as well as the resting one, and it is
   MANUAL-16's doing: will-change moved onto the --lit rule, which is
   0-3-0, so a 0-2-0 rule here can no longer stand it down. Under reduced
   motion the JS adds --lit immediately and nothing ever animates, so a
   promoted compositor layer on the LCP band would be held for the life of
   the page for nothing. Same three !important declarations as before, one
   extra selector, no new behaviour. */
@media (prefers-reduced-motion: reduce) {
  .myth-js .myth-room-torch__beam,
  .myth-js .myth-room-torch--lit .myth-room-torch__beam {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    will-change: auto;
  }
}

/* --- 3. myth-room-rail: nothing to declare here ----------------------- */

/* The stand-down STATE is declared in css/myth-room.css section 8, not
   here, because it belongs to the route that toggles it and because the
   frozen hidden state it copies lives in css/myth-bands.css. This comment
   exists so the third name in this block's heading is not looked for in
   the wrong file. */

/* =======================================================================
 * MANUAL-12 section 4.8: Divi's OWN buttons on the fourteen unbuilt pages.
 * FIX ROUND 2026-09-08 (QA D2, D3, D4). Read the whole comment before
 * touching a selector here: this block fights Divi's generated per-module
 * CSS, and every unusual thing in it is load bearing.
 *
 * THE PAGE SET, surveyed and fixed, not assumed. Twenty-five anchors on
 * fourteen published pages: /escape-room/ (1), /events/ (5), /about-us/
 * (1), /team-building/ (1), and eleven old room pages, /antidote/,
 * /arabian-nights/, /back-in-time/, /csi-times-up/, /lilith/,
 * /murder-on-the-midnight-express/, /north-pole-express/,
 * /raiders-of-the-lost-galleon/, /the-grunch/ and /the-mortician/ (1 or 2
 * each). Three drafts match the same selectors at no cost. Measured with
 * loop/work/MANUAL-12/divi-survey.mjs; every one of the twenty-five sits
 * inside both a .et_pb_section and a .et_pb_button_module_wrapper, which
 * is what the hover selector below relies on.
 *
 * Before this pass they shipped a 55.6px Maroon pill at a 50px radius in
 * Karma 18px/600 with 15px 30px 10px of padding (/the-grunch/), or
 * #EAB16A, a colour in no palette document, in Julius Sans One at 40px
 * tall (/about-us/).
 *
 * FROZEN SUBSYSTEMS, EXCLUDED THREE TIMES OVER. The Gravity Forms submit
 * on /events/ is `button.dcgd_submit_button.et_pb_contact_submit
 * .et_pb_button.gform_button.button`, and the WooCommerce and Stripe
 * checkout controls also carry .et_pb_button. Every rule below is scoped
 * to the `a.` ANCHOR form, which none of them is, AND carries the three
 * :not() exclusions. Proven, not asserted: that submit's full computed
 * style set, all 559 properties, hashes identically before and after this
 * block, and its box is 125.5 by 50px in both.
 *
 * No Divi layout, module or shortcode is edited. CSS matches the rendered
 * DOM, which is why the gift rule can be an attribute selector even though
 * the link is authored as a Divi dynamic value (@ET-DC@..., post 3279).
 *
 * ---------------------------------------------------------------------
 * WHY THERE IS SPECIFICITY AND !important HERE, AND NOWHERE ELSE
 * ---------------------------------------------------------------------
 * Divi generates, per module, per page:
 *
 *   .et_pb_button                                    (0,1,0)
 *   body .et_pb_button:hover                         (0,1,1)  color !important
 *   .et_pb_button_N_wrapper .et_pb_button_N:hover    (0,2,0)  padding-* !important
 *   .et_pb_button_N_wrapper a:hover                  (0,1,1)  transform !important
 *   body #page-container .et_pb_section
 *       .et_pb_button_N:hover                        (1,3,1)  color, border-color,
 *                                                             border-radius,
 *                                                             letter-spacing,
 *                                                             font-size, all !important
 *   .et_button_no_icon .et_pb_button:hover           (0,2,0)  padding !important
 *
 * Every selector in this block is (1,5,2) or higher, so it outranks all of
 * those on specificity. That is not enough on its own: an !important
 * declaration only loses to another !important declaration, so the seven
 * properties Divi forces are matched with !important here and NOWHERE
 * else. The list is exact and was read out of the served CSS of all
 * fourteen pages, not guessed:
 *
 *   transform  border-radius  font-size  border-color  color
 *   letter-spacing  padding
 *
 * text-transform is also !important in Divi's output and is deliberately
 * left alone, because uppercase is what this system wants anyway.
 * box-shadow is NOT forced by Divi (its hover glow is (0,1,0)), so it
 * needs no !important and does not get one.
 * ===================================================================== */

/* Rest state. PRIMARY, per the accent budget: on these pages the only
   actions are booking, an enquiry, or a gift, and the gift is demoted by
   the attribute rule further down. */
body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
  padding: 0 24px !important;
  border-radius: 4px;
  border-style: solid;
  border-width: 1px !important;
  border-color: #6E0C01;
  background-color: #6E0C01;
  color: #FFFFFF !important;
  /* R05: the same step 2 the built PRIMARY rests on, so an un-rebuilt
     Divi page and a rebuilt one carry one elevation, not two. */
  box-shadow: var(--myth-elev-2);
  /* R17 finding: two of the fourteen pages' buttons (/about-us/,
     /team-building/) carry an explicit Divi `button_font` attribute, which
     Divi renders as `font-family: ... !important` on the REST state, not
     only on hover, so this needed the same treatment the docblock above
     gives the seven hover properties: matched with !important because an
     !important declaration only loses to another. No other page's button
     sets that attribute, so this changes nothing for the other twenty-three
     anchors the original survey covered. */
  font-family: Karma, serif !important;
  font-size: 16px;
  font-weight: 700 !important;
  line-height: 1 !important;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-align: center;
  text-decoration: none;
  /* NOT `nowrap`, which is what .myth-btn uses. These labels are Divi's,
     not Marlowe's, and some of them are sentences: "Inquire About Team
     Building" is 417.5px and "Book The Mortician" plus the 14px chevron
     slot is 259.3px against a 256px column at 320. `max-width: 100%` plus
     a label that may wrap is what keeps every one of them inside its own
     container at 320, 360, 375 and 390, measured. The 48px min-height
     still holds the touch target when a label takes two lines. */
  max-width: 100%;
  white-space: normal;
  cursor: pointer;
  transition:
    transform 320ms cubic-bezier(0.2, 0.7, 0.3, 1),
    box-shadow 320ms cubic-bezier(0.2, 0.7, 0.3, 1),
    background-color 320ms cubic-bezier(0.2, 0.7, 0.3, 1),
    border-color 320ms cubic-bezier(0.2, 0.7, 0.3, 1),
    color 320ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* Divi's own ::after arrow off. It is an ETmodules glyph at
   `margin-left: -32px; opacity: 0` that animates to opacity 1 with a
   padding increase on hover, so every one of these buttons changed width
   under the pointer. The chevron that replaces it is declared below. */
body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button)::after {
  content: none;
}

/* THE GIFT LINK. SECONDARY, and ON-DARK.
   FIX, QA D2. This rule shipped the on-light values (Maroon text on a
   transparent fill) on the strength of "every measured gift button sits on
   a Divi light section". That was wrong. Measured on all eleven pages that
   carry the link, the section behind it composites to rgb(0, 0, 0) on ten
   of them and rgb(40, 12, 0) on /events/, every one under a background
   IMAGE, and QA sampled the rendered pixels at rgb(73, 56, 36) on
   /antidote/ and rgb(32, 9, 0) on /events/. Maroon on those measured
   1.09:1 and 1.56:1, against a 4.5:1 floor. It is not a mixed set: all
   eleven are dark, so the attribute rule takes the on-dark values with no
   per-page selector needed. White on the sampled backdrops measures
   11.4:1 and 19.1:1.
   A gift card is never a primary (the accent budget: PRIMARY is spent on
   booking and calling only), which is why this is a secondary at all.
   Consequence, honest rather than a defect: the three room pages whose
   only button is the gift link (Back In Time, Murder on the Midnight
   Express, North Pole Express) end up with no primary, because none of
   them offers a booking. */
body #page-container a.et_pb_button[href*="gift-cards"]:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button) {
  background-color: transparent;
  /* SOLID Gold, not the system's rgba(212, 169, 74, 0.55). The spec sets
     0.55 "because that is the point at which each clears the 3:1 floor for
     a component boundary ON ITS OWN SURFACE", and says do not LOWER it.
     This surface is not one of the two it was set against: all eleven of
     these buttons sit directly on a PHOTOGRAPH under a Divi section scrim,
     with no band behind them. Measured by sampling the real rendered pixels
     of the rectangle each button occupies (loop/work/MANUAL-12/
     divi-pixel-contrast.mjs), the lightest decile of that photograph runs
     to rgb(82, 65, 50), where the 0.55 border measures 2.40:1 to 2.43:1.
     Solid Gold on the same pixels measures 4.4:1. Raising the alpha is the
     smallest change that clears the floor: no new colour, no fill added to
     a secondary, and the label is untouched. */
  border-color: #D4A94A;
  color: #FFFFFF !important;
  box-shadow: none;
}

/* THE ON-LIGHT SECONDARY, REVIEW-12 (Vesper sections 3.2, 3.3 and 3.4).
   Opt in with module_class="myth-divi-secondary-on-light" on the Divi
   button module. Written as a reusable modifier, not a page rule, because
   R15 (/contact/) and R17 (/about-us/, /team-building/) want the same
   thing; no selector on the site names a route.

   WHY IT EXISTS. The blanket rule above makes every a.et_pb_button on an
   un-rebuilt page a PRIMARY unless its href contains gift-cards. On
   /events/ that produced four filled buttons on one surface, and all four
   are enquiries: three point at the enquiry form on the same page and the
   fourth at /team-building/, which is also an enquiry page. Law 7.1 puts
   enquiries in SECONDARY and spends PRIMARY on booking and calling only,
   so those four fills were a breach and the accent budget was spent four
   times over on a page with no booking on it. The homepage already ships
   the same four offers as ghosts (the group cards), and two pages carrying
   one offer must not disagree about its weight.

   ON-LIGHT, WHICH IS THE POINT OF THE NAME. The gift ghost above is the
   ON-DARK secondary, White on a solid Gold hairline, because all eleven of
   its grounds were measured dark. These four sit on parchment, where White
   measures 1.19:1 and would be the worst contrast failure on the site.
   Values are .myth-btn--secondary's, not invented: Maroon label at 8.69:1
   (390), 9.94:1 (768) and 9.54:1 (1440) on the darkest sampled pixel of
   that ground, and the 0.55 border compositing to rgb(159, 130, 122) at
   3.32:1 against the 3:1 floor for a component boundary. Do not lower the
   0.55.

   NO SHADOW AT REST: law 7.1, "no fill at rest, no shadow ever", and R05's
   table. The step 2 shadow these buttons carry today comes off with the
   fill. The gold bloom fires on hover only and reaches 6.00px past the
   border box, where the nearest neighbour is 27px away at every width.

   THREE !important, AND THEY ARE THE THREE DIVI FORCES. `color` at rest,
   and `color` plus `border-color` on hover, all on properties this block's
   own header enumerates as Divi-forced, and the same three the gift ghost
   already uses for White. Divi generates, per module, `body
   #page-container .et_pb_section .et_pb_button_N { color: ... !important }`
   and the same on :hover, and an !important declaration only loses to
   another one, so a Maroon label on a Divi button is impossible without
   them. Every other declaration below is a plain one that wins on
   specificity alone, which is why background-color needs no !important:
   the gift ghost proves it, unfilled today on a module carrying the same
   button_bg_color attribute. Coordinator ruling, REVIEW-12: allowed under
   the standing "unless Divi forces it" exception, recorded as an S6-style
   exception in loop/work/REVIEW-12/cortex.md. */
body #page-container a.et_pb_button.myth-divi-secondary-on-light:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button) {
  background-color: transparent;
  border-color: rgba(110, 12, 1, 0.55);
  color: #6E0C01 !important;
  box-shadow: none;
}

/* THE CHEVRON.
   FIX, QA D4. Vesper 4.8 said these buttons get no chevron "because the
   markup is Divi's and is not being edited". The markup still is not being
   edited: this is a generated ::after in the button's own flex flow, so it
   needs no HTML at all, and it lets the full hover contract (lift, glow,
   hairline, arrow slide) render here exactly as it does on .myth-btn.

   Same geometry as .myth-btn__arrow and for the same reason: a 10px glyph
   left-aligned in a reserved 14px slot, so the mandatory 4px slide happens
   entirely inside the reserved box and the button can never change width
   on hover.

   Drawn as a MASK over `background-color: currentColor`, not as a
   background-image, so the chevron inherits the button's own colour on
   every level and surface with no extra rules, exactly as `currentColor`
   does on the inline SVG elsewhere. Prefixed and unprefixed forms are
   paired on every mask property. The whole thing sits inside @supports so
   that an engine without masks gets the `content: none` above (no arrow)
   rather than a 14px solid block. */
@supports ((-webkit-mask-image: none) or (mask-image: none)) {
  body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button)::after {
    content: "" !important;
    /* `display` is the eighth and last !important in this block, and it is
       here because Divi generates, per module,
       `body #page-container .et_pb_section .et_pb_button_N:after
        { display: none !important }`
       at (1,3,1). That is the module's "no button icon" setting, which is
       correct for Divi's own ETmodules glyph (a full-em character from a
       font we do not control, which is exactly what Vesper 2.4 replaced
       everywhere else) and is being overridden here only so the system's
       own 10px chevron can take its place. Flagged to Vesper in
       loop/work/MANUAL-12/cortex.md: spec 4.8 says these buttons get no
       chevron, and the MANUAL-12 fix round asked for the full hover state
       including the arrow slide. */
    display: block !important;
    /* R04 (review A1): the same optical correction the inline svg chevron
       takes on .myth-btn__arrow, and for the same reason: the mask box is
       centred on the label's line box, which is 2.48px below the cap
       height centre of 16px Karma Bold. Measured 2.48 on all seven Divi
       buttons on /events/ and /antidote/, both engines, all three widths.

       RELATIVE AND `top`, NOT A TRANSFORM, and that is deliberate. This
       pseudo-element's `transform` is already spoken for three times over
       (the 4px hover slide, the reduced-motion reset of it, and Divi's own
       hover scale), and `font-size: 0` below makes an em value on it
       resolve to zero, so the correction has to be a px offset on a
       property nothing else in the block touches. Relative positioning is
       paint only: the box stays exactly where static put it in the flex
       flow, so no button changes width or height. */
    position: relative;
    top: -2.48px;
    flex: 0 0 14px;
    width: 14px;
    height: 10px;
    margin: 0 !important;
    padding: 0;
    opacity: 1 !important;
    font-size: 0 !important;
    line-height: 0;
    background-color: currentColor;
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M3 1.5 L6.5 5 L3 8.5' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M3 1.5 L6.5 5 L3 8.5' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: left center;
    mask-position: left center;
    -webkit-mask-size: 10px 10px;
    mask-size: 10px 10px;
    transition: transform 320ms cubic-bezier(0.2, 0.7, 0.3, 1);
  }
}

/* HOVER.
   FIX, QA D3 and D4. Before this pass the box grew by up to 31.2px wide
   and 2.4px tall under the pointer, the radius reverted to a 50px pill,
   the label jumped to 18px, the specified Gold hairline never rendered,
   and a 5 percent scale fired whatever the visitor's motion preference.
   All of it came from Divi's own !important hover declarations listed at
   the top of this block. Each one is answered here, at (1,5,2), and the
   set of properties carrying !important is exactly the set Divi forces.

   Zero layout change is now provable by inspection AND was measured: the
   animated set is transform, box-shadow, background-color, border-color
   and color, plus the chevron's own transform inside its reserved slot.
   padding, border-radius, font-size, letter-spacing and border-width are
   pinned to their resting values rather than animated. */
body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):hover {
  padding: 0 24px !important;
  border-radius: 4px !important;
  border-width: 1px !important;
  font-size: 16px !important;
  letter-spacing: 0.06em !important;
  color: #FFFFFF !important;
  /* THIS PAIR IS OUTSIDE THE POINTER QUERY ON PURPOSE, and it is the half
     of the D3 fix that is easy to get wrong. Divi's
     `.et_pb_button_N_wrapper a:hover { transform: scaleX(1.05) scaleY(1.05)
     !important }` carries no media query at all, so on a touch context,
     where `(pointer: fine)` does not match and this block's pointer half
     never applies, a tap left the button stuck at 105 percent (Fold F1).
     Neutralising the scale and Divi's own hover border here, in every
     context, and then adding the lift and the Gold hairline back inside
     the pointer query, is what makes the contract hold on a phone as well
     as under a mouse. */
  transform: none !important;
  border-color: #6E0C01 !important;
}

/* The gift secondary keeps its own resting outline in the same
   non-pointer case, rather than inheriting the primary's Maroon. */
body #page-container a.et_pb_button[href*="gift-cards"]:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):hover {
  border-color: #D4A94A !important;
}

/* REVIEW-12: the on-light secondary keeps its Maroon label under the
   pointer as well as at rest. The shared rule two blocks up pins
   `color: #FFFFFF !important` in every context, which is correct for a
   fill and wrong for an outline; this is the second of the three
   !important declarations and it is on the same Divi-forced property. The
   resting border and Maroon are already right for the non-pointer case, so
   nothing else is restated here. */
body #page-container a.et_pb_button.myth-divi-secondary-on-light:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):hover {
  color: #6E0C01 !important;
}

@media (hover: hover) and (pointer: fine) {
  body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):hover {
    transform: translateY(-2px) !important;
    border-color: #D4A94A !important;
    box-shadow: var(--myth-elev-glow-primary);
  }

  body #page-container a.et_pb_button[href*="gift-cards"]:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):hover {
    background-color: rgba(212, 169, 74, 0.14);
    border-color: #D4A94A !important;
    /* R05: this is the site's ghost on a Divi page. No shadow at rest
       (the resting rule above keeps `box-shadow: none`), the bloom on
       hover, on-dark because every one of the eleven grounds this button
       sits on was measured dark in MANUAL-12. */
    box-shadow: var(--myth-elev-glow-on-dark);
  }

  /* REVIEW-12: the on-light ghost's hover, taken from
     .myth-btn--secondary:hover so the Divi page and a built one bloom
     identically. Maroon hairline and not the primary's Gold: the third and
     last !important, answering the rule above it, which is Gold because a
     filled Maroon button needs a hue change on hover to read at all. An
     outline that is already Maroon does not, and Gold on this parchment
     ground is forbidden at 1.92:1 to 2.19:1. The bloom is the on-light
     --myth-elev-glow, not the primary's, and it is the only shadow this
     level ever carries. */
  body #page-container a.et_pb_button.myth-divi-secondary-on-light:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):hover {
    background-color: rgba(110, 12, 1, 0.10);
    border-color: #6E0C01 !important;
    box-shadow: var(--myth-elev-glow);
  }

  body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):hover::after {
    transform: translateX(4px);
  }
}

/* Pressed. 120ms, because press feedback has to feel immediate. */
body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):active {
  transition-duration: 120ms;
  transform: translateY(0) !important;
  border-color: #6E0C01 !important;
  box-shadow: inset 0 2px 4px rgba(28, 19, 16, 0.38);
}

body #page-container a.et_pb_button[href*="gift-cards"]:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):active {
  background-color: rgba(212, 169, 74, 0.22);
  border-color: #D4A94A !important;
  box-shadow: none;
}

/* REVIEW-12: pressed, from .myth-btn--secondary:active. The bloom belongs
   to hover, so it comes off in the same 120ms the lift does; the shared
   rule's inset shadow is a pressed FILL and there is no fill here. The
   Maroon border and the transform reset are already correct from the rule
   above, and no !important is added: both of these win on specificity. */
body #page-container a.et_pb_button.myth-divi-secondary-on-light:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):active {
  background-color: rgba(110, 12, 1, 0.16);
  box-shadow: none;
}

/* Reduced motion: the state change stays, the movement goes. The
   !important on transform is what stops Divi's own
   `.et_pb_button_N_wrapper a:hover { transform: scaleX(1.05) scaleY(1.05)
   !important }` firing at a visitor who has asked for less motion, which
   is what QA D3 found. Colour, border and shadow still change on hover, so
   a reduced-motion visitor keeps a pointer affordance. */
@media (prefers-reduced-motion: reduce) {
  body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button),
  body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button)::after {
    transition: none;
  }

  body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):hover,
  body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):active {
    transform: none !important;
  }

  body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):hover::after {
    transform: none;
  }
}

/* The shared ring, same halo as .myth-btn, so keyboard focus reads the
   same on a Divi page as on a built one. */
body #page-container a.et_pb_button:not(.gform_button):not(.et_pb_contact_submit):not(.dcgd_submit_button):focus-visible {
  outline: 2px solid #FFFFFF;
  outline-offset: 2px;
  box-shadow: 0 0 0 6px #1C1310;
}
