/* ══════════════════════════════════════════════════════════════════════════
   Tupperware Treasures — Jasmine's reseller page.

   The design document carried the page twice: option 2a drawn at 390px and
   option 1b drawn at 1180px, same copy in both. This is one page that is both.
   The base rules below are 2a — the phone is where a link-in-bio is opened —
   and a single breakpoint at 900px switches to 1b. Nothing sniffs the browser;
   the two layouts are one DOM under two sets of rules.

   900px is the width at which 1b's structure genuinely fits: a four-up product
   grid, three review panels side by side, and a two-column split for the about
   and contact blocks. Below it, 2a's single column and snap-scrolling rails are
   the honest layout, not a squeezed desktop.

   Only the Nocturne tokens this page actually paints with are copied here; the
   rest of that design system, and its editor runtime, do not ship. Nocturne
   sets Inter as its heading and body family; this page keeps Noto Sans SC for
   both instead, because one family covering 华文, English and Melayu is worth
   more here than a second font request on a mobile connection.

   THE REVISION: the flat pink ground now carries a lit surface. A product
   pattern bleeds through the hero and the stats band under magenta washes, the
   tiles sit on white cards with real drop shadows, the two platform buttons and
   the two WhatsApp numbers are filled in their own brands' colours, and the
   page introduces itself once on load with a short staggered entrance. Every
   animation is off under prefers-reduced-motion — see the end of the file.
   ══════════════════════════════════════════════════════════════════════════ */

:root {
  --color-bg: #d8058e;
  --color-surface: #b80478;
  --color-text: #ffffff;
  --color-accent: #ffe0f0;
  --color-divider: rgba(255, 255, 255, 0.30);

  --color-accent-100: #ffffff;
  --color-accent-200: #ffeaf5;

  --color-section: #9b0264;
  --color-section-deep: #8a0159;
  --color-section-glow: #c30480;

  --ink-90: rgba(255, 255, 255, 0.90);
  --ink-82: rgba(255, 255, 255, 0.82);
  --ink-70: rgba(255, 255, 255, 0.70);
  --ink-66: rgba(255, 255, 255, 0.66);
  --ink-60: rgba(255, 255, 255, 0.60);

  --tiktok: #000000;
  --shopee: #ee4d2d;
  --whatsapp: #25d366;

  --radius-md: 8px;
  --radius-tile: 10px;
  --radius-card: 12px;

  /* Every white tile on the page — products, reviews — is lifted by the same
     pair: a 1px contact shadow so the edge reads, and a wide soft cast in the
     page's own plum rather than in grey. */
  --shadow-tile: 0 1px 2px rgba(70, 0, 38, 0.20), 0 10px 26px -8px rgba(70, 0, 38, 0.42);
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { background: var(--color-bg); }
body {
  max-width: 1180px;
  margin: 0 auto;
  color: var(--color-text);
  font-family: 'Noto Sans SC', system-ui, -apple-system, sans-serif;
  font-size: 15px;
  line-height: 1.55;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;

  /* Two soft lights on the flat brand pink: a white one off the top-left and a
     deep plum one settling into the bottom-right. They span the document, so
     the page darkens as it is read down. */
  background-color: var(--color-bg);
  background-image:
    radial-gradient(120% 80% at 18% -10%, rgba(255, 255, 255, 0.20), rgba(255, 255, 255, 0) 55%),
    radial-gradient(90% 70% at 100% 108%, rgba(80, 0, 44, 0.42), rgba(80, 0, 44, 0) 60%);
}
h1, h2 { font-weight: 500; line-height: 1.12; letter-spacing: -0.015em; }
a { color: var(--color-accent); text-decoration: none; text-underline-offset: 3px; }
img { display: block; max-width: 100%; }
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent-100); outline-offset: 2px; }
::selection { background: rgba(255, 255, 255, 0.25); }

/* ── Motion ──
   One entrance, played once, top to bottom: the nav fades, the headline pops,
   then the lead, the buttons and the stats band rise in behind it. After that
   only the two platform buttons keep moving — they bob, each trailing a ring
   in its own brand colour, and they start at 1.2s and 1.55s, which is after
   the entrance has finished rather than on top of it.

   The design drew the two artboards with delays 0.02–0.04s apart. That gap is
   below the threshold anyone could see, so one set of timings serves both. */
@keyframes riseIn { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: none; } }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes popIn {
  0%   { opacity: 0; transform: translateY(30px) scale(0.94); }
  60%  { opacity: 1; transform: translateY(-5px) scale(1.02); }
  100% { opacity: 1; transform: none; }
}
@keyframes bob { 0%, 100% { transform: none; } 50% { transform: translateY(-5px); } }
/* The ring rides on .pill::after so that pausing the bob on hover leaves it
   pulsing, and so the button's resting shadow can be a plain declaration
   instead of being re-stated in all three keyframes. */
@keyframes ringPulse {
  0%   { box-shadow: 0 0 0 0 var(--ring-on); }
  70%  { box-shadow: 0 0 0 14px var(--ring-off); }
  100% { box-shadow: 0 0 0 0 var(--ring-off); }
}

/* ── Nav ─────────────────────────────────────────────────────────────────── */
.nav {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 18px 20px;
  animation: fadeIn 0.6s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.nav-mark { height: 20px; width: auto; }
.nav-links { display: none; }

/* Language pill. Hidden until tt.js marks the document scripted: with JS off
   the buttons do nothing, and a row of dead buttons is worse than no row. */
.seg {
  display: none;
  background: #ffffff;
  border-radius: 999px;
  padding: 2px;
  box-shadow: 0 1px 2px rgba(60, 0, 32, 0.22), 0 8px 20px -8px rgba(60, 0, 32, 0.5);
}
html.js .seg { display: inline-flex; }
.seg-btn {
  padding: 6px 11px;
  font-family: inherit; font-size: 12px; line-height: 1.2; letter-spacing: 0.02em;
  color: var(--color-bg);
  background: transparent; border: 0; border-radius: 999px;
  opacity: 0.55; cursor: pointer;
}
.seg-btn[aria-pressed='true'] {
  background: color-mix(in srgb, var(--color-bg) 12%, transparent);
  font-weight: 600; opacity: 1;
}
.seg-btn:focus-visible { outline-color: var(--color-bg); outline-offset: 0; }
.lg-long { display: none; }

/* ── Hero ──
   The pattern is laid down first and a magenta wash goes over it, thinning from
   opaque at the left edge to almost clear at the right — so the type sits on
   flat brand pink and the pattern only surfaces past the end of the lines. That
   ramp is also what lets the hero stay one column: the wide design gave it a
   second column holding a portrait / live-stream still, no such photo came with
   the archive, and the pattern now fills that half rather than nothing.

   To put the portrait back: restore `grid-template-columns: minmax(0,1.15fr)
   minmax(0,.85fr)` on .hero inside the 900px query and add the image as .hero's
   last child. The pseudo-elements below need no change — they already span the
   whole block. */
.hero { position: relative; overflow: hidden; padding: 20px 20px 32px; }
.hero::before, .hero::after { content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.hero::before {
  background-image: url('/works/tupperware/assets/pattern-products.png');
  background-size: cover; background-position: right 28%;
  opacity: 0.95;
}
.hero::after {
  background: linear-gradient(95deg,
    #d8058e 0%, rgba(216, 5, 142, 0.97) 34%, rgba(216, 5, 142, 0.72) 52%,
    rgba(216, 5, 142, 0.34) 72%, rgba(216, 5, 142, 0.18) 100%);
}
.hero > * { position: relative; z-index: 1; }

/* Only the reviews block keeps its eyebrow on a phone — the other four headings
   carry themselves, and four more lines of small caps in a single column is
   four more things to read past. All five come back on the wide layout. */
.eyebrow {
  display: none;
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--color-accent);
}
.eyebrow-always { display: block; margin-bottom: 8px; }

.hero h1 { font-size: 34px; line-height: 1.1; letter-spacing: -0.02em; margin-bottom: 14px; text-wrap: balance; }
.hero-lead {
  font-size: 17px; font-weight: 600; line-height: 1.55;
  color: #ffffff; margin-bottom: 22px; text-wrap: pretty;
  /* The lead is the one paragraph that runs over the pattern rather than beside
     it, so it carries a glow of the ground colour to keep its edges clean. */
  text-shadow: 0 1px 12px rgba(90, 0, 50, 0.45);
}
.hero h1  { animation: popIn  0.85s cubic-bezier(0.22, 1, 0.36, 1) 0.14s both; }
.hero-lead { animation: riseIn 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.34s both; }
.hero-btns { animation: riseIn 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.5s both; }

.hero-btns { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; }
.pill {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 52px; padding: 14px 18px;
  border-radius: 999px;
  color: #ffffff; font-size: 16px; font-weight: 600; letter-spacing: 0.06em;
  animation: bob 2.4s ease-in-out 1.2s infinite;
  transition: transform 0.18s ease;
}
.pill::after {
  content: ''; position: absolute; inset: 0;
  border-radius: inherit; pointer-events: none;
  animation: ringPulse 2.4s ease-out 1.2s infinite;
}
/* Hover takes the bob off rather than pausing it: a paused animation still
   holds its own transform, which would win over the lift. */
.pill:hover { animation-name: none; transform: translateY(-6px) scale(1.03); }
.pill-tiktok {
  background: var(--tiktok);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.28), 0 12px 26px -10px rgba(0, 0, 0, 0.55);
  --ring-on: rgba(0, 0, 0, 0.45); --ring-off: rgba(0, 0, 0, 0);
}
.pill-shopee {
  background: var(--shopee);
  box-shadow: 0 2px 4px rgba(120, 30, 0, 0.30), 0 12px 26px -10px rgba(150, 45, 10, 0.6);
  --ring-on: rgba(238, 77, 45, 0.65); --ring-off: rgba(238, 77, 45, 0);
}
.pill-shopee, .pill-shopee::after { animation-delay: 1.55s; }

/* ── Stats band ──
   A deeper magenta field so the four figures read as a band rather than as more
   page. The pattern goes over it in soft-light at low opacity — texture, not
   picture — and `isolation` keeps that blend inside the band instead of letting
   it reach for the hero above. */
.stats {
  position: relative; isolation: isolate; overflow: hidden;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 16px;
  padding: 26px 20px;
  background: linear-gradient(140deg, var(--color-section-deep), var(--color-section) 42%, var(--color-section-glow));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22), inset 0 -1px 0 rgba(60, 0, 32, 0.35);
  animation: riseIn 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.64s both;
}
.stats::before, .stats::after { content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.stats::before {
  background-image: url('/works/tupperware/assets/pattern-products.png');
  background-size: cover; background-position: center 30%;
  opacity: 0.22; mix-blend-mode: soft-light;
}
.stats::after {
  background: linear-gradient(100deg, rgba(138, 1, 89, 0.72), rgba(155, 2, 100, 0.42) 55%, rgba(195, 4, 128, 0.28));
}
.stat { position: relative; z-index: 1; }
.stat-n {
  font-size: 34px; line-height: 1.1; letter-spacing: -0.02em; margin-bottom: 4px;
  color: var(--color-accent-100);
  /* A hairline of light on top and a cast below: the figures are pressed into
     the band rather than printed on it. */
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3), 0 6px 16px rgba(50, 0, 28, 0.4);
}
.stat-l { font-size: 11px; color: var(--color-accent-200); }

/* ── Sections ────────────────────────────────────────────────────────────── */
.sec { padding: 0 20px 32px; }
/* The section right under the stats band pays for its own top gap. */
#sec-about { padding-top: 32px; }
.sec h2 { font-size: 22px; margin-bottom: 16px; }
.split-head h2 { margin-bottom: 14px; }
.about { font-size: 16px; line-height: 1.65; text-wrap: pretty; }
/* Wide only: on a phone the heading and the screenshots say this already. */
.note { display: none; }
.hr { display: none; }

/* Why buy from me. Rows on a phone, cards on the wide layout — and the 01/02/03
   only earns its place once they are cards, so it is off below the breakpoint. */
.reasons { list-style: none; }
.reason { display: flex; gap: 14px; padding: 14px 0; border-top: 1px solid var(--color-divider); }
.reason:last-child { border-bottom: 1px solid var(--color-divider); }
.reason-n { display: none; }
.reason-txt { flex: 1; min-width: 0; }
.reason-title { display: block; font-size: 16px; margin-bottom: 4px; }
.reason-body { display: block; font-size: 13px; line-height: 1.6; color: var(--ink-82); text-wrap: pretty; }

/* ── Rails ──
   Narrow: a snap-scrolling track that bleeds to both screen edges, so a part
   tile is always showing past the right edge — which is the affordance, and is
   why no "swipe for more" line is printed under it.
   Wide: a plain grid — see the 900px query. */
.rail {
  display: flex; gap: 12px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* Without this, mandatory snapping aligns the first tile to the scrollport
     edge and eats the 20px lead-in, leaving the rail out of line with the
     headings above it. */
  scroll-padding-left: 20px;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
  /* The cast shadows under the tiles need room below the track or the overflow
     clips them off. */
  margin: 0 -20px; padding: 0 20px 18px;
}
.rail::-webkit-scrollbar { display: none; }
.rail > * { scroll-snap-align: start; flex: none; }

.prod { width: 172px; color: var(--color-text); }
.prod-img, .prod-soon {
  display: block; height: 172px; margin-bottom: 8px;
  border-radius: var(--radius-tile); overflow: hidden;
}
/* The padding is inside the tile's height (box-sizing is border-box above), so
   the image is inset from the white edge rather than bleeding to it. */
.prod-img { background: #ffffff; padding: 8px; box-shadow: var(--shadow-tile); }
.prod-img img { width: 100%; height: 100%; object-fit: contain; }
.prod-soon {
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.28);
  background: rgba(90, 0, 50, 0.16);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16);
  font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-70);
}
.prod-name { display: block; font-size: 14px; line-height: 1.4; }
.prod-name-soon { color: var(--ink-82); }

.rev {
  width: 196px;
  border-radius: var(--radius-tile); overflow: hidden;
  background: #ffffff; box-shadow: var(--shadow-tile);
}
.rev img { width: 100%; height: auto; }

/* ── Contact ──
   Narrow: two tap-sized pills, filled in WhatsApp's own green so they read as
   the same kind of thing as the two platform buttons in the hero.
   Wide: two label/number rows with the number as the pill — see the query. */
.wa-list { display: grid; gap: 10px; }
.wa {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 52px; padding: 14px 18px;
  border: 1px solid var(--whatsapp); border-radius: 999px;
  background: var(--whatsapp);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.28),
    0 2px 4px rgba(0, 80, 40, 0.28),
    0 12px 26px -10px rgba(0, 110, 55, 0.55);
  color: #ffffff; font-size: 17px; font-weight: 600;
}
.wa-tag { font-size: 12px; font-weight: 400; opacity: 0.75; }

/* ── Foot ────────────────────────────────────────────────────────────────── */
.foot { padding: 20px; border-top: 1px solid var(--color-divider); }
.foot-mark { height: 13px; width: auto; }
.disclaimer { margin-top: 16px; font-size: 10.5px; line-height: 1.7; color: var(--ink-60); }
.disclaimer span { display: block; }
.foot-back { margin-top: 16px; font-size: 12px; }

/* ══════════════════════════════════════════════════════════════════════════
   Wide — option 1b, drawn at 1180px.
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 900px) {

  /* The nav becomes a band of its own: a plum tint, a lit top edge and a rule
     under it, so it separates from the hero it now sits directly against. */
  .nav {
    padding: 22px 64px; gap: 28px;
    background: rgba(90, 0, 50, 0.16);
    border-bottom: 1px solid rgba(255, 255, 255, 0.16);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14);
  }
  .nav-mark { height: 26px; }
  .nav-links { display: flex; gap: 28px; margin-right: auto; margin-left: 28px; }
  .nav-links a { font-size: 13px; color: var(--ink-82); }
  .nav-links a:hover { color: #ffffff; }
  .seg { padding: 3px; }
  .seg-btn { padding: 6px 14px; }
  .lg-long { display: inline; }
  .lg-short { display: none; }

  .hero { padding: 64px 64px 72px; }
  .hero h1 { font-size: 64px; line-height: 1.04; letter-spacing: -0.025em; margin-bottom: 20px; }
  .hero-lead { font-size: 21px; line-height: 1.5; max-width: 44ch; margin-bottom: 32px; }
  .hero-btns { display: flex; flex-wrap: wrap; gap: 10px; }
  /* Side by side and content-width, so they sit as two buttons rather than as
     two halves of a row. The pill radius stays: it is what pairs them with the
     WhatsApp numbers further down. */
  .pill {
    display: inline-flex; justify-content: center; gap: 6px;
    min-height: 0; padding: 13px 24px;
    font-size: 15px;
  }
  .pill:hover { transform: translateY(-6px) scale(1.04); }
  .pill-tiktok { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.28), 0 14px 30px -12px rgba(0, 0, 0, 0.6); }
  .pill-shopee { box-shadow: 0 2px 4px rgba(120, 30, 0, 0.30), 0 14px 30px -12px rgba(150, 45, 10, 0.62); }

  .stats {
    grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 32px; padding: 44px 64px;
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.22),
      inset 0 -1px 0 rgba(60, 0, 32, 0.35),
      0 18px 40px -22px rgba(60, 0, 32, 0.65);
  }
  .stat-n {
    font-size: 44px; margin-bottom: 8px;
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.35), 0 8px 22px rgba(50, 0, 28, 0.45);
  }
  .stat-l { font-size: 12px; }

  .sec { padding: 0 64px 72px; }
  .sec h2 { font-size: 30px; margin-bottom: 28px; }
  .eyebrow { display: block; margin-bottom: 10px; }
  .eyebrow-always { margin-bottom: 10px; }
  .note { display: block; font-size: 13px; color: var(--ink-66); margin-top: -22px; margin-bottom: 24px; }

  /* About and Contact become a 200px label column beside the body. */
  .split { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 56px; }
  .split-head h2 { font-size: 24px; margin-bottom: 0; }
  #sec-about { padding-top: 72px; padding-bottom: 0; }
  .about { font-size: 24px; line-height: 1.6; max-width: 40ch; }

  .hr {
    display: block; height: 1px; border: 0; margin: 0 0 72px;
    background: linear-gradient(to right,
      transparent, var(--color-divider) 48px,
      var(--color-divider) calc(100% - 48px), transparent);
  }

  /* Reasons become three cards — glass rather than a solid panel, so the ground
     and its two soft lights still come through them. */
  .reasons { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
  .reason {
    display: flex; flex-direction: column; gap: 8px;
    padding: 26px; border: 1px solid rgba(255, 255, 255, 0.24); border-radius: var(--radius-card);
    background: rgba(255, 255, 255, 0.10);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28), 0 14px 32px -16px rgba(60, 0, 32, 0.6);
  }
  .reason:last-child { border-bottom: 1px solid rgba(255, 255, 255, 0.24); }
  .reason-n {
    display: block; width: auto;
    font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; line-height: 1.2;
    color: var(--color-accent);
  }
  .reason-title { font-size: 19px; margin-bottom: 0; }

  /* Rails become grids. */
  .rail { display: grid; overflow: visible; margin: 0; padding: 0; }
  .rail-prod { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 20px; }
  /* The three screenshots are held to 760px rather than run to the full column:
     at four-up-product width they would out-shout the products above them. */
  .rail-rev { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; align-items: start; max-width: 760px; }
  .rail > * { flex: none; width: auto; }
  .prod-img, .prod-soon { height: 210px; margin-bottom: 12px; }
  .prod-img { padding: 10px; }
  .prod-name { font-size: 16px; }
  .prod:hover .prod-name { color: #ffffff; }
  /* The two placeholders lose their captions here: in a four-up grid the word
     COMING SOON in the tile is the whole message. */
  .prod-name-soon { display: none; }
  .rev { height: 330px; }
  .rev img { height: 100%; object-fit: contain; }

  /* WhatsApp becomes two rows: label left, number right — the number keeping
     the green pill it wears on a phone. */
  .wa-list { gap: 2px; }
  .wa {
    justify-content: space-between; min-height: 0; padding: 14px 0;
    border: 0; border-top: 1px solid var(--color-divider); border-radius: 0;
    background: transparent; box-shadow: none;
    font-size: 16px; font-weight: 400;
  }
  .wa:last-child { border-bottom: 1px solid var(--color-divider); }
  .wa-num {
    padding: 8px 16px; border-radius: 999px;
    background: var(--whatsapp); color: #ffffff;
    font-size: 16px; font-weight: 600;
    box-shadow: 0 2px 4px rgba(0, 80, 40, 0.24);
  }
  .wa-tag { order: -1; font-size: 13px; color: var(--ink-82); opacity: 1; }

  .foot { padding: 24px 64px 40px; border-top: 0; }
  .foot-mark { height: 16px; }
  .disclaimer span { display: inline; }
  .disclaimer span + span::before { content: ' · '; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Reduced motion — the entrance and the bobbing buttons are decoration, so
   they simply do not run. Nothing here is animated into view from opacity 0 by
   a base rule, so switching the animations off leaves every element at its
   finished state rather than invisible.
   ══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .nav, .hero h1, .hero-lead, .hero-btns, .stats, .pill, .pill::after {
    animation: none;
  }
  .pill { transition: none; }
  .pill:hover { transform: none; }
}
