/* Costume detail page styles, extracted verbatim from the page's
 * inline <style> blocks (shared type-scale block, verified identical
 * across all twelve; Wicked Broom face; Poppins face). Loaded on
 * costume singles only, after pages.css. */

  /* ---- Type scale · base 18px, ratio 1.25 (major third) ------------------
     Every size on the page is a step on this scale. Fluid steps interpolate
     between a mobile and a desktop step so the ratio holds at any width.    */
  :root {
    --t--2: 0.75rem;    /* 12 · fine print            */
    --t--1: 0.875rem;   /* 14 · small                 */
    --t-0:  1.125rem;   /* 18 · body                  */
    --t-1:  1.375rem;   /* 22 · body large            */
    --t-2:  1.75rem;    /* 28 · card title            */
    --t-3:  2.1875rem;  /* 35                         */
    --t-4:  2.75rem;    /* 44                         */
    --t-5:  3.4375rem;  /* 55 · section heading       */
    --t-6:  4.3125rem;  /* 69                         */
    --t-7:  5.375rem;   /* 86 · hero                  */
    --t-8:  6.6875rem;  /* 107 · display              */

    /* fluid pairs: clamp(mobile step, preferred, desktop step) */
    --t-hero:    clamp(2.1875rem, 5.9vw, var(--t-7));
    --t-display: clamp(2.75rem, 7.4vw, var(--t-8));
    --t-section: clamp(1.75rem, 4.4vw, var(--t-5));

    /* line-height and tracking banded by size, not set per element */
    --lh-display: 0.92;  --ls-display: -0.03em;
    --lh-head:    1.02;  --ls-head:    -0.02em;
    --lh-body:    1.55;  --ls-body:     0;
    --ls-caps:     0.06em;

    --rule: 2px;                 /* one border weight for structure */
  }
  /* Goodwill's own display face — supplied in assets/fonts/, not on any CDN.
     Caps-only by design: lowercase codepoints map to cap glyphs, so the text
     stays sentence-case for screen readers while rendering as capitals. */
  @font-face {
    font-family: "Wicked Broom";
    src: url("../../assets/fonts/wicked-broom.woff2") format("woff2");
    font-weight: 400; font-style: normal; font-display: swap;
  }

  :root { --p1: 1; --p2: 1; }   /* no-JS / no-motion default is the END state */

  html { scroll-behavior: smooth; }
  body { background: #FFFFFF; }

  /* ACT 2 — the arrival -------------------------------------------------------
     The panel never changes its box. It is always full-bleed; clip-path pulls
     its visible edges inward so it *reads* as a small inset card, then releases
     them. clip-path is paint-only — no layout thrash and no CLS, unlike
     animating width/height/inset. --p1 runs 0 → 1 across the pinned stage.     */
  .stage      { height: 150vh; }
  .stage-pin  { position: sticky; top: 0; height: 100vh; overflow: clip; }
  /* --e finishes the reveal in the first 60% of the runway and then holds,
     so the banner settles and sits rather than crawling to the last pixel. */
  :root { --e: min(1, calc(var(--p1, 0) / 0.6)); }
  .pin-inner  { position: absolute; inset: 0; }

  /* The yellow arrives full-bleed and stays that way — no clip-path growth.
     A small rounded box floating in white read as an unfinished element; the
     scroll now grows what is INSIDE the panel instead of the panel itself.  */
  .panel { position: absolute; inset: 0; }

  /* ACT 1.5 — "More BOO for your buck", the transition. Visible while the panel
     is still a small inset box; gone by the time it goes full-bleed. Replaces
     last year's flat circle with the panel itself as the backdrop.            */

  /* Staggered fade-in over --e: headline, then the figure, then the pill. */
  /* --a runs on the APPROACH, --e on the pinned scroll. The headline is
     already settled by the time the panel finishes sliding in, so the yellow
     never shows up empty; the wedge, then the model, then the pill follow. */
  /* --a holds at 0 for the first 42% of the approach, then runs to 1 by 90%,
     so the copy arrives while the panel is still moving and you can watch it
     land rather than finding it already there. */
  :root { --a: clamp(0, calc((var(--p0, 0) - 0.42) / 0.48), 1); }

  .hero-copy {
    opacity: calc(var(--a) * 1.25);
    transform: translate3d(0, calc((1 - var(--a)) * 26px), 0)
               scale(calc(0.90 + var(--a) * 0.10));
    transform-origin: 50% 0;
  }
  /* origin at the floor: the clown grows up from her boots and the wedge
     rises from the bottom edge, rather than both ballooning from centre */
  .hero-wedge {
    opacity: calc(var(--e) * 2.4);
    transform: translate3d(0, calc((1 - var(--e)) * 6%), 0)
               scale(calc(0.86 + var(--e) * 0.14));
    transform-origin: 50% 100%;
  }
  .hero-model {
    opacity: calc((var(--e) - 0.22) * 2.8);
    transform: translate3d(0, calc((1 - var(--e)) * 6%), 0)
               scale(calc(0.86 + var(--e) * 0.14));
    transform-origin: 50% 100%;
  }
  .hero-note {
    opacity: calc((var(--e) - 0.55) * 3.2);
    transform: translate3d(0, calc((1 - var(--e)) * 14px), 0)
               scale(calc(0.92 + var(--e) * 0.08));
    transform-origin: 50% 100%;
  }

  /* ACT 3 — the deck ---------------------------------------------------------
     Cards begin stacked and rotated like a held hand, then fan into the grid.
     Transform + opacity only. --p2 runs 0 → 1 as the deck enters.              */
  .deck { overflow-x: clip; }
  .deck-card {
    transform:
      translate3d(calc((1 - var(--p2)) * var(--dx)), calc((1 - var(--p2)) * var(--dy)), 0)
      rotate(calc((1 - var(--p2)) * var(--dr)))
      scale(calc(0.86 + var(--p2) * 0.14));
    opacity: calc(0.15 + var(--p2) * 0.85);
    will-change: transform;
  }
  @media (max-width: 640px) {
    .deck-card { --dx: 0% !important; --dy: 34px !important; }
  }

  /* The rule is a scaled pseudo-element rather than a border-colour fade, so it
     draws in from the left and retracts to the right instead of just appearing.
     padding-bottom replaces the transparent border, so the box keeps the exact
     height it had and nothing in the bar shifts. currentColor, never a hue. */
  .nav-link {
    position: relative;
    display: inline-flex; align-items: center;
    white-space: nowrap;
    font-weight: 700; text-transform: uppercase; letter-spacing: var(--ls-head);
    font-size: var(--t-0); color: #2A2522;
    padding-bottom: var(--rule);
  }
  .nav-link::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: 0;
    height: var(--rule); background: currentColor;
    transform: scaleX(0); transform-origin: right center;
    transition: transform 300ms cubic-bezier(.22,.61,.36,1);
  }
  .nav-link:hover::after,
  .nav-link:focus-visible::after {
    transform: scaleX(1); transform-origin: left center;
  }
  .nav-link.is-current::after { transform: scaleX(1); }

  @media (prefers-reduced-motion: reduce) {
    .nav-link::after { transition: none; }
  }

  .page-frame { container-type: inline-size; }

  .marquee-track {
    transform: translate3d(calc(var(--mq, 0) * -1%), 0, 0);
    will-change: transform;
  }



  /* Oversized square so a rotation can never expose a corner: a 4:5 tile's
     diagonal is ~160% of its width, so the layer is sized past that and centred. */
  .card-pat {
    position: absolute;
    top: 50%; left: 50%;
    width: 172%; aspect-ratio: 1; height: auto;
    background: rgba(26, 18, 6, 0.16);
    transform: translate(-50%, -50%);
    animation: patspin 90s linear infinite;
    animation-play-state: paused;      /* resumes where it left off, never snaps back */
    will-change: transform;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: center;  mask-position: center;
    -webkit-mask-size: cover;       mask-size: cover;
  }
  .pat-swirl  { -webkit-mask-image: url("../../assets/ornaments/swirl-centered.svg");    mask-image: url("../../assets/ornaments/swirl-centered.svg"); }
  .pat-spiral { -webkit-mask-image: url("../../assets/ornaments/spiral-background.svg"); mask-image: url("../../assets/ornaments/spiral-background.svg"); }

  @keyframes patspin {
    from { transform: translate(-50%, -50%) rotate(0deg); }
    to   { transform: translate(-50%, -50%) rotate(360deg); }
  }
  .idea-card:hover .card-pat,
  .idea-card:focus-visible .card-pat { animation-play-state: running; }
  /* negative delay offsets the paused angle — 120° apart, so they don't read as copies */
  #ideas li:nth-child(2) .card-pat { animation-delay: -30s; }
  #ideas li:nth-child(3) .card-pat { animation-delay: -60s; }

  /* --- shimmer: one narrow streak travelling bottom-left to top-right.
     The band is perpendicular to the travel (45deg gradient, 45deg path), and
     the oversized layer means only the streak itself ever crosses the face —
     the soft shoulders stay outside the box, which is what made it read as
     two waves before. --- */
  .shimmer { position: relative; overflow: hidden; isolation: isolate; }
  .shimmer::after {
    content: ""; position: absolute; inset: -75%; z-index: 1; pointer-events: none;
    background: linear-gradient(45deg,
      rgba(255,255,255,0) 46%,
      rgba(255,255,255,0.50) 50%,
      rgba(255,255,255,0) 54%);
    transform: translate3d(-55%, 55%, 0);
    transition: transform 900ms cubic-bezier(.33,.02,.23,1);
  }
  .shimmer:hover::after,
  .shimmer:focus-visible::after { transform: translate3d(55%, -55%, 0); }

  /* --- banner tile: scrim + circular "try this look" --- */
  .tile { position: relative; display: block; overflow: hidden; }
  .tile img { transition: transform 600ms cubic-bezier(.22,.61,.36,1); }
  .tile:hover img, .tile:focus-visible img { transform: scale(1.04); }
  .tile-scrim {
    position: absolute; inset: 0; z-index: 1; pointer-events: none;
    background: rgba(32, 28, 25, 0.55);
    opacity: 0; transition: opacity 280ms ease-out;
  }
  .tile-cta {
    position: absolute; inset: 0; z-index: 2; display: grid; place-items: center;
    pointer-events: none;
  }
  .tile-cta > span {
    display: grid; place-items: center; gap: 0.15rem;
    width: clamp(88px, 34%, 132px); aspect-ratio: 1;
    border: 2px solid #fff; border-radius: 9999px; color: #fff; text-align: center;
    opacity: 0; visibility: hidden; transform: scale(0.86);
    transition: opacity 280ms ease-out, transform 380ms cubic-bezier(.22,.61,.36,1), visibility 0s linear 280ms;
  }
  /* the deck cards are .idea-card, not .tile — same overlay, second trigger */
  .tile:hover .tile-scrim, .tile:focus-visible .tile-scrim,
  .idea-card:hover .tile-scrim, .idea-card:focus-visible .tile-scrim { opacity: 1; }
  .tile:hover .tile-cta > span,
  .tile:focus-visible .tile-cta > span,
  .idea-card:hover .tile-cta > span,
  .idea-card:focus-visible .tile-cta > span { opacity: 1; visibility: visible; transform: scale(1); transition-delay: 0s; }
  /* the shine sits under the scrim so the button stays the brightest thing */
  .deck-card .fig-shine { z-index: 1; }
  /* one photo: the arrows and dots would be decoration that does nothing */
  [data-single] .cnav, [data-single] .cdots { display: none; }


  /* ---- costume modal ------------------------------------------------------
     Native <dialog>, so focus trapping, Esc and inertness come from the
     platform rather than script. The bubble-up uses a single keyframe with an
     overshoot stop, which is what gives it the liquid bounce.                */
  .cmodal {
    width: min(1300px, 95vw); max-height: 96vh; padding: 0; border: var(--rule) solid #2A2522;
    background: var(--cream, #FBEFE1); color: #2A2522; overflow: hidden;
  }
  .cmodal::backdrop {
    background: rgba(32, 28, 25, 0);
    -webkit-backdrop-filter: blur(0px); backdrop-filter: blur(0px);
    transition: background 300ms ease-out, backdrop-filter 300ms ease-out,
                -webkit-backdrop-filter 300ms ease-out;
  }
  .cmodal[open]::backdrop {
    background: rgba(32, 28, 25, 0.42);
    -webkit-backdrop-filter: blur(16px) saturate(115%);
    backdrop-filter: blur(16px) saturate(115%);
  }
  .cmodal[open] { animation: bubbleUp 620ms cubic-bezier(.2,.9,.25,1.15) both; }
  .cmodal.is-closing { animation: bubbleDown 220ms ease-in both; }
  .cmodal.is-closing::backdrop {
    background: rgba(32, 28, 25, 0);
    -webkit-backdrop-filter: blur(0px); backdrop-filter: blur(0px);
  }

  @keyframes bubbleUp {
    0%   { opacity: 0; transform: translateY(42px) scale(.72); border-radius: 48px; }
    55%  { opacity: 1; transform: translateY(-10px) scale(1.035); border-radius: 20px; }
    78%  {             transform: translateY(3px) scale(.992);   border-radius: 10px; }
    100% { opacity: 1; transform: translateY(0) scale(1);        border-radius: 4px; }
  }
  @keyframes bubbleDown {
    from { opacity: 1; transform: translateY(0) scale(1); }
    to   { opacity: 0; transform: translateY(18px) scale(.9); }
  }

  .cmodal-grid { display: grid; grid-template-columns: 1fr; max-height: 96vh; }
  @media (min-width: 820px) {
    .cmodal { height: min(96vh, 1040px); }
    .cmodal-grid { grid-template-columns: 1fr 1fr; height: 100%; max-height: none; }
  }

  /* left column is one full-height stage; photos cross-fade, arrows step */
  .cmodal-figs { position: relative; overflow: hidden; min-height: 0; }
  /* a cutout has transparent margins, so a flat stage shows through as dead
     space — those stages carry the card's own tint and swirl instead, so the
     dark default only applies where no tint utility is present */
  .cmodal-figs:not([class*="bg-"]) { background: #2A2522; }
  .cmodal-figs .card-pat { z-index: 0; }
  .cmodal-figs img { z-index: 1; }
  .cmodal-figs img {
    position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
    opacity: 0; transition: opacity 420ms ease-out;
  }
  .cmodal-figs img.is-active { opacity: 1; }

  .cnav {
    position: absolute; top: 50%; z-index: 2; translate: 0 -50%;
    display: grid; place-items: center; width: 46px; height: 46px;
    border: var(--rule) solid #FBEFE1; border-radius: 9999px;
    background: rgba(32, 28, 25, 0.42); color: #FBEFE1; cursor: pointer;
    transition: background 180ms ease-out, transform 220ms cubic-bezier(.2,.9,.25,1.15);
  }
  .cnav:hover { background: rgba(32, 28, 25, 0.78); transform: scale(1.08); }
  .cnav-prev { left: 14px; }
  .cnav-next { right: 14px; }

  .cdots { position: absolute; bottom: 14px; left: 0; right: 0; z-index: 2;
           display: flex; justify-content: center; gap: 8px; }
  .cdots button {
    width: 9px; height: 9px; border-radius: 9999px; cursor: pointer;
    background: rgba(251, 239, 225, 0.45); border: 0;
    transition: background 180ms ease-out, transform 220ms ease-out;
  }
  .cdots button[aria-current="true"] { background: #FBEFE1; transform: scale(1.35); }

  @media (prefers-reduced-motion: reduce) {
    .cmodal-figs img { transition: none; }
    .cnav:hover { transform: none; }
  }

  .cmodal-body { padding: 1.75rem; overflow-y: auto; }
  @media (min-width: 820px) { .cmodal-body { padding: 2.5rem; } }

  .size-chip {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 3rem; padding: .45rem .7rem; border: var(--rule) solid #2A2522;
    font-weight: 700; font-size: var(--t--1); letter-spacing: var(--ls-caps);
  }
  .cmodal-close {
    position: absolute; top: .75rem; right: .75rem; z-index: 5;
    display: grid; place-items: center; width: 44px; height: 44px;
    border: var(--rule) solid #2A2522; border-radius: 9999px;
    background: var(--cream, #FBEFE1); color: #2A2522;
    transition: transform 160ms ease-out;
  }
  .cmodal-close:hover { transform: rotate(90deg); }

  @media (prefers-reduced-motion: reduce) {
    .cmodal[open], .cmodal.is-closing { animation: none; }
    .cmodal-close:hover { transform: none; }
  }

  /* ---- bats ---------------------------------------------------------------
     They swoop in from outside their corner when the section scrolls into view,
     then settle into a slow idle drift. Entry sits on the wrapper, drift on the
     <img>: one `transform` each, so neither cancels the other.               */
  .bat { position: absolute; z-index: 0; pointer-events: none; will-change: transform, opacity; }
  .bat img { display: block; width: 100%; height: auto; }
  .bat-l { left: 0; top: 1.5rem; width: clamp(120px, 13vw, 210px); }
  .bat-r { right: 0; top: 0.5rem; width: clamp(96px, 10vw, 168px); }

  .bat { opacity: 0; transform: translate3d(var(--bat-x), -46%, 0) rotate(var(--bat-r)); }
  .bat-l { --bat-x: -52%; --bat-r: -14deg; }
  .bat-r { --bat-x:  52%; --bat-r:  14deg; }
  .bat.is-in {
    opacity: 1; transform: translate3d(0, 0, 0) rotate(0deg);
    transition: opacity 620ms ease-out, transform 980ms cubic-bezier(.18,.72,.3,1.02);
  }
  .bat-r.is-in { transition-delay: 130ms; }
  .bat.is-in img { animation: batDrift 7s ease-in-out infinite; }
  .bat-r.is-in img { animation-duration: 8.4s; animation-delay: -2.6s; }

  @keyframes batDrift {
    0%, 100% { transform: translate3d(0, 0, 0)     rotate(0deg); }
    50%      { transform: translate3d(0, -11px, 0) rotate(-2.4deg); }
  }

  @media (prefers-reduced-motion: reduce) {
    .bat { opacity: 1; transform: none; transition: none; }
    .bat.is-in img { animation: none; }
  }

  /* ---- masked figure shimmer ----------------------------------------------
     The costume art is an RGBA cutout, so its own alpha channel is a pixel-
     perfect mask of the person. mask-size:cover / mask-position:center top
     mirrors the <img>'s object-cover / object-top exactly, so the mask lands
     on the figure however the box is sized.

     Two elements, because a mask applies to the whole subtree: the outer span
     carries the mask and clips, the inner <i> carries the gradient and does the
     moving. That keeps the sweep on a GPU transform instead of animating
     background-position, and reuses the same 45deg bottom-left -> top-right
     travel as the button shimmer.

     --fig is filled in by script from img.currentSrc, so the mask reuses the
     exact file srcset already downloaded rather than adding a request.      */
  .fig-shine {
    position: absolute; inset: 0; z-index: 2; overflow: hidden; pointer-events: none;
    -webkit-mask-image: var(--fig); mask-image: var(--fig);
    -webkit-mask-size: cover;       mask-size: cover;
    -webkit-mask-position: center top; mask-position: center top;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    mask-mode: alpha;
    opacity: 0; transition: opacity 220ms ease-out;
  }
  .fig-shine i {
    position: absolute; inset: -75%; display: block;
    background: linear-gradient(45deg,
      rgba(255,255,255,0) 45%,
      rgba(255,255,255,0.72) 50%,
      rgba(255,255,255,0) 55%);
    transform: translate3d(-58%, 58%, 0);
    transition: transform 950ms cubic-bezier(.33,.02,.23,1);
  }
  .idea-card:hover .fig-shine,
  .idea-card:focus-visible .fig-shine { opacity: 1; }
  .idea-card:hover .fig-shine i,
  .idea-card:focus-visible .fig-shine i { transform: translate3d(58%, -58%, 0); }

  /* no --fig resolved (script blocked, or no cutout) -> paint nothing */
  .fig-shine:not([style*="--fig"]) { display: none; }

  @media (prefers-reduced-motion: reduce) {
    .fig-shine { display: none; }
  }



  /* ---- lenis ---------------------------------------------------------------
     Lenis drives native scrollTop rather than a transform, which is why the
     sticky stage still pins. These are its required host rules.            */
  html.lenis, html.lenis body { height: auto; }
  .lenis.lenis-smooth { scroll-behavior: auto !important; }
  .lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
  .lenis.lenis-stopped { overflow: hidden; }

  .idea-card > div { transition: transform 220ms ease-out; }
  .idea-card:hover > div,
  .idea-card:focus-visible > div { transform: translate3d(0, -6px, 0); }
  .idea-arrow { transition: transform 220ms ease-out; }
  .idea-card:hover .idea-arrow { transform: translate3d(3px, -3px, 0); }

  /* Ornament watermarks — Goodwill's own campaign assets, used tonally. */
  .wm {
    position: absolute; inset: 0;
    pointer-events: none;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: center; mask-position: center;
    -webkit-mask-size: cover; mask-size: cover;
  }
  .wm-swirl {
    background: rgba(26, 18, 6, 0.085);
    -webkit-mask-image: url("../../assets/ornaments/swirl-centered.svg");
            mask-image: url("../../assets/ornaments/swirl-centered.svg");
  }
  .deck .wm { top: 0; bottom: 0; left: 0; right: 0; }
  .wm-spiral {
    background: rgba(255, 255, 255, 0.045);
    -webkit-mask-image: url("../../assets/ornaments/spiral-background.svg");
            mask-image: url("../../assets/ornaments/spiral-background.svg");
  }

  :where(a, button):focus-visible {
    outline: 3px solid #2A2522;
    outline-offset: 3px;
    border-radius: 4px;
  }
  .on-dark:focus-visible { outline-color: #FFC629; }

  /* Rule 15 / WCAG 2.3.3 — kill transforms and parallax, keep the end state. */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
    :root { --p1: 1; --p2: 1; }
    .stage { height: 100vh; }
    .stage-pin { position: relative; }
    .hero-copy, .hero-wedge, .hero-model, .hero-note, .deck-card { transform: none; opacity: 1; }
    .marquee-track { transform: none; }
    .shimmer::after { display: none; }
    .tile img { transform: none !important; }
    .tile-cta > span { transition: none; }
    .card-pat { animation: none !important; transform: translate(-50%, -50%) !important; }
  }

@font-face { font-family:Poppins; src:url('../../assets/fonts/poppins-bold.woff2') format('woff2'); font-weight:700; font-display:swap; }
