/* ============================================================
   DEMO GRID  —  the 52 templates

   The screenshots are all the same shape (518x405, ratio 1.28),
   so a plain auto-fill grid is enough — the reference site used a
   JS masonry library it did not actually need.
   ============================================================ */

.demos {
  scroll-margin-top: clamp(60px, 10vh, 90px);
  background-color: var(--c-bg);
  padding-block: var(--sp-section);
}

.demos__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin-block-end: var(--sp-heading-gap);
}

/* The numeral and title overlap slightly, echoing the hero.
   Font size is raised but the leading is left alone: forcing
   line-height to 1 here made the glyph ink overflow the box by
   13px, since .u-numeral's ink padding is calibrated against
   --lh-tight, not against 1. */
.demos__count {
  font-size: var(--fs-display);
  color: rgb(var(--rgb-ink) / 0.07);
}

.demos__title {
  margin-block-start: -0.35em;
}

/* ---- filters ---------------------------------------------------
   53 cards with no way through them is the section's real weakness,
   and the tags were already in the data — so this is navigation,
   not decoration.
   ---------------------------------------------------------------- */

.demos__filters {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-xs);
  margin-block-end: var(--sp-heading-gap);
}

/* The chips sat on `background-color: transparent`, so on either
   theme they were a faint hairline outline and nothing else — a row
   of words rather than a row of controls. They now carry the same
   surface the cards do: lighter than the page on dark, darker on
   light, from one token.

   Movement uses the individual `translate` and `scale` properties
   rather than `transform`. Three states each want a different part
   of it — hover lifts, press squashes, selected swells — and as one
   `transform` declaration the last rule to match would silently win
   and the other two would never appear. As separate properties they
   compose. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;

  padding-inline: var(--sp-md);
  padding-block: 0.5em;

  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--c-text-dim);

  background-color: var(--c-bg-elevated);
  border: var(--bw-card) solid var(--c-card-border);
  border-radius: var(--r-pill);
  cursor: pointer;

  transition:
    color            var(--dur-fast) var(--ease),
    border-color     var(--dur-fast) var(--ease),
    background-color var(--dur-fast) var(--ease),
    box-shadow       var(--dur-fast) var(--ease),
    translate        var(--dur-fast) var(--ease-out),
    scale            var(--dur-fast) var(--ease-out);
}

/* Arriving. `backwards`, NOT `both`: with `both` the element keeps
   the final keyframe after the animation ends, and an animated
   value outranks a declared one — so `scale: 1` would be pinned
   forever and every hover and press state below would be dead. With
   `backwards` the fill applies only during the delay, and the
   element hands control back to its own rules once it lands. */
@keyframes chip-in {
  from { opacity: 0; translate: 0 10px; scale: 0.88; }
  to   { opacity: 1; translate: 0 0;    scale: 1; }
}

.js .chip {
  animation: chip-in 0.4s var(--ease-out) backwards;
  animation-delay: calc(var(--i, 0) * 0.04s);
}

.chip:hover {
  color: var(--c-text);
  border-color: rgb(var(--rgb-ink) / 0.45);
  translate: 0 -2px;
  box-shadow: var(--shadow-card-rest);
}

/* Squashes under the pointer and springs back. The one piece of
   this that makes a button feel like a button rather than a link. */
.chip:active {
  scale: 0.95;
  translate: 0 0;
  transition-duration: 60ms;
}

.chip[aria-pressed='true'] {
  color: var(--c-bg);
  background-color: var(--c-text);
  border-color: var(--c-text);
  scale: 1.05;
  box-shadow: var(--shadow-card-rest);
}

.chip[aria-pressed='true']:active {
  scale: 0.98;
}

/* The selected chip's counter gets its own well, so the count reads
   as part of the control rather than as text that happened to
   invert with it. */
.chip[aria-pressed='true'] .chip__count {
  background-color: rgb(var(--rgb-surface) / 0.18);
  border-radius: var(--r-pill);
  padding-inline: 0.5em;
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .js .chip { animation: none; }
  .chip,
  .chip:hover,
  .chip:active,
  .chip[aria-pressed='true'],
  .chip[aria-pressed='true']:active {
    translate: none;
    scale: none;
  }
}

.chip__count {
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  opacity: 0.65;
  transition:
    background-color var(--dur-fast) var(--ease),
    padding          var(--dur-fast) var(--ease),
    opacity          var(--dur-fast) var(--ease);
}

/* ---- grid ----------------------------------------------------- */

/* 20rem, not 22rem: at a 768px tablet the container leaves ~728px,
   and 22rem cards needed 736px for two columns — eight pixels short,
   so the whole tablet range collapsed to a single column of oversized
   cards. 20rem fits two comfortably there and changes nothing at
   desktop widths, which were already landing on three. */
.demos__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
  gap: clamp(1rem, 2.5vw, 2rem);
}

/* ---- card ----------------------------------------------------- */

/* Outer: layout + entrance animation only. */
.demo-card {
  position: relative;
}

/* Filtered out. `display: none` rather than opacity, so the grid
   actually reflows and does not leave holes where cards used to be. */
.demo-card[hidden] {
  display: none;
}

/* Dealt back in. The stagger is set per card in JS from its position
   in the FILTERED set, not its index in the full 53 — otherwise a
   filter showing cards 40-50 would start its animation four seconds
   late. */
@keyframes demo-card-in {
  from { opacity: 0; transform: translateY(14px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}

.demos__grid.is-refiltering .demo-card:not([hidden]) {
  animation: demo-card-in 0.45s var(--ease-out) both;
  animation-delay: calc(var(--deal, 0) * 0.035s);
}

/* Inner: everything visible, plus the hover tilt.
   Split because both the entrance and the tilt animate
   `transform` and one element has only one of those. */
.demo-card__face {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
  height: 100%;

  padding: 14px;
  background-color: var(--c-bg-elevated);
  border: var(--bw-card) solid var(--c-card-border);
  border-radius: var(--r-tile);
  overflow: hidden;

  /* A card at rest is still resting ON something, so it casts a
     shadow before you touch it. Previously the shadow appeared only
     on hover, which read as the card switching on rather than
     lifting.

     Rest and hover are two named tokens rather than one shadow that
     is scaled, because the two layers inside them grow at different
     rates: the contact shadow spreads a little, the ambient one
     roughly doubles. That difference is the whole reason a lift
     looks physical. The transition is already declared on
     [data-tilt] in 91-depth.css, alongside the transform that
     raises the card — so the shadow grows and the card rises on
     exactly the same curve. */
  box-shadow: var(--shadow-card-rest);
}

/* A sheen that sweeps across on hover. Purely decorative, so it is
   a pseudo-element on the face rather than another DOM node, and it
   animates `transform` only — a moving `background-position` would
   repaint the whole card every frame. */
.demo-card__face::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;

  background-image: linear-gradient(
    100deg,
    transparent 35%,
    rgb(var(--rgb-ink) / 0.10) 50%,
    transparent 65%);
  translate: -120% 0;
  transition: translate 0.75s var(--ease-out);
}

.demo-card:hover .demo-card__face {
  --lift: -8px;
  border-color: rgb(var(--rgb-ink) / 0.42);
  box-shadow: var(--shadow-card);
}

.demo-card:hover .demo-card__face::after {
  translate: 120% 0;
}

/* The shot sits in a fixed frame and scales inside it on hover, so
   the card's own box never changes size — a card that grew would
   reflow its neighbours in the grid. */
.demo-card__frame {
  position: relative;
  overflow: hidden;
  border-radius: calc(var(--r-tile) - 6px);
  background-color: var(--c-bg-elevated);
}

.demo-card__shot {
  width: 100%;
  aspect-ratio: 590 / 300;
  object-fit: cover;
  display: block;
  transition: scale var(--dur-slow) var(--ease-out);
}

.demo-card:hover .demo-card__shot {
  scale: 1.06;
}

/* Video media wrap & cross-fade layers */
.demo-card__media-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 590 / 300;
  overflow: hidden;
  border-radius: inherit;
  background-color: var(--c-bg-elevated);
}

.demo-card__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: 590 / 300;
  object-fit: cover;
  display: block;
  z-index: 1;
  transition: opacity var(--dur-base) var(--ease), scale var(--dur-slow) var(--ease-out);
}

.demo-card__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: 590 / 300;
  object-fit: cover;
  display: block;
  z-index: 2;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.28s var(--ease), scale var(--dur-slow) var(--ease-out);
}

.demo-card.is-playing .demo-card__video {
  opacity: 1;
}

/* Sleek glassmorphism video badge */
.demo-card__badge-video {
  position: absolute;
  inset-block-start: 12px;
  inset-inline-start: 12px;
  z-index: 4;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px 4px 12px;
  font-size: var(--fs-xs, 12px);
  font-weight: var(--fw-bold, 700);
  line-height: 1;
  color: #ffffff;
  background-color: rgb(15 23 42 / 0.72);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgb(255 255 255 / 0.22);
  border-radius: var(--r-pill, 9999px);
  box-shadow: 0 4px 12px rgb(0 0 0 / 0.35);
  pointer-events: none;
  transition:
    background-color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    scale var(--dur-fast) var(--ease);
}

.demo-card__badge-video svg {
  width: 11px;
  height: 11px;
  fill: #BAEA3C;
  flex: none;
}

.demo-card:hover .demo-card__badge-video {
  border-color: rgb(186 234 60 / 0.6);
  background-color: rgb(15 23 42 / 0.85);
  scale: 1.05;
}

/* Reads the screenshot down into the card so the title does not sit
   on a hard cut between image and panel. */
.demo-card__frame::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  height: 40%;
  pointer-events: none;
  background-image: linear-gradient(
    to top, rgb(var(--rgb-raised) / 0.85), transparent);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease);
}

.demo-card:hover .demo-card__frame::after {
  opacity: 1;
}

/* The "مشاهده" button used to land in a different place on almost
   every card — measured across the first twelve: 22px, 26px and
   71px up from the card's bottom edge, a 49px spread.

   Two causes, both fixed here.

   ONE — some titles wrap to two lines ("تجزیه و تحلیل داده‌ها"),
   which makes those cards taller; grid stretches every card in the
   row to match, so a short-titled card next to a long-titled one
   ended up with 44px of slack. `margin-block-start: auto` spends
   that slack ABOVE the body instead of leaving it below, pinning
   the body to the bottom of the card whatever the row does.

   TWO — `align-items: center` centred the button against the text
   block, so a two-line title pushed it down by half a line.
   `flex-end` anchors it to the same edge the body now sits on.

   Together the button's distance from the bottom of its card is a
   constant, not a function of the data. */
/* THREE — `flex-wrap: wrap` was the worst of it. A long title
   ("تجارت فناوری اطلاعات") widened the text block enough to push
   the button onto a line of its own, where it was the only item
   and `space-between` therefore parked it at the start — the RIGHT
   edge in this RTL document. So on six of the fifty-two cards the
   button was not merely at a different height, it was on the
   opposite side. `nowrap` keeps it beside the text always; the
   text block shrinks instead, which is what `min-inline-size: 0`
   below permits. */
.demo-card__body {
  position: relative;
  z-index: 3;
  margin-block-start: auto;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-sm);
  flex-wrap: nowrap;
  padding-inline: 6px;
  padding-block-end: 6px;
}

/* A flex item will not shrink below its content width unless told
   it may — the default `min-width: auto` is exactly what would
   push the button out of the row again. */
.demo-card__text {
  min-inline-size: 0;
}

.demo-card__title {
  font-size: var(--fs-lead);
  font-weight: var(--fw-bold);
  line-height: var(--lh-snug);

  /* Capped at two lines, but NOT padded out to two.

     Reserving a second line was tried first and measured as dead
     weight: probed with the reservation lifted, all 53 titles come
     out at one line at every column count the grid produces, so it
     was adding ~22px of empty space to every card and buying
     nothing. What actually holds the button still is
     `margin-block-start: auto` on the body — the slack goes above
     the text, so the button keeps its distance from the bottom
     edge however tall the card ends up.

     The cap stays as insurance against longer copy later. It needs
     `overflow: hidden`, and that is safe here: the stretched
     ::after that makes the whole card clickable has .demo-card as
     its containing block, which is an ancestor of this element, so
     it escapes this clip rather than being cut to the title box. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* One line, truncated. These are category lists rather than prose
   — "امنیت داده ها، راه حل های فناوری اطلاعات، نرم افزار" — and
   letting them wrap re-introduces the variable height that moved
   the button in the first place. */
.demo-card__desc {
  font-size: var(--fs-sm);
  font-weight: var(--fw-light);
  color: var(--c-text-dim);
  line-height: var(--lh-snug);

  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Covers the whole card so the entire tile is clickable, while the
   link text itself stays the accessible name. */
.demo-card__link::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

/* ---- call to action -------------------------------------------
   Replaced the two badges that used to sit here. They were a
   category chip and a "free" tag, and both had stopped earning
   their space: the filter bar above says which category is being
   shown, and every template is free, so the second labelled
   nothing. Two pills also needed ~150px on a row that already held
   a title and a description, which is what pushed the body to a
   second line on a narrow card.
   ---------------------------------------------------------------- */

.demo-card__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 32px;
  height: 32px;
  padding: 0;

  color: var(--c-text-dim);
  border: 1px solid var(--c-border);
  border-radius: var(--r-pill);

  transition:
    color            var(--dur-fast) var(--ease),
    border-color     var(--dur-fast) var(--ease),
    background-color var(--dur-fast) var(--ease),
    box-shadow       var(--dur-fast) var(--ease),
    scale            var(--dur-fast) var(--ease-out);
}

.demo-card__cta svg {
  width: 1em;
  height: 1em;
  /* The chevron points left because forward is left in an RTL
     document — the same direction the card's own hover sheen
     travels. */
  transition: translate var(--dur-fast) var(--ease);
}

/* On hover the word has to become the clearest thing on the card,
   because the card is what the pointer is aiming at.

   `scale`, not bigger padding or a bigger font: the body is pinned
   to the bottom of the card, so a button that actually grew would
   push the card taller and stretch every sibling in its grid row —
   the whole row would twitch each time the pointer crossed a card.
   A transform is painted, not laid out, so nothing moves.

   1.1 is the ceiling before 15px text scaled up starts to look
   soft rather than larger. */
.demo-card:hover .demo-card__cta {
  color: var(--c-bg);
  background-color: var(--c-text);
  border-color: var(--c-text);
  scale: 1.1;
  box-shadow: 0 6px 18px rgb(var(--rgb-shadow) / 0.45);
}

.demo-card:hover .demo-card__cta svg {
  translate: -2px 0;
}

/* ---- coming soon ---------------------------------------------- */

.demo-card--soon .demo-card__face {
  align-items: center;
  justify-content: center;
  text-align: center;
  border-style: dashed;
}

/* The placeholder is a notice, not a product — it should not look
   like something sitting on the page waiting to be picked up. */
.demo-card--soon .demo-card__face,
.demo-card--soon:hover .demo-card__face {
  --lift: 0px;
  box-shadow: none;
}

/* ---- about fartak card in grid --------------------------------- */

.demo-card--about .demo-card__face {
  border-color: rgba(139, 92, 246, 0.32);
  background:
    radial-gradient(ellipse 90% 70% at 85% 20%, rgba(139, 92, 246, 0.12) 0%, transparent 60%),
    radial-gradient(ellipse 80% 60% at 15% 90%, rgba(0, 210, 255, 0.10) 0%, transparent 60%),
    var(--c-bg-tile);
  transition:
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease),
    transform var(--dur-fast) var(--ease);
}

.demo-card--about:hover .demo-card__face {
  border-color: rgba(139, 92, 246, 0.6);
  box-shadow:
    0 16px 36px -10px rgba(0, 0, 0, 0.6),
    0 0 24px -4px rgba(139, 92, 246, 0.25),
    0 0 16px -4px rgba(0, 210, 255, 0.2);
}

:root[data-theme='light'] .demo-card--about .demo-card__face {
  background:
    radial-gradient(ellipse 90% 70% at 85% 20%, rgba(139, 92, 246, 0.08) 0%, transparent 60%),
    radial-gradient(ellipse 80% 60% at 15% 90%, rgba(0, 160, 220, 0.06) 0%, transparent 60%),
    #FFFFFF;
  border-color: rgba(139, 92, 246, 0.25);
}

:root[data-theme='light'] .demo-card--about:hover .demo-card__face {
  box-shadow:
    0 14px 30px -8px rgba(0, 0, 0, 0.12),
    0 0 20px -4px rgba(139, 92, 246, 0.2);
}

.demo-card__frame--about {
  aspect-ratio: 590 / 300;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(circle at 50% 50%, rgba(139, 92, 246, 0.25) 0%, rgba(0, 210, 255, 0.15) 45%, rgba(10, 12, 18, 0.85) 85%);
}

:root[data-theme='light'] .demo-card__frame--about {
  background:
    radial-gradient(circle at 50% 50%, rgba(139, 92, 246, 0.18) 0%, rgba(0, 180, 230, 0.12) 45%, #EDF2F7 90%);
}

.demo-card__about-visual {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  width: 100%;
  height: 100%;
  position: relative;
}

.demo-card__about-ambient {
  position: absolute;
  inset: 10%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(139, 92, 246, 0.35) 0%, transparent 70%);
  filter: blur(28px);
  animation: pulseAboutGlow 4s ease-in-out infinite alternate;
}

@keyframes pulseAboutGlow {
  0% { transform: scale(0.9); opacity: 0.6; }
  100% { transform: scale(1.15); opacity: 0.95; }
}

.demo-card__about-emblem {
  position: relative;
  z-index: 1;
  width: 3.4rem;
  height: 3.4rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.25);
  box-shadow: 0 8px 24px -4px rgba(139, 92, 246, 0.4);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: #00D2FF;
  transition: transform var(--dur-base) var(--ease), color var(--dur-base) var(--ease);
}

.demo-card--about:hover .demo-card__about-emblem {
  transform: scale(1.12);
  color: #8B5CF6;
  border-color: rgba(139, 92, 246, 0.6);
}

.demo-card__about-emblem svg {
  width: 1.8rem;
  height: 1.8rem;
}

.demo-card__about-badge-tag {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.25rem 0.85rem;
  border-radius: var(--r-pill);
  background: rgba(12, 14, 22, 0.65);
  border: 1px solid rgba(139, 92, 246, 0.35);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  color: var(--c-text);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

:root[data-theme='light'] .demo-card__about-badge-tag {
  background: rgba(255, 255, 255, 0.85);
}

.demo-card__about-pulse {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: #8B5CF6;
  box-shadow: 0 0 8px #8B5CF6;
  animation: dotPulse 2s ease-in-out infinite alternate;
}

@keyframes dotPulse {
  0% { transform: scale(0.85); opacity: 0.7; }
  100% { transform: scale(1.3); opacity: 1; }
}

.demo-card__cta--about {
  color: #8B5CF6;
  border-color: rgba(139, 92, 246, 0.35);
}

.demo-card:hover .demo-card__cta--about {
  color: #FFFFFF;
  background: linear-gradient(135deg, #8B5CF6 0%, #00D2FF 100%);
  border-color: transparent;
  box-shadow: 0 6px 20px rgba(139, 92, 246, 0.4);
}

@media (max-width: 640px) {
  .demos {
    padding-block-start: clamp(5.2rem, 14vw, 7.5rem);
  }
}

