/* The app's "Send a group text, without the group" animation (markup: templates/partials/group_text_fan.html,
   motion: static/js/group-text-fan.js; plan group-text-fan-on-web, 2026-11-07).
   Drawn at the app's own point sizes (FollowUpSwift QuickStartGuideCard.transformDemo, RecipientPhoneView,
   LockScreenNotification) inside a 280 x 244 stage, then the whole stage is scaled (--s) on a wider screen, so
   every proportion stays the app's. The words on the little phones are part of the picture (the stage is
   role="img" with the app's own label); the names under the phones are real text and never under 15px. */

.gtf {
  --s: 1;
  /* The app's dark look; .gtf--auto below swaps in its light look on a light page. */
  --gtf-bezel-1: rgba(255, 255, 255, 0.25);
  --gtf-bezel-2: rgba(255, 255, 255, 0.08);
  --gtf-bezel-3: rgba(255, 255, 255, 0.03);
  --gtf-spec: rgba(255, 255, 255, 0.04);
  --gtf-sh1: rgba(0, 0, 0, 0.45);
  --gtf-sh2: rgba(0, 0, 0, 0.15);
  --gtf-note-sh: rgba(0, 0, 0, 0.3);
  --gtf-green: #5CAB78;                          /* IOSGreen */
  --gtf-name: rgba(235, 238, 245, 0.62);         /* the app's 0.35 white fails contrast on the web */
  position: relative;
  width: 100%;
  height: calc(244px * var(--s));                /* reserved: the page never jumps while this loads */
}
@media (min-width: 700px) { .gtf { --s: 1.35; } }
@media (max-width: 330px) { .gtf { --s: 0.96; } }

.gtf--auto {                                     /* LightInk.line = rgb(17, 24, 39) */
  --gtf-bezel-1: rgba(17, 24, 39, 0.12);
  --gtf-bezel-2: rgba(17, 24, 39, 0.068);
  --gtf-bezel-3: rgba(17, 24, 39, 0.03);
  --gtf-spec: rgba(17, 24, 39, 0.034);
  --gtf-sh1: rgba(17, 24, 39, 0.14);
  --gtf-sh2: rgba(17, 24, 39, 0.068);
  --gtf-note-sh: rgba(17, 24, 39, 0.135);
  --gtf-green: #2F8F57;
  --gtf-name: var(--muted, #6E6E73);
  /* The middle name sits half over the two black side phones (the app's own layout): gray on black is 3.9:1.
     A halo of the card's colour keeps it on light, so it reads at the 4.6:1 it has on the card. */
  --gtf-name-halo: 0 0 3px var(--panel-top, #fff), 0 0 6px var(--panel-top, #fff), 0 0 10px var(--panel-top, #fff);
}
@media (prefers-color-scheme: dark) {
  :root:not(.theme-light) .gtf--auto {
    --gtf-bezel-1: rgba(255, 255, 255, 0.25);
    --gtf-bezel-2: rgba(255, 255, 255, 0.08);
    --gtf-bezel-3: rgba(255, 255, 255, 0.03);
    --gtf-spec: rgba(255, 255, 255, 0.04);
    --gtf-sh1: rgba(0, 0, 0, 0.45);
    --gtf-sh2: rgba(0, 0, 0, 0.15);
    --gtf-note-sh: rgba(0, 0, 0, 0.3);
    --gtf-green: #5CAB78;
    --gtf-name: var(--muted, rgba(235, 238, 245, 0.62));
    --gtf-name-halo: none;
  }
}
:root.theme-dark .gtf--auto {
  --gtf-bezel-1: rgba(255, 255, 255, 0.25);
  --gtf-bezel-2: rgba(255, 255, 255, 0.08);
  --gtf-bezel-3: rgba(255, 255, 255, 0.03);
  --gtf-spec: rgba(255, 255, 255, 0.04);
  --gtf-sh1: rgba(0, 0, 0, 0.45);
  --gtf-sh2: rgba(0, 0, 0, 0.15);
  --gtf-note-sh: rgba(0, 0, 0, 0.3);
  --gtf-green: #5CAB78;
  --gtf-name: var(--muted, rgba(235, 238, 245, 0.62));
  --gtf-name-halo: none;
}

.gtf__stage {
  position: absolute;
  top: 0;
  left: 50%;
  width: 280px;
  height: 244px;
  margin-left: -140px;
  transform: scale(var(--s));
  transform-origin: 50% 0;
}

/* One phone: 106 wide, a 200-tall screen, 5 of space, then its name (218 in the app). Centred in the stage
   like the app's ZStack, then placed by the fan. */
.gtf__slot {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 106px;
  height: 218px;
  margin: -109px 0 0 -53px;
  transform-origin: 50% 100%;
}
.gtf__slot--1 { z-index: 1; }
.gtf__slot--2 { z-index: 3; }
.gtf__slot--3 { z-index: 2; }
/* The app offsets each phone, then rotates it about its own un-moved centre (SwiftUI .offset then
   .rotationEffect). CSS reads right to left, so translate first, then rotate: the same picture. */
.gtf__phone { width: 106px; height: 218px; }
.gtf__slot--1 .gtf__phone { transform: rotate(-7deg) translate(-74px, 12px); }
.gtf__slot--2 .gtf__phone { transform: translate(0, -4px); }
.gtf__slot--3 .gtf__phone { transform: rotate(7deg) translate(74px, 12px); }

.gtf__screen {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  height: 200px;
  padding-top: 5px;
  overflow: hidden;
  border-radius: 18px;
  background: #0A0A0A;                           /* white 0.04 */
  box-shadow: 0 4px 16px var(--gtf-sh1), 0 10px 40px var(--gtf-sh2);
  color: #fff;
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}
.gtf__screen::after {                            /* top-lit bezel */
  content: '';
  position: absolute;
  inset: 0;
  z-index: 3;
  padding: 1px;
  border-radius: inherit;
  background: linear-gradient(to bottom, var(--gtf-bezel-1), var(--gtf-bezel-2), var(--gtf-bezel-3));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}
.gtf__screen::before {                           /* specular top highlight */
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 3;
  height: 40px;
  background: linear-gradient(to bottom, var(--gtf-spec), transparent 50%);
  pointer-events: none;
}
.gtf__island { flex: none; width: 26px; height: 8px; border-radius: 4px; background: #000; }
.gtf__lock { flex: none; width: 5px; height: 6px; margin-top: 7px; fill: rgba(255, 255, 255, 0.4); }
.gtf__time {
  font-family: ui-rounded, 'SF Pro Rounded', -apple-system, system-ui, sans-serif;
  font-size: 20px;
  font-weight: 300;
  line-height: 24px;
  letter-spacing: 0.2px;
}
.gtf__date { min-height: 9px; font-size: 7px; line-height: 9px; color: rgba(255, 255, 255, 0.4); }

.gtf__note {
  display: flex;
  align-items: flex-start;
  gap: 5px;
  box-sizing: border-box;
  width: calc(100% - 8px);
  margin-top: 6px;
  padding: 6px;
  border-radius: 10px;
  background: rgba(72, 72, 74, 0.55);            /* dark ultra-thin material over the lock screen */
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  box-shadow: 0 2px 8px var(--gtf-note-sh);
  transform-origin: 50% 0;
}
.gtf__icon {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 4px;
  background: var(--gtf-green);
}
.gtf__icon svg { width: 9px; height: 9px; fill: #fff; }
.gtf__body { display: flex; flex: 1; flex-direction: column; gap: 1px; min-width: 0; text-align: left; }
.gtf__head { display: flex; align-items: baseline; justify-content: space-between; gap: 4px; }
.gtf__app { font-size: 7px; font-weight: 600; line-height: 9px; }
.gtf__now { font-size: 6px; line-height: 8px; color: rgba(255, 255, 255, 0.35); }
.gtf__from { font-size: 7px; font-weight: 500; line-height: 9px; color: rgba(255, 255, 255, 0.8); }
.gtf__msg {
  display: -webkit-box;
  overflow: hidden;
  font-size: 6.5px;
  line-height: 8px;
  color: rgba(255, 255, 255, 0.45);
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.gtf__glow { position: absolute; inset: 0; z-index: 2; background: #fff; opacity: 0; pointer-events: none; }

.gtf__name {
  display: block;
  margin-top: 5px;
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif;
  font-size: calc(15px / var(--s));              /* 15px on screen at any scale */
  font-weight: 500;
  line-height: 1.2;
  color: var(--gtf-name);
  text-shadow: var(--gtf-name-halo, none);
  text-align: center;
  white-space: nowrap;
}

/* ---------- Motion (group-text-fan.js adds .gtf--anim; without it the finished picture shows) ----------
   Phones: SwiftUI .spring(response: 0.42, dampingFraction: 0.68), 679 ms to settle. Notifications:
   .spring(response: 0.4, dampingFraction: 0.7), 628 ms. Both sampled from the spring's own formula. The
   cubic-bezier line is the fallback for a browser without linear(). */
.gtf--anim .gtf__slot {
  opacity: 0;
  transform: translateY(18px) scale(0.62);
  transition: transform 679ms cubic-bezier(0.34, 1.3, 0.64, 1), opacity 679ms cubic-bezier(0.34, 1.3, 0.64, 1);
  transition-timing-function: linear(0, 0.035, 0.122, 0.239, 0.369, 0.499, 0.62, 0.728, 0.819, 0.893, 0.951, 0.993, 1.022, 1.041, 1.051, 1.054, 1.053, 1.049, 1.043, 1.036, 1.028, 1.022, 1.016, 1.011, 1.006, 1.003, 1.001, 0.999, 0.998, 0.997, 0.997, 0.997, 0.997, 0.998, 0.998, 0.998, 1);
}
.gtf--anim .gtf__slot.is-in { opacity: 1; transform: none; }
.gtf--anim .gtf__note {
  opacity: 0;
  transform: translateY(-110px) scale(0.9);      /* drops in from the top of the screen (.move(edge: .top)) */
  transition: transform 628ms cubic-bezier(0.34, 1.3, 0.64, 1), opacity 628ms cubic-bezier(0.34, 1.3, 0.64, 1);
  transition-timing-function: linear(0, 0.033, 0.115, 0.226, 0.35, 0.475, 0.592, 0.697, 0.788, 0.863, 0.922, 0.967, 1, 1.023, 1.037, 1.044, 1.046, 1.045, 1.041, 1.036, 1.03, 1.024, 1.019, 1.014, 1.01, 1.006, 1.004, 1.002, 1, 0.999, 0.998, 0.998, 0.998, 0.998, 0.998, 0.998, 1);
}
.gtf--anim .gtf__slot.is-lit .gtf__note { opacity: 1; transform: none; }
/* The screen wakes as the message lands: white 0.15 in 80 ms, held, then out over 600 ms from 150 ms. */
.gtf--anim .gtf__slot.is-lit .gtf__glow { animation: gtf-wake 750ms both; }
@keyframes gtf-wake {
  0% { opacity: 0; animation-timing-function: ease-in; }
  10.7% { opacity: 0.15; }
  20% { opacity: 0.15; animation-timing-function: ease-out; }
  100% { opacity: 0; }
}
/* The end of each loop: everything goes together, easeOut 400 ms; a notification only fades (its removal is
   opacity alone in the app), so it stays where it is. */
.gtf--anim.gtf--out .gtf__slot { transition: transform 400ms ease-out, opacity 400ms ease-out; }
.gtf--anim.gtf--out .gtf__note { opacity: 0; transform: none; transition: opacity 400ms ease-out; }
.gtf--anim.gtf--out .gtf__glow { animation: none; }
/* Back to the start without animating the way back. */
.gtf--snap .gtf__slot, .gtf--snap .gtf__note { transition: none !important; }
.gtf--snap .gtf__glow { animation: none !important; }

@media print {
  .gtf--anim .gtf__slot, .gtf--anim .gtf__note { opacity: 1 !important; transform: none !important; }
}
