/* Hallmark · component: Home Screen widget · design-system: tokens.css / Twilight Sanctuary
 * The Whisper widget, drawn in CSS, for the one place on the site that needs a
 * tile to CHANGE STATE: the scroll demo. Everywhere else the page uses the real
 * App Store element captures. Geometry, palette and the pair photos all track
 * the real thing (specs/WIDGET_UI.md, DesignSystem.swift, seed-media), so the
 * drawn tile and the captured ones read as the same widget.
 *
 * A tile instantiates `.wg` and is driven entirely by data attributes:
 *
 *   .wg.wg--single | .wg.wg--grid    kind
 *   data-state="idle|incoming|outgoing|unpaired"
 *   data-count="0..5"                incoming badge (hidden at 0, per spec)
 *   data-sent="0..5"                 outgoing lit segments
 *   data-heart="true"                replyable-activity heart (grid cells)
 *
 * Every metric is expressed in cqw against the tile's own container, so a tile
 * is faithful at any size.
 * pre-emit critique: P5 H5 E5 S5 R5 V4 */

/* Intro-widget palette — exact sRGB values from DesignSystem.swift so the tiles
 * read as the real Home Screen widget. The shared page palette is tokens.css. */
:root {
  --wg-iris: #9e63ce;
  --wg-amber: #d6a368;
  --wg-mist: #b39ecc;
  --wg-plum: #5c3d75;
  --wg-smoke: #947bad;
  --wg-text: #f5f2ff;
  --wg-ink: #100b19;
  --wg-berry-1: #5c245e;
  --wg-berry-2: #401845;
  --wg-berry-3: #260e27;
  --wg-berry-4: #150816;
  --wg-berry-glow: #4d2675;
}

/* ── Tile ────────────────────────────────────────────────────── */

/* Outer element — owns sizing, the container context, and any placement
 * transform (hero tilt + parallax). Never animates its own surface. */
.wg {
  position: relative;
  container-type: inline-size;
  aspect-ratio: 1;
  flex: 0 0 auto;
}

/* Inner face — the berry surface. Separated from .wg so an entrance animation
 * here never collides with a placement transform there. */
.wg__face {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: 16%;
  background:
    radial-gradient(circle at 50% 26%, color-mix(in srgb, var(--wg-berry-glow) 22%, transparent) 0%, transparent 62%),
    linear-gradient(
      to bottom,
      var(--wg-berry-1) 0%,
      var(--wg-berry-2) 42%,
      var(--wg-berry-3) 82%,
      var(--wg-berry-4) 100%
    );
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--wg-text) 8%, transparent),
    0 16px 24px color-mix(in srgb, var(--wg-ink) 38%, transparent);
}

/* Whisper mark, top-right. Present on the single tile only — the grid packs
 * four cells and has no room for it. */
.wg__mark {
  position: absolute;
  inset-block-start: 11cqw;
  inset-inline-end: 8.5cqw;
  width: 12.5cqw;
  height: 12.5cqw;
  background: color-mix(in srgb, var(--wg-text) 60%, transparent);
  -webkit-mask: url("/whisper-mark-bold.svg?v=20260714") center / contain no-repeat;
  mask: url("/whisper-mark-bold.svg?v=20260714") center / contain no-repeat;
}

/* ── Cell — one pair. The single tile has one, the grid has four. ─ */

.wg__cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--wg-gap);
}

/* Avatar + its satellites (ring, count badge, heart) share one stacking box.
 * The ring and heart overlay rather than participate in layout, so the caption
 * baseline never shifts between states. */
.wg__cluster {
  position: relative;
  width: var(--wg-avatar);
  height: var(--wg-avatar);
  display: grid;
  place-items: center;
}

.wg__avatar {
  position: relative;
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  border-radius: var(--radius-capsule);
  color: var(--wg-text);
  font-family: var(--font-wordmark);
  font-size: var(--wg-initial);
  font-weight: 600;
  line-height: 1;
  box-shadow: inset 0 0 0 var(--wg-rim) color-mix(in srgb, var(--wg-text) 50%, transparent);
}

/* Pair photos, from the same seed images the App Store captures are built from
 * (launch/store-screenshots/seed-media), so the people here are the people in
 * the real renders elsewhere on the page. A photo class replaces the initial.
 * These are classes rather than an inline `style` because the site's CSP has no
 * `unsafe-inline` in style-src. */
.wg__avatar--photo {
  background-position: center;
  background-size: cover;
  color: transparent;
}

.wg__avatar--emma { background-image: url("/img/p-emma.webp?v=20260727d"); }
.wg__avatar--sofia { background-image: url("/img/p-sofia.webp?v=20260727d"); }
.wg__avatar--james { background-image: url("/img/p-james.webp?v=20260727d"); }
.wg__avatar--mina { background-image: url("/img/p-mina.webp?v=20260727d"); }
.wg__avatar--michael { background-image: url("/img/p-michael.webp?v=20260727d"); }

/* Initial-only fallback, for a pair with no photo set. */
.wg__avatar[data-tint="a"] { background: linear-gradient(135deg, var(--wg-iris), var(--wg-amber)); }
.wg__avatar[data-tint="d"] { background: linear-gradient(135deg, var(--wg-amber), var(--wg-iris)); }
.wg__avatar[data-tint="e"] { background: linear-gradient(135deg, var(--wg-mist), var(--wg-plum)); }
.wg__avatar[data-tint="m"] { background: linear-gradient(135deg, var(--wg-smoke), var(--wg-iris)); }

/* Unpaired — a held spot, not a locked door. Dashed rim, no fill, no initial.
 * The `+` glyph stands in for SF Symbol person.fill.badge.plus. */
[data-state="unpaired"] .wg__avatar {
  background: color-mix(in srgb, var(--wg-iris) 14%, transparent);
  color: color-mix(in srgb, var(--wg-mist) 85%, transparent);
  font-size: calc(var(--wg-initial) * 1.5);
  font-weight: 300;
  box-shadow: none;
  outline: var(--wg-rim) dashed color-mix(in srgb, var(--wg-iris) 55%, transparent);
  outline-offset: calc(-1 * var(--wg-rim));
}

.wg__name {
  height: var(--wg-name-h);
  color: var(--wg-text);
  font-family: var(--font-wordmark);
  font-size: var(--wg-name);
  font-weight: 600;
  line-height: 1;
  text-align: center;
  white-space: nowrap;
}

/* ── Count badge ─────────────────────────────────────────────────
 * Solid warm-cream chip, dark digit, crisp dark frame (#1728/#1790).
 * Hidden at zero — `idle` shows no badge at all. */
.wg__badge {
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: 0;
  width: var(--wg-badge);
  height: var(--wg-badge);
  display: grid;
  place-items: center;
  border-radius: var(--radius-capsule);
  background: var(--wg-text);
  color: var(--wg-ink);
  font-family: var(--font-wordmark);
  font-size: var(--wg-badge-text);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  box-shadow: 0 0 0 var(--wg-badge-rim) var(--wg-ink);
  transform: translate(var(--wg-badge-x), var(--wg-badge-x));
  transition:
    opacity var(--dur-short) var(--ease-out),
    scale var(--dur-short) var(--ease-out);
}

/* The badge carries queue depth, so it is bound to the count, not the state. */
[data-count="0"] .wg__badge,
[data-count=""] .wg__badge {
  opacity: 0;
  scale: 0.4;
}

/* ── Outgoing ring ───────────────────────────────────────────────
 * Five segments, clockwise from 12. Sent segments light first; the rest stay on
 * the quiet empty track. Hidden entirely on idle and incoming (#611) — there the
 * badge is the only signal.
 *
 * Geometry: r=16 in a 40-unit box → circumference 100.53. Each slot is
 * 16.59 of arc + 3.52 of gap = 20.11, ×5 = 100.55. */
.wg__ring {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  width: var(--wg-ring);
  height: var(--wg-ring);
  overflow: visible;
  translate: -50% -50%;
  transition: opacity var(--dur-short) var(--ease-out);
}

.wg__seg {
  fill: none;
  stroke: color-mix(in srgb, var(--wg-text) 10%, transparent);
  stroke-width: var(--wg-stroke);
  stroke-linecap: round;
  stroke-dasharray: 16.59 83.94;
  transform: rotate(-90deg);
  transform-origin: center;
  transition: stroke var(--dur-short) var(--ease-out);
}

.wg__seg:nth-child(1) { stroke-dashoffset: 0; }
.wg__seg:nth-child(2) { stroke-dashoffset: -20.11; }
.wg__seg:nth-child(3) { stroke-dashoffset: -40.22; }
.wg__seg:nth-child(4) { stroke-dashoffset: -60.33; }
.wg__seg:nth-child(5) { stroke-dashoffset: -80.44; }

/* Lit — "you said this". Cream at 70%, matching the count chip family. */
[data-sent="1"] .wg__seg:nth-child(-n + 1),
[data-sent="2"] .wg__seg:nth-child(-n + 2),
[data-sent="3"] .wg__seg:nth-child(-n + 3),
[data-sent="4"] .wg__seg:nth-child(-n + 4),
[data-sent="5"] .wg__seg:nth-child(-n + 5) {
  stroke: color-mix(in srgb, var(--wg-text) 70%, transparent);
}

/* Ring visibility follows the state table in WIDGET_UI.md. */
[data-state="idle"] .wg__ring,
[data-state="incoming"] .wg__ring {
  opacity: 0;
}

/* Not-logged-in / unpaired carry a uniform faint iris ring instead. */
[data-state="unpaired"] .wg__seg {
  stroke: color-mix(in srgb, var(--wg-iris) 18%, transparent);
}

/* ── Reply heart ─────────────────────────────────────────────────
 * Passive "your pair replied" cue, pinned to the avatar's 2 o'clock point with
 * roughly 30% of the glyph overlapping the disc (#1489/#1525). */
.wg__heart {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  width: var(--wg-heart);
  height: var(--wg-heart);
  transform: translate(calc(-50% + var(--wg-heart-x)), calc(-50% - var(--wg-heart-y)));
  transition: opacity var(--dur-short) var(--ease-out);
}

.wg__heart path {
  fill: var(--wg-text);
  stroke: var(--wg-ink);
  stroke-width: 2.8;
}

/* The heart is per-pair, so it is driven from the cell on both kinds — a single
 * tile has one cell, a grid has four. */
.wg__cell:not([data-heart="true"]) .wg__heart {
  opacity: 0;
}

/* ── Single-pair tile ────────────────────────────────────────── */

.wg--single {
  --wg-avatar: 41cqw;
  --wg-initial: 17cqw;
  --wg-rim: 0.55cqw;
  /* Ring geometry is proportional to the avatar, from WIDGET_UI.md: stroke
   * centre at 58pt around a 100pt avatar (×1.16) with a 4pt stroke (×0.04).
   * The viewBox draws at r=16 of 40, so the box is the ring diameter ÷ 0.8. */
  --wg-ring: 59.5cqw;
  --wg-stroke: 1.1;
  --wg-badge: 10.8cqw;
  --wg-badge-text: 6.55cqw;
  --wg-badge-rim: 0.62cqw;
  --wg-badge-x: 0.65cqw;
  --wg-heart: 11cqw;
  --wg-heart-x: 16.4cqw;
  --wg-heart-y: 9.5cqw;
  --wg-name: 7.95cqw;
  --wg-name-h: 21.6cqw;
  --wg-gap: 1.7cqw;
}

.wg--single .wg__cell {
  height: 64.2cqw;
  justify-content: flex-start;
  transform: translateY(1.15cqw);
}

/* ── 2x2 grid tile ───────────────────────────────────────────── */

.wg--grid {
  --wg-avatar: 28.4cqw;
  --wg-initial: 12cqw;
  --wg-rim: 0.42cqw;
  /* Mini ring: 28.5pt radius, 3pt stroke, 44pt avatar (×1.295 and ×0.068). Held
   * a little tighter than the ratio, because four cells share one tile and the
   * full-ratio ring crosses into the pair-name caption below it. */
  --wg-ring: 41cqw;
  --wg-stroke: 1.88;
  --wg-badge: 10.2cqw;
  --wg-badge-text: 6.25cqw;
  --wg-badge-rim: 0.5cqw;
  --wg-badge-x: 2.1cqw;
  --wg-heart: 10.8cqw;
  --wg-heart-x: 13.8cqw;
  --wg-heart-y: 7.97cqw;
  --wg-name: 9cqw;
  --wg-name-h: auto;
  --wg-gap: 2.05cqw;
}

.wg__grid {
  width: 81.8cqw;
  height: 81.8cqw;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 36.8cqw 36.8cqw;
  row-gap: 8.2cqw;
}

.wg--grid .wg__name {
  font-weight: 700;
}

/* Cells pull very slightly toward the tile centre, matching the app's grid
 * inset, and sit at 0.82 scale inside their 36.8cqw row. */
.wg--grid .wg__cell {
  --wg-cell-x: 0cqw;
  --wg-cell-y: 0cqw;
  justify-content: center;
  transform: translate(var(--wg-cell-x), var(--wg-cell-y)) scale(0.82);
}

.wg--grid .wg__cell:nth-child(1) { --wg-cell-x: -3.27cqw; --wg-cell-y: -2.05cqw; }
.wg--grid .wg__cell:nth-child(2) { --wg-cell-x: 3.27cqw; --wg-cell-y: -2.05cqw; }
.wg--grid .wg__cell:nth-child(3) { --wg-cell-x: -3.27cqw; --wg-cell-y: 2.05cqw; }
.wg--grid .wg__cell:nth-child(4) { --wg-cell-x: 3.27cqw; --wg-cell-y: 2.05cqw; }

/* State lives on whichever element owns a pair: the tile on `.wg--single`, the
 * cell on `.wg--grid`. Never both, or the tile-level value would mask the cells'
 * — the selectors above match any ancestor carrying the attribute. */
