/* ==================== VIEWS ====================
 * Per-tab layout + the tab-switch transition.
 * =============================================== */

.view {
  display: none;
  padding-block: clamp(28px, 5vw, 56px) var(--section-padding);
  min-height: 60vh;
}

.view.active {
  display: block;
}

/* Inactive views are display:none, so they cost nothing. This keeps the
   active one from re-rendering offscreen children during scroll. */
.view.active > .container > *:not(.hero) {
  content-visibility: auto;
  contain-intrinsic-size: auto 400px;
}

/* Fallback tab transition for browsers without the View Transitions API.
   Chrome/Safari use ::view-transition instead (see below). */
.view.entering {
  animation: view-in var(--dur-med) var(--ease-out-expo);
}

@keyframes view-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

/* View Transitions API: cross-fade the whole page, slide the new one in.
   RTL-aware — new content enters from the inline-start edge. */
::view-transition-old(page) {
  animation: vt-out var(--dur-med) var(--ease-in-out) both;
}
::view-transition-new(page) {
  animation: vt-in var(--dur-med) var(--ease-out-expo) both;
}

@keyframes vt-out {
  to { opacity: 0; transform: translateY(-10px); }
}
@keyframes vt-in {
  from { opacity: 0; transform: translateY(16px); }
}

/* ==================== HERO (About) ==================== */

.hero {
  display: grid;
  gap: clamp(28px, 5vw, 56px);
  align-items: center;
  padding-block: clamp(20px, 5vw, 48px);
}

@media (min-width: 861px) {
  .hero {
    grid-template-columns: 1.25fr 0.75fr;
  }
}


.hero-greeting {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--accent-primary);
  letter-spacing: 0.02em;
  margin-block-end: 10px;
}

.hero h1 {
  font-size: var(--fs-display);
  margin-block-end: 14px;
}

.hero-tagline {
  font-size: clamp(1.28rem, 2.5vw, 1.58rem);
  color: var(--text-secondary);
  max-width: 46ch;
  margin-block-end: 10px;
}

.hero-role {
  font-size: clamp(1.4rem, 3vw, 1.9rem);
  font-weight: 600;
  color: var(--text-secondary);
  margin-block-end: 12px;
  display: flex;
  align-items: center;
  gap: 0.35em;
}

/* Rotating word: a fixed-height slot so the line never reflows. */
.rotator {
  display: inline-flex;
  align-items: center;
  height: 1.35em;
  overflow: hidden;
  vertical-align: bottom;
}

.rotator-word {
  display: inline-block;
  font-weight: 800;
  background: var(--gradient-text);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: word-in var(--dur-slow) var(--ease-out-expo);
}

@keyframes word-in {
  from { opacity: 0; transform: translateY(0.9em); }
  to { opacity: 1; transform: none; }
}

.rotator-word.out {
  animation: word-out var(--dur-med) var(--ease-in-out) forwards;
}

@keyframes word-out {
  to { opacity: 0; transform: translateY(-0.9em); }
}

/* Set well clear of the copy above: the two buttons are the one thing on this
 * screen to act on, and a gap the size of the text's own leading makes them
 * read as part of the paragraph rather than as the way out of it. */
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-block-start: clamp(34px, 4.5vw, 50px);
}

/* ---- Portrait ---- */

/* The logo and its speech bubble as one column. `.hero-portrait` keeps its own
 * box untouched — motion.js finds it by that class for the tap animations, and
 * the arcade peeks characters out from behind it. */
.hero-portrait-col {
  display: grid;
  justify-items: center;
}

.hero-portrait {
  position: relative;
  width: min(300px, 72vw);
  aspect-ratio: 1;
  margin-inline: auto;
}

.portrait-bubble {
  position: relative;
  max-width: 30ch;
  margin-block-start: 22px;
  padding: 13px 20px;
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-md);
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 1.5;
  text-align: center;
  animation: bubble-in 640ms var(--ease-spring) 500ms both;
  transition:
    opacity var(--dur-med) var(--ease-in-out),
    transform var(--dur-med) var(--ease-in-out),
    border-color var(--dur-fast),
    background var(--dur-fast);
}

.portrait-bubble:hover {
  background: var(--bg-card-hover);
  border-color: var(--accent-primary);
}

/* The dismiss hint. Decoration — the whole bubble is the target, and the
 * button's accessible name is the message itself. */
.bubble-x {
  position: absolute;
  inset-block-start: 3px;
  inset-inline-end: 7px;
  font-size: 0.95rem;
  line-height: 1;
  color: var(--text-muted);
  transition: color var(--dur-fast);
}

.portrait-bubble:hover .bubble-x { color: var(--accent-primary); }

/* Pressed: it shrinks away, then Motion takes it out of the layout. */
.portrait-bubble.is-gone {
  opacity: 0;
  transform: scale(0.9) translateY(-6px);
  pointer-events: none;
  animation: none; /* or bubble-in's `both` would hold the entry state */
}

/* The tail: a square straddling the top edge, rotated 45° and outlined on the
 * two sides that show, so it continues the bubble's own border. Centred with
 * inset-inline + auto margins rather than a percentage offset, which would
 * land differently under RTL. */
.portrait-bubble::before {
  content: '';
  position: absolute;
  inset-block-start: -7px;
  inset-inline: 0;
  margin-inline: auto;
  width: 12px; height: 12px;
  background: var(--bg-elevated);
  border-inline-start: 1px solid var(--border-strong);
  border-block-start: 1px solid var(--border-strong);
  transform: rotate(45deg);
}

@keyframes bubble-in {
  from { opacity: 0; transform: translateY(-8px) scale(0.92); }
  to { opacity: 1; transform: none; }
}

/* It arrives with the page instead of popping in later. */
@media (prefers-reduced-motion: reduce) {
  .portrait-bubble { animation: none; }
}

.portrait-frame {
  position: absolute;
  inset: 0;
  border-radius: 42% 58% 54% 46% / 48% 44% 56% 52%;
  background: var(--gradient-primary);
  padding: 4px;
  box-shadow: var(--shadow-glow);
  animation: blob 14s var(--ease-in-out) infinite alternate;
}

@keyframes blob {
  50%  { border-radius: 56% 44% 42% 58% / 42% 56% 44% 58%; }
  100% { border-radius: 46% 54% 60% 40% / 56% 48% 52% 44%; }
}

.portrait-frame {
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

.portrait-frame > * {
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
  background: var(--bg-elevated);
}

/* A tall cut-out would lose its head and chin to a square `cover` crop, so
   fit the whole image and let the frame background carry the shape. */
.portrait-photo.fit-contain {
  object-fit: contain;
  /* The blob curves inward top and bottom, so inset the image slightly or a
     full-height subject gets clipped by the border-radius. */
  padding: 3%;
}

.portrait-initials {
  display: grid;
  place-items: center;
  font-size: clamp(2.6rem, 9vw, 4rem);
  font-weight: 800;
  color: var(--text-primary);
  letter-spacing: -0.02em;
}

/* ---- Tap-the-face easter egg ----
 * motion.js adds one of these at random on click/tap and removes it on
 * animationend. Every keyframe starts AND ends at the identity transform, so
 * removing the class never produces a visible snap back.
 * The blob animation lives on .portrait-frame and only touches border-radius,
 * so these transforms on the parent never fight it. */

.hero-portrait[class*="fx-"] { will-change: transform; }

.hero-portrait.fx-spin   { animation: fx-spin   900ms var(--ease-out-expo); }
.hero-portrait.fx-flip   { animation: fx-flip   900ms var(--ease-in-out); }
.hero-portrait.fx-jelly  { animation: fx-jelly  900ms var(--ease-out-expo); }
.hero-portrait.fx-bounce { animation: fx-bounce 900ms var(--ease-out-expo); }
.hero-portrait.fx-shake  { animation: fx-shake  700ms var(--ease-in-out); }
.hero-portrait.fx-tada   { animation: fx-tada   900ms var(--ease-in-out); }
.hero-portrait.fx-pop    { animation: fx-pop    620ms var(--ease-spring); }
.hero-portrait.fx-roll   { animation: fx-roll  1000ms var(--ease-in-out); }
.hero-portrait.fx-swing  {
  animation: fx-swing 900ms var(--ease-out-expo);
  transform-origin: top center;
}

@keyframes fx-spin {
  from { transform: rotate(0); }
  to   { transform: rotate(360deg); }
}

@keyframes fx-flip {
  from { transform: perspective(900px) rotateY(0); }
  to   { transform: perspective(900px) rotateY(360deg); }
}

@keyframes fx-jelly {
  0%, 100% { transform: scale(1, 1); }
  30% { transform: scale(1.26, 0.76); }
  40% { transform: scale(0.78, 1.22); }
  50% { transform: scale(1.14, 0.86); }
  65% { transform: scale(0.95, 1.05); }
  80% { transform: scale(1.04, 0.96); }
}

@keyframes fx-bounce {
  0%, 100% { transform: translateY(0) scaleY(1); }
  20% { transform: translateY(-30px) scaleY(1.06); }
  42% { transform: translateY(0) scaleY(0.9); }
  62% { transform: translateY(-15px) scaleY(1.03); }
  82% { transform: translateY(0) scaleY(0.96); }
}

@keyframes fx-shake {
  0%, 100% { transform: translateX(0) rotate(0); }
  15% { transform: translateX(-13px) rotate(-5deg); }
  30% { transform: translateX(12px) rotate(4deg); }
  45% { transform: translateX(-9px) rotate(-3deg); }
  60% { transform: translateX(7px) rotate(2deg); }
  80% { transform: translateX(-4px) rotate(-1deg); }
}

@keyframes fx-tada {
  0%, 100% { transform: scale(1) rotate(0); }
  10%, 20% { transform: scale(0.9) rotate(-4deg); }
  30%, 50%, 70%, 90% { transform: scale(1.14) rotate(4deg); }
  40%, 60%, 80% { transform: scale(1.14) rotate(-4deg); }
}

@keyframes fx-pop {
  0%, 100% { transform: scale(1) rotate(0); }
  35% { transform: scale(1.24) rotate(-7deg); }
  62% { transform: scale(0.93) rotate(4deg); }
}

@keyframes fx-roll {
  0%   { transform: translateX(0) rotate(0); }
  50%  { transform: translateX(-18%) rotate(-180deg); }
  100% { transform: translateX(0) rotate(-360deg); }
}

@keyframes fx-swing {
  0%, 100% { transform: rotate(0); }
  20% { transform: rotate(15deg); }
  40% { transform: rotate(-11deg); }
  60% { transform: rotate(7deg); }
  80% { transform: rotate(-4deg); }
}

/* On phones the portrait leads — it's what makes the page feel like a person.
   Everything else tightens so the CTA still clears the bottom nav.
   Must stay after the rules above: media queries add no specificity, so a
   later equal-specificity rule would otherwise win. */
@media (max-width: 860px) {
  .hero {
    padding-block: 4px 0;
    gap: 26px;
  }
  /* The grid item is the column, not the portrait inside it — `order` on the
     inner element only sorts it against the bubble and leaves the whole block
     below the copy. */
  .hero-portrait-col { order: -1; }
  .hero-portrait { width: min(215px, 50vw); }
  .portrait-bubble { margin-block-start: 16px; }
  .hero h1 { margin-block-end: 8px; }
  .hero-role { margin-block-end: 6px; }
  /* A paragraph running straight into the buttons under it reads as one block
     and is easy to mis-tap; give the row its own air. */
  .hero-actions { margin-block-start: 36px; }

  .project-detail .project-hero-img,
  .project-detail .project-hero-img.is-contain {
    display: block;
    width: calc(100vw - 14px);
    max-width: none;
    min-height: min(82vw, 420px);
    aspect-ratio: 1 / 1;
    margin-inline: calc(50% - 50vw + 7px);
    border-radius: 0;
    background: transparent;
    object-fit: contain;
    object-position: 24% center;
    padding: 18px 18px 18px 14px;
    transform: scale(1.04);
    transform-origin: center;
  }
}

/* ==================== ABOUT ==================== */

.about-body {
  display: grid;
  gap: clamp(20px, 3vw, 32px);
  max-width: 68ch;
}

.about-body p {
  color: var(--text-secondary);
  font-size: clamp(1.25rem, 2vw, 1.38rem);
}

/* Inline [text](url) links inside paragraph copy. base.css strips anchor
   styling, so they need their own colour and underline to read as links. */
.text-link {
  color: var(--accent-primary);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--dur-fast) ease;
}

.text-link:hover,
.text-link:focus-visible {
  color: var(--accent-secondary);
}

.highlight-grid {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  margin-block-start: clamp(36px, 6vw, 64px);
}

.highlight-card .h-icon {
  font-size: 1.9rem;
  line-height: 1;
  margin-block-end: 12px;
  display: block;
}

.highlight-card h3 { margin-block-end: 7px; }

.highlight-card p {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 1.7;
}

.highlight-card .h-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 8px;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 1.6;
}

.highlight-card .h-list li {
  position: relative;
  padding-inline-start: 18px;
}

.highlight-card .h-list li::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.6em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-primary);
  opacity: .75;
}

/* ---- Photo pile ----
 * A stack of prints tossed on a table. Depth is DOM order: :nth-child(1) is on
 * top, and each one behind is pushed back, rotated a little and dimmed. Photos
 * browses by moving the top node to the end, so every card's transform changes
 * at once and the transition below is the whole animation. */

.photo-pile {
  margin-block-start: clamp(44px, 8vw, 80px);
  display: grid;
  justify-items: center;
}

/* One shape for every card, whatever the photo's own proportions — a pile of
 * prints is a pile of prints. 3:4 rather than the phone's own 9:16, which would
 * be a very tall column on a desktop; each photo's `focus` decides what the
 * crop keeps. */
.photo-stack {
  position: relative;
  width: clamp(250px, 68vw, 330px);
  aspect-ratio: 3 / 4;
  /* Room for the cards fanned out behind the top one. */
  margin-block-end: 22px;
  touch-action: pan-y; /* a horizontal drag is ours; a vertical one scrolls */
}

.photo-card {
  position: absolute;
  inset: 0;
  margin: 0;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  box-shadow: var(--shadow-md);
  transform-origin: 50% 90%;
  transition:
    transform var(--dur-med) var(--ease-out-expo),
    opacity var(--dur-med) var(--ease-out-expo),
    filter var(--dur-med) var(--ease-out-expo);
  will-change: transform;
}

.photo-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

/* The three depths. Deliberately asymmetric angles — a neat fan reads as a
 * carousel, a crooked one reads as a pile someone put down. */
.photo-card:nth-child(1) {
  z-index: 3;
  transform: rotate(-1.5deg);
  cursor: grab;
}

.photo-card:nth-child(2) {
  z-index: 2;
  transform: translate(14px, 12px) rotate(4deg) scale(0.97);
  filter: brightness(0.86);
}

.photo-card:nth-child(3) {
  z-index: 1;
  transform: translate(-12px, 22px) rotate(-5.5deg) scale(0.94);
  filter: brightness(0.74);
}

/* A fourth and beyond: parked under the third, out of sight but still there. */
.photo-card:nth-child(n + 4) {
  z-index: 0;
  transform: translateY(28px) scale(0.92);
  filter: brightness(0.66);
  opacity: 0;
}

.photo-card.is-dragging {
  transition: none;      /* the pointer is driving it, not the easing */
  cursor: grabbing;
  box-shadow: var(--shadow-lg);
}

/* --fly is +1 or -1, set by Photos: the direction it was pushed. */
.photo-card.is-leaving {
  transform: translateX(calc(var(--fly, -1) * 135%)) rotate(calc(var(--fly, -1) * 16deg));
  opacity: 0;
  transition:
    transform 340ms var(--ease-in-out),
    opacity 340ms var(--ease-in-out);
}

/* One frame with no transition, so a card that has just been moved to the back
 * appears there instead of flying back across the pile. */
.photo-card.is-instant { transition: none; }

.photo-controls {
  display: flex;
  align-items: center;
  gap: 12px;
}

.photo-next {
  display: grid;
  place-items: center;
  width: 42px; height: 42px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--sheen);
  color: var(--text-primary);
  cursor: pointer;
  transition: background var(--dur-fast), transform var(--dur-fast) var(--ease-spring);
}

.photo-next:hover {
  background: var(--sheen-strong);
  transform: translateX(-3px); /* RTL: "onward" is leftward */
}

.photo-next svg { width: 18px; height: 18px; }

.photo-hint {
  color: var(--text-muted);
  font-size: var(--fs-xs);
}

/* No transitions to run, so the pile cuts from one arrangement to the next.
 * Dragging is not attached at all in that mode — the button is the way. */
@media (prefers-reduced-motion: reduce) {
  .photo-card,
  .photo-card.is-leaving { transition: none; }
  .photo-card:nth-child(1) { cursor: default; }
}

/* ---- Timeline ---- */

.timeline {
  position: relative;
  margin-block-start: clamp(36px, 6vw, 64px);
  padding-inline-start: 30px;
}

.timeline::before {
  content: '';
  position: absolute;
  inset-block: 8px 8px;
  inset-inline-start: 5px;
  width: 2px;
  background: linear-gradient(to bottom, var(--accent-primary), var(--accent-tertiary), transparent);
  opacity: 0.5;
}

.timeline-item {
  position: relative;
  padding-block-end: 30px;
}

.timeline-item::before {
  content: '';
  position: absolute;
  inset-inline-start: -30px;
  inset-block-start: 9px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--gradient-warm);
  box-shadow: 0 0 0 4px var(--bg-primary), 0 0 16px rgba(200, 116, 178, 0.7);
}

.timeline-year {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--accent-primary);
  letter-spacing: 0.04em;
}

.timeline-item h3 { margin-block: 2px 5px; }

.timeline-item p {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
}

/* ==================== PROJECTS ==================== */

.project-grid {
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}

/* Every card is laid out on the SAME five rows — thumb, title, blurb, tags,
 * "more" — so a title that runs to three lines in one card does not push its
 * blurb below the neighbour's. `subgrid` makes each card borrow the grid's
 * rows, which are sized once per band from the tallest card in it; the fallback
 * below keeps the old per-card stacking where subgrid is unsupported. */
.project-card {
  display: grid;
  grid-template-rows: auto auto 1fr auto auto;
  grid-row: span 5;
  row-gap: 9px;
  padding: 0;
  overflow: hidden;
  cursor: pointer;
  text-align: center;
}

@supports (grid-template-rows: subgrid) {
  .project-card { grid-template-rows: subgrid; }
}

.project-thumb {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--gradient-primary);
}

.project-thumb.is-logo { background: transparent; }

.project-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out-expo);
}

.project-thumb img.is-contain {
  object-fit: contain;
  padding: 14px;
}

.project-card:hover .project-thumb img { transform: scale(1.06); }

.project-thumb-placeholder {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 2.4rem;
  opacity: 0.9;
}

/* The body is only a wrapper for the four text rows — its children have to be
 * grid items of the card itself, so its padding moves onto them. */
.project-body {
  display: contents;
}

.project-body > * {
  padding-inline: 22px;
  margin: 0;
}

.project-body h3,
.project-body p { width: 100%; }

.project-body h3 {
  padding-block-start: 11px; /* + the 9px row gap under the thumb = 20px */
  font-size: calc(var(--fs-body) * 2.7);
  line-height: 1.1;
  /* Titles differ in line count; sitting them on the row's bottom edge keeps
   * the gap to the blurb identical across the band. */
  align-self: end;
}

/* 0.55 rather than the detail page's 2/3: at the card's ~290px of text width
 * that is the largest size that still sets "והנפקת חשבוניות" on one line. */
.project-card .project-body h3 .title-sub { font-size: 0.55em; }

.project-body p {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
}

.tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-block-start: 4px;
  justify-content: center;
}

/* An empty tag row still holds its (zero-height) place in the shared rows. */
.project-card .tag-row:empty { margin-block-start: 0; }

.tag {
  padding: 4px 11px;
  border-radius: var(--radius-pill);
  background: var(--sheen-mid);
  border: 1px solid var(--border-subtle);
  font-size: var(--fs-xs);
  color: var(--text-secondary);
}

.project-more {
  color: var(--accent-primary);
  font-weight: 700;
  font-size: var(--fs-sm);
  display: flex;
  align-items: center;
  gap: 6px;
  margin-block-start: 6px;
  padding-block-end: 24px;
  justify-content: center;
}

.project-more svg {
  width: 15px; height: 15px;
  transition: transform var(--dur-fast) var(--ease-spring);
}

/* RTL: the "read more" arrow points right-to-left. */
.project-card:hover .project-more svg { transform: translateX(-4px); }

/* The line after a break in a title is a subtitle, not a second headline —
 * sized off the heading it sits in (em, not rem) so it stays in proportion on
 * the card's h3 and the detail page's h2 alike. */
.title-sub {
  display: block;
  margin-block-start: 5px;
  font-size: 1.5em;
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: 0;
  color: var(--text-secondary);
}

.project-title-cta {
  display: block;
  width: fit-content;
  margin: 14px auto 0;
  padding: 4px;
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.project-title-cta .btn {
  padding: 17px 32px;
  min-height: 58px;
  font-size: 1.18rem;
  overflow: hidden;
}

.project-detail .section-head h2 {
  font-size: 6rem;
  text-align: center;
}

.project-detail .section-head h2 .title-sub {
  font-size: 3rem;
}

/* ---- Project detail ---- */

.project-detail .back-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--text-secondary);
  font-weight: 600;
  font-size: var(--fs-sm);
  margin-block-end: 24px;
  padding: 8px 4px;
  transition: color var(--dur-fast), gap var(--dur-fast);
}

.project-detail .back-link:hover {
  color: var(--accent-primary);
  gap: 11px;
}

.project-detail .back-link svg { width: 16px; height: 16px; }

.project-hero-img {
  width: 100%;
  aspect-ratio: 21 / 9;
  object-fit: cover;
  border-radius: var(--radius-lg);
  margin-block-end: 28px;
  background: var(--gradient-primary);
}

/* A logo or any artwork with its own edges: show all of it and give it room,
 * rather than cropping into the middle of the picture. */
.project-hero-img.is-contain {
  object-fit: contain;
  aspect-ratio: 1 / 1;
  padding: clamp(8px, 2vw, 20px);
  background: transparent;
}

.project-detail-body {
  max-width: 68ch;
  padding: 20px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-glass);
  display: grid;
  gap: 18px;
}

.project-summary {
  max-width: 68ch;
  margin-inline: auto;
  padding: 18px 20px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-glass);
  margin-block-end: 24px;
}

.project-summary p {
  color: var(--text-primary);
  text-align: center;
}

.project-summary-lead {
  display: block;
  font-size: 2em;
  line-height: 1.2;
  text-align: center;
}

.body-carousel {
  position: relative;
  max-width: 68ch;
  margin-inline: auto;
  padding: 18px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-glass);
}

.body-carousel-hint {
  margin-block: 10px 14px;
  color: var(--text-muted);
  font-size: 0.9em;
  text-align: center;
}

.body-carousel-viewport {
  overflow: hidden;
  touch-action: pan-y;
  cursor: grab;
}

.body-carousel-viewport:active { cursor: grabbing; }

.body-carousel-track { display: grid; }

.body-carousel-card {
  display: none;
  min-height: 150px;
  padding: 20px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-card);
}

.body-carousel-card.is-active {
  display: block;
  animation: body-card-in 280ms ease both;
}

.body-carousel-lead {
  display: block;
  font-size: 2em;
  line-height: 1.2;
  text-align: center;
}

.body-carousel-card p { color: var(--text-secondary); }

@keyframes body-card-in {
  from { opacity: 0; transform: translateX(18px); }
  to { opacity: 1; transform: translateX(0); }
}

.body-carousel-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
}

.carousel-button {
  display: inline-grid;
  width: 38px;
  height: 38px;
  place-items: center;
  padding: 0;
  border: 1px solid var(--border-subtle);
  border-radius: 50%;
  color: var(--text-primary);
  background: var(--bg-card);
  cursor: pointer;
}

.carousel-button.is-next svg { transform: rotate(180deg); }
.carousel-button svg { width: 18px; height: 18px; }
.carousel-button:disabled { opacity: 0.35; cursor: default; }
.body-carousel-count { min-width: 48px; color: var(--text-muted); text-align: center; }

.body-carousel-shovel {
  position: absolute;
  inset-block-start: 14px;
  inset-inline-start: 14px;
  font-size: 4rem;
  line-height: 1;
}

/* ==================== SCREENSHOT CAROUSEL ====================
 * A balanced coverflow: the active shot centred, its two neighbours
 * half-transparent and shrunk on either side, everything else hidden.
 *
 * The geometry rests on ONE number, --shot-w - the centre shot's share of the
 * reel's width. Because the slides are absolutely positioned they contribute no
 * height, so the reel's own aspect-ratio has to be stated, and it must equal
 *     16 / (9 × --shot-w)
 * or the shots overflow it. At 62% that is 2.87. **Change --shot-w and you must
 * recompute --shot-ratio with it** - the mobile block further down does exactly
 * that. Everything else (the sideways shift) is a percentage of a slide's own
 * width, so it follows along for free.
 *
 * --shot-dir folds RTL into the sign: in Hebrew the *next* shot belongs on the
 * left, and transforms have no logical-property equivalent to do that for us.
 * ============================================================= */

/* No card around it, by request: it sits straight on the page between the title
   and the download button, so it gets the container's full width (a bigger
   centre shot) and vertical margins instead of a border and a background. */
.shot-gallery {
  --shot-w: 62%;
  --shot-ratio: 2.87;
  --shot-shift: 60%;    /* neighbour's offset, as a share of its own width */
  --shot-dir: -1;
  position: relative;
  width: 100%;
  margin-block: clamp(26px, 5vw, 44px) clamp(20px, 4vw, 32px);
}

.shot-gallery-title {
  margin: 0 0 clamp(12px, 2vw, 18px);
  font-size: var(--fs-h3);
  text-align: center;
  color: var(--text-primary);
}

.shot-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(4px, 1.2vw, 12px);
  padding-inline: clamp(6px, 1.6vw, 14px);
}

.shot-nav {
  flex: 0 0 auto;
  /* Above every slide (they run z-index 10 and down), so a shot sliding past
     can never end up on top of the control that moves it. */
  z-index: 20;
}

.shot-nav:hover {
  border-color: var(--border-strong);
  background: var(--bg-card-hover);
}

.shot-reel {
  position: relative;
  flex: 1 1 auto;
  aspect-ratio: var(--shot-ratio);
  touch-action: pan-y;
  /* The neighbours are shifted far enough out to hang past the reel - this cuts
     them off at its edge, which is what makes them read as continuing out of
     frame instead of as two more small pictures. It is also what keeps them off
     the arrow buttons, which sit outside the reel on desktop. */
  overflow: hidden;
}

.shot-slide {
  position: absolute;
  inset-block: 0;
  /* Symmetric, so inset-inline-start centres the slide either direction. */
  inset-inline-start: calc((100% - var(--shot-w)) / 2);
  width: var(--shot-w);
  padding: 0;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-card);
  overflow: hidden;
  cursor: pointer;
  /* --offset (signed) and --depth (its absolute value) come from render.js.
     depth 0 → opaque and full size · 1 → half-transparent, 12% smaller ·
     2+ → transparent, and .is-far takes it out of the layout entirely. */
  transform:
    translateX(calc(var(--offset) * var(--shot-dir) * var(--shot-shift)))
    scale(calc(1 - var(--depth) * 0.12));
  opacity: calc(1 - var(--depth) * 0.55);
  z-index: calc(10 - var(--depth));
  transition:
    transform var(--dur-med) var(--ease-out-expo),
    opacity var(--dur-med) var(--ease-out-expo),
    box-shadow var(--dur-fast);
}

.shot-slide.is-active {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-md), var(--shadow-glow);
}

.shot-slide.is-side:hover {
  opacity: 0.72;
}

/* Beyond the immediate neighbours: gone, and out of the tab order with it. */
.shot-slide.is-far {
  visibility: hidden;
  pointer-events: none;
}

.shot-slide img {
  display: block;
  width: 100%;
  height: 100%;
  /* The exports run 1.81-1.88:1, so a 16/9 frame trims a couple of percent off
     the sides and every shot ends up the same shape. */
  object-fit: cover;
  object-position: center top;
}

/* A dark wash over the neighbours, so "behind" reads as depth rather than as a
   half-loaded image. The active shot clears it. */
.shot-slide::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--plum-darkest);
  opacity: 0.35;
  transition: opacity var(--dur-med) var(--ease-out-expo);
  pointer-events: none;
}

.shot-slide.is-active::after { opacity: 0; }

/* Scoped through .shot-gallery, not bare `.shot-caption`: both of these are a
   <p> inside .section-head, and `.section-head p` (components.css) is the more
   specific selector - it was quietly winning on max-width and margin-block. */
.shot-gallery .shot-caption {
  max-width: 56ch;
  margin: clamp(14px, 2.4vw, 20px) auto 0;
  padding-inline: 16px;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 1.5;
  text-align: center;
  min-height: 3em;   /* holds its box so the dots don't jump between captions */
}

.shot-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  margin-block-start: 4px;
}

.shot-dot {
  width: 9px;
  height: 9px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--sheen-strong);
  cursor: pointer;
  transition: background var(--dur-fast), width var(--dur-fast);
}

.shot-dot:hover { background: var(--accent-secondary); }

.shot-dot.is-active {
  width: 26px;
  border-radius: var(--radius-pill);
  background: var(--accent-primary);
}

.shot-gallery .shot-hint {
  margin: 12px auto 0;
  color: var(--text-muted);
  font-size: var(--fs-xs);
  text-align: center;
}

/* ---- the enlarged view ----
 * Full screen, no card: the picture itself is the surface, and the only chrome
 * over it is the × and a caption/arrows strip along its bottom edge.
 *
 * The panel is `fit-content`, so it shrink-wraps the picture rather than being
 * a box the picture sits inside. That is what puts the × on the IMAGE's corner
 * instead of out in the letterbox beside it - a 1.85:1 screenshot on a 16:10
 * screen leaves a wide dead band, and a viewport-corner × floats in the middle
 * of it, attached to nothing.
 * --------------------------- */

.shot-lightbox {
  position: fixed;
  inset: 0;
  z-index: 1100;   /* above the download warning (1000), which it can open over */
  display: grid;
  place-items: center;
}

.shot-lightbox[hidden] { display: none; }

.shot-lightbox-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(10, 3, 18, 0.92);
  backdrop-filter: blur(6px);
}

.shot-lightbox-panel {
  position: relative;
  z-index: 1;
  width: fit-content;
  height: fit-content;
  max-width: 100vw;
  max-height: 100vh;
  line-height: 0;   /* no descender gap under the img, so the bar sits flush */
}

.shot-lightbox-frame {
  max-width: 100vw;
  max-height: 100vh;
}

.shot-lightbox-frame img {
  display: block;
  /* As big as the screen allows, whole: enlarging is the point, so nothing is
     cropped and nothing is scaled past its own pixels. */
  max-width: 100vw;
  max-height: 100vh;
  object-fit: contain;
}

/* Caption and arrows float over the foot of the picture. They used to sit below
   it in flow, which cost the image ~26vh of the screen it was meant to fill. */
.shot-lightbox-bar {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  display: grid;
  gap: 10px;
  justify-items: center;
  padding: 44px 16px 14px;
  line-height: 1.5;
  /* Screenshots run light AND dark, so the strip brings its own scrim rather
     than trusting whatever happens to be behind the text. */
  background: linear-gradient(to top, rgba(10, 3, 18, 0.92) 0%, rgba(10, 3, 18, 0.76) 55%, rgba(10, 3, 18, 0) 100%);
  pointer-events: none;   /* the gradient is decoration; only the controls click */
}

.shot-lightbox-bar > * { pointer-events: auto; }

.shot-lightbox-caption {
  max-width: 70ch;
  margin: 0;
  color: var(--text-primary);
  font-size: var(--fs-sm);
  line-height: 1.5;
  text-align: center;
}

.shot-lightbox-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
}

.shot-lightbox-nav .carousel-button {
  background: rgba(33, 5, 53, 0.82);
  border-color: var(--border-strong);
}

.shot-lightbox-count {
  min-width: 56px;
  color: var(--text-primary);
  text-align: center;
  /* "5 / 7" is an LTR expression. Left to inherit the page's RTL it rendered as
     "7 / 5" - the numbers swap and it reads as seven of five. */
  direction: ltr;
  unicode-bidi: isolate;
}

.shot-lightbox-close {
  position: absolute;
  inset-block-start: 10px;
  inset-inline-end: 10px;
  z-index: 2;
  display: grid;
  width: 44px;
  height: 44px;
  place-items: center;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: rgba(10, 3, 18, 0.72);
  backdrop-filter: blur(4px);
  color: var(--text-primary);
  font-size: 1.7rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}

.shot-lightbox-close:hover { background: var(--accent-secondary); color: var(--on-accent); }

/* ---- controls stripped away (a tap on the picture) ----
 * `visibility: hidden` rather than `display: none`, for two reasons: opacity
 * can still transition out of it, and it takes the buttons out of the tab order
 * so nobody can focus a control that is not on screen. */
.shot-lightbox-frame { cursor: zoom-in; }
.shot-lightbox.is-bare .shot-lightbox-frame { cursor: zoom-out; }

.shot-lightbox-close,
.shot-lightbox-bar {
  transition: opacity var(--dur-med) var(--ease-out-expo), visibility var(--dur-med);
}

.shot-lightbox.is-bare .shot-lightbox-close,
.shot-lightbox.is-bare .shot-lightbox-bar {
  opacity: 0;
  visibility: hidden;
}

/* The backdrop darkens too, so a screenshot read bare sits on near-black with
   nothing at all competing with it. */
.shot-lightbox.is-bare .shot-lightbox-backdrop {
  background: rgba(4, 1, 8, 0.97);
}

.project-note {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  max-width: 68ch;
  margin-block-start: 24px;
  padding: 16px 18px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-glass);
  color: var(--text-secondary);
}

.project-note-icon { flex: none; font-size: 1.25em; line-height: 1.5; }
.project-note p { margin: 0; }

.launch-note {
  position: relative;
  max-width: 68ch;
  margin: 34px auto 0;
  padding: 26px 24px;
  overflow: hidden;
  border: 2px solid var(--plum-accent);
  border-radius: var(--radius-md);
  background: var(--bg-glass);
  color: var(--text-primary);
  text-align: center;
  box-shadow: 0 0 0 4px var(--plum-dark), 0 0 22px rgba(200, 116, 178, 0.32);
}

.launch-note::before {
  content: '';
  position: absolute;
  inset: 7px;
  border: 2px dotted var(--success);
  border-radius: 5px;
  pointer-events: none;
  animation: launch-lights 1.3s steps(2, end) infinite;
}

.launch-note > * { position: relative; z-index: 1; }

.launch-note h3 {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-block: 8px 18px;
  color: var(--success);
  font-size: clamp(1.45rem, 5vw, 2.15rem);
  font-weight: 900;
  line-height: 1.15;
}

.launch-note-trumpet {
  display: inline-block;
  flex: none;
  font-size: 1.15em;
  line-height: 1;
}

.launch-note-title { display: inline-block; }

.launch-note-copy {
  display: grid;
  gap: 10px;
  color: var(--text-primary);
}

.launch-note-copy p { margin: 0; }
.launch-note-copy p:last-child { color: var(--success); font-size: 1.2em; font-weight: 800; }

.launch-note-lights {
  color: var(--plum-light);
  font-size: 1.25rem;
  letter-spacing: 0.25em;
  line-height: 1;
  animation: launch-lights 1.3s steps(2, end) infinite alternate;
}

.launch-note-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 18px auto;
  padding: 9px 16px;
  border: 1px solid var(--success);
  border-radius: var(--radius-pill);
  color: var(--success);
  background: var(--sheen);
  font: inherit;
  font-weight: 800;
  cursor: pointer;
}

.launch-note-arrow {
  display: inline-block;
  font-size: 2em;
  line-height: 1;
  animation: launch-arrow-bounce 750ms ease-in-out infinite alternate;
}

.plan-personal-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--success);
  border-radius: var(--radius-pill);
  color: var(--success);
  background: transparent;
  cursor: pointer;
}

.plan-personal-card-link {
  align-self: center;
  margin-block-start: auto;
  margin-block-end: 10px;
  font: inherit;
  font-size: 1.5rem;
  font-weight: 800;
  padding: 16px 24px;
}

.plan-personal-arrow {
  display: inline-block;
  font-size: 1.8em;
  line-height: 0.8;
  animation: launch-arrow-bounce 750ms ease-in-out infinite alternate;
}

.personal-note {
  position: relative;
  display: block;
  max-width: 68ch;
  margin: 28px auto 0;
  padding: 28px 22px 22px;
  border: 3px solid #ffd166;
  border-radius: var(--radius-md);
  background: var(--bg-glass);
  color: var(--text-secondary);
  text-align: center;
  box-shadow: 0 0 0 4px var(--plum-dark), 0 0 24px rgba(255, 209, 102, 0.34);
  scroll-margin-block-start: 24px;
}

.personal-note::before {
  content: '';
  position: absolute;
  inset: 0 10% auto;
  height: 6px;
  border-radius: 0 0 4px 4px;
  background: #ffd166;
  box-shadow: 0 0 12px rgba(255, 209, 102, 0.75);
}

.personal-note p { margin: 0 0 12px; }
.personal-note > div > p:first-child {
  color: var(--text-primary);
  font-size: 2em;
  font-weight: 900;
}
.personal-note p:last-child { margin-block-end: 0; color: var(--text-primary); font-weight: 700; }

.personal-note .text-link {
  position: relative;
  display: block;
  width: fit-content;
  margin: 14px auto 0;
  padding: 5px 18px;
  border: 2px solid #ffd166;
  border-radius: var(--radius-pill);
  color: #ffd166;
  font-size: 1.55em;
  font-weight: 900;
  line-height: 1;
  text-decoration: none;
  text-shadow: 0 0 8px rgba(255, 209, 102, 0.7);
  box-shadow: 0 0 12px rgba(255, 209, 102, 0.45);
  animation: runway-link-glow 1.2s ease-in-out infinite alternate;
}

.personal-note .text-link::before,
.personal-note .text-link::after {
  position: absolute;
  top: 50%;
  color: #fff0a8;
  font-size: 0.7em;
  letter-spacing: 0.3em;
  white-space: nowrap;
  transform: translateY(-50%);
  animation: runway-lights 900ms steps(2, end) infinite alternate;
}

.personal-note .text-link::before {
  content: '✨ • ✨';
  inset-inline-end: 100%;
  margin-inline-end: 12px;
}

.personal-note .text-link::after {
  content: '✨ • ✨';
  inset-inline-start: 100%;
  margin-inline-start: 12px;
  animation-delay: 450ms;
}

@keyframes runway-link-glow {
  from { box-shadow: 0 0 8px rgba(255, 209, 102, 0.35); }
  to { box-shadow: 0 0 22px rgba(255, 209, 102, 0.9); }
}

@keyframes runway-lights {
  from { opacity: 0.35; filter: brightness(0.75); }
  to { opacity: 1; filter: brightness(1.5); }
}

@keyframes launch-arrow-bounce {
  from { transform: translateY(-3px); }
  to { transform: translateY(5px); }
}

@keyframes launch-lights {
  0%, 45% { opacity: 0.5; filter: brightness(0.8); }
  55%, 100% { opacity: 1; filter: brightness(1.35); }
}

.summary-free {
  display: block;
  color: var(--success);
  font-size: 1.9em;
  font-weight: 800;
  line-height: 1.15;
  text-align: center;
}

.project-detail-body p { color: var(--text-secondary); }

.project-links {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-block-start: 30px;
}

.plan-report-samples {
  margin-block-start: 18px;
  padding: 14px 16px;
  border: 1px solid rgba(200, 116, 178, 0.5);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.03);
  text-align: center;
}

.plan-report-title {
  margin: 0 0 10px;
  color: var(--text-primary);
  font-size: 1.5rem;
  font-weight: 800;
}

.plan-report-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}

.plan-report-button {
  min-width: 110px;
  justify-content: center;
  padding-inline: 14px;
}

/* ---- Plans: free vs licensed ---- */

.plans { margin-block-start: 38px; }

.plans-title {
  font-size: 1.5em;
  margin-block-end: 8px;
  text-align: center;
}

.plans-intro {
  max-width: 68ch;
  margin-inline: auto;
  color: var(--text-secondary);
  margin-block-end: 20px;
  text-align: center;
}

/* Two equal columns; one per column on a narrow screen. The paid card must not
 * grow taller-looking than the free one just because it has fewer rows, so both
 * stretch to the row height. */
.plan-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
  align-items: stretch;
  min-width: 0;
}

.plan-card {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
  overflow: hidden; /* keeps the ::before ribbon inside the rounded corner */
}

/* A coloured rule along the card's top edge — the fastest read of "these two
 * are different things" before a word is parsed. */
.plan-card::before {
  content: '';
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 3px;
  background: var(--border-strong);
}

/* The paid column carries the accent, so the eye lands on what a licence buys
 * without the free column reading as the lesser offer. */
.plan-card.is-featured {
  border-color: var(--border-strong);
  background: var(--sheen-mid);
  box-shadow: var(--shadow-md);
}

.plan-card.is-featured::before { background: var(--gradient-warm); }

.plan-head {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: 4px 12px;
  padding-block-end: 14px;
  border-block-end: 1px solid var(--border-subtle);
}

/* Sits on its own tinted tile so the emoji reads as part of the design rather
 * than as a character that wandered into the heading. */
.plan-emoji {
  font-size: 2.25rem;
  line-height: 1;
  display: grid;
  place-items: center;
  width: 69px; height: 69px;
  border-radius: var(--radius-md);
  background: var(--sheen-strong);
  border: 1px solid var(--border-subtle);
}

.plan-card.is-featured .plan-emoji {
  background: var(--sheen-strong);
  border-color: var(--border-strong);
}

.plan-titles {
  min-width: 0;
  display: grid;
  gap: 10px;
}

.plan-head h4 {
  font-size: calc(var(--fs-body) * 1.5);
  margin: 0;
}

.plan-tagline {
  margin: 0;
  color: var(--text-muted);
  font-size: calc(var(--fs-xs) * 1.5);
  line-height: 1.5;
}

.plan-card.is-featured .plan-tagline {
  color: var(--accent-primary);
  font-weight: 600;
}

.plan-badge {
  font-size: calc(var(--fs-xs) * 1.5);
  font-weight: 700;
  padding: 4px 11px;
  border-radius: var(--radius-pill);
  background: var(--sheen-strong);
  color: var(--text-secondary);
  border: 1px solid var(--border-subtle);
}

.plan-card.is-featured .plan-badge {
  background: var(--gradient-warm);
  color: var(--on-accent);
  border-color: transparent;
}

/* The price sits between the header and the list, on its own line, so the two
 * cards can be compared on the number alone before a feature is read. */
.plan-price {
  display: flex;
  align-items: baseline;
  gap: 7px;
  margin: 0;
}

.plan-price-num {
  font-size: clamp(2.55rem, 5.1vw, 3.15rem);
  font-weight: 800;
  line-height: 1.1;
  color: var(--text-primary);
}

.plan-card.is-featured .plan-price-num {
  background: var(--gradient-text);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.plan-price-unit {
  font-size: calc(var(--fs-sm) * 1.5);
  color: var(--text-muted);
  white-space: nowrap;
}

.plan-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 11px;
}

.plan-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  color: var(--text-secondary);
  font-size: calc(var(--fs-sm) * 1.5);
  line-height: 1.55;
}

.plan-list li > span:last-child {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* The tick sits on the first line of a wrapped item, not centred against the
 * whole block. Fixed width so every item's text starts on the same line,
 * whatever the emoji's own advance width turns out to be. */
.plan-bullet {
  flex: none;
  width: 1.35em;
  font-size: 1.5rem;
  line-height: 1.55;
  text-align: center;
}

.plans-note {
  position: relative;
  display: block;
  max-width: 68ch;
  margin-inline: auto;
  margin-block-start: 24px;
  padding: 16px 18px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-glass);
  color: var(--text-secondary);
  line-height: 1.45;
  direction: rtl;
}

.plans-trial-card {
  min-height: 190px;
  padding: 0;
  cursor: pointer;
  perspective: 1000px;
  touch-action: pan-y;
}

.plans-trial-flipper {
  display: grid;
  min-height: 190px;
  transform-style: preserve-3d;
  transition: transform 650ms var(--ease-in-out);
}

.plans-trial-card.is-flipped .plans-trial-flipper {
  transform: rotateY(180deg);
}

.plans-trial-face {
  grid-area: 1 / 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 22px 88px;
  backface-visibility: hidden;
  text-align: center;
}

.plans-trial-front .plans-note-emoji {
  position: static;
  transform: none;
  margin-block-end: 10px;
  font-size: 3rem;
}

.plans-trial-back {
  transform: rotateY(180deg);
}

.plans-trial-back p { margin: 8px 0 0; }

.plans-trial-card:focus-visible {
  outline: 3px solid var(--accent-primary);
  outline-offset: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .plans-trial-flipper { transition: none; }
}

.plans-note-emoji {
  position: absolute;
  inset-inline-start: 18px;
  inset-block-start: 50%;
  transform: translateY(-50%);
  font-size: 2em;
  line-height: 1;
}

.plans-note-lead {
  display: inline-block;
  font-size: 2em;
  line-height: 1.2;
}

.plans-note-copy {
  min-width: 0;
  text-align: center;
  padding-inline: 72px;
}

.plans-note-copy p {
  margin: 6px 0 0;
}

.plans-note { margin-block-end: 0; }

.price-note-card {
  max-width: 68ch;
  margin-block-start: 24px;
}

.price-note-card .plan-head {
  margin: 0;
}

/* ---- Download ---- */

.project-download {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 16px;
  width: fit-content;
  max-width: 100%;
  margin: 30px auto 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  overflow: visible;
}

.project-download .btn {
  background: linear-gradient(135deg, #f7df8d 0%, #d8aa2d 42%, #b77a00 100%);
  border: 1px solid rgba(255, 227, 151, 0.9);
  color: #fff;
  padding: 17px 32px;
  min-height: 58px;
  box-shadow: 0 12px 28px rgba(216, 170, 45, 0.34);
  overflow: hidden;
}

.project-download .btn:hover {
  box-shadow: 0 16px 36px rgba(216, 170, 45, 0.42);
}

.download-badge-icon {
  display: inline-block;
  font-size: 2em;
  line-height: 1;
  filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.35));
}

.download-button-label {
  font-size: 2em;
  line-height: 1.15;
}

.project-download .btn svg { width: 18px; height: 18px; color: #fff; }

.project-title-cta .btn {
  background: linear-gradient(135deg, #f7df8d 0%, #d8aa2d 42%, #b77a00 100%);
  border: 1px solid rgba(255, 227, 151, 0.9);
  color: #fff;
  padding: 17px 32px;
  min-height: 58px;
  box-shadow: 0 12px 28px rgba(216, 170, 45, 0.34);
  overflow: hidden;
}

.project-title-cta .btn:hover {
  box-shadow: 0 16px 36px rgba(216, 170, 45, 0.42);
}

.project-title-cta .btn svg {
  width: 18px; height: 18px;
  color: #fff;
}

.download-warning-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
}

.download-warning-modal[hidden] {
  display: none;
}

.download-warning-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(10, 14, 30, 0.62);
  backdrop-filter: blur(4px);
}

.download-warning-panel {
  position: relative;
  z-index: 1;
  width: min(680px, calc(100% - 32px));
  margin: 10vh auto 0;
  padding: 26px 26px 20px;
  border-radius: var(--radius-lg);
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(16, 29, 52, 0.98);
  box-shadow: 0 28px 80px rgba(0, 0, 0, 0.5);
  color: var(--text-primary);
}

.download-warning-panel h3 {
  margin: 0 0 18px;
  padding-inline-end: 64px;
  font-size: clamp(1.5rem, 4vw, 2.2rem);
  line-height: 1.2;
}

.download-warning-panel p {
  margin: 0 0 14px;
  color: var(--text-secondary);
  font-size: 1.08rem;
  line-height: 1.7;
}

.download-warning-panel strong {
  color: var(--text-primary);
}

.download-warning-image {
  margin: 18px 0 0;
  padding: 0;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: #0b4db4;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.04);
  max-width: 620px;
  margin-inline: auto;
}

.download-warning-image img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 220px;
  object-fit: contain;
  background: #0b4db4;
}

.download-warning-quote {
  margin-top: 18px;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  background: rgba(255, 209, 102, 0.08);
  border: 1px solid rgba(255, 209, 102, 0.26);
  color: #ffd166;
  font-weight: 800;
}

.download-warning-actions {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 18px;
}

.download-warning-close {
  position: absolute;
  top: 14px;
  left: 14px;
  width: 38px;
  height: 38px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  color: var(--text-primary);
  font-size: 1.8rem;
  line-height: 1;
  cursor: pointer;
}

.download-meta {
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

/* Its own line under the button, whatever the row wrapped like. */
.download-note {
  flex-basis: 100%;
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
}

/* ---- Empty state ---- */

.empty-state {
  text-align: center;
  padding: clamp(40px, 9vw, 80px) 20px;
  color: var(--text-muted);
}

.empty-state .emoji {
  font-size: 3rem;
  display: block;
  margin-block-end: 14px;
}

.empty-state h3 {
  color: var(--text-secondary);
  margin-block-end: 8px;
}

/* ==================== SUPPORT ==================== */

/* 220px keeps all four default options on one row inside the 1040px content
   box (4 x 220 + 3 x 18 = 934). Wider minimums drop to 3 and leave an orphan. */
.support-grid {
  display: grid;
  gap: 18px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  margin-block-start: 8px;
}

.support-card {
  display: flex;
  flex-direction: column;
  gap: 13px;
  text-align: center;
  align-items: center;
  padding-block: 30px;
  /* Cards keep the grid's default stretch, so every card in a row is the same
     height. `.s-note` below takes `flex: 1` and swallows the difference, which
     puts every card's button on the same line no matter how long each note
     runs. That holds only while the cards hold comparable content, which is why
     no card renders a QR inline any more: PayPal's codes live in its amount
     picker and Bit's behind its button, so one card with a code next to one
     without can no longer leave a visible void. */
  /* --accent comes from content.js per platform. */
  border-color: color-mix(in srgb, var(--accent, var(--accent-primary)) 26%, transparent);
}

.support-card:hover {
  border-color: color-mix(in srgb, var(--accent, var(--accent-primary)) 52%, transparent);
  box-shadow: 0 16px 44px color-mix(in srgb, var(--accent, var(--accent-primary)) 20%, transparent);
}

.support-icon {
  width: 52px;
  height: 52px;
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--accent, var(--accent-primary)) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--accent-primary)) 32%, transparent);
  transition: transform var(--dur-med) var(--ease-spring);
}

.support-card:hover .support-icon { transform: scale(1.1) rotate(-6deg); }

.support-icon svg { width: 27px; height: 27px; }

/* Official app-icon artwork brings its own brand colour and its own rounded
   corners (transparent outside them), so the tinted plate and border would only
   show as a halo behind it — drop both and let the artwork be the tile. */
.support-icon.is-logo {
  background: none;
  border: none;
}

.support-icon.is-logo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.support-card h3 { font-size: 1.3rem; }

.support-card .s-note {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 1.6;
  /* Absorbs the height difference between cards, so everything below the note
     stays aligned across the row. See the note on .support-card. */
  flex: 1;
}

.support-card .btn { width: 100%; }

/* For a `kind: 'handle'` option — something with no working web link at all, so
   we surface the number itself with a copy button rather than a dead link. No
   option uses it right now; Bit and PayBox both moved to real links. */
.handle-box {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 12px 16px;
  min-height: 46px;
  border-radius: var(--radius-md);
  background: var(--sheen);
  border: 1px dashed var(--border-strong);
  font-weight: 700;
  cursor: pointer;
  transition: background var(--dur-fast), border-color var(--dur-fast);
}

.handle-box:hover {
  background: var(--sheen-strong);
  border-color: var(--accent, var(--accent-primary));
}

.handle-box svg { width: 17px; height: 17px; opacity: 0.7; }

/* The QR art is pure black-on-white, so it needs its own light plate to stay
   scannable on the dark theme — tinting or inverting it breaks phone cameras. */
.qr-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin: 0;
  width: 100%;
}

/* Every QR under assets/img is generated on the same grid: 41 modules (a
   version-4 code's 33, plus a 4-module quiet zone each side) at 24px, i.e. a
   984px square. 164px here is those 41 modules at 4px, so the downscale is an
   exact 1:6 — and 2x/3x screens land on 1:3 and 1:2. Whole pixels per module is
   what keeps the code readable by a phone; an off-grid size smears the modules
   and scanners fail. Add a new QR at 984px/41 modules, and leave
   image-rendering at its default (smooth) — `pixelated` drops module rows when
   the browser scales down. */
.qr-box img {
  width: 164px;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-md);
  background: #fff;
  padding: 8px;
  border: 1px solid color-mix(in srgb, var(--accent, var(--accent-primary)) 32%, transparent);
}

.qr-box figcaption {
  color: var(--text-muted);
  font-size: var(--fs-xs);
  line-height: 1.5;
}

.support-note {
  text-align: center;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  margin-block-start: 30px;
  max-width: 56ch;
  margin-inline: auto;
}

/* ---------- the support amount picker ----------
   Opened from a support card whose option carries an `amounts` block. Same
   overlay language as the download warning above; the panel is narrower
   because all it holds is a row of amounts. `--accent` comes in from the
   option, so the picker wears the platform's colour like its card does. */

.amount-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  /* The QR under the button makes the panel tall enough to run off a short
     phone screen, so the overlay scrolls rather than clipping the button. */
  overflow-y: auto;
  overscroll-behavior: contain;
}

.amount-modal[hidden] { display: none; }

/* Fixed, not absolute: the overlay scrolls now, and an absolute backdrop would
   scroll away with it and expose the page underneath. */
.amount-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(10, 14, 30, 0.62);
  backdrop-filter: blur(4px);
}

.amount-panel {
  position: relative;
  z-index: 1;
  width: min(430px, calc(100% - 32px));
  margin: 12vh auto 24px;
  padding: 24px 24px 22px;
  border-radius: var(--radius-lg);
  border: 1px solid color-mix(in srgb, var(--accent) 34%, rgba(255, 255, 255, 0.16));
  background: rgba(16, 29, 52, 0.98);
  box-shadow: 0 28px 80px rgba(0, 0, 0, 0.5);
  color: var(--text-primary);
}

.amount-close {
  position: absolute;
  top: 12px;
  left: 12px;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  color: var(--text-primary);
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
}

.amount-head {
  display: flex;
  align-items: center;
  gap: 11px;
  padding-inline-end: 44px;
  margin-block-end: 8px;
}

.amount-logo {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  background: var(--sheen);
  color: var(--accent);
}

.amount-logo img { width: 34px; height: 34px; border-radius: 8px; }
.amount-logo svg { width: 24px; height: 24px; }

.amount-head h3 {
  margin: 0;
  font-size: 1.45rem;
  line-height: 1.25;
}

.amount-note {
  margin: 0 0 16px;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 1.6;
}

.amount-chips {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 9px;
}

/* "Some other amount" gets its own row - it is a different kind of choice,
   and side by side it would squeeze the numbers. */
.amount-chip.is-custom {
  grid-column: 1 / -1;
  font-size: var(--fs-sm);
}

.amount-chip {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 3px;
  padding: 13px 8px;
  border-radius: var(--radius-pill);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  background: var(--sheen);
  color: var(--text-primary);
  font-weight: 700;
  font-size: 1.25rem;
  cursor: pointer;
  transition: background var(--dur-fast), border-color var(--dur-fast), transform var(--dur-fast);
}

.amount-chip:hover { background: var(--sheen-strong); }
.amount-chip:active { transform: scale(0.97); }

/* Selection is tinted rather than filled: `--accent` is whatever the platform's
   brand colour happens to be, and light ones (BMC's yellow) would swallow any
   text laid on top of them. */
.amount-chip[aria-pressed="true"] {
  background: color-mix(in srgb, var(--accent) 24%, transparent);
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 6px 18px color-mix(in srgb, var(--accent) 22%, transparent);
  font-weight: 800;
}

.amount-symbol {
  font-size: 0.85em;
  color: var(--text-secondary);
}

.amount-custom { margin-block-start: 14px; }

.amount-custom label {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-secondary);
  margin-block-end: 7px;
}

.amount-input-wrap { position: relative; }

/* The value is LTR (dir on the input) and starts right after the currency,
   so the two read as one price the way they do on the chips - a right-aligned
   number would sit at the far end of the field from its ₪. `start` resolves
   against the input's own ltr direction, i.e. the side the ₪ is on. */
.amount-input-wrap input {
  width: 100%;
  padding: 13px 16px;
  padding-inline-start: 40px;
  min-height: 48px;
  border-radius: var(--radius-md);
  background: var(--sheen);
  border: 1px solid var(--border-subtle);
  color: var(--text-primary);
  font-size: 1.15rem;
  text-align: start;
  transition: border-color var(--dur-fast), background var(--dur-fast), box-shadow var(--dur-fast);
}

/* A donation amount is typed, not nudged one shekel at a time - and the
   spinner sits on the digits' side of an LTR field. */
.amount-input-wrap input {
  appearance: textfield;
  -moz-appearance: textfield;
}

.amount-input-wrap input::-webkit-outer-spin-button,
.amount-input-wrap input::-webkit-inner-spin-button {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
}

.amount-input-wrap input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--sheen-mid);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 26%, transparent);
}

/* The number itself is LTR (dir on the input), so the currency sits at the
   inline end of the field - the side the digits stop on in both directions. */
.amount-input-symbol {
  position: absolute;
  inset-inline-end: 14px;
  inset-block-start: 50%;
  transform: translateY(-50%);
  color: var(--text-secondary);
  pointer-events: none;
}

/* .field-error is invisible until its .field is marked invalid; here the
   message is the only signal, so it shows whenever there is text in it. */
.amount-custom .field-error {
  opacity: 1;
  transform: none;
}

.amount-go {
  width: 100%;
  margin-block-start: 18px;
}

.amount-go.is-blocked {
  opacity: 0.55;
  cursor: not-allowed;
}

/* `qr-box` already styles the image; this only gives it room. The explicit
   [hidden] rule is not redundant — `.qr-box` sets `display: flex`, and an
   author rule beats the browser's own `[hidden] { display: none }`. */
.amount-qr {
  margin-block-start: 16px;
}

.amount-qr[hidden] {
  display: none;
}

@media (max-width: 480px) {
  .amount-panel {
    margin-block-start: 8vh;
    padding: 20px 18px 18px;
  }
}

/* ==================== CONTACT ==================== */

.contact-layout {
  display: grid;
  gap: clamp(22px, 4vw, 38px);
  align-items: start;
}

@media (min-width: 861px) {
  .contact-layout {
    grid-template-columns: 1.15fr 0.85fr;
  }
  /* No contact details configured yet — don't leave an empty column. */
  .contact-layout.no-aside {
    grid-template-columns: minmax(0, 760px);
    justify-content: center;
  }
}

.contact-aside {
  display: grid;
  gap: 18px;
  position: sticky;
  inset-block-start: calc(var(--header-height) + 20px);
}

.wa-card {
  text-align: center;
  border-color: rgba(37, 211, 102, 0.28);
}

.wa-card h3 {
  margin-block-end: 8px;
  font-size: 1.3rem;
}

.wa-card p {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  margin-block-end: 18px;
}

/* One row per app group. A plain link — the target is a public join link, so
   unlike the old personal number there is nothing to keep out of the markup. */
.wa-groups {
  display: grid;
  gap: 9px;
  justify-items: start;
  list-style: none;
  margin: 0;
  padding: 0;
  text-align: start;
}

.wa-group {
  display: flex;
  align-items: center;
  /* Sized to its own name rather than stretched across the column, so a short
     group and a long one don't read as one wide block. */
  width: fit-content;
  max-width: 100%;
  gap: 11px;
  padding: 11px 13px;
  border-radius: var(--radius-md);
  border: 1px solid rgba(37, 211, 102, 0.28);
  background: rgba(37, 211, 102, 0.07);
  transition: background var(--dur-fast), border-color var(--dur-fast), transform var(--dur-fast);
}

.wa-group:hover,
.wa-group:focus-visible {
  background: rgba(37, 211, 102, 0.14);
  border-color: var(--brand-whatsapp);
  transform: translateY(-1px);
}

.wa-group svg {
  width: 21px; height: 21px;
  color: var(--brand-whatsapp);
  flex-shrink: 0;
}

.wa-group-text {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.wa-group-text strong {
  font-size: var(--fs-sm);
  color: var(--text-primary);
}

.wa-group-text small {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  line-height: 1.45;
}

/* An app whose groups were split by profession: the community link is the lead
   row, and the groups inside it are indented under it. The indent is on the
   inline-start side via a logical property, so it flips with the direction. */
.wa-community {
  display: grid;
  gap: 7px;
  /* Wraps only as much as its widest row, like the rows themselves. */
  justify-items: start;
  max-width: 100%;
  padding: 7px;
  border-radius: var(--radius-md);
  border: 1px solid rgba(37, 211, 102, 0.18);
  background: rgba(37, 211, 102, 0.04);
}

.wa-group-lead {
  background: rgba(37, 211, 102, 0.12);
}

.wa-subgroups {
  display: grid;
  gap: 7px;
  justify-items: start;
  list-style: none;
  margin: 0;
  padding: 0;
  padding-inline-start: 16px;
  position: relative;
}

/* The thread tying the groups to their community, drawn on the start edge. */
.wa-subgroups::before {
  content: '';
  position: absolute;
  inset-block: 4px;
  inset-inline-start: 6px;
  width: 1px;
  background: rgba(37, 211, 102, 0.28);
}

.wa-group-sub {
  padding: 8px 11px;
  background: transparent;
  border-color: rgba(37, 211, 102, 0.2);
}

.wa-group-sub svg {
  width: 17px; height: 17px;
}

/* The community rows carry a bilingual name ("שפכו ת׳לב - Spill It Out"), long
   enough to wrap in the aside column. Any break inside one is written into the
   content as a \n, so an automatic one would land somewhere else and read as a
   second title - hold the line once the column is wide enough for it. Below
   1024px the column gets too narrow to promise that, and wrapping is better
   than overflow. */
.wa-community .wa-group-text strong {
  font-size: var(--fs-xs);
}

@media (min-width: 1024px) {
  .wa-community .wa-group-text strong {
    white-space: nowrap;
  }
}

.direct-card {
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  display: grid;
  gap: 11px;
}

.direct-row {
  display: flex;
  align-items: center;
  gap: 10px;
  word-break: break-word;
}

.direct-row svg {
  width: 17px; height: 17px;
  color: var(--accent-primary);
  flex-shrink: 0;
}

.direct-row a { transition: color var(--dur-fast); }
.direct-row a:hover { color: var(--accent-primary); }

/* ---- Closing picture ---- */

/* Sits under the form and the aside, capped narrower than the container so it
 * reads as a sign-off rather than a banner. */
.contact-figure {
  margin: clamp(34px, 6vw, 56px) auto 0;
  max-width: 720px;
  text-align: center;
}

.contact-figure img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-subtle);
  box-shadow: var(--shadow-md);
}

.contact-figure figcaption {
  margin-block-start: 12px;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

/* Buttons standing in for links, where putting the target in an href would leak
   it into the markup. They must read as links, not chrome. */
.link-btn {
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: start;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--dur-fast);
}

.link-btn:hover,
.link-btn:focus-visible { color: var(--accent-primary); }

.response-note {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-block-start: 12px;
}

.pulse-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--brand-whatsapp);
  box-shadow: 0 0 0 0 rgba(37, 211, 102, 0.6);
  animation: pulse 2.4s infinite;
}

@keyframes pulse {
  70%  { box-shadow: 0 0 0 9px rgba(37, 211, 102, 0); }
  100% { box-shadow: 0 0 0 0 rgba(37, 211, 102, 0); }
}

/* Shown when web3formsKey is still the TODO placeholder. */
.setup-notice {
  padding: 15px 18px;
  border-radius: var(--radius-md);
  background: var(--notice-soft);
  border: 1px solid var(--notice-border);
  color: var(--notice-text);
  font-size: var(--fs-sm);
  line-height: 1.65;
  margin-block-end: 20px;
}

.setup-notice code {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 0.88em;
  padding: 1px 6px;
  border-radius: 5px;
  background: rgba(0, 0, 0, 0.28);
  direction: ltr;
  display: inline-block;
}

/* ==================== PROJECT DETAIL — MOBILE ====================
 * The project page's type scale was authored for a wide screen and none of it
 * was conditional, so a 390px phone got the desktop sizes verbatim.
 *
 * The fix is NOT to shrink everything uniformly. What makes the desktop page
 * read well is that its headings sit at 2em — double their surrounding copy —
 * and that ratio is what carries the eye down the page. Those `em` sizes are
 * already responsive (they track `--fs-body`, which drops on a phone), so the
 * headings are left alone here on purpose. Only sizes that were *absolute*
 * (the 6rem title) or that broke the 2× ratio get touched.
 * ================================================================ */
@media (max-width: 860px) {

  /* ---- Headline: the app name, as large as fits on one line ---- */

  /* Measured in the shipped face at weight 800: "שפכו ת'לב" — the wider of the
   * two headline lines — takes 463px per 100px of font-size. The container
   * leaves `100vw - 2 × clamp(18px, 5vw, 40px)`, so the largest size that still
   * sets on one line is about (100vw - 40px) / 4.63. The divisor is rounded up
   * to 4.75, which holds the headline at ~95% of the space it could fill: the
   * slack absorbs the `font-display: swap` fallback, whose metrics differ from
   * Tel Aviv Eclectic's for as long as the woff2 is still in flight.
   * Re-measure both lines if the title text or the font ever changes. */
  .project-detail .section-head h2 {
    font-size: min(calc((100vw - 44px) / 4.75), 6rem);
    line-height: 1.06;
    /* The headline's two lines are split by a <br>. nowrap keeps each of them
     * whole — the subtitle opts back out just below, since it is a sentence and
     * is supposed to wrap. */
    white-space: nowrap;
  }

  .project-detail .section-head h2 .title-sub {
    font-size: calc(var(--fs-body) * 1.15);
    white-space: normal;
    margin-block-start: 12px;
    line-height: 1.3;
  }

  .project-detail .section-head { margin-block-end: 26px; }

  /* ---- Headings the desktop scale left below 2× their copy ---- */
  .plans-title { font-size: 2em; }
  .launch-note h3 { font-size: 2em; }

  /* Inside a plan card the running text is `--fs-sm`, not `--fs-body`, so the
   * card's own headings double off that instead. */
  .plan-head h4 { font-size: calc(var(--fs-sm) * 2); }
  .plan-report-title { font-size: calc(var(--fs-sm) * 1.7); }

  /* ---- Calls to action ---- */
  .project-title-cta {
    max-width: 100%;
    margin-block-start: 18px;
  }

  .project-title-cta .btn,
  .project-download .btn {
    max-width: 100%;
    padding: 14px 22px;
    min-height: 54px;
    /* One long Hebrew label on a forced single line is what pushed these past
       the viewport; let it wrap rather than widen the button. */
    white-space: normal;
    text-align: center;
  }

  .download-button-label { font-size: 1.5em; line-height: 1.2; }
  .download-badge-icon { font-size: 1.6em; }
  .project-download { margin-block-start: 24px; }
  .plan-personal-card-link { font-size: calc(var(--fs-sm) * 1.25); padding: 13px 18px; }

  /* ---- Screenshot carousel ----
   * A wide desktop screenshot on a phone is already small, so the centre shot
   * takes more of the reel here and the neighbours peek less. --shot-ratio has
   * to be recomputed with it: 16 / (9 × 0.68) = 2.61.
   *
   * How much of a neighbour you see is (100% - --shot-w) / 2 of the reel and
   * nothing else - past that it is behind the centre shot. 68% leaves 62px on a
   * 390px phone, which still reads as a photo; 78% left a 43px sliver.
   *
   * The arrows move from beside the reel to on top of it: flanking it would eat
   * a fifth of the width at this size. They land on the peeking neighbour, so
   * they get a translucent pill to sit in. */
  .shot-gallery {
    --shot-w: 68%;
    --shot-ratio: 2.61;
    --shot-shift: 62%;
  }

  .shot-stage { padding-inline: 0; }

  .shot-nav {
    position: absolute;
    inset-block-start: 50%;
    translate: 0 -50%;
    width: 34px;
    height: 34px;
    background: rgba(33, 5, 53, 0.72);
    border-color: var(--border-strong);
  }

  /* Keyed off the data attributes, not `.is-next` - that class only means "draw
     the arrow rotated" and is on the PREVIOUS button (see .carousel-button). */
  .shot-nav[data-shot-prev] { inset-inline-start: 6px; }
  .shot-nav[data-shot-next] { inset-inline-end: 6px; }
  .shot-nav svg { width: 15px; height: 15px; }

  .shot-gallery-title { font-size: 1.9em; }
  .shot-gallery .shot-caption { font-size: var(--fs-xs); min-height: 4em; padding-inline: 12px; }

  /* The enlarged view on a phone.
     A 1.85:1 desktop screenshot on a 390px portrait screen is 211px tall, so
     "fill the screen" pulls two ways: scale it to the screen's HEIGHT and it is
     1562px wide, native resolution and beautifully sharp - but you see 24% of it
     and have to pan four screens to read one picture, starting on a fragment
     with no idea what you are looking at. So it is fitted whole instead, edge to
     edge, and detail comes from PINCH-ZOOM, which index.html leaves enabled (no
     `user-scalable=no`, no `maximum-scale`). Nothing is unreachable and the
     first thing you see is the whole shot. This is what a photo viewer does.

     The panel shrink-wraps the picture, so the × rides its corner. The bar
     cannot: over a 211px strip it would cover most of it, so on a phone it
     leaves the panel and pins to the bottom of the screen instead. */
  .shot-lightbox-bar {
    position: fixed;
    inset-inline: 0;
    inset-block-end: 0;
    padding: 30px 14px 18px;
  }

  .shot-lightbox-close { width: 40px; height: 40px; font-size: 1.5rem; }
  .shot-lightbox-caption { font-size: var(--fs-xs); }

  /* ---- Boxes: padding only, so the 2× heading ratio survives ---- */
  .project-summary { padding: 16px 15px; margin-block-end: 20px; }
  .body-carousel { padding: 14px; }
  .body-carousel-card { padding: 16px; }
  .plans-note { gap: 12px; padding: 15px; }

  /* The flip cards keep their desktop look — same faces, same flip — but the
   * 88px inline padding is desktop breathing room. On a phone it squeezes the
   * lead into a two-word column and re-wraps lines the content already broke by
   * hand ("מחיר קבוע / לתמיד"), so the copy runs the card's full width here. */
  .plans-trial-card,
  .plans-trial-flipper { min-height: 160px; }
  .plans-trial-face { padding: 20px 16px; }
  .plans-note-copy { padding-inline: 0; }

  /* Below ~400px even the full width is short of the longest lead line
   * ("עם כל היכולות"), which measures ~10.4px per 1px of font-size in the
   * shipped face; the divisor is rounded up for the swap-fallback's wider
   * metrics. Shrinking the lead keeps the hand-written line breaks intact
   * instead of letting the browser add its own. */
  .plans-note-lead { font-size: min(2em, (100vw - 68px) / 10.8); }
  .launch-note { padding: 24px 16px; }
  .personal-note { padding: 26px 16px 20px; }

  /* ---- Plan cards ---- */
  .plan-grid { gap: 14px; }
  .plan-card { gap: 14px; padding: 18px 16px; }

  /* Emoji tile, title and badge do not fit on one row at this width. Drop to
     two columns and let the badge fall under the title rather than squeezing
     the heading into a two-word column. */
  .plan-head {
    grid-template-columns: auto 1fr;
    gap: 8px 10px;
  }

  .plan-badge {
    grid-column: 2;
    justify-self: start;
    font-size: var(--fs-xs);
  }

  .plan-emoji { width: 50px; height: 50px; font-size: 1.7rem; }
  .plan-tagline { font-size: var(--fs-xs); }

  /* `nowrap` on the unit kept "לשנה ללא כל התחייבות" on one line beside a 3rem
     number — together wider than the card. */
  .plan-price { flex-wrap: wrap; }
  .plan-price-num { font-size: clamp(2rem, 8vw, 2.55rem); }
  .plan-price-unit { font-size: var(--fs-sm); white-space: normal; }

  .plan-list { gap: 10px; }
  .plan-list li { font-size: var(--fs-sm); line-height: 1.5; }
  .plan-bullet { font-size: 1.15rem; width: 1.3em; }

  .plan-report-samples { padding: 12px; }
  .plan-report-button { min-width: 0; flex: 1 1 130px; }

  /* ---- Last card: the "בקשה אישית" runway ---- */

  /* The glowing pill and the "✨ • ✨" runs flanking it are what the card is
   * for. They are absolutely positioned outside the pill on both sides, so they
   * only need to be scaled to the narrower screen — not dropped. */
  .personal-note .text-link { font-size: 1.35em; }

  .personal-note .text-link::before,
  .personal-note .text-link::after {
    font-size: 0.5em;
    letter-spacing: 0.16em;
  }

  .personal-note .text-link::before { margin-inline-end: 8px; }
  .personal-note .text-link::after { margin-inline-start: 8px; }

  /* ---- Project index cards ---- */
  .project-card h3 .title-sub { font-size: 0.6666667em; }
}
