/* ==========================================================================
   Goodwaller Stories — /stories
   Ported from the standalone goodwall-stories prototype.

   Everything is scoped under .stories26 so this sheet can never leak into
   the shared next26 pages. The prototype's own nav / footer / download-
   button rules were dropped: the page uses the shared next26 top-nav and
   footer partials instead.

   Asset URLs are relative to this file (/app/gw/css/), matching the
   convention in assets/css/style.css.
   ========================================================================== */

/* This file only loads on /stories, so it's safe to add one rule for the
   shared footer here without touching footer.html.eex itself: strip the
   footer's own background entirely so the stories bg-wall/bg-scrim show
   straight through it (same treatment as the rest of the page) — only
   the footer's own logos/text/icons paint on top, at full color. */
.footer { background: transparent; }
/* .bg-wall/.bg-scrim are position:fixed covering the full viewport (so the
   wall shows through the header too), but .footer is a plain static block
   with no z-index — in the root stacking context, fixed/positioned layers
   paint ABOVE static content regardless of DOM order. That put the dark
   .bg-scrim gradient physically on top of the footer at all times, dimming
   every element inside it uniformly (not an opacity/color bug on the
   footer's own elements — verified those already compute to full white).
   Lifting the footer into its own stacking context above both fixed
   layers (bg-wall z-index 0, bg-scrim z-index 1) fixes this: the footer's
   own semi-transparent background above is what shows the wall through,
   with nothing painted over its content any more. */
.footer { position: relative; z-index: 5; }
/* The site's own footer styling dims its text/icons to 0.7 for a subtle
   look on a solid background — against our busier photo wall that reads
   as washed out, so on this page only, show every footer element at full
   strength. !important + the wildcard guards against any element (or
   nested icon) the site styles at less than full opacity by default. */
.footer,
.footer * { opacity: 1 !important; }
.footer,
.footer a,
.footer span,
.footer p { color: #fff !important; }

.stories26 {
  --stories-bg: #0b0a11;
  --stories-text-main: #f9f2ea;
  --stories-text-secondary: #a8a5ad;
  --stories-stroke-main: #3b3841;
  --stories-green: #15c970;
  --stories-blue: #5796f4;

  position: relative;
  color: var(--stories-text-main);
  font-family: 'PP Radio Grotesk', 'DM Sans', 'Inter', system-ui, -apple-system, sans-serif;
  overflow-x: hidden;
}

.stories26 *,
.stories26 *::before,
.stories26 *::after { box-sizing: border-box; }

/* ---------- Background wall ----------
   position:fixed so it covers the full viewport — both the shared top-nav
   (translucent + blurred) and the shared footer (transparent background)
   show it through, matching the standalone prototype's look. */
.stories26 .bg-wall {
  position: fixed;
  inset: 0;
  z-index: 0;
  background-image: url('../images/homepages/next26/stories/Background-c83f0ce62506fe4bdd0ddf4b4d78be89.png?vsn=d');
  background-image: image-set(
    url('../images/homepages/next26/stories/Background-c83f0ce62506fe4bdd0ddf4b4d78be89.png?vsn=d') 1x,
    url('../images/homepages/next26/stories/Background@2x-d8085766a87c768be657ffa7c87e7f75.png?vsn=d') 2x
  );
  background-size: cover;
  background-position: center top;
  background-repeat: no-repeat;
  opacity: 0.9;
}
.stories26 .bg-scrim {
  position: fixed;
  inset: 0;
  z-index: 1;
  background:
    radial-gradient(60% 55% at 50% 38%, rgba(11,10,17,0.1) 0%, rgba(11,10,17,0.35) 62%, rgba(11,10,17,0.55) 100%),
    linear-gradient(180deg, rgba(11,10,17,0.1) 0%, rgba(11,10,17,0.2) 100%);
  pointer-events: none;
}

/* ---------- Hero ---------- */
.stories26 .stories-hero {
  position: relative;
  z-index: 5;
  /* Matches the News hero's vertical rhythm exactly (56px, 80px from the
     800px breakpoint up) so the title sits the same distance below the
     shared sticky nav as it does on every other next26 page. */
  padding-top: 56px;
  /* .stories26 has overflow-x:hidden (needed so the carousel's side cards
     never trigger a page-wide horizontal scrollbar during drag/wheel), and
     per the CSS overflow spec, pairing a non-visible overflow-x with the
     default overflow-y:visible forces overflow-y to compute as 'auto' —
     which clips the center card's box-shadow glow right at this section's
     bottom edge, since .stories26 ends here (the footer is a separate,
     sibling partial). Extra bottom padding gives the (now smaller) glow
     room to fully fade out before hitting that clip boundary instead of
     being cut off in a hard line. */
  padding-bottom: 110px;
}
/* Horizontal placement comes from the shared .container grid in the template
   (same as the News / Partners heroes), so the copy lines up with every other
   next26 page instead of sitting flush to the viewport. Only the text measure
   is set here. The carousel deliberately stays outside .container — it is
   full-bleed so the side cards can run past the viewport edge. */
.stories26 .hero-text {
  max-width: 620px;
  margin-inline: auto;
  text-align: center;
}
/* Size/family/weight/line-height come from .headline-m + .text-responsive in
   the template, shared with the Partners hero. Only the spacing is local.
   Do NOT reintroduce a font-size here: this selector (0,2,1) outranks
   .text-responsive.headline-m (0,2,0) and would silently kill the shared
   1280px type breakpoint. */
.stories26 .hero-text h1 {
  margin: 0 0 12px 0;
}
.stories26 .hero-text h1 .hero-text__bold { font-weight: 700; }
/* Likewise .body-m + .text-responsive in the template. The measure is left to
   .hero-text's max-width (620px, near the News subtitle's 640px) - the old
   422px was tuned for 18px text and wraps awkwardly at body-m's 24px. */
.stories26 .hero-text p {
  margin: 0;
  color: var(--stories-text-main);
}

/* ---------- Carousel ---------- */
.stories26 .carousel {
  position: relative;
  margin-top: 48px;
  /* Read by stories-carousel.js, which multiplies the Figma card metrics by
     it and sets the height below to match. Kept in CSS so the sizing steps
     sit with the rest of the layout rather than being buried in the script.
     Gated on height as well as width: it is vertical room we are spending,
     and a short wide laptop has none to spare. */
  --stories-card-scale: 1;
  /* Fallback only - the script overwrites this inline once it has the scale. */
  height: 504px;
  width: 100%;
  overflow: visible;
  cursor: grab;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}
.stories26 .carousel.dragging { cursor: grabbing; }
.stories26 .carousel-track {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0;
  height: 0;
}

.stories26 .card {
  position: absolute;
  left: 0;
  top: 0;
  border-radius: 20px;
  overflow: hidden;
  background: #1a191f;
  will-change: transform, width, height, opacity;
  pointer-events: none;
}
/* Side cards use the exact card silhouette exported from Figma — a
   subtly rotated-rectangle mask (not a plain rounded rect, not a
   straight corner chamfer) — applied via the PNG's own alpha shape. */
.stories26 .card--left,
.stories26 .card--right {
  border-radius: 0;
}
/* Default to the medium mask on the base .card--left/.card--right rule
   itself (not just the .card--medium combo) so a side card is NEVER
   caught unmasked — .card--small below simply overrides when it applies. */
.stories26 .card--left {
  -webkit-mask-image: url('../images/homepages/next26/stories/mask-left-medium-5c1e3d3a8c79ea5d31987af6f1e5c420.png?vsn=d');
  mask-image: url('../images/homepages/next26/stories/mask-left-medium-5c1e3d3a8c79ea5d31987af6f1e5c420.png?vsn=d');
}
.stories26 .card--left.card--small {
  -webkit-mask-image: url('../images/homepages/next26/stories/mask-left-small-cf37c95c648b2c6408adcfba98c6504d.png?vsn=d');
  mask-image: url('../images/homepages/next26/stories/mask-left-small-cf37c95c648b2c6408adcfba98c6504d.png?vsn=d');
}
.stories26 .card--right {
  -webkit-mask-image: url('../images/homepages/next26/stories/mask-right-medium-d19efb99f6554c3092562b945c297094.png?vsn=d');
  mask-image: url('../images/homepages/next26/stories/mask-right-medium-d19efb99f6554c3092562b945c297094.png?vsn=d');
}
.stories26 .card--right.card--small {
  -webkit-mask-image: url('../images/homepages/next26/stories/mask-right-small-029b2e2479731c0fdb05fb4afe5e2446.png?vsn=d');
  mask-image: url('../images/homepages/next26/stories/mask-right-small-029b2e2479731c0fdb05fb4afe5e2446.png?vsn=d');
}
.stories26 .card--left,
.stories26 .card--right {
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}
.stories26 .card.is-interactive { pointer-events: auto; }
.stories26 .card video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}
/* The hero clip sits on top of the preview and stays invisible until it is
   genuinely producing frames - the script adds card--hero-live on the video's
   own `playing` event. Swapping src on a single element blanked it for ~103ms
   first; this way the preview is still on screen underneath and the two
   dissolve instead. Both are object-fit:cover in the same box, so the fade
   lands frame-on-frame with no jump. */
.stories26 .card__video--hero {
  opacity: 0;
  transition: opacity 0.2s ease;
}
.stories26 .card--hero-live .card__video--hero { opacity: 1; }

.stories26 .card--center {
  border-radius: 24px;
  border: 1px solid transparent;
  background-image:
    linear-gradient(var(--stories-bg), var(--stories-bg)),
    linear-gradient(180deg, rgba(21,201,112,0.6) 0%, rgba(87,150,244,0.6) 100%);
  background-origin: border-box;
  background-clip: padding-box, border-box;
  box-shadow:
    0px -2px 15px 0px rgba(21,201,112,0.4),
    0px -4px 90px 0px rgba(0,54,135,0.5);
}

.stories26 .card__dim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.2s ease;
  background: linear-gradient(135deg, rgba(11,10,17,0.95) 0%, rgba(11,10,17,0.55) 45%, rgba(11,10,17,0) 80%);
}
.stories26 .card--right .card__dim {
  background: linear-gradient(225deg, rgba(11,10,17,0.95) 0%, rgba(11,10,17,0.55) 45%, rgba(11,10,17,0) 80%);
}
/* The center video gets its own bottom-to-top scrim instead of being dimmed -
   just enough to keep the name and role legible over bright video.

   It has to stop short of the clips' burnt-in subtitles, which sit roughly a
   fifth of the way up the frame. The old ramp ran to 55% and was still about
   40% black behind them, which greyed the subtitles out while the caption on
   top of it stayed crisp white. The caption only occupies the bottom ~12%, so
   the scrim is now steeper and clear by 22% - dark where the name is, gone
   before the subtitle band.

   Subtitle position is a property of each clip, so a three-line caption on a
   future video could still reach down into this. */
.stories26 .card--center .card__dim {
  opacity: 1;
  background: linear-gradient(0deg, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.6) 8%, rgba(0,0,0,0) 22%);
}

.stories26 .card__playhint {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(11,10,17,0.35);
  opacity: 0;
  transition: opacity 0.15s ease;
  pointer-events: none;
}
/* card--paused: the user paused the clip. card--idle: the centre card is
   still on its muted preview loop from page load and has not been started
   yet. The second one is deliberately a *playing* video that still shows a
   play button, so this cannot be keyed off video.paused. */
.stories26 .card--paused .card__playhint,
.stories26 .card--idle .card__playhint { opacity: 1; }
.stories26 .card__playhint svg { width: 56px; height: 56px; filter: drop-shadow(0 2px 8px rgba(0,0,0,0.5)); }

.stories26 .card__caption {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  padding: 0 16px 16px 18px;
  /* Carries its own contrast now that the scrim behind it is shorter, so a
     bright frame under the name does not cost legibility. */
  text-shadow: 0 1px 3px rgba(0,0,0,0.65);
  opacity: 0;
  transition: opacity 0.2s ease;
}
.stories26 .card--center .card__caption { opacity: 1; }

.stories26 .card__userinfo {
  display: flex;
  flex-direction: column;
  gap: 3px;
  /* The 0.75px bottom rule that used to live here divided this block from
     the tag row below it. The tags now float under the card, so the rule
     had nothing left to separate and read as a stray underline. The 44px
     right margin went with the like/comment column it used to clear. */
}
.stories26 .card__user { display: flex; align-items: center; gap: 6px; }
.stories26 .card__avatar {
  width: 26.4px; height: 26.4px; /* +10% over the original 24px */
  border-radius: 999px;
  border: 0.75px solid var(--stories-blue);
  display: flex; align-items: center; justify-content: center;
  background: var(--stories-bg);
  flex-shrink: 0;
  overflow: hidden;
}
.stories26 .card__avatar img {
  width: 100%;
  height: 100%;
  border-radius: 999px;
  object-fit: cover;
  display: block;
}
.stories26 .card__avatar svg { width: 13px; height: 13px; }
.stories26 .card__name {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--stories-text-main);
  white-space: nowrap;
}
.stories26 .card__role {
  font-size: 10.5px;
  color: var(--stories-text-main);
  line-height: 15px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Detail panel under the card, carrying the centred story's tags and the
   page's talking points on one surface. A sibling of .carousel rather than a
   child of .card, because .card clips its overflow for the rounded and masked
   silhouettes - anything below the video has to live outside it.

   The panel owns the outer placement: margin, centring, and the z-index that
   lifts it clear of the centre card's 90px glow, which bleeds well past the
   card's bottom edge and would otherwise wash the whole thing out. */
.stories26 .stories-panel {
  margin: 20px auto 0;
  /* Wide enough for the points to run as columns on desktop - stacking them
     costs ~60px of height, which is height the carousel wants. */
  width: min(calc(100% - 32px), 880px);
  padding: 16px 22px 18px;
  border-radius: 20px;
  background: rgba(249,242,234,0.06);
  border: 1px solid rgba(249,242,234,0.14);
  backdrop-filter: blur(8px);
  position: relative;
  z-index: 101;
}
/* A story with no partner logos produces no chips at all. Collapse the row
   rather than leaving its reserved height as a blank band above the divider. */
.stories26 .stories-tagrow:empty { display: none; }
/* With the row gone the divider would be the first thing in the panel, which
   reads as a stray line rather than a separator. Drop it and its spacing so a
   points-only panel is just points. */
.stories26 .stories-tagrow:empty + .stories-panel__points {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}
.stories26 .stories-tagrow {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: 7px;
  /* Matches the chip height below. Reserved so moving between stories with
     different tag counts never shifts the panel's height. */
  min-height: 43px;
}
/* Talking points. Separated from the chips by a hairline rather than just
   space, so the two halves of the panel read as related but distinct: the
   chips describe the story on screen, these describe the page. */
.stories26 .stories-panel__points {
  margin: 16px 0 0;
  padding: 16px 0 0;
  border-top: 1px solid rgba(249,242,234,0.12);
  list-style: none;
  display: grid;
  gap: 10px;
  text-align: left;
}
/* Side by side once there is room. Three stacked lines push the panel below
   the fold on a laptop; three columns keep it in view. */
@media (width >= 800px) {
  .stories26 .stories-panel__points {
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
  }
}
.stories26 .stories-panel__points li {
  position: relative;
  padding-left: 20px;
  font-size: 14px;
  line-height: 20px;
  color: var(--stories-text-main);
}
/* Custom marker rather than list-style so the dot's size, colour and optical
   alignment to the first line are all controllable. */
.stories26 .stories-panel__points li::before {
  content: "";
  position: absolute;
  left: 3px;
  top: 7px;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--stories-green);
}
/* Chips. The old fill was rgba(11,10,17,0.6) - a 60% near-black, which read
   as a chip while these sat on top of a bright video, but vanished once they
   moved onto the dark page background and left "Hero Story" looking like bare
   text. Inverted to a light translucent fill with a hairline border so the
   chip shape carries on a dark ground; the blur keeps the photo wall behind
   it from showing through as detail.
   min-height equalises the two kinds of chip. A logo chip is content-driven:
   22px mark + 6px padding + 1px border each side = 36px. A text chip is only
   9px of type, so without this it came out 23px next to a 36px neighbour. */
.stories26 .stories-tagrow__chip {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 43px;
  background: rgba(249,242,234,0.09);
  border: 1px solid rgba(249,242,234,0.20);
  backdrop-filter: blur(6px);
  padding: 7px 17px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--stories-text-main);
  white-space: nowrap;
}
.stories26 .stories-tagrow__logo {
  height: 27px;
  width: auto;
  max-width: 88px;
  object-fit: contain;
  display: block;
}
.stories26 .stories-tagrow__supported {
  font-size: 11px;
  font-weight: 700;
  color: var(--stories-text-main);
  white-space: nowrap;
}

@media (width >= 800px) {
  .stories26 .stories-hero { padding-top: 80px; }
}

/* ---------- Large viewports: use the vertical space ----------
   The carousel is a fixed drawing size, so on a tall screen it sat high
   against the nav with empty background under it. Grow the cards and push
   them down so it occupies the middle of the view.

   Gated on height as much as width, and each step sized so the detail panel
   below still clears the fold at that step's OWN minimum height:

       panel bottom = 305 + margin-top + 502 * scale + panel height

   where 305 is nav + padding-top + the copy block, 502 is the drawn height of
   the centre card, and the panel is whatever its content needs - it hugs, so
   this is the tallest story, not a fixed value. Re-measure if the copy or the
   chip rows change.

   The height gates are much higher than they were before the panel existed:
   it added ~170px below the carousel, which is ~170px the cards can no longer
   spend. Below ~1050px of viewport height nothing fits above the fold at any
   card size, so those screens keep the drawn size and scroll to the points.

   The hero's 110px bottom padding is deliberately untouched - it is what
   stops .stories26's computed overflow-y clipping the centre card's glow in
   a hard line. */
@media (width >= 1280px) and (height >= 1120px) {
  .stories26 .carousel {
    --stories-card-scale: 1.08;
    margin-top: 56px;
  }
}

@media (width >= 1440px) and (height >= 1250px) {
  .stories26 .carousel {
    --stories-card-scale: 1.20;
    margin-top: 72px;
  }
}
