/* TappyMusic — one stylesheet for every page.
 *
 * No framework, no fonts to fetch, no scripts, no analytics. The privacy policy
 * claims exactly that, so it has to stay true: everything here is CSS, and the
 * only non-text asset is a grain texture inlined as a data URI.
 *
 * The palette is the app's own. #FED508 is its accent colour, read out of
 * Assets.xcassets; the aurora and the pack chips use the accents the melody
 * packs actually ship with. A marketing site in colours the product does not
 * use is a site about a different product.
 */

:root {
  color-scheme: dark;

  /* Ink */
  --bg:        #07060d;
  --bg-2:      #0e0c19;
  --surface:   #14111f;
  --surface-2: #1b1729;
  --line:      #2a2440;
  --line-soft: #1f1b30;

  --ink:       #f6f4ff;
  --ink-2:     #c3bcd9;
  --dim:       #8d86a6;

  /* The app's accent, and three pack colours used as light sources. */
  --accent:      #fed508;
  --accent-ink:  #241c00;
  --violet:      #c7b9ff;
  --cyan:        #7fd8f7;
  --pink:        #ff9ec4;

  --measure: 40rem;   /* readable line length for prose */
  --wide:    72rem;   /* the page's outer bound */

  --radius:    1.25rem;
  --radius-sm: 0.75rem;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 clamp(16px, 0.55vw + 15px, 18px)/1.65 -apple-system, BlinkMacSystemFont,
        "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-feature-settings: "kern" 1, "liga" 1;
  overflow-wrap: break-word;
  position: relative;
  min-height: 100vh;
}

/* The aurora. Three coloured lights over near-black, fixed so the page moves
   against it. Cheap: one element, no repaint on scroll. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(60rem 40rem at 12% -8%,  rgba(199, 185, 255, 0.20), transparent 60%),
    radial-gradient(50rem 36rem at 92% 6%,   rgba(127, 216, 247, 0.16), transparent 62%),
    radial-gradient(46rem 34rem at 50% 108%, rgba(255, 158, 196, 0.14), transparent 60%),
    linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 55%, var(--bg) 100%);
}

/* Grain, so the large flat areas have some tooth. An inline SVG, not a request. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }

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

.wrap { max-width: var(--wide); margin: 0 auto; padding: 0 1.5rem; }

/* --- header -------------------------------------------------------------- */

header.site {
  position: sticky;
  top: 0;
  z-index: 20;
  padding: 0.85rem 0;
  background: rgba(7, 6, 13, 0.82);
  background: color-mix(in oklab, var(--bg) 78%, transparent);
  backdrop-filter: saturate(160%) blur(14px);
  -webkit-backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid var(--line-soft);
}

header.site .wrap { display: flex; align-items: center; gap: 1rem 1.5rem; flex-wrap: wrap; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 0.82rem;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

/* The app's own icon, at the size iOS draws it in a list. */
.brand img {
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 0.42rem;
  display: block;
  flex: none;
  box-shadow: 0 0 1rem rgba(254, 213, 8, 0.22), 0 0 0 1px rgba(255, 255, 255, 0.08);
}

/* Centred, not stretched: the badge is taller than a nav link, and a flex
   row's default would pull the current page's pill up to its height. */
nav.site {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex-wrap: wrap;
}

nav.site a {
  color: var(--dim);
  text-decoration: none;
  font-size: 0.93rem;
  padding: 0.35rem 0.7rem;
  border-radius: 999px;
  transition: color 0.18s var(--ease), background-color 0.18s var(--ease);
}

nav.site a:hover { color: var(--ink); background: rgba(255, 255, 255, 0.06); }

nav.site a[aria-current="page"] {
  color: var(--ink);
  background: rgba(255, 255, 255, 0.09);
}

/* The two links that leave this site: the other apps, and the App Store. A
   hairline sets them off, so the header does not read as one undifferentiated
   row of seven. It is drawn as a pseudo-element rather than a border, because
   these pills are pill-shaped and a border-left comes out as a crescent. */
nav.site .away {
  position: relative;
  margin-left: 0.75rem;
}

nav.site .away::before {
  content: "";
  position: absolute;
  left: -0.45rem;
  top: 50%;
  transform: translateY(-50%);
  width: 1px;
  height: 1.15rem;
  background: var(--line);
}

/* A store's badge — Apple's or Microsoft's. Apple's guidelines fix most of this: 40 px is the
   minimum height onscreen, and the clear space around it is at least a quarter
   of that height. Only the height is set here — the width follows whichever
   language's artwork is on the page (Japanese is 109 px wide where Korean is
   130) so nothing is ever stretched to a number this stylesheet invented.

   The grey border is part of the artwork, so this link paints no background of
   its own: `nav.site a:hover` would otherwise slide a pale slab under the
   badge's rounded corners. It dims instead, which the guidelines allow because
   it leaves the artwork alone. */
.store-badge {
  display: inline-flex;
  padding: 0;
  margin: 10px;
  transition: opacity 0.18s var(--ease);
}

.store-badge img { display: block; height: 40px; width: auto; }

nav.site .store-badge:hover,
.store-badge:hover { background: none; opacity: 0.82; }

/* An app that ships as several App Store records gets a badge per platform, and
   the badge artwork is identical on all of them — so the device name is what
   tells them apart. It sits above its badge, small and quiet: the badge is the
   thing being clicked, the label only says which one. */
.store-for {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 0;
}

.store-for-label {
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--dim);
  white-space: nowrap;
}

nav.site .store-for:hover { background: none; }

/* A platform whose listing is not open yet: its store's badge, unlinked and
   dimmed, with "coming soon" beneath. The artwork is untouched — the opacity
   is on the wrapper, the same treatment a badge's hover already gets — and it
   does not react to the pointer, because there is nowhere to go yet. */
.store-pending {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.store-pending .store-badge { opacity: 0.6; pointer-events: none; margin-bottom: 0; }

.store-pending-label {
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--dim);
  white-space: nowrap;
}

/* Two links plus five pages is a wide header. On a phone the nav wraps to its
   own line and the two outward links stay together at its end. */
@media (max-width: 34rem) {
  nav.site { margin-left: 0; width: 100%; }
  nav.site .away { margin-left: auto; }
}

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

.hero { padding: clamp(3.5rem, 9vw, 7rem) 0 clamp(2rem, 5vw, 3.5rem); text-align: center; }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--ink-2);
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.03);
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  margin-bottom: 1.5rem;
}

.eyebrow::before {
  content: "";
  width: 0.45rem; height: 0.45rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0.6rem var(--accent);
}

h1 {
  font-size: clamp(2.4rem, 7.5vw, 4.6rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
  font-weight: 800;
  margin: 0 0 1.1rem;
  text-wrap: balance;
}

/* One phrase carries the colour, so the headline has a focal point without
   turning into a rainbow. */
.glow {
  background: linear-gradient(100deg, var(--accent) 0%, var(--pink) 45%, var(--violet) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.lead {
  font-size: clamp(1.05rem, 1.1vw + 0.9rem, 1.3rem);
  color: var(--ink-2);
  max-width: 36rem;
  margin: 0 auto 2.25rem;
  text-wrap: pretty;
}

/* --- the emblem ---------------------------------------------------------- */

.emblem {
  position: relative;
  width: min(34rem, 82vw);
  margin: 0 auto clamp(1.5rem, 4vw, 2.5rem);
  aspect-ratio: 1000 / 688;
}

.emblem img {
  width: 100%;
  height: auto;
  display: block;
  /* The artwork is neon on black. Screening drops its black to nothing so it
     sits in the page's light rather than in a rectangle of its own — the same
     blend the splash screen uses. */
  mix-blend-mode: screen;
  /* And a soft edge, so it ends rather than stops. */
  -webkit-mask-image: radial-gradient(ellipse 78% 74% at 50% 48%, #000 55%, transparent 100%);
  mask-image: radial-gradient(ellipse 78% 74% at 50% 48%, #000 55%, transparent 100%);
}

/* A pool of the accent under it, so the emblem reads as lit rather than pasted. */
.emblem::before {
  content: "";
  position: absolute;
  inset: 12% 8% 4%;
  z-index: -1;
  background: radial-gradient(ellipse at 50% 60%, rgba(254, 213, 8, 0.18), transparent 70%);
  filter: blur(2.5rem);
}

/* --- the phones ---------------------------------------------------------- */

.shots {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: clamp(0.75rem, 2.5vw, 2rem);
  perspective: 1600px;
  margin-top: clamp(2rem, 5vw, 3.5rem);
}

.shots figure { margin: 0; }

.shots img {
  display: block;
  width: min(15rem, 27vw);
  height: auto;
  border-radius: clamp(1rem, 2.4vw, 1.9rem);
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: #000;
  box-shadow: 0 2rem 4rem rgba(0, 0, 0, 0.65), 0 0 0 0.5px rgba(255, 255, 255, 0.06);
  transition: transform 0.5s var(--ease);
}

/* The outer two sit back, the middle comes forward. Depth without a mockup
   image, and it degrades to a flat row when transforms are unavailable. */
.shots figure:first-child img  { transform: rotateY(16deg)  translateZ(-3rem) translateY(1.25rem); }
.shots figure:last-child  img  { transform: rotateY(-16deg) translateZ(-3rem) translateY(1.25rem); }
.shots figure:nth-child(2) img { width: min(16.5rem, 30vw); }

.shots figure:hover img { transform: rotateY(0) translateZ(0) translateY(-0.4rem); }

/* Landscape screenshots. A fan of three wide images is a stack of letterboxes
   with nothing to see; they want width instead, one under the other on a
   phone and side by side once there is room. */
.shots.wide { display: grid; grid-template-columns: 1fr; perspective: none; }

.shots.wide img { width: 100%; }

.shots.wide figure:first-child img,
.shots.wide figure:last-child img,
.shots.wide figure:nth-child(2) img { transform: none; width: 100%; }

.shots.wide figure:hover img { transform: translateY(-0.4rem); }

@media (min-width: 46rem) {
  .shots.wide { grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); }
}

/* --- stats --------------------------------------------------------------- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  overflow: hidden;
  margin: clamp(3rem, 7vw, 5rem) 0 0;
}

.stats div { background: var(--surface); padding: 1.5rem 1rem; text-align: center; }

.stats b {
  display: block;
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.1;
}

.stats span { font-size: 0.88rem; color: var(--dim); }

/* --- film (blocks.film) --------------------------------------------------- */

.film { margin: clamp(2.5rem, 6vw, 4rem) auto 0; max-width: 46rem; text-align: center; }
.film.portrait { max-width: min(22rem, 82vw); }

.film video {
  display: block;
  width: 100%;
  height: auto;
  background: var(--surface);
  border-radius: clamp(1rem, 2.4vw, 1.9rem);
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 2rem 4rem rgba(0, 0, 0, 0.65), 0 0 0 0.5px rgba(255, 255, 255, 0.06);
}

.film figcaption { margin-top: 0.75rem; font-size: 0.8rem; color: var(--dim); }

/* --- sections ------------------------------------------------------------ */

main { padding-bottom: clamp(3rem, 8vw, 6rem); }

section { padding: clamp(2.75rem, 7vw, 5rem) 0; }
section + section { border-top: 1px solid var(--line-soft); }

h2 {
  font-size: clamp(1.6rem, 3.4vw, 2.4rem);
  line-height: 1.15;
  letter-spacing: -0.025em;
  font-weight: 750;
  margin: 0 0 0.75rem;
  text-wrap: balance;
}

h3 { font-size: 1.1rem; font-weight: 700; margin: 2rem 0 0.4rem; letter-spacing: -0.01em; }

p, li { color: var(--ink-2); }
section > p, section > ul, section > ol, section > dl { max-width: var(--measure); }
.muted { color: var(--dim); }

ul, ol { padding-left: 1.15rem; }
li { margin: 0.35rem 0; }

/* --- cards --------------------------------------------------------------- */

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

.card {
  position: relative;
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-2) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem;
  transition: transform 0.25s var(--ease), border-color 0.25s var(--ease);
}

.card:hover {
  transform: translateY(-3px);
  border-color: rgba(254, 213, 8, 0.45);
  border-color: color-mix(in oklab, var(--accent) 40%, var(--line));
}

.card h3 { margin: 0 0 0.4rem; font-size: 1.05rem; }

/* A card with a foot — a store badge — stacks its content and pushes the foot
   to the bottom, so badges in a row line up whatever each body's length. The
   foot is sized for the widest badge the kit ships (Microsoft's German one,
   166 px plus Apple's quarter-height clear space) inside a 16rem card. */
.card.has-foot { display: flex; flex-direction: column; }
.card-foot { margin-top: auto; padding-top: 1.25rem; display: flex; justify-content: center; }
.card p { margin: 0; color: var(--dim); font-size: 0.96rem; }

.card .icon {
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 0.85rem;
  margin-bottom: 1rem;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line);
  color: var(--accent);
}

.card .icon svg { width: 1.35rem; height: 1.35rem; display: block; }

/* --- callout ------------------------------------------------------------- */

.note {
  max-width: var(--measure);
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-2) 100%);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 1.15rem 1.35rem;
  margin: 1.75rem 0;
}

.note p:first-child { margin-top: 0; }
.note p:last-child { margin-bottom: 0; }

/* --- FAQ ----------------------------------------------------------------- */

dl.faq { max-width: var(--measure); }
dl.faq dt { font-weight: 700; margin-top: 1.75rem; color: var(--ink); }
dl.faq dd { margin: 0.4rem 0 0; color: var(--dim); }

/* --- tables (the song list) ---------------------------------------------- */

table { width: 100%; border-collapse: collapse; font-size: 0.94rem; }

th, td {
  text-align: left;
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: top;
}

thead th {
  color: var(--dim);
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-bottom-color: var(--line);
}

tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: rgba(255, 255, 255, 0.025); }

td:last-child, th:last-child { text-align: right; font-variant-numeric: tabular-nums; color: var(--dim); }

/* The free song in each pack. */
td em {
  font-style: normal;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-ink);
  background: var(--accent);
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  margin-left: 0.5rem;
  white-space: nowrap;
}

.scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* --- pack blocks on the song list ---------------------------------------- */

.pack {
  --pack: var(--violet);
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-2) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.35rem 1.35rem 0.5rem;
  margin: 1.25rem 0;
}

.pack > h3 {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 1.15rem;
}

/* The pack's own accent, straight from the catalogue. */
.pack > h3::before {
  content: "";
  width: 0.85rem; height: 0.85rem;
  border-radius: 0.3rem;
  background: var(--pack);
  box-shadow: 0 0 0.8rem var(--pack);
  flex: none;
}

.pack > p { margin: 0.35rem 0 1rem; max-width: var(--measure); }

.tag {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.15rem 0.5rem;
  white-space: nowrap;
}

.tag.free { color: var(--accent-ink); background: var(--accent); border-color: transparent; }

/* --- buttons ------------------------------------------------------------- */

.button {
  display: inline-block;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
  padding: 0.8rem 1.6rem;
  border-radius: 999px;
  text-decoration: none;
  box-shadow: 0 0.6rem 1.6rem rgba(254, 213, 8, 0.22);
  transition: transform 0.2s var(--ease), box-shadow 0.2s var(--ease);
}

.button:hover { transform: translateY(-2px); box-shadow: 0 0.9rem 2rem rgba(254, 213, 8, 0.3); }

.button.ghost {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line);
  box-shadow: none;
}

.button.ghost:hover { border-color: var(--accent); box-shadow: none; }

.actions { display: flex; gap: 0.75rem; flex-wrap: wrap; justify-content: center; margin-top: 0.5rem; }

/* --- legal pages --------------------------------------------------------- */

.legal p, .legal li, .legal dd { max-width: var(--measure); }
.legal h3 { margin-top: 2.25rem; }

/* A page whose promises differ by platform says so in parts: one box per
   audience — all versions, then each platform — so a reader never takes the
   Windows paragraph for the iPhone one. The box is the card's surface and
   hairline, and its title is the page's h3; headings inside it step down. */
.legal-part {
  margin-top: 2.25rem;
  padding: 0.25rem 1.5rem 1.5rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  scroll-margin-top: 5rem;
}
.legal-part > h3 {
  margin: 1.25rem 0 0.25rem;
  font-size: 1.45rem;
  letter-spacing: -0.01em;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--line);
}
.legal h4 { margin: 1.75rem 0 0.4rem; font-size: 1rem; }
.legal-part .faq { margin-top: 0.5rem; }
.legal-toc { padding-left: 1.2rem; }
.legal-toc li { margin: 0.3rem 0; color: var(--dim); }
@media (max-width: 34rem) { .legal-part { padding: 0.25rem 1rem 1.25rem; } }
.legal section { padding-top: clamp(2rem, 5vw, 3rem); }

/* --- a screen beside its explanation -------------------------------------- */

.showcase {
  display: grid;
  grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: center;
  margin-top: 2.5rem;
}

.showcase img {
  width: 100%;
  height: auto;
  border-radius: clamp(1rem, 2vw, 1.6rem);
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: #000;
  box-shadow: 0 1.5rem 3.5rem rgba(0, 0, 0, 0.6);
}

.showcase h3 { margin-top: 0; }
.showcase p { max-width: var(--measure); }

/* One column once the phone would be too narrow to read. */
@media (max-width: 46rem) {
  .showcase { grid-template-columns: 1fr; justify-items: center; }
  .showcase img { max-width: 15rem; }
}

/* --- the app lockup ------------------------------------------------------ */

.lockup {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  flex-wrap: wrap;
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-2) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem;
  margin-top: 2rem;
}

.lockup img {
  width: 5rem;
  height: 5rem;
  border-radius: 1.15rem;
  display: block;
  flex: none;
  box-shadow: 0 0.8rem 2rem rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.08);
}

.lockup div { min-width: 12rem; flex: 1; }
.lockup h3 { margin: 0 0 0.2rem; font-size: 1.15rem; }
.lockup p { margin: 0; color: var(--dim); font-size: 0.95rem; }

/* Screen blending assumes a dark ground. In light mode the artwork needs to
   keep its own black instead. */
@media (prefers-color-scheme: light) {
  .emblem img { mix-blend-mode: normal; border-radius: 1.5rem; }
  .emblem::before { display: none; }
}

/* --- footer -------------------------------------------------------------- */

footer.site {
  border-top: 1px solid var(--line-soft);
  padding: 2.5rem 0 3.5rem;
  color: var(--dim);
  font-size: 0.9rem;
}

footer.site .wrap { display: flex; gap: 1rem 1.5rem; flex-wrap: wrap; align-items: center; }
footer.site nav { display: flex; gap: 0.25rem; flex-wrap: wrap; margin-left: auto; }
footer.site a { color: var(--dim); text-decoration: none; padding: 0.3rem 0.6rem; border-radius: 999px; }
footer.site a:hover { color: var(--ink); background: rgba(255, 255, 255, 0.06); }

/* --- language switcher ---------------------------------------------------- */

footer.site nav.languages { gap: 0.15rem; }

footer.site nav.languages a[aria-current="page"] {
  color: var(--ink);
  background: rgba(255, 255, 255, 0.08);
}

/* --- motion, print, light ------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; animation: none !important; }
  .shots figure img { transform: none !important; }
}

/* The privacy policy, terms and Impressum get printed and filed. Make that
   legible rather than a wall of dark grey. */
@media print {
  body { background: #fff; color: #000; }
  body::before, body::after { display: none; }
  header.site, footer.site nav, .shots { display: none; }
  a { color: #000; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 0.85em; color: #555; }
  .note, .card, .pack { border: 1px solid #bbb; background: none; }
}

/* The pages are dark by design — it is the app's own look — but a light
   preference is a request, not a suggestion. */
@media (prefers-color-scheme: light) {
  :root {
    color-scheme: light;
    --bg:        #fbfaff;
    --bg-2:      #f3f0fb;
    --surface:   #ffffff;
    --surface-2: #f7f5fd;
    --line:      #e0dbf0;
    --line-soft: #ebe7f7;
    --ink:       #14112a;
    --ink-2:     #3c3660;
    --dim:       #6b6488;
    --accent:     #6b3df0;
    --accent-ink: #ffffff;
    --violet:     #7b5cf0;
    --cyan:       #1f9ec4;
    --pink:       #d4568f;
  }

  body::before {
    background:
      radial-gradient(60rem 40rem at 12% -8%,  rgba(123, 92, 240, 0.10), transparent 60%),
      radial-gradient(50rem 36rem at 92% 6%,   rgba(31, 158, 196, 0.09), transparent 62%),
      radial-gradient(46rem 34rem at 50% 108%, rgba(212, 86, 143, 0.08), transparent 60%),
      linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 60%, var(--bg) 100%);
  }

  body::after { opacity: 0.02; }
  header.site {
    background: rgba(251, 250, 255, 0.86);
    background: color-mix(in oklab, var(--bg) 82%, transparent);
  }
  .shots img { border-color: rgba(0, 0, 0, 0.10); background: #111; }
  .button { box-shadow: 0 0.6rem 1.6rem rgba(107, 61, 240, 0.22); }
  tbody tr:hover td { background: rgba(0, 0, 0, 0.02); }
  nav.site a:hover, footer.site a:hover { background: rgba(0, 0, 0, 0.05); }
}

/* Igor&nbsp;Kunin */
:root {
  --accent: #d8a657;
  --accent-ink: #1a1305;
  --bg: #101820;
  --bg-2: #16212b;
  --cyan: #8fd0d8;
  --dim: #9a9284;
  --ink: #f4efe6;
  --ink-2: #cfc6b6;
  --line: #314856;
  --line-soft: #263a45;
  --pink: #e0a3a3;
  --surface: #1b2a33;
  --surface-2: #22343f;
  --violet: #9fc9a8;
}

/* The root of the Pages site — the card grid, and nothing else.
 *
 * Appended to the kit's stylesheet when `appsite/portfolio.py` builds the root,
 * rather than served beside it: one file, one request, and no second copy of a
 * token to disagree with the first. The app sites never load this.
 *
 * Nothing here names a colour. The page's palette is the one the app named in
 * portfolio_config.py publishes, and these rules name tokens only — so there is
 * no per-app CSS anywhere.
 *
 * Two accents, and the split is the point. `--accent` is the page's, and it
 * paints every button on it, so the cards offer one way in rather than three.
 * `--card-accent` is the app's own, written inline on its card by
 * `portfolio.py`; it lights that card's icon and its hover edge and reaches
 * nothing else. A card must never override `--accent` itself — see AGENTS.md.
 */

/* The kit's hero leaves room for a lead paragraph under the headline. This page
   has one word and then the apps, so the gap closes up. */
.hero { padding-bottom: 0; }
main > .wrap > section { padding-top: clamp(1.75rem, 5vw, 3rem); }
main { padding-bottom: clamp(2rem, 6vw, 4rem); }

.apps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16.5rem, 1fr));
  gap: 1.25rem;
}

.app {
  /* The app's own colour, when its card carries one. A card built from a
     manifest that has none falls back to the page's, rather than to nothing. */
  --card-accent: var(--accent);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 2.25rem 1.5rem 1.75rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: transform 0.35s var(--ease), border-color 0.35s var(--ease);
}

.app:hover {
  transform: translateY(-3px);
  border-color: var(--card-accent);
}

/* The icon at the size iOS draws it on the home screen, lit by the app's own
   accent so three cards do not read as one grey list.

   It names the icon rather than every image on a card: written as `.app img`
   this rounded off Apple's App Store badge, squared it to 88 px and hung a
   glow behind it — three things their guidelines ask nobody to do to it. */
.app .icon {
  width: 5.5rem;
  height: 5.5rem;
  border-radius: 1.3rem;
  margin-bottom: 1.1rem;
  box-shadow: 0 0 2.5rem rgba(255, 255, 255, 0.10),
              0 0 0 1px rgba(255, 255, 255, 0.08);
  box-shadow: 0 0 2.5rem color-mix(in oklab, var(--card-accent) 35%, transparent),
              0 0 0 1px rgba(255, 255, 255, 0.08);
}

.app h3 {
  margin: 0;
  font-size: 1.2rem;
}

.app h3 a {
  color: var(--ink);
  text-decoration: none;
}

.app h3 a:hover { text-decoration: underline; }

/* The app's own App Store subtitle, and the only prose on the page. */
.app p {
  margin: 0.4rem 0 1.4rem;
  color: var(--dim);
  font-size: 0.95rem;
  max-width: 20rem;
}

/* Pinned to the bottom, so the ways in line up across cards whose slogans run
   to different lengths. The badge brings its own clear space, so the row is
   centred on it rather than given a gap of its own. */
.app .actions { margin-top: auto; align-items: center; }

/* The kit's button glow is a fixed yellow; on this page it follows the page's
   own accent — the same glow under every card's buttons, not the card's. */
.app .button {
  box-shadow: 0 0.6rem 1.6rem color-mix(in oklab, var(--accent) 25%, transparent);
}

.app .button:hover {
  box-shadow: 0 0.9rem 2rem color-mix(in oklab, var(--accent) 32%, transparent);
}
