/* Prime Edge Systems - shared site styles.
 *
 * The colour tokens are copied from the application's own palette
 * (lib/src/core/theme/pes_colors.dart) so the site and the app cannot drift
 * apart. Nothing here is loaded from a third party: no web fonts, no
 * analytics, no CDN. A site promising that the app phones nobody should not
 * itself phone somebody.
 */

/* Brand palette, taken from the application's own tokens so the page and
     the app cannot drift apart. */
  :root {
    --base:      #0A0B0D;
    --raised:    #14161A;
    --overlay:   #1C1F25;
    --border:    #2B303A;
    --text:      #F3F5F8;
    --muted:     #9AA1AA;
    --accent:    #FC2023;
    --accent-dk: #D40E13;

    --nav-h:     65px;
  }

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

  html {
    -webkit-text-size-adjust: 100%;
    /* The nav is sticky, so an anchor must not land underneath it. */
    scroll-padding-top: calc(var(--nav-h) + 20px);
  }

  body {
    margin: 0;
    background: var(--base);
    color: var(--text);
    font: 16px/1.65 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter,
          Roboto, Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
  }

  /* Nothing on any page is meant to be reached by scrolling sideways. */
  html, body { overflow-x: clip; }
  @supports not (overflow: clip) {
    body { overflow-x: hidden; }
  }

  .wrap { max-width: 720px; margin: 0 auto; padding: 48px 24px 96px; }

  header { border-bottom: 1px solid var(--border); padding-bottom: 24px; margin-bottom: 40px; }
  .eyebrow {
    font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
    color: var(--accent); font-weight: 600; margin: 0 0 12px;
  }
  h1 { font-size: clamp(28px, 6vw, 40px); line-height: 1.15; margin: 0 0 12px; letter-spacing: -.02em; }
  .updated { color: var(--muted); font-size: 14px; margin: 0; }

  h2 {
    font-size: clamp(20px, 4vw, 26px); line-height: 1.25; letter-spacing: -.01em;
    margin: 56px 0 16px; padding-top: 8px;
  }
  h3 { font-size: 17px; margin: 32px 0 10px; }

  p, li { color: #DDE1E6; }
  p { margin: 0 0 16px; }
  ul { padding-left: 22px; margin: 0 0 16px; }
  li { margin-bottom: 8px; }
  strong { color: var(--text); font-weight: 600; }
  code {
    background: var(--overlay); border: 1px solid var(--border);
    padding: 2px 6px; border-radius: 4px; font-size: .9em;
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    word-break: break-all;
  }

  pre {
    background: var(--overlay); border: 1px solid var(--border); border-radius: 10px;
    padding: 14px 16px; margin: 0 0 24px; overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  /* Inside a block the border and padding are the pre's job, and a command
     line must not break-all — a wrapped path is a command that will not run. */
  pre code {
    background: none; border: 0; padding: 0; font-size: 14px;
    line-height: 1.6; word-break: normal; white-space: pre;
  }

  .lead {
    background: var(--raised); border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 10px; padding: 24px; margin: 0 0 8px;
  }
  .lead p:last-child { margin-bottom: 0; }
  .lead .big { font-size: 19px; font-weight: 600; color: var(--text); }

  .note {
    background: var(--overlay); border: 1px solid var(--border);
    border-radius: 10px; padding: 18px 20px; margin: 24px 0;
    color: var(--muted); font-size: 15px;
  }
  .note p:last-child { margin-bottom: 0; }

  /* Tables scroll rather than forcing the page to; phones are the common
     case for a policy linked from a store listing. */
  .table-scroll { overflow-x: auto; margin: 0 0 24px; -webkit-overflow-scrolling: touch; }
  table { border-collapse: collapse; width: 100%; min-width: 460px; font-size: 15px; }
  th, td { text-align: left; padding: 12px 14px; border-bottom: 1px solid var(--border); vertical-align: top; }
  th { color: var(--muted); font-weight: 600; font-size: 13px; letter-spacing: .04em; text-transform: uppercase; }
  tbody tr:last-child td { border-bottom: none; }

  a { color: var(--accent); text-decoration: none; border-bottom: 1px solid rgba(252,32,35,.35); }
  a:hover { border-bottom-color: var(--accent); }

  footer {
    margin-top: 72px; padding-top: 28px; border-top: 1px solid var(--border);
    color: var(--muted); font-size: 14px;
  }

  @media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }

  /* Printing a policy is a normal thing to want to do. */
  @media print {
    body { background: #fff; color: #000; }
    .lead, .note { border-color: #999; background: #fff; }
    p, li { color: #000; }
    a { color: #000; }
  }

/* ---------------------------------------------------------------- site ---
 * Everything below is used by the pages other than the privacy policy.
 */

.wide { max-width: 940px; }

/* A focus ring that is visible on the near-black page, on the raised
   surfaces, and on the red button alike. */
:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Hidden until focused, and hidden by clipping rather than by being parked
   off-screen, which would give the page something to scroll sideways to. */
.skip-link {
  position: absolute; top: 0; left: 0;
  width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
  background: var(--overlay); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px;
  padding: 0; font-size: 15px; font-weight: 600; z-index: 100;
}
.skip-link:focus {
  width: auto; height: auto; overflow: visible; clip-path: none;
  top: 12px; left: 12px; padding: 10px 16px;
}

/* --- navigation ------------------------------------------------------- */

/* Sticky, and slightly heavier once the page has moved. */
nav.site {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  align-items: center;
  padding: 14px 24px;
  border-bottom: 1px solid var(--border);
  background: var(--raised);
  transition: background-color .25s ease, box-shadow .25s ease, padding .25s ease;
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  nav.site {
    background: rgba(10, 11, 13, .78);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
    backdrop-filter: blur(12px) saturate(140%);
  }
  nav.site.is-scrolled { background: rgba(10, 11, 13, .94); }
}
nav.site.is-scrolled {
  padding-top: 9px; padding-bottom: 9px;
  box-shadow: 0 10px 30px -12px rgba(0, 0, 0, .9);
  border-bottom-color: #383E4A;
}
nav.site .brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--text);
  border: 0;
}
nav.site .brand img {
  width: 30px; height: 30px; display: block;
  transition: transform .25s ease;
}
nav.site.is-scrolled .brand img { transform: scale(.9); }
nav.site .brand .wordmark { white-space: nowrap; }
nav.site .brand .wordmark span { color: var(--accent); }
nav.site a { border: 0; color: var(--muted); font-size: 15px; }
nav.site a:hover, nav.site a[aria-current="page"] { color: var(--text); }
nav.site .links {
  display: flex; flex-wrap: wrap; align-items: center; gap: 20px;
}
nav.site .links a { position: relative; padding: 4px 0; }
nav.site .links a[aria-current="page"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px;
  height: 2px; background: var(--accent); border-radius: 2px;
}
nav.site .spacer { flex: 1 1 auto; }

@media (max-width: 560px) {
  nav.site { gap: 10px 16px; padding-left: 16px; padding-right: 16px; }
  nav.site .spacer { display: none; }
  nav.site .brand { flex: 1 0 100%; }
  nav.site .links { gap: 16px; font-size: 14px; }
  nav.site a { font-size: 14px; }
}

/* --- hero ------------------------------------------------------------- */

.hero { padding: 64px 0 40px; }
.hero h1 { font-size: clamp(32px, 7vw, 52px); }
.hero .tagline {
  font-size: clamp(17px, 2.4vw, 21px);
  color: var(--muted);
  max-width: 46ch;
  margin: 0 0 28px;
}

/* The home page hero puts the mark and a device beside the words. */
.hero-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 40px;
  align-items: center;
  position: relative;
}
@media (min-width: 860px) {
  .hero-split { grid-template-columns: minmax(0, 1fr) 300px; gap: 56px; }
}
.hero-mark {
  width: 76px; height: 76px; display: block; margin: 0 0 22px;
}
/* Kept inside its container: an element wider than the page is an element the
   page can be scrolled sideways to reach. */
.hero-glow {
  position: absolute;
  inset: -20% 0 auto;
  height: 460px;
  background: radial-gradient(70% 70% at 72% 28%, rgba(252, 32, 35, .17), rgba(252, 32, 35, 0) 72%);
  pointer-events: none;
  z-index: -1;
}

.cta { display: flex; flex-wrap: wrap; gap: 12px; margin: 0 0 16px; }
.button {
  display: inline-block;
  padding: 12px 20px;
  border-radius: 10px;
  font-weight: 600;
  font-size: 15px;
  border: 1px solid var(--border);
  color: var(--text);
  background: var(--overlay);
  text-decoration: none;
  transition: background-color .18s ease, border-color .18s ease, transform .18s ease;
}
.button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #0A0B0D;
}
.button.primary:hover { background: var(--accent-dk); border-color: var(--accent-dk); }
.button:hover { border-color: #4A515D; }

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 16px;
  margin: 0 0 32px;
}
.card {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--raised);
  padding: 20px;
  position: relative;
  overflow: hidden;
  transition: border-color .2s ease, background-color .2s ease, transform .2s ease;
}
.card h3 { margin: 0 0 8px; font-size: 16px; }
.card p { margin: 0; font-size: 15px; color: var(--muted); }
.card::after {
  content: "";
  position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .28s ease;
}
.card:hover, .card:focus-within {
  border-color: #414855;
  background: var(--overlay);
  transform: translateY(-3px);
}
.card:hover::after, .card:focus-within::after { transform: scaleX(1); }
.card .icon {
  display: block; width: 24px; height: 24px; margin: 0 0 12px;
  color: var(--accent);
}
.card .icon svg { width: 100%; height: 100%; display: block; }

.pill {
  display: inline-block;
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 10px;
  margin: 0 6px 6px 0;
}

.platforms { display: flex; flex-wrap: wrap; gap: 12px; margin: 24px 0; }
.platform {
  flex: 1 1 220px;
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px;
  background: var(--raised);
  transition: border-color .2s ease, transform .2s ease;
}
.platform:hover { border-color: #414855; transform: translateY(-2px); }
.platform h3 { margin: 0 0 4px; font-size: 15px; }
.platform p { margin: 0 0 10px; font-size: 14px; color: var(--muted); }

.status {
  font-size: 13px;
  color: var(--muted);
  border-left: 2px solid var(--border);
  padding-left: 12px;
}

/* --- counters --------------------------------------------------------- */

.stats {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
}
.stats li {
  margin: 0;
  padding: 22px 20px;
  background: var(--raised);
}
.stat-num {
  display: block;
  font-size: clamp(26px, 4.5vw, 34px);
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.stat-num .unit { font-size: .5em; color: var(--muted); font-weight: 600; margin-left: 4px; }
.stat-label {
  display: block;
  margin-top: 6px;
  font-size: 13.5px;
  color: var(--muted);
  line-height: 1.45;
}
.stats li:last-child .stat-num { color: var(--accent); }

/* --- device frame ----------------------------------------------------- */

.phone {
  position: relative;
  width: 100%;
  max-width: 272px;
  margin: 0 auto;
}
.phone-frame {
  position: relative;
  padding: 9px;
  border-radius: 34px;
  background: linear-gradient(155deg, #3A4049 0%, #14161A 38%, #21252C 100%);
  box-shadow:
    0 32px 60px -24px rgba(0, 0, 0, .95),
    0 0 0 1px rgba(255, 255, 255, .05) inset;
}
.phone-frame::after {
  content: "";
  position: absolute;
  top: 17px; left: 50%; transform: translateX(-50%);
  width: 54px; height: 4px; border-radius: 4px;
  background: rgba(0, 0, 0, .55);
  pointer-events: none;
}
.phone-frame img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 26px;
  background: var(--base);
}

/* --- screenshot gallery ----------------------------------------------- */

.gallery { margin: 8px 0 0; }
.gallery-inner { margin: 24px 0 0; }

/* Without JavaScript every screenshot is shown, one after another, with its
   caption. The tab list is only useful when there is script to drive it. */
.gallery-tabs, .gallery-controls { display: none; }
.gallery-stage {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 36px 24px;
  margin: 0;
}
.gallery-stage figure { margin: 0; }
.gallery-stage figcaption {
  margin: 16px auto 0;
  max-width: 34ch;
  text-align: center;
  font-size: 14.5px;
  color: var(--muted);
  line-height: 1.5;
}
.gallery-stage figcaption strong { display: block; color: var(--text); font-size: 15.5px; margin-bottom: 3px; }

.js .gallery-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 28px;
  align-items: start;
}
@media (min-width: 820px) {
  .js .gallery-inner { grid-template-columns: 262px minmax(0, 1fr); gap: 40px; }
}
.js .gallery-tabs {
  display: flex;
  flex-direction: row;
  gap: 8px;
  overflow-x: auto;
  padding: 2px 2px 10px;
  margin: 0;
  list-style: none;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}
@media (min-width: 820px) {
  .js .gallery-tabs {
    flex-direction: column;
    overflow-x: visible;
    padding: 0;
    position: sticky;
    top: calc(var(--nav-h) + 20px);
  }
}
.gallery-tab {
  display: block;
  flex: 0 0 auto;
  width: auto;
  text-align: left;
  font: inherit;
  font-size: 15px;
  line-height: 1.3;
  color: var(--muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 10px;
  padding: 10px 14px;
  cursor: pointer;
  white-space: nowrap;
  transition: color .18s ease, background-color .18s ease, border-color .18s ease;
}
@media (min-width: 820px) {
  .gallery-tab { width: 100%; white-space: normal; }
}
.gallery-tab:hover { color: var(--text); background: var(--raised); }
.gallery-tab[aria-selected="true"] {
  color: var(--text);
  background: var(--raised);
  border-color: var(--border);
  box-shadow: inset 3px 0 0 var(--accent);
  font-weight: 600;
}
.js .gallery-stage { display: block; }
.js .gallery-stage figure[hidden] { display: none; }
.js .gallery-stage figcaption { min-height: 3.2em; }

.js .gallery-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin: 18px 0 0;
}
.gallery-nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px; height: 38px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--raised);
  color: var(--text);
  cursor: pointer;
  padding: 0;
  transition: border-color .18s ease, background-color .18s ease;
}
.gallery-nav:hover { border-color: #4A515D; background: var(--overlay); }
.gallery-nav svg { width: 16px; height: 16px; display: block; }
.gallery-count {
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  min-width: 5ch;
  text-align: center;
}

/* --- desktop frame ---------------------------------------------------- */

/* The counterpart to .phone. A window chrome rather than a handset, because a
   1424x861 screenshot dropped into a phone bezel reads as a lie about what the
   desktop build looks like. */
.window {
  width: 100%;
  max-width: 1000px;
  margin: 0 auto;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--raised);
  box-shadow: 0 32px 60px -28px rgba(0, 0, 0, .95);
}
.window-bar {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 9px 12px;
  background: var(--overlay);
  border-bottom: 1px solid var(--border);
}
.window-bar i {
  width: 10px; height: 10px;
  border-radius: 999px;
  background: #3A4049;
  display: block;
}
.window img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--base);
}

/* --- platform switch --------------------------------------------------- */

/* Hidden without script: with no JavaScript both platforms are shown one after
   the other, so a control that would swap between them has nothing to do. */
.gallery-platforms { display: none; }
.js .gallery-platforms {
  /* Its own row across both columns. `.gallery-inner` is a two-column grid on
     a wide screen — the tab list, then the stage — and a third child dropped
     into it silently takes the first cell and pushes the screenshots into the
     262px column meant for the tabs. */
  grid-column: 1 / -1;
  justify-self: start;
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  margin: 0 0 18px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--raised);
}
.platform-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font: inherit;
  font-size: 14.5px;
  line-height: 1;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 999px;
  padding: 9px 16px;
  cursor: pointer;
  transition: color .18s ease, background-color .18s ease;
}
.platform-btn svg { width: 16px; height: 16px; }
.platform-btn:hover { color: var(--text); }
.platform-btn[aria-selected="true"] {
  color: var(--text);
  background: var(--overlay);
  font-weight: 600;
}

/* --- the screenshot as a button ---------------------------------------- */

/* Every screenshot opens full size. It is a real button rather than a div with
   a click handler, so it is reachable by keyboard and announced as something
   that can be activated. */
.shot {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: zoom-in;
  color: inherit;
  font: inherit;
}
.shot:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; border-radius: 14px; }
.shot .window, .shot .phone-frame { transition: transform .2s ease, box-shadow .2s ease; }
.shot:hover .window { transform: translateY(-2px); box-shadow: 0 36px 66px -26px rgba(0, 0, 0, .95); }
.shot:hover .phone-frame { transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) {
  .shot .window, .shot .phone-frame { transition: none; }
  .shot:hover .window, .shot:hover .phone-frame { transform: none; }
}
.shot-hint {
  display: block;
  margin: 10px 0 0;
  font-size: 12.5px;
  color: var(--muted);
}

/* --- lightbox ---------------------------------------------------------- */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  /* Opaque, not a tint. A screenshot is full of small text and thin lines, and
     the page showing faintly through it turns both into noise. */
  background: #06070B;
  padding: 16px;
}
.lightbox[hidden] { display: none; }
.lightbox-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex: 0 0 auto;
}
.lightbox-title { font-size: 15px; font-weight: 600; }
.lightbox-title span { display: block; font-size: 12.5px; font-weight: 400; color: var(--muted); }
.lightbox-body {
  position: relative;
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  padding: 12px 0;
}
.lightbox-body img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--base);
  cursor: zoom-in;
}

/* Fitted to the window, a 1424-wide desktop screenshot on a phone is about
   240 pixels across, which is not "full size" by any reading of it. Clicking
   the picture switches to its own pixels and lets the box scroll, so the text
   in a screenshot can actually be read on the device somebody is holding.
   The arrows go while zoomed: they are positioned against the box, and would
   slide over the picture as it scrolled. */
.lightbox-body.zoomed {
  overflow: auto;
  align-items: flex-start;
  justify-content: flex-start;
}
.lightbox-body.zoomed img {
  max-width: none;
  max-height: none;
  cursor: zoom-out;
}
.lightbox-body.zoomed .lightbox-nav { display: none; }
.lightbox-zoom-hint {
  position: absolute;
  bottom: 4px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 12px;
  color: var(--muted);
  pointer-events: none;
}
.lightbox-body.zoomed .lightbox-zoom-hint { display: none; }
.lightbox-foot {
  flex: 0 0 auto;
  text-align: center;
  font-size: 14px;
  color: var(--muted);
  max-width: 70ch;
  margin: 0 auto;
}
/* Over the picture rather than beside it. Laid out in the flex row they were
   squeezed off the edge of a phone: the image takes the full width, and there
   is nothing left for a 40px button on either side. */
.lightbox-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}
.lightbox-nav[data-dir="-1"] { left: 0; }
.lightbox-nav[data-dir="1"] { right: 0; }

.lightbox-close, .lightbox-nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  flex: 0 0 auto;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--raised);
  color: var(--text);
  cursor: pointer;
  padding: 0;
}
.lightbox-close:hover, .lightbox-nav:hover { border-color: #4A515D; background: var(--overlay); }
.lightbox-close svg, .lightbox-nav svg { width: 18px; height: 18px; display: block; }

/* Without script the gallery is a plain list of every screenshot, so both
   platforms appear in sequence and neither is hidden. */
.gallery-stage .shot + .shot { margin-top: 18px; }
.js .gallery-stage .shot[hidden] { display: none; }


/* --- privacy panel ---------------------------------------------------- */

.donate {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--raised);
  padding: 24px;
  margin: 32px 0;
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  align-items: center;
}
.donate img { width: 132px; height: 132px; border-radius: 8px; background: #fff; }
.donate div { flex: 1 1 260px; }
.donate p { margin: 0 0 12px; }

.site-footer-mark {
  display: flex; align-items: center; gap: 10px; margin: 0 0 14px;
  color: var(--text); font-weight: 600;
}
.site-footer-mark img { width: 26px; height: 26px; display: block; }

/* --- scroll reveal ---------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .js .reveal {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity .55s ease, transform .55s ease;
  }
  .js .reveal.is-in { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .card:hover, .platform:hover { transform: none; }
  .button:hover { transform: none; }
}

@media print {
  nav.site, .cta, .donate, .gallery, .hero-glow, .skip-link, .gallery-controls { display: none; }
  .stats { border-color: #999; }
  .stats li { background: #fff; }
}


/* ================================================== surface system ======
 * Adapted from Documentation/WEBSITE_SURFACE_COMPONENTS.css, which is a
 * design study rather than shipped code. The study's own hex values are not
 * used here: everything below is written against the tokens declared at the
 * top of this file, so the site still cannot drift from the application's
 * palette.
 *
 * What the study contributed: the layering order (solid base, then a
 * decorative image or gradient, then edge ornament, then live content), the
 * button roles, the panel and bubble geometry, and the rule that decoration is
 * drawn in CSS wherever the geometry has to stay crisp at any size. The
 * generated PNGs are used only where the value is texture that CSS cannot
 * produce: two backgrounds and the object illustrations.
 */

:root {
  --border-strong: #3C424E;
  --steel:         #C2C7CE;
  --steel-dim:     #6E7783;
  /* The application's own accentDarkPressed. Signal red is bright enough to
     carry a dark label on a button, but not once it is darkened for a hover
     state, and not as a link on the overlay surface: measured, #FC2023 on
     #1C1F25 is 4.22:1 and a dark label on #D40E13 is 3.63:1, both under 4.5.
     This one is 5.23:1 and 6.24:1 in those two places. */
  --accent-hi:     #FF5457;
}

/* Hovering a control on a dark page should brighten it. Darkening the red was
   also the reason its label fell below contrast. */
.button.primary:hover {
  background: var(--accent-hi);
  border-color: var(--accent-hi);
}

/* A link on the overlay surface, where plain signal red does not reach 4.5:1. */
.note a, .bubble a {
  color: var(--accent-hi);
  border-bottom-color: rgba(255, 84, 87, .38);
}
.note a:hover, .bubble a:hover { border-bottom-color: var(--accent-hi); }

/* --- layered surfaces --------------------------------------------------- */

/* A band is a section with its own surface. Decoration goes in absolutely
   positioned children so it can never take a click or reach the accessibility
   tree; content sits above it without needing a z-index of its own. */
.band {
  position: relative;
  isolation: isolate;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--raised);
  /* Padding is generous enough that a focus ring on a control inside the band
     is never reached by the corner radius clipping the decorative layers. */
  padding: clamp(22px, 3.6vw, 36px);
  margin: 32px 0;
  overflow: hidden;
}
.band > .band-layer,
.band > .corner { z-index: 0; }
.band > *:not(.band-layer):not(.corner) { position: relative; z-index: 1; }

.band-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: cover;
}

/* Backgrounds are not seamless tiles and are not claimed to be; each is used
   once, at cover, with the focal point its review note asks for. */
.band-layer--grid {
  background-image: url("art/surface-grid-768.webp");
  background-position: 100% 50%;
  opacity: .55;
}
@media (min-width: 720px), (min-resolution: 1.5dppx) {
  .band-layer--grid { background-image: url("art/surface-grid-1440.webp"); }
}

.band-layer--workbench {
  background-image: url("art/band-workbench-768.webp");
  background-position: 74% 62%;
}
@media (min-width: 720px), (min-resolution: 1.5dppx) {
  .band-layer--workbench { background-image: url("art/band-workbench-1440.webp"); }
}

/* A dependable solid base under the heading. The artwork's own left side is
   quiet, but quiet is not the same as readable, and the scrim is what makes
   the contrast measurable rather than hoped for. */
.band-layer--scrim {
  background-image: linear-gradient(
    100deg,
    #0A0B0D 4%,
    rgba(10, 11, 13, .92) 38%,
    rgba(10, 11, 13, .55) 68%,
    rgba(10, 11, 13, .18) 100%);
}
/* On a narrow screen the heading runs nearly the full width of the band, so
   the artwork has to give way rather than the text: at .95 the still life is
   still legible as a shape and the copy is effectively on flat #0A0B0D. */
@media (max-width: 700px) {
  .band-layer--scrim {
    background-image: linear-gradient(
      100deg,
      #0A0B0D 26%,
      rgba(10, 11, 13, .95) 70%,
      rgba(10, 11, 13, .8) 100%);
  }
}

.band-layer--steel {
  background-image: linear-gradient(rgba(194, 199, 206, .10), transparent 58%);
}
.band-layer--vignette {
  background-image: radial-gradient(120% 120% at 50% 0%,
                    var(--overlay), var(--raised) 62%);
}

/* --- edge ornament ------------------------------------------------------ */

/* Drawn in CSS rather than from the corner PNGs. The generated corners carry
   most of a 1254px square as transparent margin and a textured alpha edge, so
   sizing them by their visible ornament means guessing at a crop; borders and
   a masked dot grid give the same shapes at any size with clean edges. */
.corner {
  position: absolute;
  pointer-events: none;
}
.corner--tl {
  top: 0; left: 0; width: 116px; height: 116px;
  border-top: 2px solid var(--accent);
  border-left: 2px solid var(--accent);
  border-top-left-radius: 14px;
  opacity: .7;
}
/* Inset, because every band it sits in already has a one-pixel border of its
   own and a bracket flush to the edge just looks like that border drawn twice. */
.corner--tr {
  top: 12px; right: 12px; width: 84px; height: 84px;
  border-top: 2px solid var(--border-strong);
  border-right: 2px solid var(--border-strong);
  border-top-right-radius: 10px;
}
.corner--bl {
  bottom: 0; left: 0; width: 240px; height: 190px;
  background: radial-gradient(ellipse at bottom left,
              rgba(252, 32, 35, .15), rgba(252, 32, 35, 0) 70%);
}
.corner--br {
  bottom: 16px; right: 16px; width: 88px; height: 88px;
  background: radial-gradient(rgba(154, 161, 170, .5) 1px, transparent 1.7px)
              0 0 / 13px 13px;
  -webkit-mask-image: linear-gradient(135deg, transparent 28%, #000);
          mask-image: linear-gradient(135deg, transparent 28%, #000);
}

@media (max-width: 560px) {
  .corner--tl { width: 84px; height: 84px; }
  .corner--tr { width: 72px; height: 72px; }
  .corner--br { width: 64px; height: 64px; }
}

/* --- hero texture ------------------------------------------------------- */

/* The hero keeps the real screenshot as its focal point. The diagonal is a
   surface for it to sit on, masked so it never reaches the headline: the
   artwork's own left side is near-black, and the mask makes that a guarantee
   rather than a property of one crop. */
.hero { position: relative; isolation: isolate; }
.hero-texture {
  position: absolute;
  /* On a phone the hero is over a thousand pixels tall, and covering all of it
     drags the diagonal across the buttons. Anchored to the bottom instead, it
     becomes a surface for the screenshot to sit on. */
  left: -24px; right: -24px; bottom: -8px;
  height: 460px;
  z-index: -2;
  pointer-events: none;
  background-image: url("art/surface-diagonal-768.webp");
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 100% 100%;
  opacity: .9;
  /* A linear fade along the artwork's own diagonal rather than a radial one.
     A radial mask centred bottom-right let the lighter graphite plane reach
     under the end of the status line, where the measured contrast of the link
     in it fell to 3.0:1. This clears the whole copy column at every width, so
     the text is on flat #0A0B0D and the texture stays where the screenshot is. */
  -webkit-mask-image: linear-gradient(100deg,
                      transparent 58%, rgba(0, 0, 0, .45) 74%, #000 92%);
          mask-image: linear-gradient(100deg,
                      transparent 58%, rgba(0, 0, 0, .45) 74%, #000 92%);
}
@media (min-width: 720px), (min-resolution: 1.5dppx) {
  .hero-texture { background-image: url("art/surface-diagonal-1440.webp"); }
}
/* Once the hero is two columns the whole block is short enough to sit on. */
@media (min-width: 860px) {
  .hero-texture { top: -32px; height: auto; }
}

/* --- the button system -------------------------------------------------- */

/* Three roles. Primary is the one red control in a view; secondary is the
   graphite outline with the red left inset the material studies use; quiet is
   for a control that must be available without asking to be pressed. */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  cursor: pointer;
  line-height: 1.25;
  box-shadow: inset 3px 0 0 rgba(212, 14, 19, .85);
}
.button.primary { box-shadow: none; }
.button:active { transform: translateY(1px); }

.button.quiet {
  background: transparent;
  border-color: transparent;
  color: var(--muted);
  box-shadow: none;
}
.button.quiet:hover, .button.quiet:focus-visible {
  color: var(--text);
  border-color: var(--border);
  background: var(--raised);
}

.button.wide {
  min-width: min(280px, 100%);
  min-height: 56px;
  font-size: 16px;
  padding-inline: 28px;
}

.button[aria-disabled="true"], .button:disabled {
  opacity: .45;
  cursor: not-allowed;
  pointer-events: none;
}

/* A 44px minimum is already set above; on a touch screen the round controls
   must not sit below it either. */
@media (pointer: coarse) {
  .gallery-nav { min-width: 44px; width: 44px; height: 44px; }
  .help-btn { width: 44px; height: 44px; min-height: 44px; }
}

/* --- panels ------------------------------------------------------------- */

.panel {
  position: relative;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--raised);
  padding: clamp(20px, 3vw, 28px);
}
.panel > :last-child { margin-bottom: 0; }
.panel--slate { border-left: 3px solid var(--accent); }
/* The cut corner is a clip on the background rather than a shape on the box,
   so the panel still grows with its text and the notch never stretches. */
.panel--cut {
  border: 0;
  border-bottom: 2px solid var(--accent-dk);
  border-radius: 0;
  background: linear-gradient(225deg, transparent 20px, var(--raised) 20px);
  padding-right: clamp(28px, 4vw, 40px);
}

.panel ul.tight, .panel ol.tight { margin: 0; }

/* A long unbroken URL in running text — the canonical address appears as link
   text in the privacy policy — was four pixels wider than a 320px screen and
   lost its last character to the page's overflow clip. This breaks a word only
   when it would otherwise not fit at all. */
p, li { overflow-wrap: break-word; }

/* The donation block keeps its own layout but hands its surface to the band
   around it, so there is one border rather than two. */
.donate--bare { margin: 0; border: 0; background: none; padding: 0; }

/* --- speech and help bubbles -------------------------------------------- */

/* Bubble geometry only. A bubble is a shape for something being said on the
   page; none of it implies the application can be talked to. */
.bubble {
  --bubble-bg: var(--overlay);
  position: relative;
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  background: var(--bubble-bg);
  padding: 18px 20px;
  margin: 0 0 20px;
}
.bubble > :last-child { margin-bottom: 0; }
.bubble::after {
  content: "";
  position: absolute;
  width: 14px; height: 14px;
  background: var(--bubble-bg);
  transform: rotate(45deg);
}
.bubble--left { margin-left: 14px; }
.bubble--left::after {
  left: -8px; top: 26px;
  border-left: 1px solid var(--border-strong);
  border-bottom: 1px solid var(--border-strong);
}
.bubble--tooltip::after {
  bottom: -8px;
  left: calc(50% - 7px - var(--pop-shift, 0px));
  border-right: 1px solid var(--border-strong);
  border-bottom: 1px solid var(--border-strong);
}

/* --- the help disclosure ------------------------------------------------ */

/* A real <details>, so it opens from the keyboard and stays open without
   script. What it holds is a shortcut to something the page already says
   further down; nothing here is the only copy of an instruction. */
.help {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  margin: -4px 0 26px;
}
.help > summary { list-style: none; }
.help > summary::-webkit-details-marker { display: none; }

.help-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  background: var(--raised);
  color: var(--muted);
  cursor: pointer;
}
.help-btn:hover { color: var(--text); border-color: var(--steel-dim); }
.help[open] > .help-btn { color: var(--text); border-color: var(--steel-dim); }
.help-btn svg { width: 15px; height: 15px; display: block; flex: 0 0 auto; }

/* Labelled rather than an icon on its own: the label is the accessible name,
   it is live text, and it says what pressing it will tell you. */
.help-btn--text {
  width: auto;
  height: auto;
  /* 44 rather than 40, unconditionally: the coarse-pointer rule above sizes
     the icon-only variant to a 44px square, and it must not do that to one
     with a label in it. */
  min-height: 44px;
  gap: 9px;
  padding: 8px 16px;
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
}

/* A closed <details> hides its content from painting and from assistive
   technology, but the box is still laid out where it was put — off the left of
   a 320px screen, in this case. Taking it out of layout entirely means there is
   nothing there to reason about. */
.help:not([open]) > .help-pop { display: none; }

.help-pop {
  position: absolute;
  bottom: calc(100% + 14px);
  left: 50%;
  transform: translateX(calc(-50% + var(--pop-shift, 0px)));
  width: 288px;
  max-width: calc(100vw - 32px);
  z-index: 60;
  margin: 0;
  font-size: 14.5px;
  box-shadow: 0 24px 48px -20px rgba(0, 0, 0, .95);
}
.help-pop p { font-size: inherit; margin-bottom: 10px; }

/* --- feature spotlight -------------------------------------------------- */

/* Three rows for the three things the application is mostly used for, each
   with one illustration. The remaining features keep the card grid and its
   existing glyphs: repeating a black notebook eleven times would say less
   each time. */
.spotlight { display: grid; gap: 16px; margin: 0 0 16px; }
.feature-row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 20px;
  align-items: center;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--raised);
  padding: clamp(20px, 3vw, 28px);
  transition: border-color .2s ease, background-color .2s ease;
}
.feature-row:hover { border-color: #414855; }
.feature-row h3 { margin: 0 0 8px; font-size: 18px; }
.feature-row p { margin: 0; color: var(--muted); }
/* The square canvas is the artwork's own and is kept whole: these were drawn
   with margins, and cover-cropping them to a tidier box eats the margin. */
.feature-art {
  width: 100%;
  max-width: 240px;
  margin: 0 auto;
}
/* The illustrations were generated on their own near-black canvas, which is a
   shade off the surface they sit on. Rounding and outlining that rectangle
   makes it a deliberate tile rather than a seam somebody has to wonder about. */
.feature-art img, .media-art img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 10px;
  border: 1px solid var(--border);
}

@media (min-width: 760px) {
  .feature-row { grid-template-columns: 240px minmax(0, 1fr); gap: 32px; }
  .feature-row--flip { grid-template-columns: minmax(0, 1fr) 240px; }
  .feature-row--flip .feature-art { order: 2; }
}

/* --- prose beside an illustration --------------------------------------- */

.media { display: grid; gap: 24px; align-items: center; }
.media-art { width: 100%; max-width: 264px; margin: 0 auto; }
@media (min-width: 760px) {
  .media { grid-template-columns: minmax(0, 1fr) 264px; gap: 36px; }
  .media--art-first { grid-template-columns: 264px minmax(0, 1fr); }
}

/* --- navigation rail ---------------------------------------------------- */

/* The satin rail from the navigation studies, as two hairlines rather than a
   raster: a steel highlight along the top edge and a short red seam on the
   bottom one. Both are decoration on an unchanged, live set of links. */
nav.site::before, nav.site::after { content: ""; position: absolute; pointer-events: none; }
nav.site::before {
  left: 0; right: 0; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent,
              rgba(194, 199, 206, .26) 16%,
              rgba(194, 199, 206, .26) 84%, transparent);
}
nav.site::after {
  left: 0; bottom: -1px; width: 148px; height: 2px;
  background: linear-gradient(90deg, var(--accent), rgba(252, 32, 35, 0));
}

/* --- a page heading on a band ------------------------------------------- */

.band--header { padding-block: clamp(30px, 5vw, 52px); }
.band--header .band-header {
  border: 0;
  margin: 0;
  padding: 0;
  max-width: 30ch;
}
.band--header .band-header::after { content: none; }
.band--header h1 { margin-bottom: 10px; }

/* --- document headers --------------------------------------------------- */

/* The same red seam under every page header, so the download, support and
   legal pages read as one set rather than four separate documents. */
.wrap > header { position: relative; }
.wrap > header::after {
  content: "";
  position: absolute;
  left: 0; bottom: -1px;
  width: 84px; height: 2px;
  background: linear-gradient(90deg, var(--accent), rgba(252, 32, 35, 0));
}

/* --- accommodations ----------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .button:active { transform: none; }
  .feature-row { transition: none; }
}

/* Decoration is decoration: in a forced-colours mode it is noise over text the
   reader has asked the system to render its own way. */
@media (forced-colors: active) {
  .band-layer, .corner, .hero-texture, .hero-glow { display: none; }
  .band, .panel, .bubble, .feature-row, .help-pop {
    border: 1px solid ButtonText;
    background: Canvas;
  }
  .button { box-shadow: none; }
}

@media print {
  .band-layer, .corner, .hero-texture, .help { display: none; }
  .band, .panel, .bubble, .feature-row { border-color: #999; background: #fff; }
  .feature-row p, .panel p, .bubble p { color: #000; }
}
