/* ---------------------------------------------------------------------------
   site.css — the only hand-written CSS on these pages.

   apple-extracted.css carries Apple's own rules verbatim. Everything here is
   either (a) something their page gets from art we don't have, or (b) chrome
   that has to say "Charlie Bieger" rather than "Apple". Kept separate so it is
   always obvious which is which.
--------------------------------------------------------------------------- */

/* --- device ---------------------------------------------------------------- */
/* Apple's own iPhone 17 Pro bezel, from their official Design Resources
   (developer.apple.com/design/resources -> Bezel-iPhone-17.dmg, "iPhone 17 Pro
   - Silver - Portrait"). This replaces the CSS-drawn frame entirely: it is a
   real product render, so the titanium, the specular falloff, the squircle
   corners and the Dynamic Island are Apple's, not my approximation.

   The screen aperture was measured by decoding the PNG's alpha channel rather
   than guessed. In the 1350x2760 original the transparent hole is exactly
   1206x2622 at (72, 69) - the iPhone 17 Pro display, to the pixel. Those become
   the percentages below, so they hold at any --device-w.

   The frame is the element's background; the screenshot sits behind it, inset
   to the aperture. The Dynamic Island is part of Apple's artwork, so nothing is
   drawn over the screen. */
.device {
  --device-w: 232px;
  --ap-left: 5.3333%;
  --ap-top: 2.5000%;
  --ap-width: 89.3333%;
  --ap-height: 95.0000%;

  position: relative;
  width: var(--device-w);
  aspect-ratio: 1350 / 2760;
  flex: none;
  filter: drop-shadow(0 22px 40px rgba(0,0,0,.20)) drop-shadow(0 4px 10px rgba(0,0,0,.12));
}
/* The frame is an overlay, NOT the element's background. `filter` above makes
   .device a stacking context, so a z-index:-1 child can no longer sit behind
   the parent's own background — the screenshot covered Apple's Dynamic Island.
   Painting the frame as a pseudo-element above the screen is order-independent
   and cannot regress that way. */
.device::after {
  content: "";
  position: absolute;
  inset: 0;
  background: url("img/iphone-frame.png") center / 100% 100% no-repeat;
  z-index: 2;
  pointer-events: none;
}
@media only screen and (min-width: 1441px) { .device { --device-w: 262px; } }
@media only screen and (max-width: 1068px) { .device { --device-w: 216px; } }
@media only screen and (max-width: 734px)  { .device { --device-w: 190px; } }
@media only screen and (max-width: 480px)  { .device { --device-w: 176px; } }
.device-lg { --device-w: 320px; }
@media only screen and (max-width: 1068px) { .device-lg { --device-w: 280px; } }
@media only screen and (max-width: 734px)  { .device-lg { --device-w: 244px; } }

/* The screenshot, behind the frame, in the aperture. Apple's display corners
   are antialiased artwork rather than a hard mask, so the screen carries a
   matching radius to stop square pixels showing through at the corners. */
.device-screen {
  position: absolute;
  left: var(--ap-left);
  top: var(--ap-top);
  width: var(--ap-width);
  height: var(--ap-height);
  border-radius: calc(var(--device-w) * 0.126);
  overflow: hidden;
  background: #000;
  z-index: 1;
}
.device-screen img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: top center;
}

/* Apple positions .card-bg absolutely at the tile's bottom and lets their
   artwork bleed off the edge. Our frame goes in the same slot. */
.section-featured-apps .card-bg {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  left: 0; right: 0;
  height: 450px;
  overflow: hidden;
}
@media only screen and (min-width: 1441px) { .section-featured-apps .card-bg { height: 524px; } }
@media only screen and (max-width: 1068px) { .section-featured-apps .card-bg { height: 435px; } }
@media only screen and (max-width: 734px)  { .section-featured-apps .card-bg { height: 335px; } }
@media only screen and (max-width: 480px)  { .section-featured-apps .card-bg { height: 314px; } }

/* No bottom scrim. One was added here when the card art was a light app
   screenshot and the compatibility line had to stay readable over it. Apple's
   cards have no such fade, and the art is a gradient now, so it was pure
   divergence — visible as a wash across the bottom of every tile. */


/* --- page chrome ---------------------------------------------------------- */
/* Apple's globalnav is capped at 1024px while this page's content column is
   --global-content-max-width (1260px), so the wordmark sat ~118px inside the
   headline beneath it — close enough to read as a mistake rather than as a
   different measure. The bar is carrying our own short links, not their eleven
   product menus, so there is nothing that needs the narrower cap. Same column,
   same gutters, and the mark lines up with the H1 under it. */
/* Wrapped in a min-width, and it has to be. Below 833px Apple switches this
   element to position:absolute and width:100% — it stops being a centred column
   and becomes the bar's translucent background, stretched across the viewport.
   This rule outranks theirs (two ids and a class), so it was putting the desktop
   column width back: at 390px that is calc(100% - 180px) = 210px, and the frosted
   band covered the left half of the screen and stopped, on every page. */
@media only screen and (min-width: 834px) {
  #globalheader #globalnav .globalnav-content {
    /* The same column .section-content uses, so this is not a number I picked —
       it is theirs, and it tracks every breakpoint the content does. */
    width: var(--viewport-content-lg, calc(100% - 180px));
    max-width: var(--content-max-width, 1680px);
    margin-inline: auto;
    padding-inline: 0;
  }
}
/* The .topbar block that used to head this section is gone: every page ships
   Apple's own #globalheader/#globalnav, and `topbar` occurred nowhere else in
   the repo. */
/* The glyph slot in Apple's current pill holds their gradient mark; ours holds
   the app icon at the same optical size. */
.localnav-glyph img { width: 18px; height: 18px; border-radius: 22.37%; display: block; }
.localnav-link-current .localnav-glyph { display: inline-flex; align-items: center; margin-inline-end: 7px; }

.site-footer {
  background: #f5f5f7;
  color: #6e6e73;
  font-size: 12px; line-height: 1.333; letter-spacing: -0.01em;
  padding-block: 34px 44px;
}
.site-footer .section-content { display: flex; flex-direction: column; gap: 14px; }
.site-footer a { color: #424245; text-decoration: none; }
.site-footer a:hover { text-decoration: underline; }
.site-footer-links { display: flex; flex-wrap: wrap; gap: 8px 22px; }
.site-footer-legal { border-top: 1px solid #d2d2d7; padding-top: 16px; }


/* --- globalnav ------------------------------------------------------------- */
/* Apple's globalnav renders each item as an SVG (the Apple mark, then wordmark
   images for Store/Mac/iPad/...) and VISUALLY HIDES .globalnav-link-text —
   position:absolute, clip:rect(1px,1px,1px,1px) — leaving it for screen readers
   only. Ours has no wordmark artwork, so the text has to be un-hidden. This is
   the one place their nav needs overriding rather than adopting. */
#globalnav .globalnav-link-text {
  position: static;
  clip: auto;
  clip-path: none;
  width: auto;
  height: auto;
  overflow: visible;
  white-space: nowrap;
  font-size: 12px;
  line-height: 1;
}
#globalnav .globalnav-item-mark .globalnav-mark-text {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -.01em;
}
#globalnav .globalnav-item-mark { margin-inline-end: auto; }
#globalnav .globalnav-list { align-items: center; gap: 26px; }
#globalnav .globalnav-link { display: flex; align-items: center; height: 44px; }
/* Their sticky localnav offsets itself by this; without it the two tiers overlap. */
:root { --r-globalnav-height: 44px; }

/* Apple sets #globalnav to position:absolute because their flyout menus hang
   off it, which collapses #globalheader to 0 and leaves the bar with no space
   in the flow — their own page reserves it in the surrounding layout. We have
   no flyouts, so putting the nav back in flow is simpler and self-maintaining
   than hard-coding a height that has to track the bar. */
#globalheader { position: relative; }
#globalnav { position: relative; }

/* --- localnav pill --------------------------------------------------------- */
/* Apple sizes their pill as `max-height:18px` + `padding:8px 15px` + a 1px
   border, which only adds up to their real 36px under content-box sizing.
   Our reset is border-box, so that max-height was clamping the whole box and
   swallowing the padding — the label ended up flush against the pill edge.
   Opting this one element back into Apple's own box model restores it. */
nav.localnav .localnav-link { box-sizing: content-box; }

/* --- hero fan -------------------------------------------------------------- */
/* The lead image on an app page is the app itself: a group of iPhones rising
   into the hero and running off its bottom edge, the way Apple leads a product
   page. One geometry per app so the three pages don't open identically —
   `-arc` is the symmetric three-up, `-deck` a shuffled stack pushed off-centre,
   `-stair` an ascending run. They share every rule except the transforms. */
.hero-fan {
  --fan-w: 268px;
  position: relative;
  width: 100%;
  margin-top: 64px;
  /* Tall enough for the whole phone plus the swing the outer two pick up from
     their rotation. Clipping the group at the hero's edge only reads as a
     bleed when the background changes there; ours doesn't, so a cropped phone
     just looks like a mistake. */
  height: calc(var(--fan-w) * 2.20);
  /* Horizontal only — the outer phones may run past the content column, but
     nothing should be cut off the bottom. */
  overflow-x: clip;
}
@media only screen and (min-width: 1441px) { .hero-fan { --fan-w: 300px; } }
@media only screen and (max-width: 1068px) { .hero-fan { --fan-w: 236px; margin-top: 52px; } }
@media only screen and (max-width: 734px)  { .hero-fan { --fan-w: 186px; margin-top: 40px; } }
@media only screen and (max-width: 480px)  { .hero-fan { --fan-w: 158px; } }

.hero-fan .device {
  --device-w: var(--fan-w);
  position: absolute;
  top: 0;
  left: 50%;
  /* --fan-x/-y are travel in units of the device's own width, so the whole
     arrangement scales with --fan-w instead of drifting at other breakpoints. */
  --fan-x: 0;
  --fan-y: 0;
  --fan-rot: 0deg;
  --fan-scale: 1;
  transform:
    translateX(calc(-50% + var(--fan-x) * var(--fan-w)))
    translateY(calc(var(--fan-y) * var(--fan-w)))
    rotate(var(--fan-rot))
    scale(var(--fan-scale));
  transform-origin: 50% 90%;
}

/* Entrance: the group starts collapsed behind the centre phone and fans out.
   Held on a class so it plays when the hero is reached, not at parse time. */
.js .hero-fan[data-reveal] .device {
  opacity: 0;
  transform:
    translateX(-50%)
    translateY(calc(var(--fan-y) * var(--fan-w) + 44px))
    rotate(0deg)
    scale(calc(var(--fan-scale) * .94));
}
.hero-fan[data-reveal] .device {
  transition:
    transform 1.15s cubic-bezier(0.28, 0.11, 0.32, 1) var(--fan-delay, 0s),
    opacity .7s cubic-bezier(0.28, 0.11, 0.32, 1) var(--fan-delay, 0s);
}
/* Specificity has to match the `.js .hero-fan[data-reveal]` initial state above,
   or the hidden rule wins and the fan never arrives. */
.js .hero-fan[data-reveal].is-revealed .device,
.hero-fan.is-revealed .device {
  opacity: 1;
  transform:
    translateX(calc(-50% + var(--fan-x) * var(--fan-w)))
    translateY(calc(var(--fan-y) * var(--fan-w)))
    rotate(var(--fan-rot))
    scale(var(--fan-scale));
}
@media (prefers-reduced-motion: reduce) {
  .js .hero-fan[data-reveal] .device { opacity: 1; transform: translateX(calc(-50% + var(--fan-x) * var(--fan-w))) translateY(calc(var(--fan-y) * var(--fan-w))) rotate(var(--fan-rot)) scale(var(--fan-scale)); }
  .hero-fan[data-reveal] .device { transition: none; }
}

/* The centre device is the subject; the flankers sit behind it and are dimmed
   very slightly so the group reads as depth rather than three flat cut-outs. */
.hero-fan .device { z-index: 1; }
.hero-fan .fan-lead { z-index: 3; }
.hero-fan .fan-side { z-index: 2; }
/* Dim the screen only — putting it over the whole element would grey out
   Apple's titanium frame along with it. */
.hero-fan .fan-side .device-screen::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .07);
  pointer-events: none;
}

/* arc — symmetric three-up */
.hero-fan-arc .device:nth-child(1) { --fan-x: -.80; --fan-y: .085; --fan-rot: -9deg;  --fan-scale: .90; --fan-delay: .10s; }
.hero-fan-arc .device:nth-child(2) { --fan-x:  0;   --fan-y: 0;    --fan-rot: 0deg;   --fan-scale: 1;   --fan-delay: 0s;   }
.hero-fan-arc .device:nth-child(3) { --fan-x:  .80; --fan-y: .085; --fan-rot:  9deg;  --fan-scale: .90; --fan-delay: .16s; }

/* deck — shuffled and pushed off-centre */
.hero-fan-deck .device:nth-child(1) { --fan-x: -.66; --fan-y: .13; --fan-rot: -14deg; --fan-scale: .84; --fan-delay: .16s; }
.hero-fan-deck .device:nth-child(2) { --fan-x: -.10; --fan-y: .02; --fan-rot: -3deg;  --fan-scale: .95; --fan-delay: .08s; }
.hero-fan-deck .device:nth-child(3) { --fan-x:  .52; --fan-y: 0;   --fan-rot:  6deg;  --fan-scale: 1;   --fan-delay: 0s;   }

/* stair — an ascending run, left to right */
.hero-fan-stair .device:nth-child(1) { --fan-x: -.86; --fan-y: .17; --fan-rot: -5deg; --fan-scale: .86; --fan-delay: .16s; }
.hero-fan-stair .device:nth-child(2) { --fan-x: -.02; --fan-y: .07; --fan-rot: -2deg; --fan-scale: .93; --fan-delay: .08s; }
.hero-fan-stair .device:nth-child(3) { --fan-x:  .82; --fan-y: 0;   --fan-rot:  4deg; --fan-scale: 1;   --fan-delay: 0s;   }

/* The fan already carries air under the phones, so the hero trims its own. */
.hero-light:has(.hero-fan) { padding-bottom: 40px; }
.section-welcome:has(.hero-fan) { padding-bottom: 40px; }

/* --- legal pages ----------------------------------------------------------- */
/* Terms and privacy read better as a single narrow measure than in Apple's
   wide content column — around 66 characters, which is where their own legal
   pages sit too. */
.legal {
  max-width: 692px;
  margin-inline: auto;
  padding: 88px 22px 120px;
  color: #1d1d1f;
  font-family: var(--sk-font-text, "SF Pro Text", "SF Pro Icons", "Helvetica Neue", Helvetica, Arial, sans-serif);
}
@media only screen and (max-width: 734px) { .legal { padding-top: 56px; padding-bottom: 80px; } }
.legal h1 {
  font-size: 40px; line-height: 1.1; font-weight: 600; letter-spacing: -0.003em;
  margin: 0 0 8px;
}
@media only screen and (max-width: 734px) { .legal h1 { font-size: 32px; } }
.legal .legal-meta { font-size: 14px; line-height: 1.4285914286; color: #6e6e73; margin: 0 0 12px; }
.legal .legal-lede { font-size: 19px; line-height: 1.4211; color: #1d1d1f; margin: 0 0 8px; }
.legal h2 {
  font-size: 24px; line-height: 1.1666666667; font-weight: 600; letter-spacing: 0.009em;
  margin: 48px 0 12px;
}
.legal h3 { font-size: 17px; line-height: 1.4118; font-weight: 600; margin: 28px 0 6px; }
.legal p,
.legal li { font-size: 17px; line-height: 1.4705882353; letter-spacing: -0.022em; color: #1d1d1f; }
.legal p { margin: 0 0 16px; }
/* apps.css strips list markers globally; legal copy needs them back. */
.legal ul { margin: 0 0 16px; padding-inline-start: 22px; list-style: disc; }
.legal li::marker { color: #86868b; }
.legal li { margin-bottom: 8px; }
.legal a { color: #06c; text-decoration: none; }
.legal a:hover { text-decoration: underline; }
/* An unwritten policy has to LOOK unwritten. These two rules exist so a draft
   can sit on the live site without ever being mistaken for the real thing —
   a half-written policy read as binding is worse than an obviously missing one. */
.legal .legal-draft {
  border: 1px solid #d2d2d7; border-left: 3px solid #b4442e; border-radius: 12px;
  background: #fbfbfd; padding: 16px 20px; margin: 0 0 32px;
}
.legal .legal-draft p { margin: 0; color: #6e6e73; }
.legal .legal-draft strong { color: #1d1d1f; }
.legal .legal-placeholder { color: #86868b; font-style: italic; }
.legal .legal-placeholder::before { content: "To write: "; font-style: normal; color: #b4442e; }
.legal hr { border: 0; border-top: 1px solid #d2d2d7; margin: 48px 0; }
/* The one line on these pages that has to be impossible to skim past. */
.legal .legal-callout {
  background: #f5f5f7;
  border-radius: 18px;
  padding: 24px 28px;
  margin: 0 0 8px;
}
.legal .legal-callout p:last-child { margin-bottom: 0; }

/* --- spotlight gallery grounds --------------------------------------------- */
/* Apple fills .hero-image with a photographic ground per card, which is the
   single biggest reason their cards read richer than a screenshot on a flat
   colour. Theirs is their artwork; these are built from each app's own palette
   as a layered radial mesh, with the device sunk into the ground rather than
   sitting on top of it. */
.section-hero .hero-ground {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background-color: var(--ground-base);
  background-image:
    radial-gradient(120% 90% at 82% 8%,  var(--ground-hi)  0%, transparent 58%),
    radial-gradient(95% 75% at 12% 92%,  var(--ground-lo)  0%, transparent 62%),
    radial-gradient(70% 55% at 50% 50%,  var(--ground-mid) 0%, transparent 78%);
}
.ground-eagle-one { --ground-base:#C7343F; --ground-hi:#FF7A4E; --ground-lo:#7E2E86; --ground-mid:#E8452F; }

.eagle-spotlight-capture {
  position: absolute;
  bottom: 0;
  left: 50%;
  width: min(50%, 600px);
  height: auto;
  border-radius: 22px 22px 0 0;
  box-shadow: 0 -8px 45px rgba(0, 0, 0, .18);
  transform: translateX(-50%);
}

.section-featured-apps .eagle-feature-capture {
  display: block;
  width: min(88%, 440px);
  height: auto;
  margin-top: 32px;
  border-radius: 18px;
  box-shadow: 0 18px 38px rgba(0, 0, 0, .2);
}

@media only screen and (max-width: 734px) {
  .eagle-spotlight-capture { width: 72%; }
  .section-featured-apps .eagle-feature-capture { margin-top: 18px; }
}

/* Apple's column reserves 220px beneath the copy for the card's art. The
   reserve stays — it is the slot real screenshots drop into — but nothing is
   drawn in it now: the stand-in that was there read as a ghost phone rather
   than as artwork. The ground's own gradient mesh carries the card until there
   is something real to put in it. */

/* Their copy column sits over the ground; ours has to say so explicitly since
   the ground is an element rather than a background image. */
.section-hero .gallery-item .column { position: relative; z-index: 1; }
/* No measure override here. Apple's own .hero-gallery-copy is max-width:750px
   and the headline is unconstrained; the ch-based measures I had invented were
   narrowing both by roughly half. */

/* The three .card-bg art treatments that were here are gone. `art-device`
   restated all six declarations of .card-bg at identical values — it could
   never change anything, but it silently re-asserted over any edit to the base
   rule — and `art-crop`/`art-tilt` appear in no HTML at all. Apple has one
   .card-bg rule and no art-treatment modifier. */

/* --- routers --------------------------------------------------------------- */
/* Apple ships NO background on `.router`. The colour comes from two variant
   classes on the same element:
     .section-routers .privacy       linear-gradient(-45deg,#072030,#082728 33%,#212037 66%,#38172a)
     .section-routers .accessibility background-color:#2997ff
   Ours were named `router-privacy` / `router-work`, so both matched nothing and
   a flat #1d1d1f was added to cover the gap — which is why the page used to
   close on two identical near-black rectangles instead of their colour event.
   The first card is theirs verbatim. The second takes their blue but keeps a
   name that describes what it is, since `accessibility` also carries copy
   clamps tuned to their sentences. */
.section-routers .router-blue { background-color: #2997ff; }
/* Their icon block is a <picture> with no CSS size — the PNG's intrinsic
   dimensions do the work. Ours is inline SVG, so it carries its own. */
.section-routers .router .router-icon { display: flex; color: #fff; }
.section-routers .router .router-icon svg { display: block; }

/* Their .hero-image is centred with a translateX(-50%) that assumes a
   left:50% image rather than an inset:0 element, which slid our ground half a
   card to the left. (The container's opacity gate is handled in apps.js by
   adding the .mounted class their own CSS waits for.) */
.section-hero .hero-ground { transform: none; left: 0; }

/* --- spotlight rail -------------------------------------------------------- */
/* Apple stacks every card in ONE grid cell (grid-column:1/1) on a rail whose
   overflow is visible, then translates each card horizontally from script.
   That is what lets the previous and next cards bleed past the rail and stay
   visible all the way to the window edges.

   I first built this as a scroll-snap rail, which looks equivalent until you
   notice a scroll container clips at its own padding box — so the neighbouring
   cards were being cut off exactly at the rail edge and no peek survived. The
   grid-plus-transform model is the only one that reproduces the bleed. */
.section-hero .gallery-container .hero-item-container {
  overflow: visible;
  /* The card is max-width:955px (theirs) inside a track as wide as the content
     column, so every pixel of slack fell on ONE side: measured 80px of gap left
     and 165px right at a 1200px viewport, which reads as the whole carousel
     sitting off to one side. Centring the grid item splits it — 122.5 either
     side — and it stays centred at every width because the track does. */
  justify-items: center;
}
.section-hero .gallery-container .gallery-item {
  /* --slide-x is written per card by apps.js: (i - current) * pitch. */
  transform: translate3d(var(--slide-x, 0px), 0, 0);
  /* 800ms is theirs (model.Slide.duration = .8). The curve was not: 0.28,0.11,
     0.32,1 is Apple's localnav-pill ease. animateToItem uses 0.645,0.045,0.355,1
     for a fresh advance and 0.23,1,0.32,1 when it cuts off a slide already in
     flight — apps.js swaps --slide-ease for the interrupt. */
  transition: transform 800ms var(--slide-ease, cubic-bezier(0.645, 0.045, 0.355, 1));
  height: auto;
  align-self: stretch;
}
@media (prefers-reduced-motion: reduce) {
  .section-hero .gallery-container .gallery-item { transition: none; }
}
/* The first layout must be placed, not animated. Without this the six cards
   start stacked at --slide-x:0 and visibly slide apart over 800ms on load.
   apps.js drops .is-placing once the opening offsets are committed. */
.section-hero .gallery-container .hero-item-container.is-placing .gallery-item {
  transition: none;
}
/* While a finger is down the rail tracks 1:1, so the transition has to be off
   or every frame would ease toward the last committed position. Apple's
   SwipeDrag does the same: transitions are suspended for the duration of the
   drag and reinstated for the release snap. */
.section-hero .gallery-container .hero-item-container.is-dragging .gallery-item {
  transition: none;
}
.section-hero .gallery-container .hero-item-container.is-dragging { cursor: grabbing; }
/* Only the inner boxes may stay visible. Apple deliberately clips on
   .gallery-container (`overflow:clip`), which is full-bleed width — so the
   off-screen cards are cut at the WINDOW edges rather than at the content
   column, which preserves the peek and stops the six cards (running out to
   x≈7700) from giving the whole page a horizontal scrollbar. Listing
   .gallery-container as visible here was what let that scrollbar appear. */
.section-hero,
.section-hero .hero-spotlight,
.section-hero .gallery-section-content { overflow: visible; }

/* Their column is centred over the art with room reserved beneath it
   (padding-block-end:220px) — we keep that and put the device in the space it
   reserves, which is how their own cards are composed. */
.section-hero .gallery-container .gallery-item .content .column { z-index: 1; }
.section-hero .hero-gallery-icon img { width: 64px; height: 64px; border-radius: 22.37%; display: block; }
@media only screen and (max-width: 734px) { .section-hero .hero-gallery-icon img { width: 54px; height: 54px; } }

/* Apple's column reserves 220px under the copy (padding-block-end) for the
   card's art. That reserve stays — it is the slot real screenshots will drop
   into — but nothing is drawn in it for now: the placeholder that was there
   read as a ghost phone rather than as art. The ground's own gradient mesh
   carries the card until there is something real to put in it. */

/* Apple's router chevron is a ligature in SF Pro Icons, which renders as tofu
   without their icon font loaded. An inline SVG is one glyph's worth of markup
   and can't fail that way. */
.section-routers .router-cta .icon-after svg,
.section-hero .hero-gallery-link .icon-after svg { margin-inline-start: 5px; vertical-align: -1px; }
/* Apple's spotlight CTA is one line of body text plus a glyph, not a filled
   pill. .icon-wrapper gives it the inline-flex box; the colour comes from the
   card's own theme-dark text colour. */
.section-hero .hero-gallery-link {
  display: inline-flex; align-items: center;
  color: #fff; text-decoration: none;
}
.section-hero .hero-gallery-link:hover .icon-copy { text-decoration: underline; }

/* Two more grounds per app, so a rail of six doesn't repeat the same three
   colours back to back. Savory and Wishlist have no pages yet — their cards say
   "Coming soon" — but they still have to look like something. */
.ground-savory    { --ground-base:#C33B3A; --ground-hi:#FFB27A; --ground-lo:#8E2B57; --ground-mid:#EE4E44; }
.ground-wishlist  { --ground-base:#4B2CB8; --ground-hi:#8FD4FF; --ground-lo:#2A1770; --ground-mid:#7A4AE0; }
.ground-savory-warm   { --ground-base:#B8323F; --ground-hi:#FFC79E; --ground-lo:#7A1F3D; --ground-mid:#E9615A; }
.ground-wishlist-deep { --ground-base:#33218C; --ground-hi:#7FD1FF; --ground-lo:#1A1050; --ground-mid:#6A5BE8; }
.ground-eagle-one-dusk{ --ground-base:#8E3A86; --ground-hi:#F0674B; --ground-lo:#3E2160; --ground-mid:#D64B72; }

/* --- page header ----------------------------------------------------------- */
/* Nothing overridden here any more. Measuring the live page showed that
   .hero-copy's 310px width and .hero-header's 80px-top / no-bottom padding are
   Apple's actual spec, not the bugs I had taken them for. Their rules stand. */

/* --- spotlight card interior ----------------------------------------------- */
/* Apple pulls .hero-gallery-icon in by -14px/-18px because their chiclet art
   ships with transparent shadow margin baked into the PNG. Ours is edge to
   edge, so those offsets dragged the headline up over the icon — the same
   trap their .card-chiclet sets, and the same fix. */
.section-hero .gallery-container .gallery-item .content .hero-gallery-icon {
  /* Cancelling their -14px/-18px is right; adding 24px on top of it was not.
     Their negatives exist only to eat the PNG's baked shadow margin (their own
     --chiclet-shadow-offset-bottom is -18px), so the value that reproduces
     their layout box on edge-to-edge art is zero. The 24px was 24px of dead
     space between icon and headline on all six cards, taken out of a card whose
     height is fixed by .hero-item-container's min-height:600px. */
  margin-top: 0;
  margin-bottom: 0;
}
/* Their chiclet art is 96.5px tall including baked-in transparent shadow
   margin, which the -14/-18 pulls cancel, leaving a 64.5px layout box. Ours is
   edge to edge, so 64px with no pull is the same box. */
.section-hero .hero-gallery-icon img { width: 64px; height: 64px; }
/* Their column stays a block at this width — 750px wide, contents centred by
   the inherited text-align. Making it a flex column shrank it to 569px and
   changed every measure inside it, so the icon is centred the way theirs is
   instead: as a block image inside a full-width picture. */
.section-hero .gallery-container .gallery-item .content .hero-gallery-icon img {
  margin-inline: auto;
}

/* --- the filter row (removed) ----------------------------------------------
   Apple's .section-content-filters — the sticky, frosting pill row — is gone
   from this page. It was a tabnav of Overview + Eagle One, which with a single
   app is a row of two pills where one is the page you are already on. It can
   come back with the second app; Apple's rules for it are still in
   apps-overview.css, and apps.js's .stick handler no-ops when the element is
   absent, so restoring it is markup only. */


/* --- tile chiclet ---------------------------------------------------------- */
/* Apple's chiclet has no width of its own — it comes from the PNG they serve
   per breakpoint, and .card-chiclet only applies the negative --chiclet-shadow-
   offset that cancels the transparent shadow margin baked into that art. Ours
   is edge-to-edge, so it needs both an explicit size and no negative pull.
   These sizes are read off their own cards: about an eighth of --card-width. */
.section-featured-apps .card-chiclet {
  width: 48px; height: 48px;
  /* Apple's card-header measures exactly 32px tall — the height of the app
     name's line box — because their 60px chiclet art is pulled back in by
     --chiclet-shadow-offset (-10px top, -18px bottom) so only 32px of it
     occupies layout while the rest overhangs. Ours is 48px of solid glyph, so
     -8px top and bottom lands the same 32px box, symmetrically. */
  margin: -8px 0;
  border-radius: 22.37%;
  overflow: hidden;
  flex: none;
  box-shadow: 0 4px 14px rgba(0,0,0,.18);
}
/* Exactly one step. Their chiclet <picture> carries two sources — (max-width:
   1068px) and (min-width:0px) — and their only CSS step is the shadow-offset
   change at 1068. The 1441 and 734 steps here were invented: the 1441 one grew
   .card-header from 32px to 36px and shifted the app name down 2px against an
   unchanged 32px line box. */
@media only screen and (max-width: 1068px) { .section-featured-apps .card-chiclet { width: 44px; height: 44px; } }
.section-featured-apps .card-chiclet img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Their art sits in the band .tile-content reserves with padding-bottom, and
   the compatibility block is pinned over it at the card's bottom edge. */
.section-featured-apps .card-bg { top: auto; }

/* --- last two measured deltas ---------------------------------------------- */
/* Apple's spotlight copy is clamped in three steps, and only the widest one was
   here. Stated without a media query it out-scored their own 352px and 250px
   rules (identical selector, identical specificity, this file loads last), so
   every sentence on all six cards wrapped at the wrong point on tablet and
   phone. Their steps, restated in order. */
@media only screen and (min-width: 735px) {
  .section-hero .gallery-container .gallery-item .content .hero-gallery-copy { max-width: 750px; }
}
@media only screen and (max-width: 734px) {
  .section-hero .gallery-container .gallery-item .content .hero-gallery-copy { max-width: 352px; }
}
@media only screen and (max-width: 480px) {
  .section-hero .gallery-container .gallery-item .content .hero-gallery-copy { max-width: 250px; }
}
/* Their card is 609.5px because .hero-item-container has min-height:600px and
   .gallery-item is height:100% of the grid row. Our flex rail plus the
   .align-top class (align-self:flex-start) stopped the item stretching, so it
   collapsed to content height and cards of different copy lengths came out
   different heights. */

/* --- "Coming soon" in the CTA slot ----------------------------------------- */
/* Apple's non-button CTA in this slot is .link, which inherits the card's body
   colour. Ours is a statement rather than a link, so it keeps that treatment
   but drops the pointer affordance — there is nowhere to go yet. */
/* The same label in the SPOTLIGHT. Apple's colour rule for this slot is
   `.section-featured-apps .card .ctas-wrapper .link`, scoped to the tile
   section — the hero is .section-hero, so in there the label fell back to the
   default near-black and sat unreadable on a dark card. It also has to take the
   hero CTA's own metrics rather than the tile's, so it lines up where the
   "Learn more" link does on the Eagle One cards. */
.section-hero .gallery-item .ctas-wrapper .cta-soon {
  display: inline-flex;
  align-items: center;
  color: #fff;
  cursor: default;
  font-size: 17px;
  line-height: 1.2353;
  letter-spacing: -.022em;
  opacity: .8;
}

.section-featured-apps .card .ctas-wrapper .cta-soon {
  display: inline-flex;
  align-items: center;
  height: 24px;                     /* the .button-reduced box it replaces */
  /* Apple's element in this slot computes 400 / opacity 1. The 600 and .85 that
     were here blended the near-white toward pink against the card gradients. */
  cursor: default;
}

/* --- globalnav below Apple's collapse point -------------------------------- */
/* Under 834px Apple replaces this bar with a hamburger, so every rule for the
   expanded list assumes a flyout: the items are faded to opacity:0, the mark is
   pinned to a 48px box sized for the Apple logo, and the links are given widths
   sized for wordmark images (125px for a four-letter word). Left alone that
   pushed the row 22px past the viewport and gave the page a horizontal
   scrollbar; undoing it one property at a time kept losing to selectors like
   `#globalnav .globalnav-item:not(.globalnav-menu, .globalnav-item-menu)`,
   which scores (1,2,0) because :not() counts its most specific argument.

   Scoping to both ids (2,1,0) settles it in one place. Our nav here is three
   short text links, so it needs none of the flyout machinery.

   The breakpoint was 1023px, a number that appears nowhere in globalheader.css
   — their collapse is 833/834. In the 834-1023 band this block fired against
   rules that were not active, so `margin:0` below beat
   .globalnav-item-mark's margin-inline-end:auto and the two right-hand links
   collapsed leftward to sit beside the mark. */
@media only screen and (max-width: 833px) {
  #globalheader #globalnav .globalnav-content {
    padding-inline: 16px;
    box-sizing: border-box;
  }
  #globalheader #globalnav .globalnav-list {
    padding: 0;
    gap: 20px;
    justify-content: flex-start;
    width: auto;
  }
  #globalheader #globalnav .globalnav-item,
  #globalheader #globalnav .globalnav-link {
    opacity: 1;
    width: max-content;
    min-width: 0;
    max-width: none;
    margin: 0;
    padding-inline: 0;
    flex: 0 0 auto;
  }
  #globalheader #globalnav .globalnav-link-text { white-space: nowrap; }
}
@media only screen and (max-width: 480px) {
  #globalheader #globalnav .globalnav-list { gap: 16px; }
  #globalheader #globalnav .globalnav-link-text { font-size: 11px; }
  #globalheader #globalnav .globalnav-item-mark .globalnav-mark-text { font-size: 13px; }
}

/* --- artwork placeholders -------------------------------------------------- */
/* Standing in for screenshots that aren't final. Deliberately the same box as
   .device — same width tokens, same 1350/2760 aspect, same drop shadow — so
   every measurement matched against Apple's layout still holds when real art
   drops in and this class comes back out. */
.ph {
  --device-w: 232px;
  position: relative;
  width: var(--device-w);
  aspect-ratio: 1350 / 2760;
  flex: none;
  border-radius: calc(var(--device-w) * 0.148);
  background-color: var(--ph-base, #8E8E93);
  background-image:
    radial-gradient(120% 80% at 78% 6%,  var(--ph-hi)  0%, transparent 60%),
    radial-gradient(100% 70% at 14% 96%, var(--ph-lo)  0%, transparent 64%);
  box-shadow: 0 22px 40px rgba(0,0,0,.20), 0 4px 10px rgba(0,0,0,.12);
}
@media only screen and (min-width: 1441px) { .ph { --device-w: 262px; } }
@media only screen and (max-width: 1068px) { .ph { --device-w: 216px; } }
@media only screen and (max-width: 734px)  { .ph { --device-w: 190px; } }
@media only screen and (max-width: 480px)  { .ph { --device-w: 176px; } }


.ph-1 { --ph-base:#E8452F; --ph-hi:#FF8A4E; --ph-lo:#B02F86; }
.ph-2 { --ph-base:#D9647F; --ph-hi:#FFC79E; --ph-lo:#8E2B57; }
.ph-3 { --ph-base:#7A4AE0; --ph-hi:#8FD4FF; --ph-lo:#2A1770; }
.ph-4 { --ph-base:#2C6E63; --ph-hi:#6FD8B0; --ph-lo:#123C46; }
.ph-5 { --ph-base:#3C5BC4; --ph-hi:#7FD1FF; --ph-lo:#1B2A7A; }
.ph-6 { --ph-base:#C4453C; --ph-hi:#FFB199; --ph-lo:#6E1F3C; }

/* A part-visible card is a target, so it should say so. The current one is not
   — its own link handles clicks. */
.section-hero .gallery-container .gallery-item { cursor: pointer; }
.section-hero .gallery-container .gallery-item.active { cursor: default; }

/* Apple declares `position:absolute; height:14px` and leans on the flex
   container's static position to centre these. That resolves differently
   depending on how a browser treats the static position of an abspos flex
   child, which is how ours ended up off-centre. Pinning them removes the
   ambiguity and lands in the same place. */
.play-pause-button { position: relative; }
.play-pause-button svg {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  height: 14px;
  width: auto;
}

/* --- last rail bottom padding ---------------------------------------------- */
/* Apple's `.section-featured-apps .gallery-content:last-of-type:not(.gallery-tv)
   :not(.gallery-apple-creator-studio){padding-bottom:54px}` is absorbed on
   apple.com by a hidden creator-studio gallery, so no visible rail ever takes
   it and every one keeps .section-more-bottom-pad's 150px. Ours has no hidden
   last child, so the in-development rail was landing 96px closer to the routers
   than the shipped rail sits to it. Their rule is (0,5,0); this has to match or
   it loses regardless of load order. */
.section-featured-apps .featured-apps-panel-avp .gallery-content.section-more-bottom-pad:last-of-type { padding-bottom: 150px; }
@media only screen and (max-width: 1068px) { .section-featured-apps .featured-apps-panel-avp .gallery-content.section-more-bottom-pad:last-of-type { padding-bottom: 134px; } }
@media only screen and (max-width: 734px)  { .section-featured-apps .featured-apps-panel-avp .gallery-content.section-more-bottom-pad:last-of-type { padding-bottom: 124px; } }
@media only screen and (max-width: 480px)  { .section-featured-apps .featured-apps-panel-avp .gallery-content.section-more-bottom-pad:last-of-type { padding-bottom: 118px; } }


/* The maker's byline under the hero copy. Sized and coloured as secondary text
   so it reads as an attribution rather than a second headline — it is there to
   be found and followed, not to compete with the positioning line above it. */
.hero-byline {
  margin: 10px 0 0;
  font-size: 17px;
  line-height: 1.4705882353;
  letter-spacing: -0.022em;
  color: #6e6e73;
}
.hero-byline a { color: inherit; text-decoration: none; border-bottom: 1px solid rgba(110,110,115,.35); }
.hero-byline a:hover { color: #1d1d1f; border-bottom-color: rgba(29,29,31,.5); }
@media (max-width: 734px) {
  .hero-byline { font-size: 15px; }
}

/* ---------------------------------------------------------------------------
   Small screens: metrics that only work when there is a photograph in them.

   Two of Apple's numbers are load-bearing on apple.com and actively wrong here,
   for the same reason: their cards carry product photography that bleeds to the
   bottom edge, and ours carry a gradient. A reserve of empty space reads as
   composition when something is in it and as a mistake when nothing is.
--------------------------------------------------------------------------- */
@media only screen and (max-width: 734px) {
  /* The spotlight card. .hero-item-container is min-height:600px, dropping to
     555px under 1068px and never again — so at 390px wide the card is 350x555
     with the content ending at 298. Forty-six per cent of it was empty gradient.
     The 130px reserve on the column is where Apple puts the product shot.
     Height comes from the content now: the container is a grid with every card
     in one cell, so the tallest still sets the height and the row cannot jump
     as it advances. */
  .section-hero .gallery-container .hero-item-container { min-height: 0; }
  .section-hero .gallery-container .content.row { padding-top: 52px; padding-bottom: 52px; }
  /* padding-block-end, not padding-bottom, and with the .gallery-item in the
     selector: Apple writes the logical property and one more class than the
     obvious override needs, so the obvious override silently loses and the card
     keeps its reserve. Matching the selector exactly and landing later in the
     cascade is what actually wins it. */
  .section-hero .gallery-container .gallery-item .content .column { padding-block-end: 44px; }

  /* The local nav pills overflowed by 27px at 390, which is the worst amount to
     overflow by: enough to clip the first and last word, not enough to look
     like a row you are meant to scroll. Apple's own phone rule tightens the gap
     to 10px; a little more, and the four fit. It is still a scroller below
     about 360px, which is correct — it just is not one when it does not need
     to be. */
  nav.localnav .localnav-items { padding: 12px 14px; }
  nav.localnav .localnav-item + .localnav-item { margin-inline-start: 5px; }
  nav.localnav .localnav-link { padding: 8px 11px; }
}

/* --- glyph art ------------------------------------------------------------- */
/* Savory and Wishlist have no screenshots yet, so their rail tiles carry a mark
   instead of a ghost phone: the app's own icon on the lead tile, then one glyph
   per feature on a frosted squircle. It reads as art rather than as something
   missing. When real screenshots exist, swap .glyph-art for a .device exactly as
   the Eagle One rail does; .card-bg needs nothing else. */
.section-featured-apps .card-bg .glyph-art {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 78%;
}
.glyph-art-icon {
  width: 168px; height: 168px;
  border-radius: 22.37%;
  box-shadow: 0 24px 48px rgba(0,0,0,.24), 0 6px 14px rgba(0,0,0,.14);
}
.glyph-art-tile {
  display: flex; align-items: center; justify-content: center;
  width: 168px; height: 168px;
  border-radius: 22.37%;
  color: #fff;
  background: rgba(255,255,255,.16);
  border: 1px solid rgba(255,255,255,.32);
  box-shadow: 0 24px 48px rgba(0,0,0,.16), inset 0 1px 0 rgba(255,255,255,.28);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
          backdrop-filter: blur(18px) saturate(140%);
}
.glyph-art-tile .glyph { width: 46%; height: 46%; }
@media only screen and (max-width: 734px) {
  .glyph-art-icon, .glyph-art-tile { width: 128px; height: 128px; }
}

/* The art area stops short of the tile's footnote (.comp, ~36px plus its
   padding) instead of running to the bottom edge. A full phone is taller than
   the space left under the copy, so it is cropped there, and a short fade makes
   the crop read as a bleed rather than a cut. Without this the phone covered
   the white footnote text. */
.section-featured-apps .card-bg {
  bottom: 68px;
  height: 382px;
  -webkit-mask-image: linear-gradient(#000 80%, transparent);
          mask-image: linear-gradient(#000 80%, transparent);
}
@media only screen and (min-width: 1441px) { .section-featured-apps .card-bg { height: 456px; } }
@media only screen and (max-width: 1068px) { .section-featured-apps .card-bg { height: 367px; } }
@media only screen and (max-width: 734px)  { .section-featured-apps .card-bg { height: 267px; } }
@media only screen and (max-width: 480px)  { .section-featured-apps .card-bg { height: 246px; } }
.section-featured-apps .card-bg .glyph-art-icon { position: static; inset: auto; }
