/* SAFA promo + privacy pages.
   Plain CSS, no framework, no build step, no third-party request of any kind:
   the page claims the app does not track, so the page must not either.

   The page's one idea: a scroll through it is a scroll through one day.
   Every section carries a time-of-day marker running Subuh to Isya, the app's
   own 3px meter doubles as the scroll progress line, and the page closes on
   the next day's dawn — because recurrence is the product's whole premise.
   Times match the design system's fake-but-consistent Jakarta timetable.

   All motion beyond the IntersectionObserver reveal is CSS scroll-driven
   animation: no JavaScript, absent in older engines, and switched off wholesale
   by prefers-reduced-motion. */

/* --------------------------------------------------------------- base --- */

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

html { -webkit-text-size-adjust: 100% }

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

body {
  margin: 0;
  background: var(--bg-app);
  color: var(--text-body);
  font: var(--type-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, p, ul, ol, figure { margin: 0 }
ul, ol { padding: 0 }

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

img { max-width: 100%; height: auto; display: block }

/* Times, reps and ratios never reflow as digits change. */
time, .num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1 }

:where(a, button, summary):focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

input:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 0;
}

.lompat {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--surface);
  color: var(--text-strong);
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--sp-6);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-md);
  z-index: 30;
}
.lompat:focus { left: var(--sp-5); top: var(--sp-5) }

/* ------------------------------------------------------------- layout --- */

/* Page-level display sizes. The app's scale tops out at 40px because it lives
   on a phone; a landing hero at desktop width earns the streak-numeral class
   of size (72). Tone stays the same — the type gets larger, never louder. */
:root {
  --fs-hero: clamp(36px, 5.6vw, 64px);
  --fs-bab: clamp(26px, 3.6vw, 44px);
  --dur-reveal: 640ms;
  --ease-keluar: cubic-bezier(0.16, 1, 0.3, 1);
}

.wrap {
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  padding-inline: var(--gutter);
}
.wrap--sempit { max-width: 720px }

/* Pacing over rules: the day-markers carry each section's own hairline, so
   sections separate by whitespace, not borders. */
section { padding-block: clamp(72px, 11vw, 152px); scroll-margin-top: 72px }

.overline {
  font: var(--type-label);
  letter-spacing: var(--ls-overline);
  text-transform: uppercase;
  color: var(--text-muted);
}

.judul {
  font: var(--fw-bold) var(--fs-bab) / var(--lh-snug) var(--font-ui);
  letter-spacing: var(--ls-tight);
  color: var(--text-strong);
  text-wrap: balance;
}

.teks { max-width: 60ch; color: var(--text-body) }
.teks + .teks { margin-top: var(--sp-6) }
.teks--redup { color: var(--text-muted) }

/*
 * The paragraph that answers the heading is set a step larger than the ones
 * that qualify it.
 *
 * Every paragraph on the page used to be the same size and the same colour, so
 * a section's opening claim and its third caveat carried identical weight —
 * eleven paragraphs of undifferentiated grey. Type that never varies is what a
 * page looks like when nobody decided what mattered.
 *
 * One step, not two: the heading is still the loudest thing in the section.
 */
.judul + .teks,
.judul + .teks[style] {
  font-size: clamp(17px, 1.15vw + 13px, 19px);
  line-height: 1.5;
  color: var(--text-strong);
  max-width: 52ch;
}

/* --------------------------------------------------------- day marker ---
   The structural device of the page: a clock time and the slot it belongs to,
   then a hairline running out. Section order follows the day; the marker is
   the proof. In the "seven in the morning" section the slot is a dash —
   07.00 belongs to no waktu, which is that section's entire point. */

.penanda {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  margin-bottom: clamp(28px, 4vw, 48px);
  font: var(--type-label);
  letter-spacing: var(--ls-overline);
  text-transform: uppercase;
  /* Was --text-faint: 2.79:1 on the light background. The marker is meant to be
     quiet, and it still is — one step up the scale, with the hour carrying the
     emphasis instead of the colour gap doing it. */
  color: var(--text-muted);
}
.penanda time { font-weight: var(--fw-semibold); color: var(--text-body) }
.penanda::after { content: ""; flex: 1; height: 1px; background: var(--border) }

/* ------------------------------------------------------ progress line ---
   The app reports progress with a 3px meter that never applauds. The same
   meter, laid along the top of the viewport, is the day filling up as you
   scroll. Pure CSS: without scroll-timeline support it stays at scale 0 and
   simply never appears. */

.progres-hari {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 20;
  pointer-events: none;
}
.progres-hari > i {
  display: block;
  height: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
}
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .progres-hari > i {
      animation: isi-hari linear both;
      animation-timeline: scroll(root);
    }
  }
}
@keyframes isi-hari { to { transform: scaleX(1) } }

/* --------------------------------------------------------------- head --- */

.kepala {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--bg-app);
  border-bottom: var(--bw) solid var(--border);
}
.kepala > .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  min-height: 64px;
}

.merek {
  display: flex;
  align-items: center;
  min-height: 44px;
  gap: var(--sp-5);
  text-decoration: none;
  color: var(--text-strong);
}
.merek:hover { color: var(--text-strong) }
/* The launcher-icon tile: mark on --safa-ink, rounded like an app icon. */
.merek img { width: 28px; height: 28px; border-radius: 7px; display: block }

.wordmark {
  font-family: var(--font-wordmark);
  font-weight: 300;
  font-size: var(--fs-title);
  letter-spacing: var(--ls-wordmark);
  text-transform: uppercase;
  /* The tracking is on the right of the last letter too; pull it back so the
     wordmark's optical edge is the letter, not the space after it. */
  margin-right: calc(var(--ls-wordmark) * -1);
}

.kepala nav {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  font: var(--type-body-sm);
}
/*
 * 44px, not the 20px line box these had.
 *
 * The design system's own rule is "target ketuk minimum 48dp tanpa
 * pengecualian", and a bare text link honours the type scale while quietly
 * ignoring that: the words are the right size, the thing you can hit is not.
 * Height comes from padding so the type is untouched.
 */
.kepala nav .nav-teks {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--sp-2);
  color: var(--text-muted);
  text-decoration: none;
}
.kepala nav .nav-teks:hover { color: var(--text-strong) }

/* Narrow screens keep only the action. "Cara kerja" and "Privasi" both live
   elsewhere on the page, the goal does not. */
@media (max-width: 559px) {
  .kepala nav .nav-teks { display: none }
}

/* Header-sized action: shorter than the 48px page buttons so the bar stays
   64px, still above the 44px floor. */
.tombol--rapat {
  min-height: 44px;
  padding-inline: var(--sp-6);
  font: var(--fw-semibold) var(--fs-body-sm) / 1 var(--font-ui);
}

/* ------------------------------------------------------------ tombols --- */

.tombol {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
  min-height: var(--touch-min);
  padding: 0 var(--sp-8);
  border: var(--bw) solid transparent;
  border-radius: var(--r-md);
  font: var(--type-body-strong);
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-standard),
              transform var(--dur-press) var(--ease-standard);
}
.tombol:active { transform: scale(var(--press-scale)) }

.tombol--utama {
  background: var(--accent-fill);
  color: var(--text-on-accent);
}
.tombol--utama:hover {
  background: var(--accent-fill-press);
  color: var(--text-on-accent);
}

.tombol--kedua {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--text-strong);
}
.tombol--kedua:hover { background: var(--surface-sunken); color: var(--text-strong) }

/* Closed testing: there is no store listing to link to yet, so the primary
   action is a plain statement of fact with a way to reach a person. */
.uji {
  scroll-margin-top: 84px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: var(--sp-7) var(--sp-8);
  background: var(--surface);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-lg);
  max-width: 30rem;
  /* Narrow screens: the card is the full width of the column and reads as its
     own object, so it centres on its own axis. */
  text-align: center;
}

/* From the width where the hero splits into two columns, the card sits beside
   the mockup as part of the text column — so it aligns with the headline above
   it rather than centring against it. The closing card keeps centring: its
   whole section is centred. */
@media (min-width: 900px) {
  .hero .uji { text-align: left }
}
.uji strong { font: var(--type-body-strong); color: var(--text-strong) }
.uji span { font: var(--type-body-sm); color: var(--text-muted) }

/* The signup, folded behind a native <details> so it needs no script.
   The summary is the call to action; opening it reveals three fields. */
.daftar { margin-top: var(--sp-2) }
.daftar summary {
  list-style: none;
  user-select: none;
  -webkit-appearance: none;
  /* Block-level so the auto margins bite — .tombol is inline-flex, which
     margin: auto cannot centre. */
  display: flex;
  width: fit-content;
  margin-inline: auto;
}
.daftar summary::marker,
.daftar summary::-webkit-details-marker { display: none }
.daftar[open] summary { background: var(--accent-fill-press) }
/* Field labels stay left-set: a label centred over a full-width input is
   harder to scan, and the eye should start where the typing starts. */
.daftar form { margin-top: var(--sp-6); display: grid; gap: var(--sp-5); text-align: left }
/* No summary to open, so no top gap to clear either. */
.daftar--terbuka form { margin-top: var(--sp-5) }

.isian { display: grid; gap: var(--sp-2) }
.isian span { font: var(--type-label); color: var(--text-muted) }
.isian input {
  min-height: var(--touch-min);
  padding: 0 var(--sp-6);
  border: var(--bw) solid var(--border-strong);
  border-radius: var(--r-md);
  /* A well nested inside a card, per the design system. `--surface` here made
     the field exactly the colour of the card it sits on — in dark mode the two
     were both #141817, leaving only a faint 1px line to say a field was there. */
  background: var(--surface-sunken);
  color: var(--text-strong);
  font: var(--type-body);
  width: 100%;
}
.isian input::placeholder { color: var(--text-faint) }

/* Honeypot: invisible to people, present for bots. The workflow answers a
   filled one identically and simply sends nothing. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.tautan-kecil {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font: var(--type-body-sm);
  color: var(--text-muted);
}
.tautan-kecil:hover { color: var(--text-strong) }

/* --------------------------------------------------------------- hero ---
   The opening scene: the whole viewport, dawn's time in the corner, the type
   large and unhurried, the phone drifting slower than the page around it. */

.hero {
  min-height: calc(100svh - 64px);
  display: grid;
  align-content: center;
  padding-block: clamp(48px, 7vw, 96px);
}
.hero .wrap {
  display: grid;
  gap: clamp(40px, 6vw, 88px);
  align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 900px) {
  .hero .wrap { grid-template-columns: minmax(0, 1fr) auto }
}

.hero .penanda { margin-bottom: clamp(20px, 3vw, 36px) }

.hero h1 {
  font: var(--fw-bold) var(--fs-hero) / 1.08 var(--font-ui);
  letter-spacing: -0.03em;
  color: var(--text-strong);
  text-wrap: balance;
}
.hero .subjudul { margin-top: var(--sp-8); max-width: 46ch; color: var(--text-muted) }
.hero .aksi { margin-top: var(--sp-10); display: flex; flex-direction: column; gap: var(--sp-6); align-items: flex-start }

/* Page-load sequence: one orchestrated entrance, then stillness. */
@media (prefers-reduced-motion: no-preference) {
  .muncul { animation: naik 900ms var(--ease-keluar) both }
  /* nth-child, not nth-of-type: the entrance order is document order across
     mixed elements (div, h1, p, div), which nth-of-type would count per tag. */
  .muncul:nth-child(2) { animation-delay: 100ms }
  .muncul:nth-child(3) { animation-delay: 200ms }
  .muncul:nth-child(4) { animation-delay: 300ms }
  .hero .telepon { animation: naik 1100ms var(--ease-keluar) 250ms both }
}

/*
 * The hero phone does two things at once, and they used to fight.
 *
 * `.hero .telepon` above sets the `animation` shorthand, which resets
 * `animation-timeline` to `auto` — and at 0,2,0 it outranked `.paralaks`
 * (0,1,0), so the entrance silently killed the drift. The browser supported
 * scroll timelines perfectly well; the cascade simply never let one through.
 *
 * Both are declared together instead: the entrance on the document timeline,
 * the drift on the view timeline, one list each.
 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .hero .telepon.paralaks {
      animation: naik 1100ms var(--ease-keluar) 250ms both, hanyut linear both;
      animation-timeline: auto, view();
      animation-range: normal, entry 0% exit 100%;
    }
  }
}
@keyframes naik {
  from { opacity: 0; transform: translateY(18px) }
  to { opacity: 1; transform: none }
}

/* ----------------------------------------------------------- parallax ---
   Two speeds only: the phone drifts against the hero, and the closing dawn
   numeral drifts behind the closing words. Depth, not spectacle. */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .paralaks {
      animation: hanyut linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
    .paralaks--jauh {
      animation: hanyut-jauh linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
  }
}
@keyframes hanyut {
  from { transform: translateY(34px) }
  to { transform: translateY(-34px) }
}
@keyframes hanyut-jauh {
  from { transform: translateY(80px) }
  to { transform: translateY(-80px) }
}

/* --------------------------------------------------- sepuluh waktu ---
   The ten are a real sequence through the day, so each carries its index.
   The numeral is the structure; no decoration on top of it. */

.waktu {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--sp-4);
  margin-top: clamp(28px, 4vw, 44px);
}
.waktu li {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  min-height: 60px;
  padding: 0 var(--sp-6);
  background: var(--surface);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-md);
  font: var(--type-body-sm);
  color: var(--text-body);
  transition: border-color var(--dur-fast) var(--ease-standard);
}
.waktu li:hover { border-color: var(--border-strong) }
.waktu i {
  font: var(--fw-semibold) var(--fs-caption) / 1 var(--font-num);
  font-style: normal;
  font-variant-numeric: tabular-nums;
  /* --text-faint measured 4.25:1 on the card in dark mode, under AA. These
     numerals carry the order of the day, so they are content, not decoration. */
  color: var(--text-muted);
}

/* ------------------------------------------------------------- kartu --- */

/* --------------------------------------------------------- tiga hal ---
 * Three labelled lines, read straight from the source document, which lists
 * them inside one block rather than as three separate things.
 *
 * They were three bordered cards in a row. Nothing was wrong with the border —
 * the design system builds cards exactly that way — but three equal boxes side
 * by side is the shape every generated marketing page arrives in, and side by
 * side they also stopped being a *list*: the reader met them as parallel
 * options instead of three points in a row.
 *
 * A counted list, separated by the hairline this page already separates
 * everything with. The number is the section's own promise made visible.
 */
.tiga {
  margin-top: clamp(28px, 4vw, 44px);
  counter-reset: tiga;
  border-top: var(--bw) solid var(--border);
}
.tiga > div {
  counter-increment: tiga;
  display: grid;
  grid-template-columns: 2.25rem 1fr;
  gap: 0 var(--sp-5);
  padding-block: var(--sp-8);
  border-bottom: var(--bw) solid var(--border);
}
.tiga > div::before {
  content: counter(tiga);
  grid-row: 1 / span 2;
  font: var(--fw-semibold) var(--fs-body-sm) / 1.5 var(--font-num);
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.tiga dt { font: var(--type-title); color: var(--text-strong) }
/* The UA stylesheet indents dd by 40px; the number column already does that job. */
.tiga dd { margin: var(--sp-4) 0 0; max-width: 54ch; font: var(--type-body); color: var(--text-muted) }

@media (max-width: 560px) {
  .tiga > div { grid-template-columns: 1.75rem 1fr; padding-block: var(--sp-7) }
}

/* Shaum Mode carries the logo's deep teal, the one quiet second colour. */
.shaum { background: var(--status-shaum-quiet) }
.shaum .judul, .shaum .teks { color: var(--safa-ink) }
.shaum .teks--redup { color: color-mix(in srgb, var(--safa-ink) 72%, transparent) }
.shaum .penanda, .shaum .penanda time { color: color-mix(in srgb, var(--safa-ink) 60%, transparent) }
.shaum .penanda::after { background: color-mix(in srgb, var(--safa-ink) 20%, transparent) }
@media (prefers-color-scheme: dark) {
  .shaum .judul { color: var(--status-shaum) }
  .shaum .teks { color: var(--text-body) }
  .shaum .teks--redup { color: var(--text-muted) }
  .shaum .penanda { color: var(--text-faint) }
  .shaum .penanda time { color: var(--text-muted) }
  .shaum .penanda::after { background: var(--border) }
}

/* ------------------------------------------------------------ penutup ---
   The page ends where the day restarts: tomorrow's Subuh, huge and barely
   there, drifting behind the closing words. Sixty days is this, repeated. */

.penutup {
  position: relative;
  overflow: hidden;
  text-align: center;
  padding-block: clamp(112px, 16vw, 208px);
}
.penutup .wrap { position: relative; z-index: 1 }
.penutup .judul { margin-inline: auto }
.penutup .teks { margin-inline: auto; margin-top: var(--sp-7) }
.penutup .aksi { margin-top: var(--sp-10); display: flex; flex-direction: column; align-items: center; gap: var(--sp-6) }
.penutup .penanda { justify-content: center }
.penutup .penanda::after { display: none }

.fajar {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  z-index: 0;
  pointer-events: none;
  user-select: none;
}
.fajar time {
  font: var(--fw-bold) clamp(160px, 28vw, 320px) / 1 var(--font-num);
  letter-spacing: -0.04em;
  color: color-mix(in srgb, var(--text-strong) 5%, transparent);
  white-space: nowrap;
}

/* --------------------------------------------------------------- kaki --- */

.kaki {
  border-top: var(--bw) solid var(--border);
  padding-block: var(--sp-10);
  font: var(--type-body-sm);
  /* --text-faint measured 2.79:1 on the light background — well under AA.
     Quiet is the intent here, illegible is not. */
  color: var(--text-muted);
}
.kaki .wrap {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-5) var(--sp-7);
  align-items: center;
  justify-content: space-between;
}
.kaki nav { display: flex; flex-wrap: wrap; gap: 0 var(--sp-7) }
.kaki nav a { display: inline-flex; align-items: center; min-height: 44px }

/* ------------------------------------------------------ phone mockup ---
   Rebuilt from the tokens rather than screenshotted, so it stays honest to the
   app and stays sharp at any density. Decorative: hidden from screen readers,
   its content is in the prose already. */

.telepon {
  --tel-w: 340px;
  /* min(), not width + max-width: at 375px the frame is 5px wider than the
     column and was overhanging into the right gutter. */
  width: min(var(--tel-w), 100%);
  margin-inline: auto;
  padding: 10px;
  background: var(--n-950);
  border-radius: var(--r-2xl);
  border: var(--bw) solid var(--border-strong);
}
/* In dark mode the bezel and the page background are the same near-black, so
   the frame stops reading as a device. Lift it instead of darkening it. */
@media (prefers-color-scheme: dark) {
  .telepon { background: var(--n-800) }
}
.layar {
  background: var(--bg-app);
  border-radius: 20px;
  overflow: hidden;
  padding: var(--sp-5) 14px var(--sp-8);
}

.bar-sistem {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font: var(--fw-semibold) var(--fs-micro) / 1 var(--font-num);
  color: var(--text-muted);
  padding: 0 var(--sp-2) var(--sp-6);
}
.bar-sistem span:last-child { letter-spacing: 0.14em }

.layar-kepala { padding: 0 var(--sp-2) var(--sp-7) }
/*
 * Only the hero screen carries an action beside its title, and only it wraps
 * its overline and title in an element of their own.
 *
 * Making this a row for every screen put the gallery phones' overlines up on
 * the title's line — "SESI BERJALAN  Setelah Subuh" — because there the two
 * are direct children and became flex siblings.
 */
.hero .layar-kepala {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-5);
}
/* The named entry to the library, as the app now labels it. */
.pil-latihan {
  flex: none;
  font: var(--fw-semibold) var(--fs-micro) / 1 var(--font-ui);
  color: var(--text-strong);
  background: var(--surface);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-pill);
  padding: 8px 11px;
}
.layar-kepala .overline { font-size: var(--fs-micro) }
.layar-kepala .judul-layar {
  font: var(--fw-bold) var(--fs-h2) / var(--lh-snug) var(--font-ui);
  letter-spacing: var(--ls-snug);
  color: var(--text-strong);
  margin-top: var(--sp-2);
}

.kartu-sesi {
  background: var(--surface);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-xl);
  padding: var(--sp-6);
}
.kartu-sesi .baris-atas {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
}
.pil {
  font: var(--fw-semibold) var(--fs-micro) / 1 var(--font-num);
  color: var(--accent-text);
  background: var(--accent-quiet);
  padding: 5px 9px;
  border-radius: var(--r-pill);
  white-space: nowrap;
}
.jam-besar {
  display: flex;
  align-items: baseline;
  gap: var(--sp-5);
  margin-top: var(--sp-6);
}
.jam-besar b {
  font: var(--fw-bold) 40px / 1 var(--font-num);
  letter-spacing: -0.03em;
  color: var(--text-strong);
}
.jam-besar i {
  font: var(--fw-bold) var(--fs-title) / 1.1 var(--font-ui);
  font-style: normal;
  letter-spacing: var(--ls-snug);
  color: var(--text-strong);
}
.acuan { font: var(--type-caption); color: var(--text-faint); margin-top: var(--sp-4) }

.kartu-sesi .baris-bawah {
  margin-top: var(--sp-6);
  padding-top: var(--sp-5);
  border-top: var(--bw) solid var(--border);
}
.nama-sesi { font: var(--type-body-strong); color: var(--text-strong) }
.ringkas { font: var(--type-caption); color: var(--text-muted); margin-top: 2px }

/* Full-width, like the real screen: the app learned that a session which can
   only be started by guessing that a row is a button does not get started. */
.tombol-layar {
  display: block;
  margin-top: var(--sp-6);
  text-align: center;
  background: var(--accent-fill);
  color: var(--text-on-accent);
  font: var(--fw-semibold) var(--fs-body-sm) / 1 var(--font-ui);
  padding: 13px var(--sp-6);
  border-radius: var(--r-md);
}

.blok-jadwal { margin-top: var(--sp-7); padding: 0 var(--sp-2) }
.blok-jadwal .baris-atas {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
}
.blok-jadwal .baris-atas .overline { font-size: var(--fs-micro) }
.blok-jadwal .baris-atas span:last-child { font: var(--type-caption); color: var(--text-muted) }

/* Secondary action, outlined — "Latihan di luar jadwal" on Hari Ini. */
.tombol-sekunder {
  display: block;
  margin-top: var(--sp-7);
  text-align: center;
  font: var(--fw-semibold) var(--fs-body-sm) / 1 var(--font-ui);
  color: var(--text-strong);
  background: var(--surface);
  border: var(--bw) solid var(--border-strong);
  border-radius: var(--r-md);
  padding: 13px var(--sp-6);
}

/* The two figures and their ratio, at the foot of Hari Ini. */
.badan { margin-top: var(--sp-8) }
.badan-baris {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-4);
  margin-top: var(--sp-4);
}
.ubin {
  padding: var(--sp-5) var(--sp-4);
  background: var(--surface);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-lg);
}
.ubin > span { display: block; font: var(--type-caption); color: var(--text-muted) }
.ubin > b {
  display: block;
  margin-top: 5px;
  font: var(--fw-bold) var(--fs-body) / 1.2 var(--font-num);
  color: var(--text-strong);
}
.ubin > b > i { font: var(--type-caption); color: var(--text-muted); margin-left: 2px }
/* WHtR carries its band as a fill behind the figure, never as the figure
   itself (D-51). Pastel, so it marks a band rather than raising an alarm. */
/* The band colours are app-only (D-51) and deliberately absent from the web
   tokens, so this is the literal rather than a var() whose fallback always
   fires and reads as if a token existed. */
.ubin[data-pita] { background: #cdead1; border-color: transparent }
.ubin[data-pita] > span, .ubin[data-pita] > b { color: #3a3a38 }

.meter {
  height: 3px; /* reports, never applauds */
  margin-top: var(--sp-4);
  background: var(--surface-sunken);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.meter > i { display: block; height: 100%; background: var(--accent); border-radius: var(--r-pill) }

.rel { margin-top: var(--sp-6); display: flex; flex-direction: column }
.rel-baris {
  display: grid;
  grid-template-columns: 44px 14px 1fr;
  align-items: start;
  gap: var(--sp-4);
  padding-block: var(--sp-4);
}
.rel-baris > time {
  font: var(--fw-semibold) var(--fs-body-sm) / 1.2 var(--font-num);
  color: var(--text-muted);
}
.rel-titik {
  justify-self: center;
  margin-top: 3px;
  width: 10px;
  height: 10px;
  border-radius: var(--r-pill);
  border: 2px solid var(--border-strong);
  background: var(--bg-app);
}
.rel-baris[data-status="selesai"] .rel-titik { background: var(--status-selesai); border-color: var(--status-selesai) }
.rel-baris[data-status="aktif"] .rel-titik { border-color: var(--accent); box-shadow: var(--ring-focus) }
.rel-slot { display: block; font: var(--type-body-sm); color: var(--text-body) }
.rel-baris[data-status="selesai"] .rel-slot { color: var(--text-muted) }
.rel-sub { display: block; font: var(--fs-micro) / 1.3 var(--font-ui); color: var(--text-faint); margin-top: 1px }

/* ------------------------------------------------------------ galeri ---
   Three more screens, side by side on a wide viewport and a snapping swipe
   deck on a narrow one — no script either way, `scroll-snap` does it.

   Each frame drifts at its own rate so the row has depth without anything
   moving on its own: the page has to be scrolled for any of it to happen. */

.galeri {
  margin-top: clamp(32px, 5vw, 56px);
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: min(300px, 78vw);
  gap: clamp(20px, 4vw, 48px);
  /* Three rows shared by every figure: the devices, then the caption titles,
     then the caption bodies.

     It was two, which aligned the phones but stopped there: inside a caption
     the title and the body were still stacked normally, so the middle title
     wrapping to two lines pushed its own body a line lower than its
     neighbours'. Three columns of text starting at three different heights —
     the same ragged edge as before, moved down one element.

     (Two rows was itself the fix for the phones: left to a per-figure flex
     column, a caption whose title wrapped stole that height from its own
     phone — 575, 550, 575.) */
  grid-template-rows: 1fr auto auto;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* The gutter is paid back here so the deck can bleed to both edges while its
     first and last cards still line up with the text above. */
  padding-inline: var(--gutter);
  margin-inline: calc(var(--gutter) * -1);
  padding-bottom: var(--sp-6);
  scrollbar-width: none;
}
.galeri::-webkit-scrollbar { display: none }

@media (min-width: 900px) {
  .galeri {
    grid-auto-columns: 1fr;
    overflow: visible;
    padding-inline: 0;
    margin-inline: 0;
  }
}

.galeri figure {
  margin: 0;
  scroll-snap-align: center;
  display: flex;
  flex-direction: column;
}
/* Each figure hands its two children to the gallery's own rows, so the phones
   are sized against each other rather than against their own captions. */
@supports (grid-template-rows: subgrid) {
  .galeri figure {
    display: grid;
    grid-row: span 3;
    grid-template-rows: subgrid;
  }
  /* The caption hands its own two children up to the same rows, so titles sit
     on one baseline and bodies on another, however many lines a title takes. */
  .galeri figcaption {
    display: grid;
    grid-row: span 2;
    grid-template-rows: subgrid;
  }
}
.galeri .telepon { --tel-w: 100%; flex: 1 }
.galeri .layar { height: 100% }
.galeri figcaption { margin-top: var(--sp-7) }
.galeri figcaption b {
  display: block;
  font: var(--type-title);
  color: var(--text-strong);
}
.galeri figcaption span {
  display: block;
  margin-top: var(--sp-3);
  font: var(--type-body-sm);
  color: var(--text-muted);
}

/* Staggered drift. Only on wide viewports: inside a horizontal scroller the
   vertical offsets fight the snap. */
@supports (animation-timeline: view()) {
  @media (min-width: 900px) and (prefers-reduced-motion: no-preference) {
    .galeri figure:nth-child(1) .telepon,
    .galeri figure:nth-child(3) .telepon {
      animation: hanyut linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
    .galeri figure:nth-child(2) .telepon {
      animation: hanyut-jauh linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
  }
}


/* ------------------------------------------- isi layar: sesi berjalan --- */

.kartu-progres {
  background: var(--surface);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
}
.kartu-progres b { display: block; font: var(--type-body-strong); color: var(--text-strong) }
.kartu-progres .sisa { display: block; font: var(--type-caption); color: var(--text-muted); margin-top: 3px }
.kartu-progres .jendela { display: block; font: var(--fs-micro) / 1.4 var(--font-ui); color: var(--text-faint); margin-top: var(--sp-4) }

.gerakan { margin-top: var(--sp-6) }
.gerakan .overline { font-size: var(--fs-micro); display: block; margin-bottom: var(--sp-3) }

.baris-set {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-md);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface);
}
.baris-set[data-tercentang] { border-color: var(--accent) }
.kotak {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border: 1.5px solid var(--border);
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
}
.baris-set[data-tercentang] .kotak { border-color: var(--accent) }
.kotak svg { width: 13px; height: 13px; stroke: var(--accent); fill: none; stroke-width: 2.5 }
.baris-set .label { flex: 1; font: var(--type-body-sm); color: var(--text-body) }
.baris-set .nilai {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-2);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-4);
  font: var(--fw-medium) var(--fs-body-sm) / 1 var(--font-num);
  font-variant-numeric: tabular-nums;
  color: var(--text-strong);
  background: var(--surface-sunken);
}
.baris-set .nilai i { font-style: normal; font-size: var(--fs-micro); color: var(--text-muted) }

.tambah-set {
  margin-top: var(--sp-3);
  border: 1px dashed var(--border);
  border-radius: var(--r-md);
  padding: var(--sp-5);
  text-align: center;
  font: var(--fs-micro) / 1 var(--font-ui);
  color: var(--text-muted);
}

.tuntas {
  margin-top: var(--sp-6);
  font: var(--type-caption);
  color: var(--text-muted);
}

.bilah-istirahat {
  margin-top: var(--sp-5);
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  background: var(--surface-raised);
  border: var(--bw) solid var(--accent);
  border-radius: var(--r-lg);
  padding: var(--sp-5) var(--sp-6);
}
.bilah-istirahat .ket { flex: 1; font: var(--type-caption); color: var(--text-muted) }
.bilah-istirahat .num { font: var(--fw-bold) var(--fs-title) / 1 var(--font-num) ; color: var(--text-strong) }
.bilah-istirahat .lewati {
  font: var(--fs-micro) / 1 var(--font-ui);
  color: var(--text-strong);
  border: var(--bw) solid var(--border-strong);
  border-radius: var(--r-sm);
  padding: var(--sp-3) var(--sp-4);
}

/* -------------------------------------------------- isi layar: streak --- */

.streak-angka {
  font: var(--fw-bold) 64px / 1 var(--font-num);
  font-variant-numeric: tabular-nums;
  color: var(--text-strong);
  display: block;
  margin-top: var(--sp-3);
}
/* Rule 5, FR-35a: the value never appears without the reason beside it. */
.streak-alasan {
  display: block;
  margin-top: var(--sp-4);
  font: var(--type-caption);
  color: var(--text-muted);
}

.tiga-angka {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-4);
  margin-top: var(--sp-6);
}
.tiga-angka div {
  background: var(--surface);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-md);
  padding: var(--sp-5) var(--sp-4);
  text-align: center;
}
.tiga-angka b { display: block; font: var(--fw-bold) var(--fs-title) / 1 var(--font-num); color: var(--text-strong) }
.tiga-angka span { display: block; margin-top: var(--sp-2); font: var(--fs-micro) / 1.2 var(--font-ui); color: var(--text-muted) }

.kalender {
  margin-top: var(--sp-4);
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}
.kalender .hari {
  text-align: center;
  font: var(--fw-semibold) 9px / 1 var(--font-ui);
  letter-spacing: var(--ls-overline);
  text-transform: uppercase;
  color: var(--text-faint);
  padding-bottom: var(--sp-2);
}
.kalender i {
  aspect-ratio: 1;
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
  font: var(--fw-semibold) 10px / 1 var(--font-num);
  font-style: normal;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
/* The status weights are deliberately unequal: only "done" is saturated, a
   missed day is a muted fact and never red (design system, rule 4). */
.kalender i[data-h="qualified"] { background: var(--status-selesai); color: var(--text-on-accent) }
.kalender i[data-h="bolong"] { background: var(--surface-sunken); border: 1px solid var(--status-bolong) }
/* Nothing drawn — the app stopped dashing an unscheduled day (D-82/D-83): a
   1,68:1 line the eye has to work past before it reads the date, for a state
   the legend never named. A day with nothing scheduled reports nothing. */
.kalender i[data-h="netral"] { }
.kalender i[data-h="kosong"] { color: var(--text-faint) }
/* Today: outlined, not filled — it has no verdict yet (D-67). */
.kalender i[data-h="hariini"] { border: 1.5px solid var(--accent); color: var(--text-strong) }

/* -------------------------------------------------- isi layar: progres --- */

.segmen {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 3px;
  padding: 3px;
  background: var(--surface-sunken);
  border-radius: var(--r-md);
  margin-top: var(--sp-4);
}
.segmen span {
  text-align: center;
  padding: var(--sp-3) 0;
  border-radius: var(--r-sm);
  font: var(--fs-micro) / 1 var(--font-ui);
  color: var(--text-muted);
}
.segmen span[data-aktif] { background: var(--surface); color: var(--text-strong); font-weight: var(--fw-semibold) }

.grafik {
  margin-top: var(--sp-5);
  background: var(--surface);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
}
.grafik .kepala {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
}
.grafik .kepala b { font: var(--fw-semibold) var(--fs-body-sm) / 1 var(--font-ui); color: var(--text-strong) }
.grafik .kepala span { font: var(--fw-bold) var(--fs-body-sm) / 1 var(--font-num); font-variant-numeric: tabular-nums; color: var(--text-strong) }
.grafik svg { display: block; width: 100%; height: 54px; margin-top: var(--sp-4) }
/* One 2px line, its area at 10%, a single dot on the last entry. No target
   line, no healthy/unhealthy bands — a product rule, not a preference. */
.grafik .garis { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round }
.grafik .area { fill: var(--accent); opacity: 0.1 }
.grafik .titik { fill: var(--accent) }
.grafik .sumbu {
  display: flex;
  justify-content: space-between;
  margin-top: var(--sp-3);
  font: var(--fs-micro) / 1 var(--font-ui);
  color: var(--text-faint);
}

/* ------------------------------------------------------------- reveal ---
   The only script on the page adds `siap` then `tampil`; the CSS staggers the
   section's children into place. Anything unobserved stays visible, so the
   content reads fine with JavaScript switched off. */

.reveal.siap > * {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity var(--dur-reveal) var(--ease-keluar),
              transform var(--dur-reveal) var(--ease-keluar);
}
.reveal.tampil > * { opacity: 1; transform: none }
.reveal.tampil > *:nth-child(2) { transition-delay: 80ms }
.reveal.tampil > *:nth-child(3) { transition-delay: 160ms }
.reveal.tampil > *:nth-child(4) { transition-delay: 240ms }
.reveal.tampil > *:nth-child(5) { transition-delay: 320ms }
.reveal.tampil > *:nth-child(6) { transition-delay: 400ms }
.reveal.tampil > *:nth-child(7) { transition-delay: 460ms }
.reveal.tampil > *:nth-child(8) { transition-delay: 520ms }
.reveal.tampil > *:nth-child(9) { transition-delay: 570ms }
.reveal.tampil > *:nth-child(10) { transition-delay: 610ms }

@media (prefers-reduced-motion: reduce) {
  .reveal.siap > * { opacity: 1; transform: none; transition: none }
}

/* --------------------------------------------------------------- prosa ---
   Privacy page. Long-form reading, so the measure is tight and the rhythm is
   set by spacing rather than rules. */

.prosa { padding-block: clamp(40px, 6vw, 72px) }
.prosa h1 {
  font: var(--fw-bold) clamp(26px, 4vw, var(--fs-display)) / var(--lh-snug) var(--font-ui);
  letter-spacing: var(--ls-tight);
  color: var(--text-strong);
}
.prosa h2 {
  font: var(--type-h2);
  letter-spacing: var(--ls-snug);
  color: var(--text-strong);
  margin-top: var(--sp-11);
  margin-bottom: var(--sp-5);
}
.prosa p, .prosa li { max-width: 68ch }
.prosa p + p { margin-top: var(--sp-6) }
.prosa ul { margin-top: var(--sp-6); padding-left: var(--sp-7) }
.prosa li + li { margin-top: var(--sp-4) }
.prosa li::marker { color: var(--text-faint) }

.meta {
  margin-top: var(--sp-7);
  font: var(--type-body-sm);
  color: var(--text-muted);
}
.meta b { font-weight: var(--fw-semibold); color: var(--text-body) }

.catatan {
  margin-top: var(--sp-9);
  padding: var(--sp-7);
  background: var(--surface);
  border: var(--bw) solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-lg);
  font: var(--type-body-sm);
  color: var(--text-muted);
}

.ringkasan {
  margin-top: var(--sp-9);
  padding: var(--sp-8);
  background: var(--surface);
  border: var(--bw) solid var(--border);
  border-radius: var(--r-lg);
}
.ringkasan p { max-width: none }
.ringkasan p + p { margin-top: var(--sp-5) }
