/* The public product-updates surface.

   Two places share this stylesheet:
     - the public pages (dashboard/templates/product_updates/) — the full
       changelog (.product-updates__*) and one story per update
       (.product-update__*), both rendered as light paper sheets exactly
       like the marketing site;
     - the landing page's own chapter (.landing-product-updates__*): a
       filmstrip of image-led panels, the fourth clipped by the sheet's
       right edge — the entire invitation to keep looking, so no arrows,
       no dots.

   Every colour comes from the --lp-* roles surfaces.css has already swapped
   for .landing__section--light: headings are --lp-ink-strong, the accent is
   --lp-current (aliased there to --lp-accent). Never a literal.

   Panels are not cards. The screenshot is the object; the date, title and
   teaser sit directly on the paper. The only framing is a hairline bezel on
   the screenshot itself — what lets a dark capture hold an edge against
   light paper, the same principle devices.css uses for product shots inside
   light chapters. */

/* ---------- The full changelog (index.html) ---------- */

.product-updates__header {
  padding-block: 1rem 2.5rem;
  border-bottom: 1px solid var(--lp-line);
}

.product-updates__intro,
.landing-product-updates__intro {
  max-width: 52ch;
  margin: 1rem 0 0;
  color: var(--lp-mute);
  font-size: 1.0625rem;
  line-height: 1.6;
}

.product-updates__empty .landing__text-link,
.landing-product-updates__head .landing__text-link {
  gap: .5rem;
}

.product-updates__empty {
  padding-block: 3rem 1rem;
}

.product-updates__empty h2 {
  margin: 0 0 .75rem;
  color: var(--lp-ink-strong);
  font-size: 1.375rem;
}

.product-updates__empty p {
  max-width: 52ch;
  margin: 0 0 1.5rem;
  color: var(--lp-mute);
}

.product-updates__list {
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
  margin: 2.5rem 0 0;
  padding: 0;
  list-style: none;
}

.product-updates__thumb {
  display: block;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--lp-hull) 14%, transparent);
  border-radius: var(--lp-radius-sm);
  transition: border-color 180ms ease-out;
}

.product-updates__thumb:hover,
.product-updates__thumb:focus-visible {
  border-color: var(--lp-accent);
}

.product-updates__thumb img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  transition: transform 180ms ease-out;
}

.product-updates__thumb:hover img,
.product-updates__thumb:focus-visible img {
  transform: scale(1.04);
}

@media (prefers-reduced-motion: reduce) {
  .product-updates__thumb:hover img,
  .product-updates__thumb:focus-visible img {
    transform: none;
  }
}

.product-updates__row-title {
  margin: 1rem 0 .5rem;
  font-size: 1.375rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--lp-ink-strong);
}

.product-updates__row-title a:hover {
  color: var(--lp-accent);
}

.product-updates__row-teaser {
  max-width: 65ch;
  margin: 0;
  color: var(--lp-mute);
}

@media (width >= 40rem) {
  .product-updates__row {
    display: grid;
    grid-template-columns: 14rem minmax(0, 1fr);
    align-items: start;
    gap: 2rem;
  }

  .product-updates__row-body:only-child {
    grid-column: 1 / -1;
  }
}

/* A changelog is read newest-first and browsed backwards, so this is not the
   dashboard's page-size-and-goto pagination component — that component also
   depends on 03-components/ CSS the public shell never loads (base.css keeps
   application chrome off the marketing surface). Older/newer only, in the
   page's own words: "Older"/"Newer" say what the visitor gets, "Previous"/
   "Next" don't on a reverse-chronological list. */
.product-updates__pager {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .75rem;
  margin-top: 3rem;
  padding-top: 2rem;
  border-top: 1px solid var(--lp-line);
}

.product-updates__pager-status {
  order: 1;
  color: var(--lp-mute);
  font-size: .875rem;
}

.product-updates__pager-link--newer {
  order: 0;
  flex-direction: row-reverse;
}

/* The icon subset carries only fa-arrow-right (see docs/frontend/overview.md
   on the landing icon subset) — mirrored for the reverse direction rather
   than pulling in a second glyph. */
.product-updates__pager-link--newer i {
  transform: scaleX(-1);
}

.product-updates__pager-link--older {
  order: 2;
}

/* A disabled edge renders as a span, not an anchor, so it is already out of
   the tab order and carries no href to click through on. Only its look needs
   settling here. Selectors are fully qualified with the ancestor
   .landing__section--light so the specificity reliably outranks surfaces.css's
   light-section icon-colour rule, regardless of stylesheet load order. */
.landing__section--light .landing__text-link.product-updates__pager-link--disabled {
  border-color: transparent;
  color: var(--lp-faint);
  pointer-events: none;
}

.landing__section--light .landing__text-link.product-updates__pager-link--disabled i {
  color: var(--lp-faint);
}

@media (width >= 30rem) {
  .product-updates__pager {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }
}

/* ---------- One story (detail.html) ---------- */

.product-update__back {
  display: inline-block;
  margin-bottom: 1.5rem;
  color: var(--lp-mute);
  font-size: .875rem;
  font-weight: 600;
}

.product-update__back:hover {
  color: var(--lp-accent);
}

.product-update__shot {
  margin: 2rem 0;
}

/* This screenshot is not a link (unlike .product-updates__thumb and
   .landing-product-updates__shot below), so its bezel lives directly on the
   img — no hover to clip a scale inside. */
.product-update__shot img {
  width: 100%;
  border: 1px solid color-mix(in srgb, var(--lp-hull) 14%, transparent);
  border-radius: var(--lp-radius-sm);
}

/* The rendered Markdown body — held to a 65-75 character measure so a long
   update reads like an article, not a full-width dump of prose. */
.product-update__body {
  max-width: 70ch;
  color: var(--lp-ink);
  font-size: 1.0625rem;
  line-height: 1.7;
}

.product-update__body > :first-child {
  margin-top: 0;
}

.product-update__body > :last-child {
  margin-bottom: 0;
}

.product-update__body p,
.product-update__body ul,
.product-update__body ol {
  margin: 0 0 1.25rem;
}

.product-update__body h2,
.product-update__body h3,
.product-update__body h4 {
  margin: 2rem 0 .75rem;
  color: var(--lp-ink-strong);
  font-weight: 600;
  line-height: 1.3;
}

.product-update__body a {
  color: var(--lp-accent);
  text-decoration: underline;
  text-underline-offset: .15em;
}

.product-update__body code {
  padding: .1em .4em;
  border-radius: 4px;
  background: var(--lp-paper-soft);
  color: var(--lp-ink-strong);
  font-family: var(--lp-mono);
  font-size: .9em;
}

.product-update__body blockquote {
  margin: 1.5rem 0;
  padding: 1rem 1.25rem;
  border-radius: var(--lp-radius-sm);
  background: var(--lp-paper-soft);
  color: var(--lp-mute);
}

.product-update__body hr {
  margin: 2rem 0;
  border: 0;
  border-top: 1px solid var(--lp-line);
}

/* render_body() (cloud/services/announcements/service.py) resolves inline
   ![alt](image:<slug>) tokens into this figure markup regardless of caller —
   shared with the dashboard's own announcement detail page. Only the figure
   chrome is styled here; sizing and alignment come from the inline
   --announcement-img-size custom property the service already sets. */
.product-update__body .announcement-detail__figure {
  width: var(--announcement-img-size, 100%);
  max-width: 100%;
  margin: 0 0 1.25rem;
}

.product-update__body .announcement-detail__figure img {
  border: 1px solid color-mix(in srgb, var(--lp-hull) 14%, transparent);
  border-radius: var(--lp-radius-sm);
}

.product-update__body .announcement-detail__figure--left {
  margin-inline: 0;
}

.product-update__body .announcement-detail__figure--center {
  margin-inline: auto;
}

.product-update__body .announcement-detail__figure--right {
  margin-inline: auto 0;
}

.product-update__body .announcement-detail__figure figcaption {
  margin-top: .5rem;
  color: var(--lp-mute);
  font-size: .875rem;
  text-align: center;
}

.product-update__neighbours {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  margin-top: 3rem;
  padding-top: 2rem;
  border-top: 1px solid var(--lp-line);
  list-style: none;
}

.product-update__neighbours a {
  color: var(--lp-ink-strong);
  font-weight: 600;
}

.product-update__neighbours a:hover {
  color: var(--lp-accent);
}

.product-update__neighbours a:last-child {
  margin-left: auto;
}

/* ---------- The landing chapter: filmstrip ---------- */

.landing-product-updates__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem 2rem;
  margin-bottom: 0;
}

.landing-product-updates__strip[hidden] {
  display: none;
}

/* One row of horizontally-scrolling columns. grid-auto-columns is wide
   enough on mobile to show roughly 1.5 panels (inviting a swipe) and narrows
   at 40rem so three panels show in full with the fourth cut by the sheet's
   trailing edge — the strip bleeds past the container's own gutter so that
   cut lands at the paper, not short of it. */
.landing-product-updates__strip {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 62%;
  gap: 1.5rem;
  margin: 2.5rem calc(-1 * var(--lp-gutter)) 0 0;
  padding: 0 var(--lp-gutter) .5rem 0;
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
}

.landing-product-updates__item {
  scroll-snap-align: start;
}

/* Block, not the default inline: the whole panel — shot, date, title and
   teaser — is one link, and its hoverable/focusable area has to match that
   full box exactly, not just the inline runs its text content would
   otherwise produce. */
.landing-product-updates__link {
  display: block;
}

/* Reserves the panel's height before its image arrives — without this the
   strip has zero height at first paint and the page jumps when the feed
   lands. `overflow: hidden` + the border here (not on the img) is what lets
   the image scale on hover without the frame's own edge moving with it —
   the bezel stays put; only the screenshot inside it moves. */
.landing-product-updates__shot {
  aspect-ratio: 16 / 10;
  margin-bottom: 1rem;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--lp-hull) 14%, transparent);
  border-radius: var(--lp-radius-sm);
  transition: border-color 180ms ease-out;
}

.landing-product-updates__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 180ms ease-out;
}

.landing-product-updates__date {
  display: block;
  margin-bottom: .35rem;
  color: var(--lp-accent);
  font-size: .875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.landing-product-updates__title {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin: 0 0 .35rem;
  color: var(--lp-ink-strong);
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.3;
}

.landing-product-updates__title-icon {
  font-size: .75rem;
  color: var(--lp-accent);
  transition: transform 180ms ease-out;
}

.landing-product-updates__body {
  margin: 0;
  color: var(--lp-mute);
  font-size: .9375rem;
  line-height: 1.5;
}

/* One movement, not three effects: the screenshot scales very slightly
   inside its own clipped frame, the frame's bezel picks up the accent, and
   the title's arrow slides — all from hovering or focusing anywhere on the
   panel, since the whole thing is one link. */
.landing-product-updates__link:hover .landing-product-updates__shot,
.landing-product-updates__link:focus-visible .landing-product-updates__shot {
  border-color: var(--lp-accent);
}

.landing-product-updates__link:hover .landing-product-updates__shot img,
.landing-product-updates__link:focus-visible .landing-product-updates__shot img {
  transform: scale(1.04);
}

.landing-product-updates__link:hover .landing-product-updates__title-icon,
.landing-product-updates__link:focus-visible .landing-product-updates__title-icon {
  transform: translateX(3px);
}

@media (prefers-reduced-motion: reduce) {
  .landing-product-updates__link:hover .landing-product-updates__shot img,
  .landing-product-updates__link:focus-visible .landing-product-updates__shot img,
  .landing-product-updates__link:hover .landing-product-updates__title-icon,
  .landing-product-updates__link:focus-visible .landing-product-updates__title-icon {
    transform: none;
  }
}

@media (width >= 40rem) {
  .landing-product-updates__strip {
    grid-auto-columns: 34%;
  }
}

/* The strip still scrolls with a trackpad, a touch swipe or arrow keys once
   focused; only the always-on scrollbar chrome is hidden, and only where a
   fine pointer means it isn't the visitor's one way to reach it. */
@media (pointer: fine) {
  .landing-product-updates__strip {
    scrollbar-width: none;
  }

  .landing-product-updates__strip::-webkit-scrollbar {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .landing-product-updates__strip {
    scroll-snap-type: none;
  }
}
