/* ===============================================================
   Phone-frame layout. Same tokens, same fonts, same copy as the
   live site: only the structure differs.

   The idea taken from the reference: the page is a rounded CONTAINER
   with its own surface, sitting on a lighter backdrop, with the banner
   filling its top and the corners matching.
   =============================================================== */

/* Every token below is in use. Seven were deleted on 2026-09-26 after being
   declared and referenced nowhere on any page: --radius, --text-body, and five
   left behind when the channel buttons were rebuilt on 2026-09-24
   (--caramel-press, --surface-hi, --note-on-caramel, --note-on-surface,
   --shadow). Two more the same day when the video caption moved under the
   picture and its scrim went with it (--scrim-strong, --scrim-soft). A
   declared token nobody uses reads as part of the system and is not. */
:root {
  --bg: oklch(0.21 0.028 48);
  --bg-deep: oklch(0.16 0.022 46);
  --surface: oklch(0.28 0.034 50);
  --ink: oklch(0.95 0.018 78);
  --ink-muted: oklch(0.76 0.032 72);
  --caramel: oklch(0.79 0.135 68);
  --paprika: oklch(0.63 0.16 38);
  --on-caramel: oklch(0.2 0.04 50);

  --text-display: clamp(1.7rem, 5.5vw, 1.95rem);
  --text-title: 1.0625rem;
  --text-small: 0.875rem;

  --shadow-lift: oklch(0.16 0.02 46 / 0.7);
  /* The plate behind the play badge. --bg-deep with alpha, written as a token
     so it follows the palette if that changes. */
  --badge-plate: oklch(0.16 0.02 46 / 0.62);

  /* Channel buttons. Specified as sRGB hex by Or, so they are kept exactly as
     given rather than re-expressed in OKLCH, which would shift them slightly. */
  --btn-lead: #f0a94b;
  --btn-lead-hover: #ffba60;
  --btn-lead-ink: #24170f;
  --btn-quiet: #2a201a;
  --btn-quiet-hover: #36291f;
  --btn-quiet-ink: #fff4e8;
  --btn-quiet-edge: #65503e;

  /* Platform marks. The one place a colour from outside this palette appears,
     and only ever inside the circle. */
  --app-whatsapp: #25d366;
  --app-telegram: #26a5e4;
  --app-discord: #5865f2;
  --app-ink-dark: #15100d;
  --app-ink-light: #ffffff;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  /* Semantic, not magic numbers. Overlay = the play badge, above the picture
     inside the card; floating = the QR, above the page entirely. */
  --z-overlay: 2;
  --z-floating: 10;
}

@supports not (color: oklch(0 0 0)) {
  :root {
    --bg: #241610; --bg-deep: #19100b; --surface: #35231a;
    --ink: #f7eade; --ink-muted: #c2aa97; --caramel: #e8a75c;
    --paprika: #c4562f; --on-caramel: #2b1a11;
    --shadow-lift: rgba(25,16,11,.7);
    --badge-plate: rgba(25,16,11,.62);
  }
}

@font-face { font-family: "Rubik"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("assets/fonts/rubik-hebrew.woff2") format("woff2");
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F; }
@font-face { font-family: "Rubik"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("assets/fonts/rubik-hebrew.woff2") format("woff2");
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F; }
@font-face { font-family: "Rubik"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("assets/fonts/rubik-hebrew.woff2") format("woff2");
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F; }
@font-face { font-family: "Rubik"; font-style: normal; font-weight: 800; font-display: swap;
  src: url("assets/fonts/rubik-hebrew.woff2") format("woff2");
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F; }
@font-face { font-family: "Rubik"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("assets/fonts/rubik-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Rubik"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("assets/fonts/rubik-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Rubik"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("assets/fonts/rubik-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Rubik"; font-style: normal; font-weight: 800; font-display: swap;
  src: url("assets/fonts/rubik-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

*, *::before, *::after { box-sizing: border-box; }
html { color-scheme: dark; -webkit-text-size-adjust: 100%; }

/* The backdrop the phone sits on: cocoa, lighter than the phone itself,
   exactly the relationship in the reference. */
body {
  margin: 0;
  min-height: 100vh;
  min-height: 100svh;
  padding: clamp(0.5rem, 3vw, 2.5rem) clamp(0.5rem, 3vw, 1.5rem);
  background: var(--surface);
  color: var(--ink);
  font-family: Rubik, "Segoe UI", ui-sans-serif, system-ui, "Noto Sans Hebrew", sans-serif;
  font-size: clamp(1rem, 1vw + 0.84375rem, 1.125rem);
  line-height: 1.6;
  letter-spacing: 0.01em;
  font-feature-settings: "kern" 1;
  -webkit-font-smoothing: antialiased;
}

/* ---------------- the phone ---------------- */

.phone {
  width: 100%;
  /* 36rem = 576px, measured against linktr.ee, whose card renders 579px wide
     at a 1280px viewport. The previous 26rem was 39% narrower and read as
     cramped next to it. */
  max-width: 36rem;
  margin: 0 auto;
  border-radius: 30px;
  overflow: hidden;
  background: radial-gradient(120% 70% at 50% 0%, var(--bg) 0%, var(--bg) 45%, var(--bg-deep) 100%);
  box-shadow: 0 30px 70px var(--shadow-lift);
  padding-bottom: 1.5rem;
}

.ph-head { position: relative; text-align: center; }

/* The language switch (Or's pick A, 2026-10-06): on the banner's top corner,
   the end side, so it is the first thing in view and away from the play
   badge's corner on the video below. 44px tall for a thumb. A plate behind
   it, since the photo under it can be light or dark. */
.lang-pill {
  position: absolute;
  top: 12px;
  inset-inline-end: 12px;
  z-index: var(--z-overlay);
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 16px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  box-shadow: inset 0 0 0 1px var(--caramel);
  color: var(--caramel);
  font-size: var(--text-small);
  font-weight: 700;
  text-decoration: none;
}
.lang-pill:active { transform: translateY(1px); }

/* English page: one line saying the channels are in Hebrew. */
.lang-note {
  margin: 0 0 1rem;
  color: var(--ink-muted);
  font-size: var(--text-small);
  text-align: center;
}

/* Banner fills the top; the container's overflow clips it to the radius.
   The ratio must sit on the CONTAINER, not the img: on the img alone it does
   not constrain and the photo renders at roughly double the height. */
.ph-banner { position: relative; aspect-ratio: 1200 / 800; }
.ph-banner img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ph-banner::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, transparent 55%, var(--bg) 100%);
}

.ph-head h1 {
  margin: -0.4rem 0 0;
  font-size: var(--text-display);
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.02em;
  position: relative;
}

/* The caramel line under the name. It carries Hebrew now, not a Latin handle,
   so the direction override that @ordromi needed is gone: forcing LTR here
   would lay the sentence out backwards. */
.tagline {
  margin: 0.45rem auto 0;
  max-width: 22rem;
  padding: 0 1.1rem;
  color: var(--caramel);
  font-size: var(--text-small);
  font-weight: 500;
  line-height: 1.55;
  text-wrap: balance;
}


/* ---------------- body of the phone ---------------- */

/* Padding grows with the wider card so the buttons do not run edge to edge. */
.ph-body { padding: 1.35rem 1.35rem 0; }

/* lead video card, overlay style */
.vcard {
  position: relative;
  display: block;
  border-radius: 16px;
  overflow: hidden;
  text-decoration: none;
  color: var(--ink);
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}

/* A 4:5 picture with the caption UNDER it, since 2026-09-26. The old 16:10
   card showed a 450px band of a vertical frame and laid the caption over its
   bottom third, so Or's face and the food could never both be seen. Taller
   picture, nothing on top of it.

   height:auto is load-bearing. The HTML width/height attributes become CSS
   presentational hints, and an explicit height from them beats aspect-ratio:
   both the banner and this card once rendered at double height that way.
   4/5 must match CARD_RATIO in tools/build-links.mjs. */
.vcard { background: var(--bg-deep); }

/* Capped on wide screens. At full card width a 4:5 picture is 533x666 on a
   desktop — a portrait taller than the screen, upscaled from a 720px frame
   and visibly soft. 22rem is wider than any phone's card (323px at 390), so
   phones are untouched. */
.vcard { max-width: 22rem; margin-inline: auto; }
.vcard img { width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; display: block; }

/* A badge in the corner, not a glyph in the middle. Centred, it lands on
   whatever the frame is about — with a person in shot that means directly on
   their face. The corner says "this plays" just as clearly and leaves the
   picture alone. See The Badge Stays Out Of The Frame Rule in DESIGN.md.

   inset-inline-start puts it top-right on this RTL page, matching the corner
   a Hebrew reader starts from. */
.vcard-play {
  position: absolute;
  top: 10px;
  inset-inline-start: 10px;
  width: 34px; height: 34px;
  padding: 7px;
  box-sizing: border-box;
  border-radius: 999px;
  z-index: var(--z-overlay);
  fill: var(--ink);
  /* A plate behind it, because a frame can be light or dark where the badge
     sits and a bare white glyph disappears on a pale one. */
  background: var(--badge-plate);
}

.vcard-cap { display: block; padding: 0.75rem 1rem 0.85rem; text-align: center; }
/* Captions are real TikTok captions, so they run long. Two lines, then
   ellipsis: three would cover too much of the frame behind them.

   width:100% is load-bearing. A -webkit-box shrink-wraps to its content
   rather than filling the caption, so without it the text overflows the
   card and the first word is clipped by the card's overflow:hidden. */
.vcard-title {
  display: -webkit-box;
  width: 100%;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  font-weight: 700;
  font-size: var(--text-title);
  line-height: 1.4;
}
/* --text-small, not 0.75rem. The Fourteen Pixel Floor in DESIGN.md is not
   advisory: this page is read one-handed, at arm's length, at night. */
.vcard-src { display: block; margin-top: 0.1rem; color: var(--ink-muted); font-size: var(--text-small); }


/* ---------------- social row ---------------- */

/* TikTok and Instagram as marks rather than full-width buttons, BELOW the
   channels and quiet. Most visitors arrive FROM TikTok, so the TikTok link is
   the one they need least. Until 2026-09-26 the pair sat above the channels as
   two caramel circles, the same colour as the WhatsApp button, and pulled the
   eye before it; now they are the page's sign-off, dark like the secondary
   buttons with the caramel in the mark only. The brands are recognisable
   enough to survive without a label — which is exactly why this treatment
   does not work for the channels, whose whole problem is that people do not
   know they exist. */
.social {
  list-style: none;
  margin: 1.6rem 0 0;
  padding: 0;
  display: flex;
  justify-content: center;
  gap: 0.9rem;
}

.social a {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 999px;
  background: var(--btn-quiet);
  border: 1px solid var(--btn-quiet-edge);
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease), background-color 160ms var(--ease);
}

/* 52px clears the 44px minimum tap target with room to spare; the icon inside
   stays 24px so the circle reads as a button, not a sticker. Caramel on the
   quiet fill measures 7.66:1, and 6.77:1 on hover. */
.social svg { width: 24px; height: 24px; fill: var(--caramel); }

/* Hover and press lighten to the secondary buttons' hover step, the same
   colour feedback the channel buttons give. Colour, not only movement: a
   thumb covers a scale, and reduced motion removes it. */
@media (hover: hover) and (pointer: fine) {
  .social a:hover { transform: translateY(-3px); box-shadow: 0 10px 24px var(--shadow-lift); background: var(--btn-quiet-hover); }
}

.social a:active { transform: scale(0.96); background: var(--btn-quiet-hover); }

@media (prefers-reduced-motion: reduce) {
  .social a { transition: background-color 160ms ease; }
  .social a:hover, .social a:active { transform: none; }
}

/* ---------------- link cards ---------------- */

/* The button background carries priority; the coloured circle identifies the
   platform. WhatsApp is deliberately the loudest — a design decision, not a
   conclusion from click data, which does not exist yet. */

.links { list-style: none; margin: 0.9rem 0 0; padding: 0; display: grid; gap: 12px; }

.links a {
  display: flex; align-items: center; gap: 0.85rem;
  min-height: 82px; padding: 15px 14px;
  border-radius: 16px;
  text-decoration: none;
  text-align: start;
  -webkit-tap-highlight-color: transparent;
  /* 160ms, and only the two properties that actually change. */
  transition: transform 160ms var(--ease), background-color 160ms var(--ease);
}

/* Text sits against the reading edge rather than centred, so the eye returns
   to one line down the stack instead of hunting the middle of each button. */
.title { display: block; font-weight: 700; font-size: var(--text-title); line-height: 1.3; text-align: start; }
.note { display: block; font-weight: 400; font-size: var(--text-small); line-height: 1.4; text-align: start; color: inherit; }

.primary { background: var(--btn-lead); color: var(--btn-lead-ink); }
/* The 1px border is what separates these from the page: the fill is within
   1.1:1 of the backdrop, so the boundary is doing the work on its own. */
.secondary { background: var(--btn-quiet); color: var(--btn-quiet-ink); border: 1px solid var(--btn-quiet-edge); }

/* ---- the platform circle ---------------------------------------------
   38px disc, 21px mark. The circle is the only place a platform colour is
   allowed: it brings its own background, so the mark stays legible where the
   raw brand colour on caramel would not be — WhatsApp green measures 1.01:1
   against the lead button. */
.links a > svg:first-of-type {
  box-sizing: content-box;
  width: 21px; height: 21px; flex: 0 0 21px;
  padding: 8.5px;
  border-radius: 999px;
  display: block;
}

.links a[data-brand="whatsapp"] > svg:first-of-type { background: var(--app-whatsapp); fill: var(--app-ink-dark); }
.links a[data-brand="telegram"] > svg:first-of-type { background: var(--app-telegram); fill: var(--app-ink-dark); }
.links a[data-brand="discord"] > svg:first-of-type { background: var(--app-discord); fill: var(--app-ink-light); }
/* The calculators have no brand colour: the page's own accent, with the same
   dark ink the caramel WhatsApp button already uses. */
.links a[data-brand="tools"] > svg:first-of-type { background: var(--caramel); fill: var(--app-ink-dark); }

/* (pointer: fine) as well as (hover: hover): some touch devices report hover
   support and then fire it on tap, leaving the lift stuck until the next tap.

   .vcard:hover lives in this block rather than one of its own, and the
   placement matters: the reduced-motion override below cancels it with an
   equal-specificity selector and wins only because it comes later in the
   file. A hover block appended after it would silently bring the lift back
   for people who asked for no motion. It used to sit at top level, ungated,
   against this page's own hover rule. */
@media (hover: hover) and (pointer: fine) {
  .links a:hover { transform: translateY(-2px); }
  .primary:hover { background: var(--btn-lead-hover); }
  .secondary:hover { background: var(--btn-quiet-hover); }
  .vcard:hover { transform: translateY(-3px); box-shadow: 0 14px 34px var(--shadow-lift); }
}

/* Pressed sits 1px BELOW rest, so the press reads as the button going down
   rather than the hover lift simply cancelling. */
.links a:active { transform: translateY(1px); }

/* Press feedback for the largest tap target on the page, matching the
   buttons. Ungated, because :active is touch feedback and has to work on
   phones.

   Two orderings pin it here. It must come AFTER the hover gate above: while
   pressed, :hover and :active both match with equal specificity, so whichever
   is later wins — placed earlier, a desktop press showed the -3px hover lift
   instead of the press. And it must come BEFORE the reduced-motion override
   below, which cancels it the same way. Caught by the Playwright probe on
   2026-09-26, not by reading. */
.vcard:active { transform: translateY(1px); }
.primary:active { background: var(--btn-lead-hover); }
.secondary:active { background: var(--btn-quiet-hover); }

.links .arrow {
  margin-inline-start: auto; flex: 0 0 18px; width: 18px; height: 18px;
  fill: none; stroke: currentColor; stroke-width: 2.5;
  stroke-linecap: round; stroke-linejoin: round;
}
/* The chevron is drawn pointing left, the way an RTL page reads; on the
   English page it points the way that page reads. */
[dir="ltr"] .links .arrow { transform: scaleX(-1); }

.links span:not(.title):not(.note) { flex: 1; min-width: 0; }

/* The heading for the page's actual ask, so it reads as one: cream, Title
   size, bold. It was Small and muted until 2026-09-26 — the quietest text on
   the page sitting on top of the most important thing on it. */
.group-label {
  margin: 1.5rem 0 0;
  color: var(--ink);
  font-size: var(--text-title);
  font-weight: 700;
  line-height: 1.3;
  text-align: center;
}

footer {
  margin-top: 1.6rem;
  color: var(--ink-muted);
  font-size: var(--text-small);
  text-align: center;
}

/* The privacy link. Present because the page sets analytics cookies, and
   deliberately the quietest thing on it — underlined only on hover, so it
   reads as a footnote rather than competing with five buttons. Colour is
   --ink-muted, already measured against this background for the footer. */
/* No margin-top: it used to separate this from the footer's text line, which
   was removed on 2026-09-26. The link is now the footer's only child, and
   footer's own 1.6rem already sets the gap. */
.fine-print {
  display: block;
  color: var(--ink-muted);
  font-size: var(--text-small);
  text-decoration: none;
}

@media (hover: hover) and (pointer: fine) {
  .fine-print:hover { text-decoration: underline; text-underline-offset: 3px; }
}

:focus-visible { outline: 3px solid var(--paprika); outline-offset: 3px; }

/* Reduced motion keeps the colour change — it is feedback, not decoration —
   and drops the movement entirely, including the 1px press. */
@media (prefers-reduced-motion: reduce) {
  .links a, .vcard { transition: background-color 160ms ease; }
  .links a:hover, .links a:active, .vcard:hover, .vcard:active { transform: none; }
}

/* ---------------------------------------------------------------
   Motion

   Restored from the pre-redesign page. Content is visible by default
   and the entrance is additive, so a reduced-motion user or a headless
   renderer still gets a complete page.
   --------------------------------------------------------------- */

/* One entrance, top to bottom in the order the eye reads the page, with each
   reveal fitted to what it reveals rather than one move repeated eight times:

     fade  — the banner, the group label, the footer. A photo and two quiet
             lines have nowhere to arrive from; moving them only adds noise.
     rise  — the name, the tagline, the video card, the channel buttons. The
             things you read or press come up into place.
     pop   — the two social circles. Marks, so they settle from slightly small,
             with no overshoot.

   The selectors are class-scoped :nth-child, never :nth-of-type. :nth-of-type
   counts element TYPE and ignores the class: inside <nav> the first <ul> is
   .social, so .links:nth-of-type(1) matched nothing and until 2026-09-26
   Discord, the third <li>, arrived first while the icon row never animated. */
@media (prefers-reduced-motion: no-preference) {
  .ph-banner   { animation: fade 0.6s var(--ease) backwards; }
  .ph-head h1  { animation: rise 0.6s var(--ease) 0.06s backwards; }
  .tagline     { animation: rise 0.6s var(--ease) 0.12s backwards; }
  .vcard       { animation: rise 0.6s var(--ease) 0.18s backwards; }

  .group-label { animation: fade 0.5s var(--ease) 0.26s backwards; }

  .links li { animation: rise 0.6s var(--ease) backwards; }
  .links li:nth-child(1) { animation-delay: 0.32s; }
  .links li:nth-child(2) { animation-delay: 0.38s; }
  .links li:nth-child(3) { animation-delay: 0.44s; }

  .social li { animation: pop 0.45s var(--ease) backwards; }
  .social li:nth-child(1) { animation-delay: 0.52s; }
  .social li:nth-child(2) { animation-delay: 0.57s; }

  footer { animation: fade 0.5s var(--ease) 0.64s backwards; }
}

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

@keyframes fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes pop {
  from {
    opacity: 0;
    transform: scale(0.85);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ---------------------------------------------------------------
   QR code

   The one element that inverts the palette: scanners need dark modules
   on a light field, and an inverted code fails on older readers.

   Fixed, so it can never change the page height. Two conditions, not
   one: zooming in shrinks the effective viewport, and a fixed overlay
   on a short screen sits on top of the buttons.
   --------------------------------------------------------------- */

.qr {
  display: none;
}

/* 1024px, not 768px. The card is 36rem (576px) and the QR block needs about
   194px of clearance each side, so the viewport must reach ~996px before there
   is room. At 768px the QR sat on top of the video card and the TikTok button.
   Measured from a screenshot, not guessed. */
@media (min-width: 1024px) and (min-height: 560px) {
  .qr {
    display: block;
    position: fixed;
    bottom: 1.5rem;
    /* Physical `right`, deliberately: the logical property resolves to the
       left on an RTL page, and bottom-right is what was asked for. */
    right: 1.5rem;
    z-index: var(--z-floating);
  }

  .qr a {
    display: block;
    padding: 0.7rem;
    border-radius: 16px;
    background: var(--ink);
    box-shadow: 0 10px 30px var(--shadow-lift);
    text-align: center;
    text-decoration: none;
    transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
  }

  .qr img {
    display: block;
    width: 148px;
    height: 148px;
  }

  .qr span {
    display: block;
    margin-top: 0.4rem;
    color: var(--on-caramel);
    font-size: var(--text-small);
    font-weight: 500;
    line-height: 1.2;
  }

  /* Focus lifts at any pointer: it is keyboard feedback, not hover. */
  .qr a:focus-visible {
    transform: translateY(-3px);
    box-shadow: 0 16px 38px var(--shadow-lift);
  }
}

/* Hover is gated like every other hover on the page. The width gate above is
   not enough: a touchscreen laptop passes it, and a tap would leave the lift
   stuck until the next tap elsewhere. */
@media (min-width: 1024px) and (min-height: 560px) and (hover: hover) and (pointer: fine) {
  .qr a:hover {
    transform: translateY(-3px);
    box-shadow: 0 16px 38px var(--shadow-lift);
  }
}

@media (prefers-reduced-motion: reduce) {
  .qr a { transition: none; }
  .qr a:hover, .qr a:focus-visible { transform: none; }
}