/* ============================================================================
   PUE overrides on the QF chrome
   Loaded after qf-tokens.css and qf-chrome.css so this is the one place local
   deviations from the design system live. Nothing in qf-tokens.css (verbatim DS
   tokens) or qf-chrome.css (a faithful port of the DS SiteHeader / PueFooter) is
   edited, so both can be refreshed from the design system without losing this.

   Keep this file small, and note why each rule exists.
   ============================================================================ */

:root {
    /* Header lockup height. The DS ships 40px (2.5rem) and treats 36px as the
       floor, below which the Arabic strokes close up — there is no documented
       ceiling, so a larger lockup is a local choice, not a violation.
       rem, so it scales with the reader's root font size like the type does. */
    --pue-header-logo-height: 3.5rem;    /* 56px at the default 16px root */
    --pue-header-logo-height-sm: 2.75rem;/* 44px on narrow screens */

    /* Header text. The DS puts the utility row and the small button at 12px;
       both read as too small next to a 56px lockup, so they come up to the body
       size. The nav was already at body size. */
    --pue-header-nav-size: 1.125rem;     /* 18px, was 1rem */
    --pue-header-utility-size: 1rem;     /* 16px, was 0.875rem */
    --pue-header-button-size: 1rem;      /* 16px, was 0.875rem */

    /* Footer. Only the lockup is matched to the header; the type deliberately is
       not, so the lockup is the largest thing in the band. The DS ships the
       footer lockup at 46px against a 40px header one; both are now the same
       height, and the type sits at or near the DS size. */
    --pue-footer-logo-height: 3.5rem;    /* 56px, was 46px — same as the header */
    --pue-footer-logo-height-sm: 2.75rem;/* 44px on narrow screens */
    --pue-footer-link-size: 1rem;        /* 16px, the DS size, was 18px */
    --pue-footer-legal-size: 0.875rem;   /* 14px, was 15px */
    --pue-footer-social-size: 1.25rem;   /* 20px, was 24px */
}

/* Targets the lockup only: the nav tree glyph (.qf-nav a img) and the footer
   lockup (.pue-footer-lockup) are untouched. */
.qf-page .qf-header-logo img {
    height: var(--pue-header-logo-height);
    width: auto;
}

/* Header text only — page content and forms keep the DS scale. */
.qf-page .qf-nav a { font-size: var(--pue-header-nav-size); }
.qf-page .qf-utility { font-size: var(--pue-header-utility-size); }
.qf-page .qf-header-actions .qf-btn { font-size: var(--pue-header-button-size); }

/* ---- Footer (PueFooter): lockup matched to the header, type at the DS size ---- */
.qf-page .pue-footer-lockup { height: var(--pue-footer-logo-height); }
.qf-page .pue-footer-link { font-size: var(--pue-footer-link-size); }
.qf-page .pue-footer-legal .qf-shell { font-size: var(--pue-footer-legal-size); }
.qf-page .pue-credit { font-size: var(--pue-footer-legal-size); }
.qf-page .pue-footer-socials svg {
    width: var(--pue-footer-social-size);
    height: var(--pue-footer-social-size);
}

/* The band is black rather than Sidra green: at the top of the page the green is
   the brand, at the bottom it competes with the content above it. */
.qf-page .pue-footer { background: #000; }

/* The underline comes down with the link type — the DS's .45 white was drawn
   against 18px and reads as a heavy rule under 16px. */
.qf-page .pue-footer-link { border-bottom-color: rgba(255, 255, 255, .35); }

/* Nav-less header (layout: 'nav' => false): the hairline the nav row used to
   carry moves onto the header itself. */
.qf-page .qf-header--bare { border-bottom: var(--border-hairline); }

/* The DS nav scrolls horizontally with a hidden scrollbar, which on a phone just
   looks like a clipped last item. Wrap instead, so every destination is visible. */
@media (max-width: 700px) {
    .qf-page .qf-nav {
        flex-wrap: wrap;
        overflow-x: visible;
        gap: 0 var(--space-5);
    }
}

@media (max-width: 560px) {
    .qf-page .qf-header-logo img {
        height: var(--pue-header-logo-height-sm);
    }
    .qf-page .pue-footer-lockup {
        height: var(--pue-footer-logo-height-sm);
    }
    /* Full-width so the tap target is not a narrow strip beside the lockup. */
    .qf-page .qf-header-actions { width: 100%; }
    .qf-page .qf-header-actions .qf-btn { flex: 1 1 auto; }
}

/* ---- Signed-in name and sign-out control ----
   Local addition, not part of the design system chrome — kept here so
   qf-chrome.css stays refreshable from the DS. Sits in the header's action row
   beside the existing buttons. On every page of this site: see
   qf/partials/auth-nav.blade.php and App\View\Composers\QfChromeComposer. */
.qf-page .qf-auth {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}
.qf-page .qf-auth-who {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
    text-align: end;
}
.qf-page .qf-auth-label {
    font-size: var(--text-micro);
    color: var(--text-subtle);
}
.qf-page .qf-auth-name {
    font-size: var(--text-body-sm);
    color: var(--qf-ink);
}
/* How long this sign-in has left. Quiet by default: it is reassurance, and a
   fortnight of it shouting would train people not to see it. .is-soon is the last
   day, where it becomes something to act on. */
/* Its own line under the name, and never a reason for the header to grow: the
   row already carries a name, a Hub link and a sign-out button. nowrap keeps
   "Expires in 14 days renew" from breaking mid-phrase when the name is long. */
.qf-page .qf-auth-left {
    display: block;
    margin-top: 2px;
    font-size: var(--text-micro);
    line-height: 1.3;
    white-space: nowrap;
    color: var(--text-subtle);
    font-variant-numeric: tabular-nums;
}
.qf-page .qf-auth-left.is-soon {
    color: var(--qf-adham);
    font-weight: var(--weight-medium);
}
/* The way out of the countdown, underlined so it reads as the action it is. */
.qf-page .qf-auth-renew {
    margin-inline-start: var(--space-2);
    color: var(--text-link);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.qf-page .qf-auth-left.is-soon .qf-auth-renew { color: var(--qf-adham); }
.qf-page .qf-auth-form { margin: 0; }
.qf-page .qf-auth-link {
    font-size: var(--text-body-sm);
    color: var(--text-link);
    text-decoration: underline;
    text-underline-offset: 3px;
}
/* The sign-out submit has to look like the links beside it without losing the
   button semantics a POST needs. No outline reset: the focus ring stays. */
.qf-page .qf-auth-link--button {
    appearance: none;
    padding: 0;
    background: none;
    border: 0;
    font-family: inherit;
    cursor: pointer;
}

@media (max-width: 560px) {
    .qf-page .qf-auth {
        width: 100%;
        justify-content: space-between;
        gap: var(--space-3);
    }
    .qf-page .qf-auth-who { text-align: start; }
    /* The sign-in button is a .qf-btn, which the rule above stretches; the auth
       row manages its own width, so opt back out. */
    .qf-page .qf-header-actions .qf-auth .qf-btn { flex: 0 0 auto; }
}

/* ---- Code sign-in: the header popup and the standalone page ----
   One set of rules for both, since qf.partials.sign-in-form is shared. Square
   corners and hairlines like the rest of the hub; the dialog leans on the
   browser's own backdrop and focus handling rather than reimplementing them. */
.qf-signin-dialog {
    width: min(92vw, 30rem);
    padding: var(--space-8);
    color: var(--qf-ink);
    background: var(--surface-page);
    border: 1px solid var(--qf-ink);
    border-radius: var(--radius-none);
    box-shadow: var(--shadow-overlay);
}
.qf-signin-dialog::backdrop { background: rgba(17, 17, 17, .45); }

.qf-signin-dismiss { display: flex; justify-content: flex-end; margin: 0 0 calc(-1 * var(--space-4)); }
.qf-signin-dismiss button {
    appearance: none;
    padding: 0 var(--space-1);
    font-size: var(--text-title-2);
    line-height: 1;
    color: var(--text-subtle);
    background: none;
    border: 0;
    cursor: pointer;
}
.qf-signin-dismiss button:hover { color: var(--qf-ink); }

.qf-signin-title,
.qf-signin-heading {
    font-family: var(--font-serif-display);
    font-weight: var(--weight-regular);
    font-size: var(--text-title-1);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-display);
    margin-bottom: var(--space-5);
}

/* The page variant sits in a reading column rather than filling the shell. */
.qf-signin-page { max-width: 34rem; padding-block: var(--space-8) var(--space-16); }
.qf-signin-eyebrow { font-size: var(--text-body-sm); margin-bottom: var(--space-4); }
.qf-signin-heading { margin-bottom: var(--space-4); }
.qf-signin-lead {
    margin-bottom: var(--space-6);
    font-size: var(--text-body);
    line-height: var(--leading-body);
    color: var(--text-muted);
}

.qf-signin-form { display: flex; flex-direction: column; gap: var(--space-2); margin: 0; }
.qf-signin-label { font-size: var(--text-caption); font-weight: var(--weight-medium); }
.qf-signin-input {
    width: 100%;
    padding: var(--space-3);
    font-family: inherit;
    font-size: var(--text-body);
    color: var(--qf-ink);
    background: var(--surface-page);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-xs);
    outline: none;
}
.qf-signin-input:focus { border-color: var(--qf-ink); }
/* The code is six characters read off a phone: spaced out and large enough to
   check against the notification without squinting. */
.qf-signin-input--code {
    font-size: var(--text-title-2);
    letter-spacing: 0.4em;
    text-align: center;
}
.qf-signin-submit { margin-top: var(--space-3); align-self: flex-start; }
.qf-signin-sent { font-size: var(--text-body-sm); color: var(--text-muted); }
.qf-signin-note {
    margin-top: var(--space-3);
    font-size: var(--text-caption);
    color: var(--text-subtle);
    line-height: var(--leading-body);
}
.qf-signin-status {
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-5);
    font-size: var(--text-body-sm);
    background: var(--surface-tint);
    border-inline-start: 2px solid var(--qf-sidra-green);
}
.qf-signin-error { font-size: var(--text-caption); color: var(--qf-adham); }
.qf-signin-resend { margin-top: var(--space-5); }
.qf-signin-textlink {
    appearance: none;
    padding: 0;
    font-family: inherit;
    font-size: var(--text-caption);
    color: var(--text-link);
    background: none;
    border: 0;
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}

/* ---- Breadcrumbs (qf.partials.breadcrumbs) ----
   Sits above the page head, so it needs no space of its own beyond a margin —
   .qf-main already carries the section padding. */
.qf-page .qf-crumbs { margin-bottom: var(--space-6); }
.qf-page .qf-crumbs-list {
    display: flex;
    /* Never wraps into a ladder: the last crumb truncates instead. */
    flex-wrap: nowrap;
    align-items: baseline;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--text-body-sm);
}
.qf-page .qf-crumbs-item { flex: 0 0 auto; color: var(--text-muted); }

/* The separator is generated content on purpose. No browser exposes it to the
   accessibility tree, and you cannot put aria-hidden on a pseudo-element — so
   this is the accessible construction, not a shortcut. "/" rather than a chevron:
   square-first, and direction-neutral for RTL. */
.qf-page .qf-crumbs-item + .qf-crumbs-item::before {
    content: "/";
    margin-inline-end: var(--space-2);
    color: var(--qf-ink-30);
}

.qf-page .qf-crumbs-item a { color: var(--text-muted); text-decoration: none; }
.qf-page .qf-crumbs-item a:hover {
    color: var(--qf-ink);
    text-decoration: underline;
    text-underline-offset: 3px;
}
.qf-page .qf-crumbs-item [aria-current="page"] { color: var(--qf-ink); }

/* The current crumb is the one safe to truncate: it repeats the <h1> directly
   beneath it, so an ellipsis loses nothing. Deliberately NOT hidden on narrow
   screens — that would drop aria-current from the DOM on a phone, so a screen
   reader would hear a different trail depending on viewport width. */
.qf-page .qf-crumbs-item:last-child {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   PORT DEVIATION (Eventra): declare the colour scheme.

   The chrome ships no color-scheme declaration. On an OS in dark mode Chrome
   then applies its own auto-darkening to the whole document, and because every
   page below <main> here is Tailwind with no dark variants, the result is the
   chrome inverted around light cards — grey text on white, unreadable.

   Declaring light opts the document out of that. Remove this the day the pages
   get a real dark pass, and load qf-theme-dark.css again alongside it.
   --------------------------------------------------------------------------- */
:root {
  color-scheme: light;
}

/* ---------------------------------------------------------------------------
   PORT DEVIATION (Eventra): a wider content column for the purchase pages.

   .qf-main caps at --qf-content-max (1180px), which is right for the reading
   pages this chrome was built for. The ticket selection page is a two-column
   working surface — a list of tiers beside a live order summary — and at 1180px
   the summary crowds the tiers on a laptop. Opted in per page with
   'bodyClass' => 'qf-page--wide' so every other page keeps the DS width.
   --------------------------------------------------------------------------- */
.qf-page--wide .qf-main {
  max-width: 80rem; /* matches Tailwind max-w-7xl on the page's own container */
}

/* ---------------------------------------------------------------------------
   PORT DEVIATION (Eventra): let a page section that paints its own ground keep
   its own heading colour.

   qf-chrome.css sets `.qf-page :where(h1..h6) { color: var(--text-heading) }`.
   The page content below <main> is Tailwind, whose utilities live in
   @layer utilities — and unlayered CSS beats layered CSS whatever the
   specificity, so `text-white` on the element could never win. The event header
   band (Sidra green, full width) rendered its title in near-black ink at
   1.74:1, under the 3:1 large-text minimum this project holds itself to.

   Scoped to sections that declare .text-white, so an ordinary heading keeps the
   design-system ink.
   --------------------------------------------------------------------------- */
.qf-main .text-white :where(h1, h2, h3, h4, h5, h6),
.qf-main .text-white:where(h1, h2, h3, h4, h5, h6) {
  color: #fff;
}

/* ---------------------------------------------------------------------------
   PORT DEVIATION (Eventra): hand the page's own element defaults back to Tailwind.

   The rule above fixes ONE symptom of a general problem. qf-chrome.css also sets,
   all unlayered:

       .qf-page :where(h1..h6)   { margin: 0 }
       .qf-page :where(p, ul, ol){ margin: 0 }
       .qf-page :where(a)        { color: var(--text-link) }
       .qf-page :where(img, svg) { display: block }

   Those are correct for the chrome, which is hand-written CSS. Everything below
   <main> here is Tailwind, and Tailwind emits its utilities in @layer utilities —
   unlayered CSS beats layered CSS whatever the specificity. So on a ported page
   `mb-6` on a heading did nothing, `text-white` on a green button rendered the
   label in dark green on dark green, and every inline icon dropped onto its own
   line. None of it is visible in the markup; all of it is obvious on screen.

   `revert-layer` is the exact tool: from an unlayered declaration it rolls the
   value back to the last cascade layer that set it. That is Tailwind's own
   utility if the element carries one, and Tailwind's preflight (@layer base)
   otherwise — which is precisely the behaviour these pages had under
   layouts.app, before the port. So this is not a new look; it is the pages
   keeping the look they were designed with.

   Scoped to .qf-main, so the header and footer above and below it are untouched
   and still get the design system's own defaults.

   Heading COLOUR is deliberately not reverted: the DS ink is the right default
   and the .text-white exception above is the one case that needed an answer.
   --------------------------------------------------------------------------- */
.qf-page .qf-main :where(h1, h2, h3, h4, h5, h6, p, ul, ol) { margin: revert-layer; }
.qf-page .qf-main :where(a) { color: revert-layer; }
.qf-page .qf-main :where(img, svg) { display: revert-layer; }

/* ---------------------------------------------------------------------------
   PORT DEVIATION (Eventra): hide the dark-theme lockup.

   The header partial renders both colourways and lets CSS choose. The base rule
   that hides the dark one lives in qf-theme-dark.css (line 279 upstream), which
   this port deliberately does not load — so both lockups rendered, stacked on
   top of each other. This project is light-only (see the color-scheme note
   above), so the dark lockup never shows.
   --------------------------------------------------------------------------- */
.qf-header-logo .qf-lockup-dark,
.pue-footer-lockup-dark {
  display: none;
}

/* ---------------------------------------------------------------------------
   PUE: the page-head band, and how it lines up.

   A band that paints its own ground (the green event header, the green title
   band on the account and content pages) has to run the full width of the
   column while its TEXT stays on the same left edge as the content below it.
   Sitting it plainly inside .qf-main gives the opposite: green inset by the
   gutter, text inset by the gutter twice over, and nothing aligned with
   anything.

   So it bleeds back out by exactly the gutter .qf-main applied, then re-applies
   that gutter inside itself. Same construction the DS footer uses (full-width
   ground, .qf-shell inside), expressed for an element that is already inside
   the shell. Works unchanged on qf-page--wide, since both sides are the gutter,
   not the width.
   --------------------------------------------------------------------------- */
.qf-page .qf-main .qf-band {
  /* Percentages in margin and padding both resolve against .qf-main's content
     box, so this is exact: pull each edge out by half the difference between the
     viewport and the column, then put the same amount back as padding. The ground
     runs edge to edge; the text stays on the column's left edge. */
  margin-inline: calc(50% - 50vw);
  padding-inline: calc(50vw - 50%);
  /* The content under it is a sibling, so no space-y on the page reaches across
     this gap — without a margin the first card sits against the green. */
  margin-bottom: var(--space-8);
}

/* 100vw counts the scrollbar; the document does not. Without this every band
   overhangs the page by the scrollbar's width and the whole document gains a
   horizontal scrollbar — on Windows and Linux, where scrollbars take space.

   `clip` rather than `hidden` on purpose: `hidden` would make the body a scroll
   container, and .qf-header is position: sticky, which would then stick to a
   container of unbounded height and never stick at all. `clip` creates no scroll
   container, so the header keeps working. */
.qf-page {
  overflow-x: clip;
}

/* Two bands in a row — the journey stepper above the event header — are one
   masthead, so the gap belongs after the pair, not between them. */
.qf-page .qf-main .qf-band:has(+ .qf-band) {
  margin-bottom: 0;
}

/* The band is the first thing in <main>, and .qf-main already opened with
   --section-y-sm of air. Pull it up so the green starts at the top of the page
   rather than floating below a band of white. */
.qf-page .qf-main > .qf-band:first-child {
  margin-top: calc(-1 * var(--section-y-sm));
}

/* ---------------------------------------------------------------------------
   PUE: one radius, and it is square.

   The chrome is square by construction — .qf-btn is border-radius: var(--radius-none),
   and the DS scale tops out at 4px. The page content underneath had arrived from
   a prototype with rounded / rounded-md / rounded-lg / rounded-xl / rounded-2xl /
   rounded-3xl in play at once, so cards, buttons and inputs disagreed with the
   header, the footer, and each other.

   Decided 2026-08-15: square, matching the design system. Done here rather than
   by rewriting ~250 class attributes, so it is one reversible decision in the
   place this project already keeps its chrome deviations — and so markup added
   later inherits it instead of reintroducing the drift.

   Genuinely circular things keep their shape: step bullets, avatars, status
   pills, icon chips. Those are `rounded-full` in the markup, which is the
   signal for "this is a circle", not "this is a soft corner".
   --------------------------------------------------------------------------- */
.qf-page .qf-main :where([class*="rounded"]) { border-radius: var(--radius-none); }
.qf-page .qf-main :where(.rounded-full)      { border-radius: var(--radius-pill); }

/* ---------------------------------------------------------------------------
   PUE: a narrow column for the single-purpose pages.

   Sign in, the OTP steps, register, ticket verification, the payment handoff and
   the payment-failed dead end are all one short task with one answer. At the
   1180px reading width they were a small centred card floating in a wide empty
   page, and each had picked its own centring — so the page head above them could
   never line up with the thing it was heading.

   34rem is the width the design system already gives its own sign-in page
   (.qf-signin-page above). Opted in per page with 'bodyClass' => 'qf-page--narrow',
   the same way qf-page--wide works at the other end.
   --------------------------------------------------------------------------- */
.qf-page--narrow .qf-main {
  max-width: 34rem;
}

/* ---------------------------------------------------------------------------
   PUE: a submenu under a nav item.

   The design system's nav is flat. This site runs several shows at once and they
   differ only by a start time, so "the other one" is a thing people reach for
   constantly — and reaching for it meant going back to the listing every time.

   Deliberately no JavaScript. :hover opens it with a pointer, :focus-within opens
   it for the keyboard (tabbing into a link inside it keeps it open), and there is
   no state to get stuck: a page whose bundle failed to load still has a working
   menu, and the parent link still goes to the full listing either way.
   --------------------------------------------------------------------------- */
/* `.qf-nav` is `overflow-x: auto` in the design system, so it can scroll a long
   nav sideways on a narrow screen. Overflow creates a clipping context, which
   cropped the submenu to the height of the nav row — it rendered as a sliver over
   the tabs instead of a panel under them. Wide screens do not need the scroll (the
   nav fits, and below 700px it wraps instead — see the rule further up), so the
   clip is lifted exactly where the panel needs to escape. */
@media (min-width: 701px) {
    .qf-page .qf-nav { overflow: visible; }
}

.qf-page .qf-nav-item {
    position: relative;
    display: inline-flex;
    align-items: center;
}

/* The caret sits inside the link so it shares the hover and focus target. */
.qf-page .qf-nav-item--has-sub > a {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.qf-page .qf-subnav {
    position: absolute;
    inset-block-start: 100%;
    inset-inline-start: 0;
    z-index: 40;
    /* An absolutely positioned box shrink-to-fits against its containing block, and
       that is the nav item — about 82px. So once the links were allowed to wrap the
       panel collapsed onto its 16rem floor and set an event title in three lines
       inside a box entitled to 22rem. max-content asks for the width the longest
       item actually wants; the cap still decides how much of it it gets. */
    width: max-content;
    min-width: 16rem;
    max-width: min(22rem, 90vw);
    margin: 0;
    padding: var(--space-2) 0;
    list-style: none;
    background: var(--surface-page);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-none);
    box-shadow: var(--shadow-overlay);

    /* visibility, not display, so the transition can run and so the links stay
       out of the tab order while closed. */
    visibility: hidden;
    opacity: 0;
    transition: opacity var(--duration-fast) var(--ease-standard),
                visibility var(--duration-fast) var(--ease-standard);
}

.qf-page .qf-nav-item--has-sub:hover > .qf-subnav,
.qf-page .qf-nav-item--has-sub:focus-within > .qf-subnav {
    visibility: visible;
    opacity: 1;
}

.qf-page .qf-subnav a {
    display: block;
    padding: var(--space-2) var(--space-4);
    font-size: var(--text-body-sm);
    line-height: var(--leading-body);
    color: var(--qf-ink);
    text-decoration: none;

    /* `.qf-page .qf-nav a` in qf-chrome.css is `white-space: nowrap`, which is right
       for the tabs — a tab label must never wrap mid-word — but these links are
       inside the same nav, so they matched it too. An event title plus its date
       needs about 451px and the panel is capped at 22rem, so the line ran straight
       out of the white box. Same specificity as that rule, and this file loads
       after it, so the reset lands. */
    white-space: normal;
    overflow-wrap: break-word;
}

.qf-page .qf-subnav a:hover,
.qf-page .qf-subnav a:focus-visible {
    background: var(--surface-tint);
    color: var(--qf-ink);
}

/* On a phone the nav already wraps (see the rule further up), and a floating
   panel over a wrapped row is worse than an indented list. Show it inline and
   always open — the menu is short, and this is the one place the whole list of
   events is reachable without a hover a touch screen does not have. */
@media (max-width: 700px) {
    .qf-page .qf-nav-item--has-sub {
        display: block;
        width: 100%;
    }
    .qf-page .qf-subnav {
        position: static;
        visibility: visible;
        opacity: 1;
        width: auto;
        min-width: 0;
        max-width: none;
        padding: 0 0 var(--space-2);
        border: 0;
        box-shadow: none;
        background: none;
    }
    .qf-page .qf-subnav a {
        padding-inline: var(--space-4) 0;
        padding-block: var(--space-1);
        color: var(--text-muted);
    }
    /* The caret claims a menu that no longer opens or closes here. */
    .qf-page .qf-nav-item--has-sub > a svg { display: none; }
}

/* ---------------------------------------------------------------------------
   PUE: depth on the green bands.

   This is a ticketing site for concerts, graduations and school shows, and flat
   Sidra green bands read like a form. The prototype's answer had been rounded
   corners and drop shadows, neither of which the DS has.

   The answer here is light rather than ornament: the green is lit from the
   content's side and falls away toward the outer edge, and a hairline in white
   closes the band's bottom edge. No tile, no second colour — the band is still
   one flat brand green at a glance, it just has a direction.
   --------------------------------------------------------------------------- */
:root {
    /* The band's closing hairline. White at low alpha rather than a second
       brand colour: it reads as the edge of the green, not as trim on it. */
    --pue-band-edge: rgba(255, 255, 255, .22);
    --pue-band-edge-weight: 1px;
}

/* Every green band on the site: <x-page-head>, <x-event-header>, <x-event-hero>.
   Matched on the pair rather than on a new class, so a band added later gets the
   treatment without anyone remembering to ask for it. */
.qf-page .qf-main .qf-band.bg-primary {
    position: relative;
    /* The wash is a z-index:-1 child, and without a stacking context here it
       would slide behind the band's own green and vanish. */
    isolation: isolate;
    /* Inset, not a border: a border would change the band's box and undo the
       bleed arithmetic in .qf-band above. */
    box-shadow: inset 0 calc(-1 * var(--pue-band-edge-weight)) 0 0 var(--pue-band-edge);
}

.qf-page .qf-main .qf-band.bg-primary::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    /* One soft ellipse of light over the column where the title sits, and a
       deepening toward the outer edge. Both are the band's own green mixed with
       white and black, so the band cannot drift off palette. */
    background:
        radial-gradient(120% 180% at 18% 0%,
            color-mix(in srgb, var(--qf-sidra-green) 82%, #ffffff) 0%,
            transparent 62%),
        linear-gradient(100deg,
            transparent 40%,
            color-mix(in srgb, var(--qf-sidra-green) 78%, #000000) 100%);
    /* Settles in rather than appearing: the band is the first thing on the page
       and this is the only motion on load. */
    animation: pue-band-settle 620ms var(--ease-out, cubic-bezier(.2, .7, .3, 1)) both;
}

/* The bands carry dir themselves, so this is the whole of the RTL story. */
.qf-page .qf-main .qf-band.bg-primary[dir="rtl"]::before {
    background:
        radial-gradient(120% 180% at 82% 0%,
            color-mix(in srgb, var(--qf-sidra-green) 82%, #ffffff) 0%,
            transparent 62%),
        linear-gradient(260deg,
            transparent 40%,
            color-mix(in srgb, var(--qf-sidra-green) 78%, #000000) 100%);
}

@keyframes pue-band-settle {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* Two green bands in a row (the progress steps sit above the event band on the
   journey pages) would put a hairline through the middle of the green. The edge
   belongs at the bottom of the run, not inside it. */
.qf-page .qf-main .qf-band.bg-primary:has(+ .qf-band.bg-primary) {
    box-shadow: none;
}

@media (prefers-reduced-motion: reduce) {
    .qf-page .qf-main .qf-band.bg-primary::before { animation: none; }
}

/* ---------------------------------------------------------------------------
   PUE: gradient-hero.

   The event page's hero has carried this class since the prototype and no
   stylesheet has ever defined it — the "gradient" was flat bg-primary. Defined
   here rather than deleted from the markup, because the event page is the one
   page a buyer arrives on from a poster or a WhatsApp link and it should not
   look like the policies page.

   Sidra green deepened toward the outer corner, so the poster on the end side
   sits on the darkest ground and the title on the lightest.
   --------------------------------------------------------------------------- */
.qf-page .qf-main .qf-band.gradient-hero {
    background-image: linear-gradient(115deg,
        color-mix(in srgb, var(--qf-sidra-green) 88%, #ffffff) 0%,
        var(--qf-sidra-green) 46%,
        color-mix(in srgb, var(--qf-sidra-green) 82%, #000000) 100%);
}

.qf-page .qf-main .qf-band.gradient-hero[dir="rtl"] {
    background-image: linear-gradient(245deg,
        color-mix(in srgb, var(--qf-sidra-green) 88%, #ffffff) 0%,
        var(--qf-sidra-green) 46%,
        color-mix(in srgb, var(--qf-sidra-green) 82%, #000000) 100%);
}

/* ---------------------------------------------------------------------------
   PUE: the event card.

   The listing is the only page that is nothing but artwork, and the cards were
   answering a hover with a drop shadow — the one thing the DS explicitly does
   not do (it shifts colour instead, and the square decision above already took
   the rounded corners the shadow was pairing with).

   So the artwork answers instead. On hover the poster eases up a few percent
   behind the card's own square crop, a scrim lifts off it, and the border takes
   the brand green. The card itself never moves and nothing changes size, so a
   row of cards cannot reflow — the only animated properties are transform and
   opacity, both composited.
   --------------------------------------------------------------------------- */
.qf-page .qf-main .pue-event-card {
    position: relative;
    transition: border-color 220ms var(--ease-standard, cubic-bezier(.4, 0, .2, 1));
}

.qf-page .qf-main .pue-event-card:hover,
.qf-page .qf-main .pue-event-card:focus-within {
    border-color: var(--qf-sidra-green);
}

/* The poster, whichever branch of <x-event-poster> rendered. */
.qf-page .qf-main .pue-event-card img,
.qf-page .qf-main .pue-event-card .pue-poster-plate {
    /* Slower on the way in than the border, so the image reads as the thing
       that moved. */
    transition: transform 480ms var(--ease-out, cubic-bezier(.2, .7, .3, 1)),
                opacity 240ms linear;
    will-change: transform;
}

.qf-page .qf-main .pue-event-card:hover img,
.qf-page .qf-main .pue-event-card:focus-within img,
.qf-page .qf-main .pue-event-card:hover .pue-poster-plate,
.qf-page .qf-main .pue-event-card:focus-within .pue-poster-plate {
    transform: scale(1.045);
}

/* A scrim over the artwork at rest, gone on hover. It gives the title beneath a
   settled, printed look and makes the hover feel like the image waking up,
   without touching the artwork's own colours. */
.qf-page .qf-main .pue-event-card__media {
    position: relative;
    display: block;
    overflow: hidden;
}

.qf-page .qf-main .pue-event-card__media::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(to top,
        color-mix(in srgb, var(--qf-sidra-green) 55%, transparent) 0%,
        transparent 55%);
    opacity: 1;
    transition: opacity 320ms var(--ease-standard, cubic-bezier(.4, 0, .2, 1));
}

.qf-page .qf-main .pue-event-card:hover .pue-event-card__media::after,
.qf-page .qf-main .pue-event-card:focus-within .pue-event-card__media::after {
    opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
    .qf-page .qf-main .pue-event-card img,
    .qf-page .qf-main .pue-event-card .pue-poster-plate,
    .qf-page .qf-main .pue-event-card__media::after {
        transition: none;
    }
    .qf-page .qf-main .pue-event-card:hover img,
    .qf-page .qf-main .pue-event-card:focus-within img,
    .qf-page .qf-main .pue-event-card:hover .pue-poster-plate,
    .qf-page .qf-main .pue-event-card:focus-within .pue-poster-plate {
        transform: none;
    }
}

/* ---------------------------------------------------------------------------
   PUE: numbered steps.

   The How it works page had round bullets from the prototype; round is the DS
   signal for "avatar / status pill", and a step number is neither. A square
   block in the brand green with the number in white, sized off the type scale so
   it stays aligned with the heading beside it at any root font size.
   --------------------------------------------------------------------------- */
.qf-page .qf-main .pue-step-mark {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    inline-size: 2.75rem;
    block-size: 2.75rem;
    background: var(--qf-sidra-green);
    color: #ffffff;
    /* Tabular, so 1 / 2 / 3 and a two-digit step share one optical width. */
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: var(--text-body-lg);
    line-height: 1;
}

/* ---------------------------------------------------------------------------
   PUE: the step list on How it works.

   The tick is a square, not a check glyph and not a disc. A check would claim
   the reader has done the thing; a disc is a bullet, and this list already sits
   under a numbered step that owns the sequencing. So it is the smallest possible
   echo of .pue-step-mark above it — same green, same square corners — which is
   what ties the detail to the step it belongs to.

   Sized in em and given a top margin in em as well, so it stays centred on the
   first line at any font size rather than drifting when the copy reflows.
   --------------------------------------------------------------------------- */
.qf-page .qf-main .pue-step-tick {
    flex: 0 0 auto;
    inline-size: 0.4em;
    block-size: 0.4em;
    margin-block-start: 0.7em;
    background: var(--qf-sidra-green);
}

/* The steps rise in, staggered, the same way the event cards do — three of them,
   read top to bottom, which is exactly the shape that stagger suits. Three fixed
   delays and no nth-child(n+4) tail: a fourth step is a copy change away, and it
   should join the last one rather than push the page further out. */
.qf-page .qf-main .pue-step {
    animation: pue-card-in 420ms var(--ease-out) both;
}

.qf-page .qf-main .pue-step:nth-child(2) { animation-delay: 70ms; }
.qf-page .qf-main .pue-step:nth-child(n+3) { animation-delay: 140ms; }

@media (prefers-reduced-motion: reduce) {
    .qf-page .qf-main .pue-step { animation: none; }
}

/* ---------------------------------------------------------------------------
   PUE: the no-artwork plate.

   Backs <x-event-poster> when an event has no banner on the disk. The stock
   answer was public/img/event-placeholder.jpg — "PUE TICKETS" in grey on
   near-black, in neither the palette nor the QF font — and it appeared on the
   listing, the journey pages, the order summary and My Tickets alike.

   Brand green lit from one corner with the lockup centred instead, so a show
   whose poster has not been uploaded yet still looks like this site. The caller
   supplies the box; everything here is ground and centring.
   --------------------------------------------------------------------------- */
.qf-page .qf-main .pue-poster-plate {
    display: grid;
    place-items: center;
    overflow: hidden;
    /* Deeper than the bands' green on purpose: the plate has to read as an
       object sitting on the hero, not as a hole in it. */
    background-color: color-mix(in srgb, var(--qf-sidra-green) 88%, #000000);
    background-image:
        radial-gradient(90% 120% at 22% 12%,
            color-mix(in srgb, var(--qf-sidra-green) 92%, #ffffff) 0%,
            transparent 68%);
}

.qf-page .qf-main .pue-poster-plate > img {
    /* Percentage of the plate, not a fixed size: the same component fills a
       26rem hero and a 44px-tall thumbnail in the order summary. */
    inline-size: min(58%, 18rem);
    block-size: auto;
    opacity: .92;
}

/* ---------------------------------------------------------------------------
   PUE: the editorial text link.

   qf-chrome.css line 39 sets `.qf-page :where(a) { text-decoration: none; }`.
   That file is unlayered, so it beats every Tailwind utility regardless of
   specificity — the same trap this file already documents for headings and
   text-white. `underline decoration-2` on an anchor inside .qf-page renders
   flat, silently. It is not just one link: any `underline` class in a buyer view
   is dead for the same reason.

   So the treatment lives here, as a class, rather than as a `!` variant fighting
   the layer — and it is a drawn rule rather than text-decoration, because a rule
   can animate and an underline cannot. At rest the link carries a faint hairline
   in its own colour; on hover the full-weight rule sweeps in from the leading
   edge.
   --------------------------------------------------------------------------- */
.qf-page .qf-main .pue-textlink {
    position: relative;
    display: inline-block;
    color: var(--qf-sidra-green);
    font-weight: 600;
    text-decoration: none;
    /* Room for the rule, so it never collides with a descender in either the
       Latin or the Arabic face. */
    padding-block-end: .18em;
}

.qf-page .qf-main .pue-textlink::before,
.qf-page .qf-main .pue-textlink::after {
    content: "";
    position: absolute;
    inset-block-end: 0;
    inset-inline: 0;
    block-size: 2px;
    background: currentColor;
    pointer-events: none;
}

/* The hairline that says "link" before anyone points at it. */
.qf-page .qf-main .pue-textlink::before {
    opacity: .28;
}

/* The rule that answers the pointer. scaleX is composited; width is not. */
.qf-page .qf-main .pue-textlink::after {
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 260ms var(--ease-out, cubic-bezier(.2, .7, .3, 1));
}

.qf-page .qf-main [dir="rtl"] .pue-textlink::after {
    transform-origin: right center;
}

/* The dir attribute sits on <html> and on the bands, not on the prose blocks a
   link lives in, so the descendant rule above misses most Arabic links. :dir()
   reads the computed direction wherever it was set. Kept as its own rule: an
   engine that does not know the pseudo-class drops this block and keeps the one
   above rather than dropping both. */
.qf-page .qf-main .pue-textlink:dir(rtl)::after {
    transform-origin: right center;
}

.qf-page .qf-main .pue-textlink:hover::after,
.qf-page .qf-main .pue-textlink:focus-visible::after {
    transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
    .qf-page .qf-main .pue-textlink::after { transition: none; }
}

/* ---------------------------------------------------------------------------
   PUE: give `hover:underline` back its underline.

   Ten links across the buyer views — the auth pages, the checkout consent line,
   the ticket link on the confirmation page — carry Tailwind's `hover:underline`
   and have never underlined on hover, for the reason above: the unlayered
   `.qf-page :where(a) { text-decoration: none }` outranks the utility whatever
   its specificity. The affordance was written, shipped and silently dropped.

   Keyed on the utility already in the markup rather than renaming ten links to a
   local class, since the intent there is Tailwind's and reads correctly in the
   template. Underline only, in the link's own colour: those links set their own.
   --------------------------------------------------------------------------- */
.qf-page .qf-main a[class*="hover:underline"]:hover,
.qf-page .qf-main a[class*="hover:underline"]:focus-visible {
    text-decoration: underline;
    text-decoration-thickness: 1.5px;
    text-underline-offset: 3px;
}

/* ---------------------------------------------------------------------------
   PUE: the primary call to action.

   One button on the site's one marquee page, so it is the only place worth
   animating properly. It was a white block with a yellow rule under it that grew
   a yellow ground on hover — three colours for one action.

   Now it is white, and on hover a deep green ground sweeps in from the leading
   edge and the label crosses to white with it. The fill is a scaleX on a
   negative-z-index child, so it is composited and the button's box never
   changes; the sweep direction follows the writing direction.
   --------------------------------------------------------------------------- */
.qf-page .qf-main .pue-cta {
    position: relative;
    /* Without a stacking context the fill would slide behind the band. */
    isolation: isolate;
    overflow: hidden;
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    background: #ffffff;
    color: var(--qf-sidra-green);
    transition: color 240ms var(--ease-standard, cubic-bezier(.4, 0, .2, 1));
}

.qf-page .qf-main .pue-cta::before {
    content: "";
    position: absolute;
    inset: 0;
    /* Above the button's own white, below its label: a negative z-index child
       paints over the parent's background and under its in-flow content. */
    z-index: -1;
    background: color-mix(in srgb, var(--qf-sidra-green) 92%, #000000);
    /* The fill carries its own white hairline, so it arrives with the sweep and
       holds the button's silhouette. On the element itself the ring would paint
       with the background — underneath this fill — and never be seen. Without it
       the filled button dissolves into the hero's own green. */
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .9);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 340ms var(--ease-out, cubic-bezier(.2, .7, .3, 1));
}

.qf-page .qf-main [dir="rtl"] .pue-cta::before,
.qf-page .qf-main .pue-cta[dir="rtl"]::before {
    transform-origin: right center;
}

.qf-page .qf-main .pue-cta:dir(rtl)::before {
    transform-origin: right center;
}

.qf-page .qf-main .pue-cta:hover,
.qf-page .qf-main .pue-cta:focus-visible {
    color: #ffffff;
}

.qf-page .qf-main .pue-cta:hover::before,
.qf-page .qf-main .pue-cta:focus-visible::before {
    transform: scaleX(1);
}

/* The icon leans toward the action it performs. */
.qf-page .qf-main .pue-cta svg,
.qf-page .qf-main .pue-cta i {
    transition: transform 340ms var(--ease-out, cubic-bezier(.2, .7, .3, 1));
}

.qf-page .qf-main .pue-cta:hover svg,
.qf-page .qf-main .pue-cta:hover i {
    transform: translateX(2px);
}

.qf-page .qf-main [dir="rtl"] .pue-cta:hover svg,
.qf-page .qf-main [dir="rtl"] .pue-cta:hover i {
    transform: translateX(-2px);
}

.qf-page .qf-main .pue-cta:dir(rtl):hover svg,
.qf-page .qf-main .pue-cta:dir(rtl):hover i {
    transform: translateX(-2px);
}

@media (prefers-reduced-motion: reduce) {
    .qf-page .qf-main .pue-cta,
    .qf-page .qf-main .pue-cta::before,
    .qf-page .qf-main .pue-cta svg,
    .qf-page .qf-main .pue-cta i {
        transition: none;
    }
    /* Still a state change, just not a sweep. */
    .qf-page .qf-main .pue-cta:hover::before,
    .qf-page .qf-main .pue-cta:focus-visible::before { transform: scaleX(1); }
    .qf-page .qf-main .pue-cta:hover svg,
    .qf-page .qf-main .pue-cta:hover i { transform: none; }
}

/* ---------------------------------------------------------------------------
   PUE: the hero poster.

   The artwork on the event page, hung on the green. It had a yellow block offset
   behind it — a second colour doing the job of an edge. A hairline in white and
   a shadow that reads as depth on dark green do it instead, and the plate rises
   into place once on load so the page has one moment of movement where the
   artwork is.
   --------------------------------------------------------------------------- */
.qf-page .qf-main .pue-hero-poster {
    box-shadow:
        0 0 0 1px rgba(255, 255, 255, .22),
        0 18px 40px -18px rgba(0, 0, 0, .55);
    animation: pue-hero-poster-in 620ms var(--ease-out, cubic-bezier(.2, .7, .3, 1)) both;
}

@keyframes pue-hero-poster-in {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .qf-page .qf-main .pue-hero-poster { animation: none; }
}

/* ===========================================================================
   PUE: orange is the interaction accent.

   The site had exactly two answers to a pointer: whatever hover a given Tailwind
   button happened to declare inline — a slightly different one per file — and, on
   the nav, nothing anyone would notice.

   Orange is not a new colour here. It is the design system's own interaction
   accent, and the tokens say so out loud:

       --text-link-hover : var(--qf-orange)     (qf-tokens.css:61)
       --accent-primary  : var(--qf-orange)     (qf-tokens.css:73)
       --accent-editorial: var(--qf-yellow)

   Yellow is the *editorial* accent in that list, never the interaction one —
   which is exactly why it kept reading as decoration when it was tried as a
   hover. So the whole site now runs on one sentence: **green is the brand,
   orange is the response.**

   Two shapes, one idea. A rectangular control answers with an orange rule that
   sweeps in along its bottom edge from the leading side. A round control has no
   edge to draw on, so it answers by taking the accent into its border and its
   icon. Both at the same duration and easing, so the site has one reflex.

   All of it is transform, opacity and colour — nothing changes size, so no hover
   can reflow a row of cards or a nav bar. Everything below is additive: no rule
   here sets a background, so the per-button colour designs already in the Blade
   files keep working exactly as written and simply gain the accent underneath.
   =========================================================================== */
:root {
    --pue-accent: var(--qf-orange);
    /* The chrome's own qf-btn--accent:hover value, so the one place orange is
       used as text lands on the shade the DS already ships. */
    --pue-accent-deep: #e85712;
    /* One rhythm for every hover on the site. Slower than the DS's 120ms colour
       fade, because a rule that sweeps needs to be seen doing it. */
    --pue-sweep: 240ms;
}

/* ---------------------------------------------------------------------------
   Rectangular controls — the rule under everything that acts.

   Keyed on the Tailwind signatures already in the markup rather than renaming 67
   buttons across 30 templates. [class~="…"] matches a whole class token only, so
   bg-primary/10 tints and border-primary/20 hairlines are left out; :is(a, button)
   keeps it to things that are actually pressed, so the border-gray-300 on a card
   or an input is untouched.

   Two families are deliberately absent. Destructive controls (border-red-300,
   text-red-600) keep red as their only accent — an action that deletes something
   should not borrow the colour every other button uses. And round controls are
   excluded here and handled below.
   --------------------------------------------------------------------------- */
.qf-page .qf-main :is(a, button):not([class~="rounded-full"]):is(
    [class~="bg-primary"],
    [class~="border-primary"],
    [class~="bg-accent"],
    [class~="border-accent"],
    [class~="border-gray-300"],
    [class~="border-zinc-300"]
),
.qf-page .qf-btn {
    position: relative;
    /* The rule is drawn inside the box, so nothing needs clipping — and no
       overflow:hidden that could crop a focus ring. */
    isolation: isolate;
}

.qf-page .qf-main :is(a, button):not([class~="rounded-full"]):is(
    [class~="bg-primary"],
    [class~="border-primary"],
    [class~="bg-accent"],
    [class~="border-accent"],
    [class~="border-gray-300"],
    [class~="border-zinc-300"]
)::after,
.qf-page .qf-btn::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    inset-block-end: 0;
    block-size: 2px;
    background: var(--pue-accent);
    pointer-events: none;
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--pue-sweep) var(--ease-out);
}

/* Directional motion follows the writing direction. :dir() rather than
   [dir="rtl"] because dir sits on <html> and on the bands, never on the blocks
   these buttons live in. */
.qf-page .qf-main :is(a, button):is(
    [class~="bg-primary"],
    [class~="border-primary"],
    [class~="bg-accent"],
    [class~="border-accent"],
    [class~="border-gray-300"],
    [class~="border-zinc-300"]
):dir(rtl)::after,
.qf-page .qf-btn:dir(rtl)::after {
    transform-origin: right center;
}

.qf-page .qf-main :is(a, button):is(
    [class~="bg-primary"],
    [class~="border-primary"],
    [class~="bg-accent"],
    [class~="border-accent"],
    [class~="border-gray-300"],
    [class~="border-zinc-300"]
):is(:hover, :focus-visible)::after,
.qf-page .qf-btn:is(:hover, :focus-visible)::after {
    transform: scaleX(1);
}

/* A control that cannot be pressed does not answer. */
.qf-page .qf-main :is(a, button):is(:disabled, [aria-disabled="true"])::after,
.qf-page .qf-btn:disabled::after {
    display: none;
}

/* ---------------------------------------------------------------------------
   Round controls — the accent moves into the edge instead.

   The quantity steppers and the pill-shaped chips are circles: a 2px bar across
   the bottom of a circle is a chord, not an underline. They take the accent into
   their border and their glyph instead, which is the same event told in the shape
   the control actually has.
   --------------------------------------------------------------------------- */
.qf-page .qf-main .quantity-btn,
.qf-page .qf-main :is(a, button)[class~="rounded-full"] {
    transition: border-color var(--pue-sweep) var(--ease-standard),
                color var(--pue-sweep) var(--ease-standard),
                background-color var(--pue-sweep) var(--ease-standard);
}

.qf-page .qf-main .quantity-btn:not(:disabled):is(:hover, :focus-visible),
.qf-page .qf-main :is(a, button)[class~="border-gray-300"][class~="rounded-full"]:not(:disabled):is(:hover, :focus-visible) {
    border-color: var(--pue-accent);
    color: var(--pue-accent-deep);
}

/* ---------------------------------------------------------------------------
   The trailing icon leans toward the action.

   Last child only: an arrow after the label is going somewhere, a ticket glyph
   before it is not.
   --------------------------------------------------------------------------- */
.qf-page .qf-main :is(a, button) > svg:last-child,
.qf-page .qf-btn > svg:last-child {
    transition: transform var(--pue-sweep) var(--ease-out);
}

.qf-page .qf-main :is(a, button):is(
    [class~="bg-primary"],
    [class~="border-primary"],
    [class~="bg-accent"]
):hover > svg:last-child {
    transform: translateX(3px);
}

.qf-page .qf-main :is(a, button):is(
    [class~="bg-primary"],
    [class~="border-primary"],
    [class~="bg-accent"]
):dir(rtl):hover > svg:last-child {
    transform: translateX(-3px);
}

/* The checklist item that is not decoration: a clickable thing has to look
   clickable before it is hovered. */
.qf-page .qf-main button:not(:disabled):not([aria-disabled="true"]) {
    cursor: pointer;
}

@media (prefers-reduced-motion: reduce) {
    .qf-page .qf-main :is(a, button)::after,
    .qf-page .qf-btn::after,
    .qf-page .qf-main .quantity-btn,
    .qf-page .qf-main :is(a, button)[class~="rounded-full"],
    .qf-page .qf-main :is(a, button) > svg:last-child,
    .qf-page .qf-btn > svg:last-child {
        transition: none;
    }
    .qf-page .qf-main :is(a, button):hover > svg:last-child {
        transform: none;
    }
}

/* ---------------------------------------------------------------------------
   The primary nav — an indicator, not just a colour.

   qf-chrome.css gives each tab a transparent 2px bottom border that only the
   current page fills in, and a hover that shifts the label from muted to ink.
   On a bar of six items that hover is invisible at a glance.

   The orange indicator sweeps in under the item being pointed at, from the
   leading edge, and lands on exactly the line the DS's own active underline
   occupies — so hover reads as "this is where you would land" while the ink
   underline keeps meaning "this is where you are". The DS active treatment is
   untouched.
   --------------------------------------------------------------------------- */
.qf-page .qf-nav a {
    position: relative;
}

.qf-page .qf-nav a::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    /* -2px, not 0: absolute offsets resolve against the padding box, and the
       tab's own transparent bottom border is 2px deep. Without this the
       indicator would float two pixels above the active underline. */
    inset-block-end: -2px;
    block-size: 2px;
    background: var(--pue-accent);
    pointer-events: none;
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--pue-sweep) var(--ease-out);
}

.qf-page .qf-nav a:dir(rtl)::after {
    transform-origin: right center;
}

.qf-page .qf-nav a:is(:hover, :focus-visible)::after {
    transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
    .qf-page .qf-nav a::after { transition: none; }
}

/* ---------------------------------------------------------------------------
   The submenu, the utility row and the sign-out control.

   The rest of the chrome that answers a pointer. The submenu rows and the
   sign-out button had no hover at all; the utility row had one that went to ink,
   which is roughly the grey the row already looked like.
   --------------------------------------------------------------------------- */
.qf-page .qf-nav-sub a,
.qf-page .qf-utility a,
.qf-page .qf-auth-link {
    transition: color var(--duration-fast) var(--ease-standard),
                background-color var(--duration-fast) var(--ease-standard);
}

.qf-page .qf-nav-sub a:is(:hover, :focus-visible),
.qf-page .qf-utility a:is(:hover, :focus-visible),
.qf-page .qf-auth-link:is(:hover, :focus-visible) {
    color: var(--pue-accent-deep);
}

/* A floating panel needs the row to light up, not just the words in it — a bare
   colour change on a menu item reads as a rendering glitch. */
.qf-page .qf-nav-sub a:is(:hover, :focus-visible) {
    background-color: var(--qf-surface-tint-2);
}

/* ---------------------------------------------------------------------------
   Inline links inside the content.

   An anchor carrying text-primary and nothing else is a body link, and it goes to
   the accent on hover — precisely what --text-link-hover asks for, and what the
   chrome already does for every unstyled link.

   The :not() pair is what keeps this off buttons. Several bordered buttons are
   also text-primary anchors, and letting the label follow turned the whole
   control orange — border, icon and words — which is far more than "you are
   pointing at this". A button already answers with its rule; only text that has
   nothing but its colour needs the colour to change.
   --------------------------------------------------------------------------- */
.qf-page .qf-main a[class~="text-primary"]:not([class*="border-"]):not([class*="bg-"]) {
    transition: color var(--duration-fast) var(--ease-standard);
}

.qf-page .qf-main a[class~="text-primary"]:not([class*="border-"]):not([class*="bg-"]):is(:hover, :focus-visible) {
    color: var(--pue-accent-deep);
}

/* ---------------------------------------------------------------------------
   The two treatments that already had motion, brought onto the accent.

   .pue-cta and .pue-textlink were built in the previous pass, before orange was
   the interaction colour. They keep their motion — the sweeping fill, the drawn
   rule — and simply answer in orange now, so the site has one interaction colour
   rather than two.
   --------------------------------------------------------------------------- */

/* The hero CTA: the hairline around the sweeping fill turns orange and arrives
   with it. This is the one place the accent gets to be the edge of a whole shape
   rather than a rule under one — it is the site's single primary action, on its
   one marquee page. */
.qf-page .qf-main .pue-cta::before {
    box-shadow: inset 0 0 0 1px var(--pue-accent);
}

/* Body links: the drawn rule is currentColor, so label and rule go to the accent
   together. The idle rule stays green at 28% — the link belongs to the brand
   until it is pointed at. */
.qf-page .qf-main .pue-textlink {
    transition: color var(--pue-sweep) var(--ease-standard);
}

.qf-page .qf-main .pue-textlink:is(:hover, :focus-visible) {
    color: var(--pue-accent-deep);
}

@media (prefers-reduced-motion: reduce) {
    .qf-page .qf-main .pue-textlink { transition: none; }
}

/* ---------------------------------------------------------------------------
   The listing arrives.

   The one page that is purely a grid gets the grid treatment: the cards rise and
   fade in, staggered, so the eye is walked along the row instead of having the
   whole thing appear at once. Five delays and then it stops — past the fifth card
   nobody is watching, and a long tail of delays on a full listing would feel like
   the page was still loading.
   --------------------------------------------------------------------------- */
.qf-page .qf-main .pue-event-card {
    animation: pue-card-in 420ms var(--ease-out) both;
}

.qf-page .qf-main .pue-event-card:nth-child(2) { animation-delay: 60ms; }
.qf-page .qf-main .pue-event-card:nth-child(3) { animation-delay: 120ms; }
.qf-page .qf-main .pue-event-card:nth-child(4) { animation-delay: 180ms; }
.qf-page .qf-main .pue-event-card:nth-child(n+5) { animation-delay: 240ms; }

@keyframes pue-card-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .qf-page .qf-main .pue-event-card { animation: none; }
}

/* ---------------------------------------------------------------------------
   Phone menu (qf/partials/mobile-menu.blade.php). Not part of the design system.

   At 700px and below the header becomes the lockup and one button; the button
   wipes a flat Sidra-green sheet down over the page and the links rise into it
   one after another. Everything is gated on html.pue-js, which the menu's own
   script sets: without it the desktop rows stay, stacked as before, so a page
   whose script failed still has its navigation. Motion is transform, opacity
   and clip-path only; the reduced-motion branch at the end turns it into a fade.
   --------------------------------------------------------------------------- */
.qf-page .pue-mnav-toggle,
.qf-page .pue-mnav { display: none; }

@media (max-width: 700px) {
    .pue-js .qf-page .qf-header-actions,
    .pue-js .qf-page .qf-header-bar { display: none; }
    .pue-js .qf-page .qf-header-top { flex-wrap: nowrap; align-items: center; }
    /* The nav row carried the header's bottom hairline; it is hidden now. */
    .pue-js .qf-page .qf-header { border-bottom: var(--border-hairline); }

    .pue-js .qf-page .pue-mnav-toggle { display: inline-flex; }
    .pue-js .qf-page .pue-mnav { display: flex; }
}

/* Both the hamburger and the sheet's X: two bars that fold into a cross. */
.qf-page .pue-mnav-toggle,
.qf-page .pue-mnav-close {
    appearance: none;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    margin-inline-start: auto;
    padding: 0;
    border: 1px solid currentColor;
    border-radius: 0;
    background: none;
    color: var(--text-brand);
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: transform var(--duration-fast) var(--ease-standard),
                color var(--duration-base) var(--ease-standard);
}
.qf-page .pue-mnav-close { display: inline-flex; color: var(--qf-white); }
.qf-page .pue-mnav-toggle:active,
.qf-page .pue-mnav-close:active { transform: scale(.94); color: var(--qf-orange); }
.qf-page .pue-mnav-bars { position: relative; display: block; width: 22px; height: 12px; }
.qf-page .pue-mnav-bars span {
    position: absolute;
    inset-inline: 0;
    height: 2px;
    background: currentColor;
    transition: transform 320ms cubic-bezier(.5, 0, .1, 1);
}
.qf-page .pue-mnav-bars span:first-child { top: 0; }
.qf-page .pue-mnav-bars span:last-child  { bottom: 0; inset-inline-start: 8px; }
.qf-page .pue-mnav-toggle:hover .pue-mnav-bars span:last-child { transform: translateX(-8px); }
.qf-page .pue-mnav-bars.is-x span:first-child { transform: translateY(5px) rotate(45deg); }
.qf-page .pue-mnav-bars.is-x span:last-child  { inset-inline-start: 0; transform: translateY(-5px) rotate(-45deg); }

/* ---- The sheet ---- */
.qf-page .pue-mnav {
    position: fixed;
    inset: 0;
    z-index: 1000;
    flex-direction: column;
    height: 100dvh;
    padding: max(env(safe-area-inset-top), 12px) 20px max(env(safe-area-inset-bottom), 20px);
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--qf-sidra-green);
    color: var(--qf-white);
    visibility: hidden;
    clip-path: inset(0 0 100% 0);
    transition: clip-path 360ms cubic-bezier(.7, 0, .84, 0), visibility 0s linear 360ms;
}
.pue-mnav-open .qf-page .pue-mnav {
    visibility: visible;
    clip-path: inset(0 0 0 0);
    transition: clip-path 560ms cubic-bezier(.16, 1, .3, 1), visibility 0s;
}
.pue-mnav-open,
.pue-mnav-open body { overflow: hidden; }

.qf-page .pue-mnav-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: 12px;
    border-bottom: 1px solid rgba(255, 255, 255, .18);
}
.qf-page .pue-mnav-logo img { display: block; height: var(--pue-header-logo-height-sm); width: auto; }

.qf-page .pue-mnav-body { flex: 1 0 auto; padding-top: 28px; }

/* Every block rises in on its own beat, set by --i in the markup. */
.qf-page .pue-mnav-item,
.qf-page .pue-mnav-actions,
.qf-page .pue-mnav-who,
.qf-page .pue-mnav-foot {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity 160ms var(--ease-standard), transform 160ms var(--ease-standard);
}
.pue-mnav-open .qf-page .pue-mnav-item,
.pue-mnav-open .qf-page .pue-mnav-actions,
.pue-mnav-open .qf-page .pue-mnav-who,
.pue-mnav-open .qf-page .pue-mnav-foot {
    opacity: 1;
    transform: none;
    transition: opacity 420ms var(--ease-out), transform 620ms cubic-bezier(.16, 1, .3, 1);
    transition-delay: calc(180ms + var(--i, 5) * 60ms);
}

.qf-page .pue-mnav-link {
    position: relative;
    display: flex;
    align-items: baseline;
    gap: 14px;
    padding: 10px 0;
    font-size: 2rem;
    font-weight: var(--weight-medium, 500);
    line-height: 1.15;
    letter-spacing: -.01em;
    color: var(--qf-white);
    text-decoration: none;
    transition: color var(--duration-base) var(--ease-standard), transform var(--duration-base) var(--ease-standard);
}
.qf-page .pue-mnav-num {
    font-size: .8rem;
    font-weight: var(--weight-regular, 400);
    letter-spacing: .08em;
    color: var(--qf-sidra-green-40);
    font-variant-numeric: tabular-nums;
}
/* Where you are: a short white rule ahead of the number. */
.qf-page .pue-mnav-link[aria-current="page"]::before {
    content: "";
    position: absolute;
    inset-inline-start: -20px;
    top: 50%;
    width: 8px;
    height: 2px;
    background: var(--qf-white);
}
.qf-page .pue-mnav-link:active,
.qf-page .pue-mnav-link:focus-visible { color: var(--qf-orange); transform: translateX(6px); }
.qf-page .pue-mnav-link:dir(rtl):active,
.qf-page .pue-mnav-link:dir(rtl):focus-visible { transform: translateX(-6px); }

.qf-page .pue-mnav-sub {
    margin: 0 0 8px;
    padding: 0 0 0 36px;
    list-style: none;
    border-inline-start: 1px solid rgba(255, 255, 255, .18);
    margin-inline-start: 8px;
}
.qf-page .pue-mnav-sub a {
    display: block;
    padding: 10px 0;
    min-height: 44px;
    font-size: 1rem;
    color: rgba(255, 255, 255, .78);
    text-decoration: none;
    transition: color var(--duration-base) var(--ease-standard);
}
.qf-page .pue-mnav-sub a:active,
.qf-page .pue-mnav-sub a:focus-visible { color: var(--qf-orange); }

.qf-page .pue-mnav-actions {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 10px;
    margin-top: 32px;
}
.qf-page .pue-mnav-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 52px;
    padding: 0 16px;
    font-size: 1rem;
    font-weight: var(--weight-medium, 500);
    text-decoration: none;
    border: 1px solid var(--qf-white);
    color: var(--qf-white);
    transition: background-color var(--duration-base) var(--ease-standard),
                border-color var(--duration-base) var(--ease-standard),
                color var(--duration-base) var(--ease-standard),
                transform var(--duration-fast) var(--ease-standard);
}
.qf-page .pue-mnav-btn--primary { background: var(--qf-white); color: var(--qf-sidra-green); }
.qf-page .pue-mnav-btn:active {
    transform: scale(.97);
    background: var(--qf-orange);
    border-color: var(--qf-orange);
    color: var(--qf-white);
}

.qf-page .pue-mnav-who {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 20px;
    padding-top: 16px;
    border-top: 1px solid rgba(255, 255, 255, .18);
}
.qf-page .pue-mnav-who-label { display: block; font-size: var(--text-micro); color: var(--qf-sidra-green-40); }
.qf-page .pue-mnav-who-name { display: block; font-size: 1rem; }
.qf-page .pue-mnav-who form { margin: 0; }
.qf-page .pue-mnav-logout {
    appearance: none;
    min-height: 44px;
    padding: 0 4px;
    border: 0;
    background: none;
    font: inherit;
    color: var(--qf-white);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}
.qf-page .pue-mnav-logout:active { color: var(--qf-orange); }

.qf-page .pue-mnav-foot {
    --i: 8;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 18px;
    margin-top: 32px;
    padding-top: 16px;
    border-top: 1px solid rgba(255, 255, 255, .18);
    font-size: var(--text-body-sm);
}
.qf-page .pue-mnav-foot a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    color: rgba(255, 255, 255, .78);
    text-decoration: none;
    transition: color var(--duration-base) var(--ease-standard);
}
.qf-page .pue-mnav-foot .pue-mnav-icon { justify-content: center; min-width: 36px; }
.qf-page .pue-mnav-foot a:active,
.qf-page .pue-mnav-foot a:focus-visible { color: var(--qf-orange); }

.qf-page .pue-mnav :focus-visible { outline: 2px solid var(--qf-white); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
    .qf-page .pue-mnav,
    .pue-mnav-open .qf-page .pue-mnav {
        clip-path: none;
        opacity: 0;
        transition: opacity 150ms linear, visibility 0s linear 150ms;
    }
    .pue-mnav-open .qf-page .pue-mnav { opacity: 1; transition: opacity 150ms linear, visibility 0s; }
    .qf-page .pue-mnav-item,
    .qf-page .pue-mnav-actions,
    .qf-page .pue-mnav-who,
    .qf-page .pue-mnav-foot,
    .pue-mnav-open .qf-page .pue-mnav-item,
    .pue-mnav-open .qf-page .pue-mnav-actions,
    .pue-mnav-open .qf-page .pue-mnav-who,
    .pue-mnav-open .qf-page .pue-mnav-foot { opacity: 1; transform: none; transition: none; }
    .qf-page .pue-mnav-bars span,
    .qf-page .pue-mnav-link { transition: none; }
}
