/* ============================================================
   MILKY LANE · ROUND 2 DESIGN SYSTEM
   ------------------------------------------------------------
   Ported from Hayley's milkylanehomepagev2.html, 25 Aug 2026.
   That file is a redesign of OUR homepage - it hot-links our
   staged Netlify build and links our real page set - so this is
   her direction applied to the existing site, not a new brand.

   Her file passes every Round 1 rule we enforce: no text-shadow,
   no retired star, no old pink, no pink-to-blue gradient, no
   white on a pink or blue fill, no em dash, one h1, no heading
   skips, every img with alt. What it lacked was the head
   plumbing - canonical, OG, consent gate, schema - which the
   site already has and keeps.

   Loaded AFTER home.css / menu-styles.css and BEFORE
   ml-polish.css so page-level polish still wins where it must.
   ============================================================ */

/* --- Driftline ML · the repaired build (Brand Kit §4) -------
   B4 was blocked on this file. It was embedded in Hayley's page
   as a data URI; extracted and shipped here. Family name is
   "Driftline ML", distinct from the earlier "Driftline". */
@font-face {
  font-family: "Driftline ML";
  src: url("fonts/DriftlineML.ttf") format("truetype");
  font-weight: 400; font-style: normal; font-display: swap;
}

:root {
  --font-script: "Driftline ML", "Driftline", "Brush Script MT", cursive;
  --script:      "Driftline ML", "Driftline", "Brush Script MT", cursive;

  /* Brand Kit §9 spacing scale */
  --r2-xs: 4px; --r2-s: 8px; --r2-m: 16px;
  --r2-l: 24px; --r2-xl: 40px; --r2-xxl: 64px; --r2-xxxl: 100px;

  --brick-tile: url("images/backgrounds/brick-tile-640.webp");

  --pink-rgb: 255,140,224;
  --blue-rgb: 91,208,230;
}

/* ============================================================
   §4a · HEADER COLOUR ROTATION
   ------------------------------------------------------------
   Heads cycle white -> pink -> blue down the page and never run
   two alike consecutively. The eyebrow and script never match
   the head they sit with. Applied per section by
   tools/brand/apply-head-rotation.py, which is re-runnable.
   ============================================================ */
.h-white .section-title, .h-white .display,
.h-white h2:not([class*="card"]):not(.gcm__title) { color: var(--ml-white); }
.h-pink  .section-title, .h-pink  .display,
.h-pink  h2:not([class*="card"]):not(.gcm__title) { color: var(--ml-pink); }
.h-blue  .section-title, .h-blue  .display,
.h-blue  h2:not([class*="card"]):not(.gcm__title) { color: var(--ml-blue); }

/* An accent word inside a head takes the SECTION'S accent colour, not
   its own literal one. Round 1 coloured the last word to match the head,
   which after this rotation would mean a pink word inside a pink head.
   Deriving it from a-* instead guarantees head and accent always differ,
   with no markup churn and nothing to re-run when a section moves. */
.a-pink .section-title .pink,  .a-pink .section-title .blue,
.a-pink .section-title .white, .a-pink .display .pink,
.a-pink .display .blue,        .a-pink .display .white { color: var(--ml-pink); }
.a-blue .section-title .pink,  .a-blue .section-title .blue,
.a-blue .section-title .white, .a-blue .display .pink,
.a-blue .display .blue,        .a-blue .display .white { color: var(--ml-blue); }

/* .stroke is the outlined-word device on the pink CTA banners and is not
   part of the rotation - leave it alone. */
.a-pink .section-title .stroke, .a-blue .section-title .stroke { color: inherit; }

/* The rotation recolours eyebrows that are TEXT on the section ground.
   Some eyebrows are filled pills - pink background, ink type - and this
   !important rule was repainting their text pink, so they rendered as
   empty pink capsules on about, careers, community, contact and delivery.
   Filled eyebrows are excluded; they set their own colour. Add any new
   filled eyebrow to this list.

   The exclusions sit inside :where() on purpose. A chain of :not(.class)
   adds each argument's specificity, which pushed this rule to 0-8-0 and made
   it beat every ground-specific correction on the site - pink type stayed
   pink on cream, blue stayed blue on the pink franchise band. :where()
   contributes zero, so the rule keeps its intended 0-2-0 and a section that
   paints its own ground can still override it. */
.a-pink  [class*="eyebrow"]:not(:where(.ab-hero__eyebrow, .ct-hero__eyebrow, .dl-hero__eyebrow, .nf__eyebrow, .photo-break__eyebrow, [class*="chip"])), .a-pink  .script { color: var(--ml-pink) !important; }
.a-blue  [class*="eyebrow"]:not(:where(.ab-hero__eyebrow, .ct-hero__eyebrow, .dl-hero__eyebrow, .nf__eyebrow, .photo-break__eyebrow, [class*="chip"])), .a-blue  .script { color: var(--ml-blue) !important; }
.a-white [class*="eyebrow"]:not(:where(.ab-hero__eyebrow, .ct-hero__eyebrow, .dl-hero__eyebrow, .nf__eyebrow, .photo-break__eyebrow, [class*="chip"])), .a-white .script { color: var(--ml-white) !important; }

/* ============================================================
   THE EYEBROW STAYS IN DRIFTLINE
   ------------------------------------------------------------
   Round 2 demoted section eyebrows to a small tracked label in
   the body face, moving the script into the headline instead.
   Reverted 26 Aug 2026 - Christian did not like the result, and
   without the headline sub-lines it left the brand script with
   nowhere to appear at all. The eyebrow is Driftline again, as
   home.css sets it; only the .script sub-line rule below is
   kept, ready for when those lines are written.
   ============================================================ */

/* Anton and Driftline ship ONE weight. Always set 400 or the
   browser fakes a bold and smears the strokes. §4 */
.script, .display, .section-title, .stat-num,
.marquee span, .marquee__track span { font-weight: 400; }

/* Headroom so accents on caps are not clipped. §4/Q28 */
.section-title, .display {
  padding-top: .06em;
  padding-bottom: .12em;
  overflow: visible;
}

.lede {
  font-size: clamp(1rem, 1.5vw, 1.18rem);
  max-width: 60ch;
  color: var(--ml-text, #EDEDF0);
  font-weight: 500;
}

/* ============================================================
   §12b · BRICK GROUND
   ------------------------------------------------------------
   A 640px seamless tile, not a photograph, under a 30% ink
   scrim so type stays legible. Used as a whole-section ground
   at roughly every fourth section, the cadence Hayley set.
   ============================================================ */
.r2-brick {
  background-image: var(--brick-tile);
  background-size: 640px auto;
  background-color: var(--ml-ink);
  position: relative;
}
.r2-brick::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: rgba(14,14,16,.30);
  pointer-events: none;
}
.r2-brick > * { position: relative; z-index: 1; }

/* ============================================================
   §2 · THE LOGO CARRIES NO GLOW
   ------------------------------------------------------------
   Round 1 shipped a pink drop-shadow on the header mark. The
   Brand Kit is explicit: no glow, no shadow, no recolour.
   ============================================================ */
.logo img { filter: none !important; }

/* ============================================================
   §10 · CARDS
   ------------------------------------------------------------
   The accent lives in the glow, not a border. Hover adds
   exactly 35% more light (.28 -> .378) - intensity only, never
   a colour change, never a size jump.
   ============================================================ */
.r2-card {
  position: relative; display: flex; flex-direction: column;
  background: var(--ml-charcoal, #1A1A1E);
  border: 1px solid rgba(255,255,255,.07);
  border-radius: 18px; overflow: hidden;
  text-decoration: none; color: inherit;
  --card-accent: var(--pink-rgb);
  --card-glow: .28;
  box-shadow:
    0 0 0 1px rgba(var(--card-accent), calc(var(--card-glow) * .55));
  transition: box-shadow .22s ease, transform .22s ease;
}
.r2-card--pink { --card-accent: var(--pink-rgb); }
.r2-card--blue { --card-accent: var(--blue-rgb); }
.r2-card:hover, .r2-card:focus-within { --card-glow: .378; transform: translateY(-3px); }
.r2-card__media { position: relative; aspect-ratio: 4/3; overflow: hidden; background: #101014; }
.r2-card__media img, .r2-card__media video {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .5s ease;   /* full colour at rest, no wash */
}
.r2-card:hover .r2-card__media img { transform: scale(1.04); }
.r2-card__body { padding: var(--r2-l) var(--r2-l) var(--r2-xl); display: flex; flex-direction: column; gap: 10px; flex: 1; }
.r2-card h3 {
  margin: 0; font-family: var(--display, "Anton", Impact, sans-serif);
  font-weight: 400; text-transform: uppercase;
  font-size: 1.45rem; line-height: 1; letter-spacing: .01em;
}
.r2-card--pink h3 { color: var(--ml-pink); }
.r2-card--blue h3 { color: var(--ml-blue); }
.r2-card p { margin: 0; font-size: .95rem; color: var(--ml-text-dim, #C9C9CE); line-height: 1.6; }

.r2-badge {
  position: absolute; top: 14px; left: 14px; z-index: 2;
  font-size: .66rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; padding: 7px 13px; border-radius: 999px;
  background: var(--ml-pink);
  color: var(--ml-ink);           /* D1 · ink on pink, never white */
}
.r2-badge--blue { background: var(--ml-blue); }

@media (prefers-reduced-motion: reduce) {
  .r2-card, .r2-card__media img { transition: none; }
  .r2-card:hover, .r2-card:focus-within { transform: none; }
  .r2-card:hover .r2-card__media img { transform: none; }
}

/* ============================================================
   LIGHT AND FILLED GROUNDS OPT OUT OF THE ROTATION
   ------------------------------------------------------------
   26 Aug 2026. The rotation assumes an ink ground. On a pink,
   blue or paper fill it produced exactly what you would expect:
   a pink head on pink (#franchise), a blue eyebrow on pink, and
   a white head on pink at 2.08:1 (#careers) - the same D1 failure
   we spent a pass removing everywhere else.

   On any filled section everything goes to Ink Black, which
   measures 9.26:1 on Milky Pink. These five are the complete set
   of section classes that paint a light or brand-colour ground;
   `tools/verify-site.py` fails the build if a new one appears
   with white type on it.
   ============================================================ */
.franchise-section:not(.careers-teaser), .cta-banner,
.ab-section--paper, .ct-stores, .dl-how {
  /* the rotation classes stay on the tag - they are simply outranked here */
}
.franchise-section:not(.careers-teaser) .section-title, .franchise-section:not(.careers-teaser) .display,
.franchise-section:not(.careers-teaser) h2, .franchise-section:not(.careers-teaser) h3,
.cta-banner .section-title, .cta-banner .display, .cta-banner h2,
.ab-section--paper .section-title, .ab-section--paper .display, .ab-section--paper h2,
.ct-stores .section-title, .ct-stores .display, .ct-stores h2,
.dl-how .section-title, .dl-how .display, .dl-how h2,
.franchise-section:not(.careers-teaser) [class*="eyebrow"], .cta-banner [class*="eyebrow"],
.ab-section--paper [class*="eyebrow"], .ct-stores [class*="eyebrow"],
.dl-how [class*="eyebrow"],
.franchise-section:not(.careers-teaser) .script, .cta-banner .script,
.ab-section--paper .script, .ct-stores .script, .dl-how .script,
.franchise-section:not(.careers-teaser) .lbl, .franchise-section:not(.careers-teaser) .num {
  color: var(--ml-ink) !important;
}
/* An accent word on a fill takes white, not a brand colour - it is the
   only second tone with any contrast left on pink. */
.franchise-section:not(.careers-teaser) .section-title .pink, .franchise-section:not(.careers-teaser) .section-title .blue,
.franchise-section:not(.careers-teaser) .display .pink,       .franchise-section:not(.careers-teaser) .display .blue,
.cta-banner .section-title .pink,        .cta-banner .section-title .blue {
  color: var(--ml-white) !important;
}
/* .stroke is the outlined device on the CTA banners - it sets its own
   colour through -webkit-text-stroke and must not be overpainted. */
.cta-banner h2 .stroke, .franchise-section:not(.careers-teaser) h2 .stroke {
  color: transparent !important;
}

/* ============================================================
   SECTION RHYTHM · blank, ground, blank
   ------------------------------------------------------------
   26 Aug 2026. Twenty-two pairs of consecutive sections both
   painted a ground. The specials band sat directly under the
   brick/photo band above it and the two fought - which is what
   Christian saw as the specials background conflicting with the
   header above it.

   tools/brand/apply-rhythm.py tags any section whose ground is a
   pink/blue radial glow wash AND whose previous section already
   paints something. Those give way to plain ink so the page
   alternates. Deliberate feature bands - a pink CTA banner, a
   brick ground, a photo break, a hero - keep theirs, because
   those ARE the "ground" in the rhythm.

   Re-run the script whenever sections are added or reordered.
   ============================================================ */
section.r2-flat {
  background-image: none !important;
  background-color: var(--ml-ink, #0E0E10) !important;
}
/* The washes are often painted on a ::before layer too. */
section.r2-flat::before {
  background-image: none !important;
}

/* ============================================================
   R3 · ARTWORK DIVIDERS  ·  .ml-divider
   ------------------------------------------------------------
   Photographs of the murals and neon at Newstead and Southlands,
   run as a full-bleed band between sections. Christian supplied
   the source frames; these are our own walls.

   The band IS a ground, so it obeys the same rhythm rule the
   sections do - apply-dividers.py only places one where BOTH
   neighbours are unpainted, and never against a hero, CTA
   banner, brick band or photo break. Dropping a photo band next
   to a painted section would recreate the exact fault the
   rhythm pass was written to remove.

   Brand Kit:
   · no text-shadow on the tag - it sits on its own scrim instead
   · pink and blue on dark grounds only (the scrim guarantees it)
   · the spray drip is the mark, never the retired star
   · no pink-to-blue gradient anywhere in here
   ============================================================ */
.ml-divider {
  position: relative;
  /* Height is declared, and the img carries width/height, so the
     band reserves its space before the photo loads - no shift. */
  --band-h: clamp(148px, 16vw, 256px);
  /* How tall the image inside the band is, and therefore how much there is
     to reveal. A full viewport on desktop. Less on a phone: `cover` on a
     1.6:1 source means the rendered WIDTH is height x 1.6, so a full-height
     image on a 390px-wide phone needs a 1350px source - 180KB per band for a
     screen that cannot show the detail. 62vh halves that and still reveals
     most of the picture. */
  --art-h: 100vh;
  height: var(--band-h);
  margin: 0;
  overflow: hidden;
  background-color: var(--ml-ink, #0E0E10);
  border-top: 1px solid rgba(255, 255, 255, .08);
  border-bottom: 1px solid rgba(255, 255, 255, .08);
}

.ml-divider__art {
  display: block;
  width: 100%;
  /* A full viewport tall, so the band is a window onto the whole photograph
     rather than a fixed slice of it. Christian, 26 Aug: "make the dividers
     all do what this one does where if you scroll it scrolls the image as
     well to reveal more" - pointing at .atmo-band, which gets that from
     `background-attachment: fixed`. This reproduces the same reveal but
     keeps srcset and lazy loading, and it works on mobile, where the
     atmo-bands fall back to `scroll` and lose the effect entirely.
     Earlier passes at 175%, 145% and 130% of the band height were all far
     too small a travel to read as this. */
  height: var(--art-h);
  object-fit: cover;
  object-position: center;
  transform: translateY(calc(-1 * var(--art-h) + var(--band-h)));
  /* No `will-change: transform` here. It would promote all 13 full-viewport
     images to their own compositor layers for the life of the page; a
     scroll-driven animation gets promoted while it is actually running
     anyway. */
}

/* Chrome/Edge drive this off the scroll timeline with no JS at all. */
@supports (animation-timeline: view()) {
  /* The timeline is NAMED on the band, not taken as an anonymous view() on
     the image. `view()` measures the subject against its nearest scrollport
     ancestor, and .ml-divider is `overflow: hidden` - which makes the band
     itself that scrollport. It never scrolls, so progress was constant and
     the image sat at one transform the whole way down the page. The band's
     own nearest scrollport is the document, which is what we want. */
  .ml-divider {
    view-timeline-name: --ml-band;
    view-timeline-axis: block;
  }
  .ml-divider__art {
    /* Longhand on purpose: the `animation:` shorthand resets duration to 0s,
       and a scroll-driven animation with a 0s duration never advances. */
    animation-name: ml-divider-pan;
    animation-duration: auto;
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: --ml-band;
    animation-range: entry 0% exit 100%;
  }
}
/* Direction matches .atmo-band: with the image pinned to the viewport, a band
   low on screen shows the BOTTOM of the picture and a band high on screen
   shows the top, so the content slides downward through the window as you
   scroll down. Running it the other way reads as the band sliding up past a
   still photo, which is a different effect. */
@keyframes ml-divider-pan {
  from { transform: translateY(calc(-1 * var(--art-h) + var(--band-h))); }
  to   { transform: translateY(0); }
}

/* Safari and Firefox get the same motion from ml-polish.js, which sets
   --pan on the band. The fallback only runs where the timeline is missing. */
@supports not (animation-timeline: view()) {
  .ml-divider__art {
    transform: translateY(calc((1 - var(--pan, 0)) * (-1 * var(--art-h) + var(--band-h))));
  }
}

/* Motion is decoration here. Off means off, and the band parks mid-photo -
   the most representative slice - rather than at the top of the pan. */
@media (prefers-reduced-motion: reduce) {
  .ml-divider__art {
    animation: none !important;
    transform: translateY(calc((-1 * var(--art-h) + var(--band-h)) / 2)) !important;
  }
}

/* The photographs are lit venue interiors and run bright at the
   edges. This keeps the band sitting DOWN against the ink either
   side of it, so it reads as a breath rather than a flashbulb. */
.ml-divider::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(8, 8, 10, .72) 0%, rgba(8, 8, 10, .12) 38%,
                                     rgba(8, 8, 10, .12) 62%, rgba(8, 8, 10, .55) 100%);
  pointer-events: none;
}

.ml-divider__tag {
  position: absolute; left: 0; bottom: 0; z-index: 2;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 16px 9px 14px;
  font-size: 11.5px; font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase;
  text-shadow: none;
  background: rgba(9, 9, 11, .78);
  -webkit-backdrop-filter: blur(5px);
          backdrop-filter: blur(5px);
  border-top-right-radius: 10px;
}
/* D1 · both marks sit on the .78-alpha ink scrim above, never on
   the photo itself: pink 10.4:1, blue 9.7:1 against it. */
.ml-divider--pink .ml-divider__tag { color: var(--ml-pink, #FF8CE0); }
.ml-divider--blue .ml-divider__tag { color: var(--ml-blue, #5BD0E6); }


/* Shorter pan, for a photograph whose top and bottom edges do not hold up.
   The band is a window onto --art-h of image and it travels the whole of it
   as you scroll, so EVERY horizontal slice gets shown - a frame that is only
   good through the middle will park its dead edge on screen. Trimming the
   travel keeps the window inside the part of the picture that reads.
   Deliberately declared BEFORE the mobile block below, so phones still get
   the smaller 62vh and its byte saving. */
.ml-divider--tight { --art-h: 72vh; }

@media (max-width: 720px) {
  .ml-divider { height: clamp(112px, 26vw, 150px); --art-h: 62vh; }
  .ml-divider__tag { font-size: 10.5px; padding: 7px 13px 7px 11px; letter-spacing: .16em; }
}

/* Atmosphere bands · same divider, no venue tag.
   These six were .atmo-band, a second divider system with its own CSS
   duplicated across home.css and menu-styles.css, using
   `background-attachment: fixed` for its parallax - which browsers disable on
   mobile, so phones got a static image and none of the reveal. They are
   ordinary dividers now, so every band on the site moves the same way.
   They carry no tag because the photographs carry no venue attribution and
   guessing which store a DJ booth belongs to is not something to guess at. */
.ml-divider--plain .ml-divider__tag { display: none; }
.ml-divider--plain::after {
  /* Without a tag the left edge has nothing anchoring it, so the scrim can
     come off and the photograph runs full width. */
  background: linear-gradient(180deg, rgba(8,8,10,.34) 0%, rgba(8,8,10,0) 26%,
                                      rgba(8,8,10,0) 74%, rgba(8,8,10,.34) 100%);
}
