/* ==========================================================================
   Heica - site stylesheet.

   Light only. The app is locked to the light appearance (a white wash over a
   slow pastel gradient, near-black ink, white capsules), and so is the site:
   there is no dark theme here and there should not be one. The Faster site
   this was cloned from is dark first because Faster's one colour is additive
   light; Heica's colour is in its icon, a blue mosaic turning into a sunset,
   and that reads on a light ground.

   The one moving colour on the page is the app's own conversion band, the
   SpectrumRim shader ported to WebGL in js/spectrum.js and laid on the app
   icon. Its four hues are the app's, identical in the app and here.

   Loaded after Bootstrap 5.2.0, which is the only external dependency on the
   page and is used for its grid and a handful of utility classes. Everything
   with a look of its own is written here.
   ========================================================================== */

/* ── Fonts ───────────────────────────────────────────────
   Self hosted, licence files sit next to the files themselves:
   ZalandoSans-OFL.txt, ZalandoSansExpanded-OFL.txt, IBMPlexMono-OFL.txt.

   Zalando Sans for body copy and Zalando Sans Expanded for headings, the same
   pairing and the same files as the PlainText and Faster sites, so the
   Metromedya sites read as one family. IBM Plex Mono carries the figures:
   percentages, megabytes, pixel sizes. Figures that are compared line up.
   ─────────────────────────────────────────────────────── */

@font-face {
  font-family: 'Zalando Sans';
  src: url('../assets/fonts/ZalandoSans-VariableFont_wdth_wght.ttf') format('truetype-variations');
  font-weight: 100 900;
  font-stretch: 75% 125%;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Zalando Sans';
  src: url('../assets/fonts/ZalandoSans-Italic-VariableFont_wdth_wght.ttf') format('truetype-variations');
  font-weight: 100 900;
  font-stretch: 75% 125%;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'Zalando Sans Expanded';
  src: url('../assets/fonts/ZalandoSansExpanded-VariableFont_wght.ttf') format('truetype-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Zalando Sans Expanded';
  src: url('../assets/fonts/ZalandoSansExpanded-Italic-VariableFont_wght.ttf') format('truetype-variations');
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'IBM Plex Mono';
  src: url('../assets/fonts/IBMPlexMono-300.woff2') format('woff2');
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'IBM Plex Mono';
  src: url('../assets/fonts/IBMPlexMono-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'IBM Plex Mono';
  src: url('../assets/fonts/IBMPlexMono-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'IBM Plex Mono';
  src: url('../assets/fonts/IBMPlexMono-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

/* ── Tokens ────────────────────────────────────────────── */

:root {
  /* Ground and ink. The app's window is a white wash; the page ground is one
     step warmer than pure white so white cards still read as cards. */
  --ground: #f6f6f4;
  --raised: #ffffff;
  --raised-2: #f0f0ed;
  --ink: #0b0b0c;
  --muted: rgba(11, 11, 12, 0.66);
  /* 0.60, not the 0.46 this started at. On a light ground 0.46 measures 3.2:1
     against #f6f6f4, under the 4.5:1 WCAG AA asks for normal text, and this
     token carries real content: the note under each quality row, the system
     requirement line, the eyebrows, the footer links. 0.60 measures 5.07:1
     on the page and 4.99:1 on the shaded default row, and still reads a step
     back from --muted. Faster can hold a fainter value because its ink is
     white on black. Measured 2026-09-04. */
  --faint: rgba(11, 11, 12, 0.60);
  --rule: rgba(11, 11, 12, 0.12);
  --rule-strong: rgba(11, 11, 12, 0.26);
  /* The floating language pill sits over whatever scrolls under it. */
  --chip-bg: rgba(246, 246, 244, 0.85);

  /* The app's band. SpectrumRim.metal carries one palette for every state and
     the app draws it in both Faster and Heica; the site uses the same four.
     js/spectrum.js carries the float values itself; these exist for the flat
     CSS uses: the timeline's fill and the quality bars. */
  --rim-1: #ff5c9e;
  --rim-2: #ff9457;
  --rim-3: #59e6d9;
  --rim-4: #8c6bff;

  /* The icon's own two colours, for the quality bars: the mosaic's blue and
     the sunset's orange. Sampled from AppIcon-1024.png. */
  --mosaic: #2f7ff0;
  --sunset: #ff7a3d;

  --sans: 'Zalando Sans', -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI',
    'Helvetica Neue', 'Noto Sans', 'Hiragino Sans', 'Yu Gothic', 'Apple SD Gothic Neo',
    'Malgun Gothic', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
  --font-heading: 'Zalando Sans Expanded', 'Zalando Sans', -apple-system, BlinkMacSystemFont,
    'Helvetica Neue', 'Segoe UI', 'Hiragino Sans', 'Apple SD Gothic Neo', 'PingFang SC',
    Arial, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas,
    'Liberation Mono', monospace;

  /* One size for the app icon wherever it appears, the front page and the
     three sub pages alike. The family holds a flat 156px in both places. */
  --app-icon-size: clamp(7rem, 17vw, 9.75rem);

  --measure: 46rem;
  --gutter: clamp(1.25rem, 5vw, 2.5rem);
  --radius: 14px;
}

/* ── Reset and base ────────────────────────────────────── */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.62;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img,
picture,
svg,
video {
  max-width: 100%;
  height: auto;
  display: block;
}

/* Links hold their ink and answer the pointer with a rule under them, the way
   the whole family does. Semibold at rest so a link can be found without
   colour; the weight never changes on hover, which would reflow the line. */
a {
  color: var(--ink);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--rule-strong);
  text-underline-offset: 0.22em;
  transition: text-decoration-color 0.05s ease, text-decoration-thickness 0.05s ease;
}

/* Bootstrap styles a and a:hover, and it loads before this file, so every
   state has to be named here or its blue comes through. */
a,
a:link,
a:visited,
a:hover,
a:focus,
a:active {
  color: var(--ink);
}

a:hover,
a:focus-visible {
  text-decoration-color: currentColor;
  text-decoration-thickness: 2px;
}

/* Links that are already a shape of their own: artwork, a bordered button, a
   menu row. A rule across the bottom of those is noise. */
.app-store-badge,
a[aria-label],
a.icon-link,
a.btn,
a.skip-link,
a.footer-name,
a.publisher-link,
.language-selector a,
.language-menu a {
  font-weight: inherit;
  text-decoration: none;
}

/* The publisher's name in the colophon: a link, but not one the page is
   asking anyone to press. */
a.publisher-link {
  color: inherit;
}

a.publisher-link:hover {
  color: inherit;
  text-decoration: none;
}

@media (prefers-reduced-motion: reduce) {
  a {
    transition: none;
  }
}

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 3px;
}

hr.trail {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: var(--ground);
  padding: 0.75rem 1.25rem;
  z-index: 10000;
}

.skip-link:focus {
  left: 0;
  color: var(--ground);
}

/* ── Layout ────────────────────────────────────────────── */

.wrap {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
  padding-block: clamp(3rem, 8vw, 5.5rem);
}

.wrap--wide {
  max-width: 72rem;
}

.wrap--flush {
  padding-block: 0;
}

section > .wrap:first-child {
  padding-top: 0;
}

/* ── Type ──────────────────────────────────────────────── */

h1,
h2,
h3 {
  font-family: var(--font-heading);
  font-weight: 700;
  line-height: 1.14;
  letter-spacing: -0.02em;
  margin: 0;
  text-wrap: balance;
}

h1 {
  font-size: clamp(2rem, 5.2vw, 3.1rem);
}

h2 {
  font-size: clamp(1.55rem, 3.4vw, 2.2rem);
}

h3 {
  font-size: 1.125rem;
}

p {
  margin: 0 0 1.1em;
}

p:last-child {
  margin-bottom: 0;
}

.eyebrow {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 0.85rem;
}

.lede {
  font-size: 1.15rem;
  color: var(--muted);
  max-width: 40rem;
  margin-top: 1rem;
}

.note {
  font-size: 0.9rem;
  color: var(--faint);
  margin-top: 1.25rem;
}

/* ── Language selector ─────────────────────────────────────
   Pinned to the top right, and it is the only thing above the hero. A
   <details> element, not a Bootstrap dropdown: it opens, closes and takes the
   keyboard with no script at all, and carries no flag emoji.
   ─────────────────────────────────────────────────────── */

.language-selector {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 1000;
}

.language-selector summary {
  list-style: none;
  cursor: pointer;
  font-size: 0.9rem;
  color: var(--muted);
  padding: 0.5rem 0.95rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  white-space: nowrap;
  background: var(--chip-bg);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  transition: border-color 0.18s ease, color 0.18s ease;
}

.language-selector summary::-webkit-details-marker {
  display: none;
}

.language-selector summary:hover,
.language-selector[open] summary {
  color: var(--ink);
  border-color: var(--rule-strong);
}

.language-menu {
  position: absolute;
  top: calc(100% + 0.5rem);
  right: 0;
  z-index: 50;
  margin: 0;
  padding: 0.4rem;
  list-style: none;
  min-width: 11rem;
  background: var(--raised);
  border: 1px solid var(--rule);
  border-radius: 12px;
  box-shadow: 0 18px 44px rgba(11, 11, 12, 0.14);
}

.language-menu a {
  display: block;
  padding: 0.5rem 0.7rem;
  border-radius: 8px;
  font-size: 0.92rem;
  color: var(--muted);
}

.language-menu a:hover,
.language-menu a:focus-visible {
  background: var(--raised-2);
  color: var(--ink);
  text-decoration: none;
}

.language-menu a[aria-current='true'] {
  color: var(--ink);
}

/* ── Buttons ───────────────────────────────────────────── */

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  justify-content: center;
  margin-top: clamp(2.25rem, 4vw, 3.25rem);
}

/* The app's own secondary button: a white capsule with a hairline. */
.btn,
a.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--sans);
  font-size: 0.95rem;
  font-weight: 560;
  line-height: 1.2;
  padding: 0.8rem 1.4rem;
  border-radius: 999px;
  border: 1px solid var(--rule-strong);
  color: var(--ink);
  background: var(--raised);
  cursor: pointer;
  text-decoration: none;
  transition: border-color 0.18s ease, background 0.18s ease, color 0.18s ease;
}

.btn:hover,
a.btn:hover,
.btn:focus-visible {
  border-color: var(--ink);
  color: var(--ink);
  text-decoration: none;
}

/* The Apple badge, same treatment and same rendered height as the sibling
   sites. Never recoloured, never inverted: the art is a black pill with a
   white stroke and white lettering and reads on a light ground as it is. */
.app-store-badge {
  display: inline-block;
  transition: transform 0.2s ease;
}

.app-store-badge:hover {
  transform: scale(1.05);
}

.app-store-badge img {
  height: clamp(56px, 8vw, 76px);
  width: auto;
}

/* No Mac App Store record exists yet, so the badge is shown but not linked. */
.app-store-badge--pending {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  cursor: default;
}

.app-store-badge--pending img {
  opacity: 0.55;
}

.app-store-badge-note {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}

.pending {
  color: var(--faint);
  border-bottom: 1px dashed var(--rule-strong);
}

/* ── Hero ──────────────────────────────────────────────── */

.hero {
  text-align: center;
  /* No masthead, so the hero has to clear the floating language pill on its
     own. */
  padding-top: clamp(4.5rem, 9vw, 7rem);
}

/* The square app icon, then the name in type, the way the sibling sites open. */
.hero-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(0.9rem, 2.4vw, 1.4rem);
  margin: 0 auto clamp(2.25rem, 5vw, 3.5rem);
}

/* The icon and the rim surface over it. The corners are transparent in the
   file, so the shadow follows the squircle and no radius is set here. The
   band answers the pointer, the way the app's window edge lights while it
   converts; there is no scale or tilt underneath it. */
.app-icon {
  position: relative;
  display: block;
  width: var(--app-icon-size);
  line-height: 0;
}

/* No CSS shadow. The image is the app's compiled icon and macOS's own icon
   shadow is baked into it; a drop-shadow on top of that blurred the baked one
   into a grey smear around the whole squircle (seen 2026-09-04). */
.app-icon img {
  display: block;
  width: 100%;
  height: auto;
}

.app-icon canvas {
  position: absolute;
  pointer-events: none;
}

.hero-name {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(1.4rem, 3vw, 1.85rem);
  letter-spacing: -0.02em;
  line-height: 1;
}

.hero-meta {
  font-family: var(--mono);
  font-size: 0.85rem;
  color: var(--faint);
  margin-top: 1rem;
  letter-spacing: 0.02em;
}

.hero-copy {
  max-width: 38rem;
  margin: clamp(2.75rem, 4.5vw, 3.75rem) auto 0;
  color: var(--muted);
  text-align: left;
}

/* Only ever seen by someone who tried to drop a file on the page. */
.drop-guard {
  max-width: 34rem;
  margin: 1.5rem auto 0;
  font-size: 0.9rem;
  color: var(--ink);
}

.drop-guard[hidden] {
  display: none;
}

/* ── Gallery strip ─────────────────────────────────────────
   Same construction as every sibling site. Not rendered while SCREENS in
   _dev/build.js is empty; the rules stay so the day the store screenshots
   land nothing here has to be written again.
   ─────────────────────────────────────────────────────── */

.gallery-section {
  position: relative;
}

.gallery-scroll-wrapper {
  position: relative;
}

.gallery-scroll-wrapper::before,
.gallery-scroll-wrapper::after {
  content: '';
  position: absolute;
  top: -120px;
  bottom: -120px;
  width: 80px;
  z-index: 2;
  pointer-events: none;
  -webkit-backdrop-filter: blur(40px);
  backdrop-filter: blur(40px);
}

.gallery-scroll-wrapper::before {
  left: 0;
  -webkit-mask-image: linear-gradient(to right, black, transparent);
  mask-image: linear-gradient(to right, black, transparent);
}

.gallery-scroll-wrapper::after {
  right: 0;
  -webkit-mask-image: linear-gradient(to left, black, transparent);
  mask-image: linear-gradient(to left, black, transparent);
}

.scroll-container {
  overflow-x: scroll;
  overflow-y: hidden;
  white-space: nowrap;
  /* Room for the band's bloom above and below the tiles. */
  padding-top: 40px;
  padding-bottom: 52px;
  scrollbar-width: none;
  -ms-overflow-style: none;
  touch-action: pan-x;
  -webkit-overflow-scrolling: touch;
}

.scroll-container::-webkit-scrollbar {
  display: none;
}

.gridscroll {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  padding-left: max(var(--gutter), calc((100vw - 72rem) / 2 + var(--gutter)));
  padding-right: max(var(--gutter), calc((100vw - 72rem) / 2 + var(--gutter)));
}

.gridscroll .shot {
  position: relative;
  flex: 0 0 auto;
  height: clamp(260px, 34vw, 440px);
}

.gridscroll picture {
  display: block;
  height: 100%;
}

.shot-rim {
  position: absolute;
  pointer-events: none;
  z-index: 1;
}

.gridscroll img {
  height: 100%;
  width: auto;
  max-width: none;
  display: block;
  object-fit: contain;
  border-radius: 14px;
  box-shadow: 0 0 0 1px var(--rule), 0 18px 40px rgba(11, 11, 12, 0.12);
  transition: transform 0.24s ease;
}

.gridscroll img:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
}

.custom-scrollbar-wrap {
  display: flex;
  align-items: center;
  height: 18px;
  max-width: 72rem;
  margin: 0.5rem auto 0;
  padding: 0 var(--gutter);
  cursor: pointer;
}

.custom-scrollbar-track {
  position: relative;
  width: 100%;
  height: 6px;
  border-radius: 10px;
  background: var(--rule);
  transition: height 0.3s ease;
}

.gallery-section:hover .custom-scrollbar-track {
  height: 12px;
}

.custom-scrollbar-thumb {
  position: absolute;
  top: 0;
  bottom: 0;
  min-width: 40px;
  border-radius: 10px;
  background: var(--rule-strong);
  cursor: grab;
  transition: background-color 0.3s ease;
}

.custom-scrollbar-thumb:hover {
  background: var(--ink);
}

.custom-scrollbar-thumb.dragging {
  background: var(--ink);
  cursor: grabbing;
}

/* ── Feature cards ─────────────────────────────────────── */

.features-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 1rem;
  margin-top: 2.5rem;
}

.feature-card {
  background: var(--raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 1.5rem 1.4rem;
}

.feature-card h3 {
  margin-bottom: 0.5rem;
}

.feature-card p {
  color: var(--muted);
  font-size: 0.95rem;
  margin: 0;
}

/* ── Quality: what one photo does at each setting ──────────
   Heica's own section, the equivalent of Faster's speed table. Six rows, one
   per setting, each with the measured size and a bar that is the size as a
   fraction of the largest. The bar runs from the icon's blue (the mosaic, the
   HEIC side) to its orange (the sunset, the JPG side), so the section speaks
   the icon's language without a third colour.
   ─────────────────────────────────────────────────────── */

.quality-panel {
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--raised);
  padding: clamp(1.25rem, 3vw, 2rem);
  margin-top: 2.5rem;
}

.quality-sample {
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--faint);
  letter-spacing: 0.02em;
  margin: 0 0 1rem;
}

/* No overflow: hidden here. Each bar's glow spills 22px above and below the
   capsule, and clipping the list would shear it off the top and bottom rows.
   The corners are rounded on the first and last row instead. */
.quality-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: 10px;
}

.quality-list li {
  display: grid;
  grid-template-columns: 3.5rem minmax(6rem, 1fr) 4.5rem minmax(10rem, 1.4fr);
  align-items: center;
  gap: 1rem;
  padding: 0.65rem 0.9rem;
  background: var(--raised);
  font-size: 0.92rem;
}

.quality-list li:first-child {
  border-radius: 9px 9px 0 0;
}

.quality-list li:last-child {
  border-radius: 0 0 9px 9px;
}

.quality-list li.is-default {
  background: var(--raised-2);
}

.quality-pct {
  font-family: var(--mono);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* ── The bars: Faster's export bar, six times ──────────────
   FSGlowProgress: a capsule track at 8 percent ink with the app's band laid
   over the filled part, at full intensity, period 1.5 and a bloom scaled to
   0.4. The geometry is the app's, multiplied by --bar-scale. js/spectrum.js
   draws the band; the scroll fills each bar to its own data-target.

   --bar-scale stays under 1.3. The band is a fixed 6.2 points, so past that it
   stops reaching the centre line and the bar turns into a hollow outline with
   a dark seam down the middle instead of a lit core (Faster measured this at
   1.9). 1.25 is Faster's own value.
   ─────────────────────────────────────────────────────── */
.rim-progress {
  --bar-scale: 1.25;
  --bar-height: calc(8px * var(--bar-scale));
  position: relative;
  height: var(--bar-height);
  /* Above the row's text, so the glow is not cut by a neighbour row's
     opaque background. */
  z-index: 1;
}

.rim-progress-track {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: rgba(11, 11, 12, 0.08);
}

/* Oversized by the app's 18 point margin on every side and pulled back by the
   same amount, so the bloom has room inside the surface instead of being
   clipped at the bounds. js/spectrum.js sizes it. */
.rim-progress-glow {
  position: absolute;
  left: calc(-18px * var(--bar-scale));
  top: calc(-18px * var(--bar-scale));
  pointer-events: none;
}

.quality-size {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-align: right;
  white-space: nowrap;
}

.quality-note {
  color: var(--faint);
  font-size: 0.85rem;
}

/* ── The three rules ───────────────────────────────────── */

.rules-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 1rem;
  margin-top: 2.5rem;
}

.rule-card {
  position: relative;
  background: var(--raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 1.5rem 1.4rem 1.5rem 4.1rem;
}

.rule-index {
  position: absolute;
  left: 1.4rem;
  top: 1.45rem;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  border: 1px solid var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 0.8rem;
  font-weight: 500;
}

.rule-card h3 {
  margin-bottom: 0.5rem;
}

.rule-card p {
  color: var(--muted);
  font-size: 0.95rem;
  margin: 0;
}

/* ── Steps timeline ─────────────────────────────────────
   Same class contract as the sibling sites so js/steps3d.js is portable.
   ─────────────────────────────────────────────────────── */

.steps-timeline {
  position: relative;
  margin-top: 2.5rem;
  padding-left: 3.25rem;
  max-width: 44rem;
}

.timeline-track {
  position: absolute;
  left: 1.09rem;
  top: 0.6rem;
  bottom: 0.6rem;
  width: 2px;
  background: var(--rule);
  border-radius: 2px;
}

.timeline-track::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: var(--track-progress, 0%);
  border-radius: 2px;
  background: linear-gradient(180deg, var(--rim-1), var(--rim-2), var(--rim-3), var(--rim-4));
  transition: height 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

.timeline-step {
  position: relative;
  padding-bottom: 2rem;
  opacity: 0;
  transform: translateY(24px);
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.4s ease;
}

.timeline-step:last-child {
  padding-bottom: 0;
}

.timeline-step.visible {
  opacity: 1;
  transform: translateY(0);
}

.step-number {
  position: absolute;
  left: -3.25rem;
  top: -0.1rem;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  border: 1px solid var(--rule-strong);
  background: var(--ground);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--muted);
}

.timeline-step.visible .step-number {
  color: var(--ink);
  border-color: var(--ink);
}

.step-content {
  color: var(--muted);
}

.step-content strong {
  color: var(--ink);
  font-weight: 620;
}

/* ── Spec table ────────────────────────────────────────── */

.scroller {
  overflow-x: auto;
  margin-top: 2rem;
}

table.spec {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}

table.spec th,
table.spec td {
  text-align: left;
  padding: 0.8rem 1rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}

table.spec th {
  font-weight: 560;
  white-space: nowrap;
  width: 34%;
  color: var(--ink);
}

table.spec td {
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.88rem;
}

table.spec tr:last-child th,
table.spec tr:last-child td {
  border-bottom: 0;
}

/* ── Support and info cards ────────────────────────────── */

.support-section {
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: clamp(1.5rem, 4vw, 2.5rem);
  background: var(--raised);
}

.info-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: 1.25rem;
  margin-top: 1.25rem;
}

.info-card {
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 1.5rem;
  background: var(--raised);
}

.info-card h3 {
  margin-bottom: 1rem;
}

.info-card ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.info-card li {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: space-between;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--rule);
  font-size: 0.92rem;
}

.info-card li:last-child {
  border-bottom: 0;
}

.info-label {
  color: var(--faint);
}

.info-value {
  color: var(--muted);
  text-align: right;
}

.disclaimer {
  margin-top: 2rem;
  font-size: 0.82rem;
  color: var(--faint);
  line-height: 1.6;
}

/* ── Sub page heads and prose ──────────────────────────── */

.subhead {
  text-align: center;
  padding-top: clamp(4.5rem, 9vw, 6.5rem);
  padding-bottom: 2rem;
}

/* The way back to the front page from a sub page: the icon, linked. Same size
   as the front page's, and the same band answers the pointer. */
.subhead-icon {
  display: block;
  width: var(--app-icon-size);
  margin: 0 auto 1.5rem;
}

.page-label {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: 0.85rem;
}

.prose h2 {
  margin-top: 2.75rem;
  margin-bottom: 0.85rem;
}

.prose h3 {
  margin-top: 1.75rem;
  margin-bottom: 0.5rem;
}

.prose p,
.prose li {
  color: var(--muted);
}

.prose ul {
  margin: 0 0 1.1em;
  padding-left: 1.2rem;
}

.prose li {
  margin-bottom: 0.4rem;
}

.prose strong {
  color: var(--ink);
}

.faq-item {
  padding: 1.35rem 0;
  border-bottom: 1px solid var(--rule);
}

.faq-item:last-child {
  border-bottom: 0;
}

.faq-item h3 {
  margin-bottom: 0.5rem;
}

.faq-item p {
  color: var(--muted);
  margin: 0;
}

/* ── Footer ────────────────────────────────────────────── */

footer {
  padding-bottom: 3rem;
}

.footer-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding-top: 2.5rem;
}

.footer-inner p {
  margin: 0;
  color: var(--faint);
  font-size: 0.88rem;
}

.footer-name {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -0.02em;
}

.footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.footer-nav a {
  color: var(--faint);
  font-size: 0.88rem;
}

.footer-nav a:hover,
.footer-nav a:focus-visible {
  color: var(--ink);
}

/* ── Responsive ────────────────────────────────────────── */

@media (max-width: 768px) {
  .quality-list li {
    grid-template-columns: 3rem 1fr 4.5rem;
  }

  .quality-note {
    grid-column: 1 / -1;
  }

  .footer-inner {
    flex-direction: column;
    align-items: flex-start;
  }

  .hero-copy {
    text-align: left;
  }

  .gallery-scroll-wrapper::before,
  .gallery-scroll-wrapper::after {
    width: 24px;
  }

  .custom-scrollbar-track,
  .gallery-section:hover .custom-scrollbar-track {
    height: 8px;
  }
}

@media (max-width: 480px) {
  .steps-timeline {
    padding-left: 2.75rem;
  }

  .step-number {
    left: -2.75rem;
    width: 1.9rem;
    height: 1.9rem;
    font-size: 0.78rem;
  }

  .timeline-track {
    left: 0.92rem;
  }
}

/* ── Reduce Motion ─────────────────────────────────────────
   The app turns its own band off under this setting. The site does the same:
   the spectrum stays, it simply stops moving.
   ─────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
