/* ==========================================================================
   SizeUp — landing page
   --------------------------------------------------------------------------
   The material is Apple's "liquid glass". Three things do the work, and all
   three have to be present or it collapses into flat 2018 glassmorphism:

     1. blur() *and* saturate(). Saturation is what reads as refraction; blur
        on its own is grey fog.
     2. A directional specular rim. Real glass catches light on one side of its
        edge — a uniform 1px white border is the single loudest "AI glass" tell.
        Done here with a mask-composite gradient ring so it follows the radius.
     3. Grain. Mathematically smooth gradients are the other giveaway.

   backdrop-filter is the most expensive property in CSS, so it is rationed:
   `.glass` (real backdrop-filter) is used on exactly three surfaces — the
   sticky nav, the hero display, the download slab. Everything else uses
   `.panel`, which is the same visual grammar minus the filter. They sit over
   the same aurora, so the difference is barely legible; the cost difference
   is enormous. Glass is also never nested inside glass — nested backdrop roots
   behave inconsistently across engines and double the work.
   ========================================================================== */

/* ---------------------------------------------------------------- tokens */
:root {
  --ink:       #f2f5ff;
  --ink-dim:   rgb(233 238 255 / .72);
  /* Was .40, which measured 3.4:1 on the page background — below WCAG AA's 4.5:1 for
     normal-size text, and this token is used on real prose (the hero fineprint, the
     download note), not just decorative labels. .58 clears it. */
  --ink-faint: rgb(233 238 255 / .58);

  --void:      #06070d;
  --brand:     #4a7fe8;
  --brand-up:  #6fa0ff;
  --cyan:      #5ac8fa;
  --violet:    #6e5cf0;

  /* Diagonal, so every panel has a lit side and a shadowed side. */
  --fill: linear-gradient(148deg,
            rgb(255 255 255 / .105) 0%,
            rgb(255 255 255 / .042) 42%,
            rgb(255 255 255 / .028) 60%,
            rgb(255 255 255 / .072) 100%);
  --frost: blur(30px) saturate(190%) brightness(1.06);

  /* Tight contact shadow + wide ambient one. */
  --lift: 0 2px 6px rgb(2 4 12 / .34), 0 28px 64px -18px rgb(2 4 12 / .72);

  --r-lg: 30px;
  --r-md: 20px;
  --r-sm: 12px;

  --spring: cubic-bezier(.32, 1.28, .38, 1);
  --glide:  cubic-bezier(.22, .78, .26, 1);

  /* The page is edge-to-edge (viewport-fit=cover), so on a notched iPhone in
     landscape the safe area is wider than the design gutter. max() takes whichever
     is larger, which keeps every section clear of the notch and the home indicator
     without touching the layout on any device that has neither. */
  --gutter-base: clamp(1.25rem, 5vw, 5rem);
  --gutter: max(var(--gutter-base), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px));
  --measure: 1200px;

  /* Body text is the system face: on a Mac that is SF Pro, which is exactly
     right for a Mac utility and costs nothing to load. The display face is
     Instrument Sans — tighter and more characterful than the usual grotesques,
     and it keeps the headlines from reading as stock UI. */
  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Instrument Sans", system-ui, sans-serif;
  --display: "Instrument Sans", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

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

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  min-height: 100vh;
  background: var(--void);
  color: var(--ink);
  font: 400 clamp(1rem, .96rem + .2vw, 1.06rem)/1.62 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* `clip` rather than `hidden`: `overflow-x: hidden` makes the element a scroll
     container, which breaks `position: sticky` on the nav in several engines.
     `clip` cuts the overflow without that side effect; the `hidden` line stays as
     the fallback for engines that don't know `clip` yet. Both are a backstop —
     nothing on the page is supposed to overflow in the first place. */
  overflow-x: hidden;
  overflow-x: clip;
}

img { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; padding: 0; }
:focus-visible { outline: 2px solid var(--brand-up); outline-offset: 3px; border-radius: 6px; }

/* ---------------------------------------------------------------- atmosphere */
.atmosphere { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }

/* Enormous, slow, pre-blurred colour masses. Without them the glass blurs a flat
   black backdrop and the whole material dies. Only `transform` animates —
   animating `filter` or `background-position` would re-run the blur every frame. */
.aurora {
  position: absolute;
  border-radius: 50%;
  filter: blur(110px);
  opacity: .5;
  will-change: transform;
}
.aurora--a {
  width: 62vw; height: 62vw; min-width: 460px; min-height: 460px;
  top: -24vw; left: -14vw;
  background: radial-gradient(circle at 40% 40%, var(--brand), rgb(74 127 232 / 0) 68%);
  animation: drift-a 28s var(--glide) infinite alternate;
}
.aurora--b {
  width: 54vw; height: 54vw; min-width: 400px; min-height: 400px;
  top: 4vh; right: -18vw;
  background: radial-gradient(circle at 50% 50%, var(--violet), rgb(110 92 240 / 0) 66%);
  animation: drift-b 34s var(--glide) infinite alternate;
}
.aurora--c {
  width: 46vw; height: 46vw; min-width: 340px; min-height: 340px;
  top: 52vh; left: 24vw;
  opacity: .28;
  background: radial-gradient(circle at 50% 50%, var(--cyan), rgb(90 200 250 / 0) 64%);
  animation: drift-c 41s var(--glide) infinite alternate;
}
@keyframes drift-a { to { transform: translate3d(9vw, 7vh, 0) scale(1.14); } }
@keyframes drift-b { to { transform: translate3d(-11vw, 12vh, 0) scale(.88); } }
@keyframes drift-c { to { transform: translate3d(7vw, -11vh, 0) scale(1.2); } }

/* This product's subject is the grid. Stating it faintly gives the glass
   something with structure to distort, not just soft colour. */
.grid-field {
  position: absolute;
  inset: -1px;
  background-image:
    linear-gradient(rgb(255 255 255 / .045) 1px, transparent 1px),
    linear-gradient(90deg, rgb(255 255 255 / .045) 1px, transparent 1px);
  background-size: 88px 88px;
  mask-image: radial-gradient(ellipse 94% 70% at 50% 18%, #000 6%, transparent 76%);
}

/* Static, deliberately. Animated feTurbulence is a Safari performance disaster,
   and the still grain does the whole job: it kills gradient banding and takes
   the CG sheen off every smooth surface on the page. */
.grain {
  position: absolute;
  inset: 0;
  opacity: .14;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

@media (prefers-reduced-motion: reduce) { .aurora { animation: none; } }

@media (max-width: 40rem) {
  /* Phone GPUs pay for a large blur radius far more than a desktop one does, and the
     aurorae are the biggest blurred surfaces on the page — three of them, animating,
     beneath three backdrop-filtered panels. At this size the colour masses are only
     ~400px across, so a smaller radius is not visible, and it takes a real bite out
     of the per-frame compositing cost. */
  .aurora { filter: blur(64px); }
  /* Proportion, not performance: an 88px grid across a 375px screen is four cells,
     which reads as a few stray lines rather than as a grid. */
  .grid-field { background-size: 64px 64px; }
}

/* ---------------------------------------------------------------- material */
.glass, .panel {
  position: relative;
  isolation: isolate;
  border-radius: var(--r-lg);
  background: var(--fill);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .14),          /* bright top lip */
    inset 0 -14px 26px -14px rgb(0 0 0 / .38),     /* dark lower belly */
    var(--lift);
}

/* The only three surfaces that pay for a backdrop filter. */
.glass {
  -webkit-backdrop-filter: var(--frost);
          backdrop-filter: var(--frost);
}

/* The specular rim. A padding-box/border-box mask XOR leaves a 1px ring, which —
   unlike `border` — can carry a gradient, so light falls off around the shape the
   way it does on a real bevelled edge. Brightest at top-left, the implied source. */
.glass::before, .panel::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(150deg,
              rgb(255 255 255 / .58) 0%,
              rgb(255 255 255 / .13) 26%,
              rgb(255 255 255 / .03) 48%,
              rgb(255 255 255 / .10) 78%,
              rgb(255 255 255 / .34) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}

/* Sheen pooled at the top, as if lit from above and in front. Kept very low
   contrast — this is the detail that tips into cheap plastic the moment it shouts. */
.glass::after, .panel::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  background: radial-gradient(120% 76% at 22% -14%, rgb(255 255 255 / .15), transparent 62%);
  pointer-events: none;
}

/* Content sits above both pseudo-elements. */
.glass > *, .panel > * { position: relative; z-index: 3; }

/* Cursor-tracked specular hotspot; --mx/--my are written by app.js on pointermove.
   True refraction would need backdrop-filter: url(#displacement), which is
   Chromium-only — no use for a Mac app's audience. This reads as glass and ships
   everywhere. The faint cyan/warm pair at opposite corners fakes the chromatic
   split you get at the edge of a real lens. */
.glass--live::after {
  background:
    radial-gradient(240px 240px at var(--mx, 50%) var(--my, 0%), rgb(255 255 255 / .13), transparent 68%),
    radial-gradient(90% 60% at 0% 0%, rgb(90 200 250 / .11), transparent 55%),
    radial-gradient(90% 60% at 100% 100%, rgb(255 120 160 / .09), transparent 55%),
    radial-gradient(120% 76% at 22% -14%, rgb(255 255 255 / .15), transparent 62%);
}

/* ---------------------------------------------------------------- type */
h1, h2, h3 {
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: -.035em;
  line-height: .99;
  text-wrap: balance;
}

h1 { font-size: clamp(2.7rem, 5.4vw, 4.5rem); }
h2 { font-size: clamp(2rem, 4.2vw, 3.3rem); }
h3 { font-size: 1.2rem; line-height: 1.24; letter-spacing: -.022em; }

/* Gradient text exactly once on the page — in the hero. Everywhere else the
   emphasis comes from contrast, which is why this one still lands. */
.hero h1 em {
  font-style: normal;
  background: linear-gradient(96deg, var(--brand-up) 0%, var(--cyan) 48%, var(--violet) 100%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}
h3 em { font-style: italic; color: var(--brand-up); }

.eyebrow {
  display: block;
  margin-bottom: 1.05rem;
  font: 500 .72rem/1 var(--mono);
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

kbd {
  display: inline-grid;
  place-items: center;
  min-width: 1.85em;
  height: 1.85em;
  padding: 0 .4em;
  border-radius: 7px;
  background: linear-gradient(180deg, rgb(255 255 255 / .17), rgb(255 255 255 / .055));
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .44),
    inset 0 0 0 1px rgb(255 255 255 / .1),
    0 2px 4px rgb(0 0 0 / .34);
  /* Deliberately the system face, not the mono: ⌃⌥⇧⌘ and the arrows come from
     SF Pro on a Mac, which draws them exactly as the OS does. JetBrains Mono
     substitutes a fallback for several of them and ⇧ lands as a hollow diamond. */
  font: 500 .82em/1 var(--sans);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

code { font: 400 .82em/1.5 var(--mono); color: var(--ink-dim); overflow-wrap: anywhere; }

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .6rem;
  max-width: 100%;
  padding: .92rem 1.5rem;
  border-radius: 999px;
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: -.012em;
  white-space: nowrap;
  transition: transform .32s var(--spring), box-shadow .32s var(--glide), background-color .3s var(--glide);
}
/* Lift on hover only where hovering is a thing. On a touchscreen `:hover` sticks
   after a tap, so the button stays raised until you touch elsewhere. */
@media (hover: hover) {
  .btn:hover { transform: translateY(-2px); }
}
.btn:active { transform: translateY(0) scale(.985); transition-duration: .09s; }

.btn--primary {
  background: linear-gradient(178deg, var(--brand-up), var(--brand) 58%, #2f5fc4);
  color: #fff;
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .46),
    inset 0 -1px 0 rgb(0 0 0 / .18),
    0 2px 8px rgb(30 62 140 / .4),
    0 16px 38px -10px rgb(58 106 220 / .62);
}
@media (hover: hover) {
  .btn--primary:hover {
    box-shadow:
      inset 0 1px 0 rgb(255 255 255 / .5),
      0 4px 12px rgb(30 62 140 / .44),
      0 24px 52px -12px rgb(74 127 232 / .78);
  }
}

.btn--ghost,
.btn--sm:not(.btn--primary) {
  background: rgb(255 255 255 / .065);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .14), inset 0 1px 0 rgb(255 255 255 / .22);
  color: var(--ink);
}
@media (hover: hover) {
  .btn--ghost:hover, .btn--sm:not(.btn--primary):hover { background: rgb(255 255 255 / .12); }
}

.btn--sm { padding: .55rem 1.1rem; font-size: .92rem; }
.btn--lg { padding: 1.1rem 2rem; font-size: 1.08rem; }

.btn__meta {
  padding-left: .72rem;
  border-left: 1px solid rgb(255 255 255 / .26);
  font: 400 .8em/1 var(--mono);
  opacity: .82;
}

/* "Download SizeUp 1.0.0 · 3.6 MB · .dmg" on one nowrap line is ~370px, which runs
   off the side of every phone. Stack the metadata under the label instead of
   letting it push the pill past the viewport — the divider rule becomes a rule
   above rather than beside. */
@media (max-width: 30rem) {
  .btn--lg {
    flex-direction: column;
    gap: .5rem;
    padding: .95rem 1.4rem;
    font-size: 1rem;
    white-space: normal;
    text-align: center;
  }
  .btn--lg .btn__meta {
    padding: .5rem 0 0;
    border-left: 0;
    border-top: 1px solid rgb(255 255 255 / .26);
    align-self: stretch;
  }
}

/* ---------------------------------------------------------------- nav */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: clamp(1rem, 4vw, 2.5rem);
  padding: .8rem var(--gutter);
  transition: background-color .4s var(--glide), box-shadow .4s var(--glide);
}
/* Frosted only once it actually overlaps content — a permanently frosted bar
   over the hero reads as a seam across the page. */
.nav.is-stuck {
  background: rgb(6 7 13 / .55);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
          backdrop-filter: blur(22px) saturate(180%);
  box-shadow: 0 1px 0 rgb(255 255 255 / .07), 0 12px 32px -18px rgb(0 0 0 / .9);
}

.nav__brand {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin-right: auto;
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.06rem;
  letter-spacing: -.025em;
}
.nav__brand img { border-radius: 7px; }

.nav__links { display: flex; gap: 1.7rem; font-size: .94rem; color: var(--ink-dim); }
.nav__links a { transition: color .25s; }
@media (hover: hover) { .nav__links a:hover { color: var(--ink); } }
@media (max-width: 860px) { .nav__links { display: none; } }
/* Nav Download is the one control above the fold on a phone; 33px tall is too small
   to hit reliably while scrolling one-handed. */
@media (pointer: coarse) { .nav .btn--sm { padding: .7rem 1.2rem; } }

/* ---------------------------------------------------------------- hero */
/* Asymmetric on purpose: copy left, product right. A centred hero column is the
   layout every generated landing page reaches for first. */
.hero {
  display: grid;
  grid-template-columns: minmax(0, .88fr) minmax(0, 1.12fr);
  align-items: center;
  gap: clamp(2.5rem, 5vw, 4.5rem);
  max-width: var(--measure);
  margin-inline: auto;
  padding: clamp(2.5rem, 6vw, 5rem) var(--gutter) clamp(2.5rem, 5vw, 4rem);
}
@media (max-width: 1000px) {
  .hero { grid-template-columns: 1fr; gap: 3rem; }
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  padding: .42rem 1rem .42rem .7rem;
  border-radius: 999px;
  background: rgb(255 255 255 / .06);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .13), inset 0 1px 0 rgb(255 255 255 / .2);
  font-size: .845rem;
  color: var(--ink-dim);
  transition: background-color .3s, color .3s;
}
@media (hover: hover) { .pill:hover { background: rgb(255 255 255 / .1); color: var(--ink); } }
.pill__dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #37d67a;
  box-shadow: 0 0 0 3px rgb(55 214 122 / .2), 0 0 12px rgb(55 214 122 / .9);
}

.hero h1 { margin: 1.5rem 0 0; }

.lede {
  max-width: 42ch;
  margin-top: 1.4rem;
  font-size: clamp(1.04rem, .98rem + .35vw, 1.18rem);
  line-height: 1.58;
  color: var(--ink-dim);
  text-wrap: pretty;
}

.cta { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: 2rem; }
.fineprint { margin-top: 1.25rem; font-size: .83rem; color: var(--ink-faint); }

/* ---------------------------------------------------------------- demo stage */
.screen { overflow: hidden; border-radius: var(--r-lg); }

.screen__bar {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .78rem 1.1rem;
  box-shadow: inset 0 -1px 0 rgb(255 255 255 / .08);
}
.dot { width: 11px; height: 11px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgb(0 0 0 / .18); }
.dot--r { background: #ff5f57; } .dot--y { background: #febc2e; } .dot--g { background: #28c840; }

.screen__label {
  margin-inline: auto;
  padding-right: 44px;                  /* optically centre against the three dots */
  font: 500 .8rem/1 var(--mono);
  letter-spacing: .04em;
  color: var(--ink-faint);
  transition: color .3s var(--glide);
}
.screen__label.is-hot { color: var(--brand-up); }

/* Two displays side by side, so "next monitor" is shown rather than claimed. */
.screen__rig {
  position: relative;
  display: grid;
  grid-template-columns: 1fr .52fr;
  gap: 10px;
  aspect-ratio: 16 / 9.6;
  padding: 10px;
  background:
    radial-gradient(70% 90% at 16% 6%, rgb(110 92 240 / .34), transparent 62%),
    radial-gradient(80% 90% at 90% 94%, rgb(90 200 250 / .26), transparent 60%),
    linear-gradient(160deg, #101736, #070a18 62%, #0b0f22);
}
/* On a phone the rig is only ~335px wide. At the desktop 16/9.6 that leaves a 200px
   stage, and quarters end up ~90×45 — too small to read as windows. A taller box and
   a narrower second display buy back the height without changing the geometry the
   demo is actually showing. */
@media (max-width: 40rem) {
  .screen__rig { grid-template-columns: 1fr .44fr; aspect-ratio: 16 / 12; }
}
/* A phone held sideways has ~390px of height, and the stage's aspect box wants 456
   at that width — the demo would not fit on screen in one piece. Drive it off the
   viewport height instead. Nothing about the geometry changes: app.js measures the
   desks live and re-derives the frame from a ResizeObserver, so the demo stays the
   real calculation at whatever size the box ends up. */
@media (orientation: landscape) and (max-height: 32rem) {
  .screen__rig { aspect-ratio: auto; height: 62vh; }
}

.desk {
  position: relative;
  border-radius: 10px;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .07);
  transition: box-shadow .4s var(--glide);
}
.desk.is-active { box-shadow: inset 0 0 0 1px rgb(111 160 255 / .38); }
.desk__tag {
  position: absolute;
  left: 9px; bottom: 7px;
  font: 400 .62rem/1 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: rgb(255 255 255 / .22);
}

/* The window. Position and size are set in px against the rig by app.js, from the
   same fractions/margins/gaps the Swift FrameCalculator uses — the demo is the
   real geometry, not a hand-tuned imitation of it.
   No backdrop-filter here: it lives inside .screen, which already is a backdrop
   root, and nesting them is both unreliable and expensive. A plain translucent
   fill over the wallpaper is indistinguishable at this size. */
.win {
  position: absolute;
  display: flex;
  flex-direction: column;
  gap: .4rem;
  padding: .5rem .6rem;
  border-radius: 9px;
  background: linear-gradient(155deg, rgb(233 240 255 / .26), rgb(200 218 255 / .12));
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .5),
    inset 0 0 0 1px rgb(255 255 255 / .18),
    0 14px 30px -10px rgb(0 0 0 / .7);
  overflow: hidden;
  transition:
    left .58s var(--spring), top .58s var(--spring),
    width .58s var(--spring), height .58s var(--spring);
}
.win__bar { display: flex; gap: 3.5px; }
.win__bar i { width: 5px; height: 5px; border-radius: 50%; background: rgb(255 255 255 / .4); }
.win__name {
  font: 600 .74rem/1 var(--display);
  letter-spacing: -.01em;
  color: rgb(255 255 255 / .9);
  white-space: nowrap;
}
/* A quarter tile on a phone is narrower than the word "Editor", so the label was
   sitting flush against — and visually breaching — the window's right edge. The
   traffic lights alone still read as a window at that size. */
@media (max-width: 40rem) {
  .win { padding: .4rem .45rem; }
  .win__name { display: none; }
}
@media (prefers-reduced-motion: reduce) { .win { transition-duration: .01ms; } }

/* --- try bar */
.trybar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .9rem 1.4rem;
  margin-top: .85rem;
  padding: 1rem 1.25rem;
  border-radius: var(--r-md);
}
.trybar__hint { font-size: .89rem; color: var(--ink-dim); }
.trybar__hint strong { color: var(--ink); font-weight: 600; }
.trybar__hint kbd { margin: 0 .08em; }

/* Keyboard wording by default — including on a touch-capable laptop, where both
   inputs exist and the chords do work. Only a device whose *primary* pointer is a
   finger gets the tap wording. */
.trybar__hint-tap { display: none; }
@media (hover: none) and (pointer: coarse) {
  .trybar__hint-keys { display: none; }
  .trybar__hint-tap { display: inline; }
}
.trybar__keys { display: flex; flex-wrap: wrap; gap: .4rem; }

.chip {
  padding: .38rem .8rem;
  border: 0;
  border-radius: 999px;
  background: rgb(255 255 255 / .06);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .12), inset 0 1px 0 rgb(255 255 255 / .18);
  font: 500 .82rem/1.35 var(--sans);
  color: var(--ink-dim);
  cursor: pointer;
  transition: background-color .24s, color .24s, transform .3s var(--spring);
}
@media (hover: hover) {
  .chip:hover { background: rgb(255 255 255 / .12); color: var(--ink); transform: translateY(-1px); }
}
/* The chip row is the primary control on a phone — the keyboard chords it doubles as
   a legend for aren't reachable there. 28px tall is under every touch-target
   guideline; min-height pins the result at 44px rather than leaving it to whatever
   padding plus the current font size happens to add up to. */
@media (pointer: coarse) {
  .chip {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: .5rem 1.05rem;
    font-size: .88rem;
  }
}
.chip.is-active {
  background: linear-gradient(178deg, var(--brand-up), var(--brand));
  color: #fff;
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .42), 0 6px 18px -6px rgb(74 127 232 / .8);
}

/* ---------------------------------------------------------------- sections */
.section {
  max-width: var(--measure);
  margin-inline: auto;
  padding: clamp(3.5rem, 7vw, 6rem) var(--gutter);
}
.section--tight { padding-block: clamp(1.75rem, 3.5vw, 2.75rem); }

.section__head { max-width: 48ch; margin-bottom: clamp(2.5rem, 5vw, 3.5rem); }
.section__head p { margin-top: 1.1rem; font-size: 1.04rem; color: var(--ink-dim); text-wrap: pretty; }

/* ---------------------------------------------------------------- action cards */
/* Five groups of unequal length. Left to auto-fit they break 4 + 1, which strands
   a lone card on its own row. A six-column track lets the top row take three
   cards of two and the bottom row two of three — balanced, and it puts the four
   Spaces rows beside the two Displays rows where they read as a pair. */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr)); gap: 1rem; }
@media (min-width: 940px) {
  .cards { grid-template-columns: repeat(6, 1fr); }
  .cards > :nth-child(-n+3) { grid-column: span 2; }
  .cards > :nth-child(n+4)  { grid-column: span 3; }
}

.card { padding: 1.35rem 1.45rem 1.45rem; border-radius: var(--r-md); }
.card__title {
  margin-bottom: 1rem;
  font: 500 .68rem/1 var(--mono);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.row {
  display: flex;
  align-items: center;
  gap: .8rem;
  padding: .48rem 0;
  font-size: .93rem;
}
.row + .row { box-shadow: inset 0 1px 0 rgb(255 255 255 / .07); }
.row__glyph { flex-shrink: 0; color: var(--brand-up); opacity: .9; }
.row__name { margin-right: auto; white-space: nowrap; }
.row__keys { display: flex; gap: .18rem; flex-shrink: 0; }

/* ---------------------------------------------------------------- snapback band */
/* Full bleed, and the only section that breaks the measure — the differentiator
   should not sit in the same box as everything else. */
.band {
  position: relative;
  margin-block: clamp(1rem, 2.5vw, 2rem);
  padding: clamp(3rem, 6.5vw, 5rem) 0;
  background: linear-gradient(180deg, transparent, rgb(74 127 232 / .06) 30%, rgb(110 92 240 / .07) 70%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .07), inset 0 -1px 0 rgb(255 255 255 / .07);
}
.band__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(2rem, 5vw, 4rem);
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
@media (max-width: 880px) { .band__inner { grid-template-columns: 1fr; } }
.band__copy p { margin-top: 1.1rem; max-width: 44ch; color: var(--ink-dim); text-wrap: pretty; }
.band__keys { display: flex; gap: .22rem; margin-top: 1.6rem; }

.mini {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: var(--r-md);
  padding: 9px;
  background:
    radial-gradient(70% 90% at 20% 10%, rgb(110 92 240 / .3), transparent 60%),
    linear-gradient(160deg, #0f1533, #070a18);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .09), var(--lift);
}
.mini__win {
  position: absolute;
  border-radius: 8px;
  background: linear-gradient(155deg, rgb(233 240 255 / .28), rgb(200 218 255 / .12));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .5), inset 0 0 0 1px rgb(255 255 255 / .18);
  transition: left .6s var(--spring), top .6s var(--spring), width .6s var(--spring), height .6s var(--spring);
}
.mini__caption {
  margin-top: .8rem;
  font: 500 .78rem/1 var(--mono);
  letter-spacing: .06em;
  color: var(--ink-faint);
  transition: color .3s;
}
.mini__caption.is-hot { color: var(--brand-up); }
@media (prefers-reduced-motion: reduce) { .mini__win { transition-duration: .01ms; } }

/* ---------------------------------------------------------------- bento */
/* Six equal cards, two clean rows. One card spanning two columns makes seven
   units in a three-column grid, which can never fill evenly — the sixth card ends
   up stranded alone on a third row. */
.bento { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(268px, 100%), 1fr)); gap: 1rem; }
@media (min-width: 920px) { .bento { grid-template-columns: repeat(3, 1fr); } }

.feat {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .65rem;
  padding: 1.7rem;
  border-radius: var(--r-md);
  transition: transform .45s var(--glide), box-shadow .45s var(--glide);
}
@media (hover: hover) {
  .feat:hover {
    transform: translateY(-3px);
    box-shadow:
      inset 0 1px 0 rgb(255 255 255 / .18),
      inset 0 -14px 26px -14px rgb(0 0 0 / .38),
      0 2px 6px rgb(2 4 12 / .34), 0 40px 72px -22px rgb(2 4 12 / .85);
  }
}
.feat p { font-size: .95rem; color: var(--ink-dim); text-wrap: pretty; }
.feat p em { font-style: italic; color: var(--ink); }

.feat__icon {
  display: grid;
  place-items: center;
  width: 42px; height: 42px;
  margin-bottom: .3rem;
  border-radius: 12px;
  background: linear-gradient(160deg, rgb(255 255 255 / .19), rgb(255 255 255 / .05));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .4), inset 0 0 0 1px rgb(255 255 255 / .1);
  color: var(--brand-up);
}

/* ---------------------------------------------------------------- cheat sheet */
.cheats { padding: clamp(1.75rem, 4vw, 2.5rem); border-radius: var(--r-md); }
.cheats__head { margin-bottom: 1.5rem; }
.cheats__list {
  display: grid;
  /* min() is the guard that makes auto-fit safe below the stated minimum: a bare
     minmax(330px, …) keeps a 330px track on a 320px phone and pushes the row off
     the side, because auto-fit only ever removes *columns*, never shrinks the floor. */
  grid-template-columns: repeat(auto-fit, minmax(min(330px, 100%), 1fr));
  gap: .1rem clamp(1.5rem, 4vw, 3.5rem);
}
/* Two columns, not three: three pushed each chord and its meaning to opposite
   ends of the column, which is the one relationship this table exists to show. */
@media (min-width: 780px) { .cheats__list { grid-template-columns: repeat(2, 1fr); } }
.cheats__list li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .35rem .9rem;
  padding: .62rem 0;
  font-size: .93rem;
  color: var(--ink-dim);
  box-shadow: inset 0 -1px 0 rgb(255 255 255 / .06);
}
/* The fixed column keeps the chords aligned with each other; below that width there
   is no room for a column *and* its label, so the label wraps under instead. */
.cheats__keys { display: flex; align-items: center; gap: .2rem; min-width: 9.5rem; }
@media (max-width: 26rem) { .cheats__keys { min-width: 0; } }
.cheats__keys span { padding: 0 .18rem; color: var(--ink-faint); font-size: .82em; }
.cheats__keys kbd:last-child { min-width: auto; padding: 0 .5em; }

/* ---------------------------------------------------------------- trust */
.trust {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .55rem 1.6rem;
  max-width: var(--measure);
  margin: clamp(1.5rem, 4vw, 3rem) auto 0;
  padding-inline: var(--gutter);
  font: 500 .78rem/1 var(--mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.trust li { display: flex; align-items: center; gap: 1.6rem; }
.trust li:not(:last-child)::after {
  content: "";
  width: 3px; height: 3px;
  border-radius: 50%;
  background: currentColor;
  opacity: .5;
}
/* The dots separate items on a single line. Once the list wraps to four lines on a
   phone, the item that ends a line trails a dot pointing at nothing — so drop them
   and let the wider gap do the separating. */
@media (max-width: 40rem) {
  .trust { gap: .6rem 1.15rem; letter-spacing: .1em; }
  .trust li { gap: 0; }
  .trust li:not(:last-child)::after { display: none; }
}

/* ---------------------------------------------------------------- download */
.download { padding: clamp(2.5rem, 6vw, 4rem) clamp(1.5rem, 5vw, 3.5rem); text-align: center; }

.download__icon {
  width: clamp(84px, 12vw, 112px);
  margin: 0 auto 1.4rem;
  border-radius: 24px;
  filter: drop-shadow(0 20px 40px rgb(58 106 220 / .45));
}
.download h2 { font-size: clamp(1.85rem, 3.8vw, 2.7rem); }
.download__sub { margin: .85rem auto 1.9rem; color: var(--ink-dim); }
.download .btn { margin-inline: auto; }

.specs {
  display: grid;
  /* One column on a phone: three 1fr tracks on a 320px screen give each spec ~100px,
     which wraps "macOS 13.0 or later" onto three lines and still clips the third
     column's heading. Stacked, each spec reads as a row of a table. */
  grid-template-columns: 1fr;
  gap: 1px;
  margin-top: 2.4rem;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: rgb(255 255 255 / .08);      /* shows through the gap as hairlines */
  text-align: left;
}
/* Exactly three, never auto-fit: the three short specs fill one row and the hash
   spans the next. auto-fit picks four at this width and strands an empty cell. */
@media (min-width: 34rem) { .specs { grid-template-columns: repeat(3, 1fr); } }
/* A grid item's default min-width is `auto` — i.e. its content — so the unbroken
   64-character hash widens its track and drags the whole slab past the viewport.
   min-width: 0 lets the track shrink and the hash wrap instead. */
.specs > div { min-width: 0; padding: .85rem 1.05rem; background: rgb(12 14 26 / .5); }
.specs--full { grid-column: 1 / -1; }
.specs dt {
  font: 500 .66rem/1 var(--mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.specs dd { margin: .42rem 0 0; font-size: .92rem; }

.download__note {
  max-width: 58ch;
  margin: 1.9rem auto 0;
  font-size: .87rem;
  line-height: 1.6;
  color: var(--ink-faint);
}
.download__note strong { color: var(--ink-dim); font-weight: 600; }

/* ---------------------------------------------------------------- footer */
.footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 2rem;
  max-width: var(--measure);
  margin: clamp(3rem, 7vw, 5rem) auto 0;
  padding: 2.5rem var(--gutter) 4rem;
  font-size: .87rem;
  color: var(--ink-faint);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .07);
}
.footer__brand { display: flex; align-items: center; gap: .55rem; color: var(--ink-dim); font-weight: 600; }
.footer__brand img { border-radius: 6px; }
.footer nav { display: flex; flex-wrap: wrap; gap: 1.3rem; }
.footer nav a { transition: color .25s; }
@media (hover: hover) { .footer nav a:hover { color: var(--ink); } }
.footer > p { flex-basis: 100%; }

/* ---------------------------------------------------------------- reveal */
.reveal {
  opacity: 0;
  transform: translateY(20px);
  animation: rise .9s var(--glide) forwards;
  animation-delay: var(--d, 0ms);
}
@keyframes rise { to { opacity: 1; transform: none; } }

.on-scroll {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .8s var(--glide), transform .8s var(--glide);
}
.on-scroll.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal { animation: none; opacity: 1; transform: none; }
  .on-scroll { opacity: 1; transform: none; transition: none; }
}

/* --------------------------------------------------------------------------
   Fallbacks
   -------------------------------------------------------------------------- */

/* Without backdrop-filter the glass surfaces would render as near-invisible
   4%-white rectangles. Give them an opaque tint instead. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass, .nav.is-stuck { background: rgb(23 27 46 / .94); }
}

/* Some people find translucency itself hard to read through, and macOS exposes
   that preference. Honour it: keep the layout, drop the see-through. */
@media (prefers-reduced-transparency: reduce) {
  .glass, .panel, .nav.is-stuck { background: #12162a; -webkit-backdrop-filter: none; backdrop-filter: none; }
  .glass::after, .panel::after, .glass--live::after { display: none; }
  .aurora { opacity: .3; }
  .grain { display: none; }
}
