/* ============================================================================
   EasyTransfer — the on-card library app.

   Constraints this stylesheet is written under, all of them load-bearing:

   · It runs from file:// on a microSD card as often as from a Raspberry Pi, so
     there is no CDN, no webfont and no build step. Everything is here or on the
     card next to it.
   · It is read outdoors, in sun, on a cracked shared phone, by someone who may
     be reading slowly. Hence the size of everything, the contrast floor, and the
     refusal to use grey text on grey.
   · Two of its three languages are cursive Arabic script, where habits that are
     harmless in Latin — letter-spacing, mid-word breaks — render words broken.
     See "Arabic script" at the bottom; those rules are not optional.
   ========================================================================= */

/* "Sunrise" is the default: bright, warm and easy to read outdoors, where a
   lit screen beats a dark one in the sun. "Lantern Night" is a tap away in the
   header and is only used when someone chooses it (data-theme="dark"), so the
   library looks the same bright way on every phone the first time it opens.
   Every text/ground pair below clears 4.5:1.

   The colour plan (docs/design/color.md has the reasoning):
   · 60  warm paper ground and cards. Never pure white, never cold grey.
   · 30  cocoa ink and one brand colour, a lantern-lit plum. Not violet on
         black: that pairing is what every generated site looks like.
   · 10  lantern gold, kept for the one thing we most want tapped (Share).
   · Kinds get one colour each, picked at matched strength so they sit
         together: Films clay, Scripture plum, Audio Bibles teal, Books gold.
   · Green means done or offline, nothing else.
   The old names stay (--purple is the brand plum, --sky is teal) so every
   page keeps working; read the name as a role, not a hue. */
:root {
  --ground:      #FBF7F1;
  --surface:     #FFFDF9;
  --sunk:        #F4ECE2;
  --raised:      #FFFDF9;

  --ink:         #2A1E22;
  --ink-soft:    #66565B;

  --purple:      #7A2C5E;
  --purple-deep: #5A1F45;
  --purple-wash: #F6E9F0;

  --gold:        #FFBE1A;
  --gold-deep:   #D39200;
  --gold-wash:   #FDF0D2;
  --gold-text:   #875800;
  --glow:        #FFE7A3;

  --green:       #2B7A4B;
  --green-deep:  #1E5A36;
  --green-wash:  #E5F1E7;

  --sky:         #1C6B6A;
  --sky-deep:    #124E4D;
  --sky-wash:    #E2F0EE;

  --clay:        #B04E2A;
  --clay-deep:   #87391C;
  --clay-wash:   #FAE8DF;

  /* Words sitting on a coloured button face, and the faces themselves. */
  --on-face:     #FFFFFF;
  --on-gold:     #3A2605;
  --green-face:  #2B7A4B;  --green-edge: #1E5A36;
  --sky-face:    #1C6B6A;  --sky-edge:   #124E4D;
  --clay-face:   #B04E2A;  --clay-edge:  #87391C;
  /* Small coloured words on a wash. */
  --green-text:  #256B42;  --sky-text:   #1A6362;  --clay-text: #9E4524;

  --line:        #EADFD3;
  --line-strong: #D8C8B8;

  --r-sm: 12px;
  --r:    18px;
  --r-lg: 26px;
  --r-pill: 999px;

  --press: 4px;
  --tap:   56px;
  --tabbar: 78px;

  --wrap: 62rem;
  --gap:  1rem;
  --focus: #7A2C5E;

  /* Rounded faces the phone already has (SF Rounded on Apple, Nunito or
     Varela Round where installed). No web font is downloaded, by rule. */
  --display: ui-rounded, "SF Pro Rounded", "Nunito", "Varela Round",
             -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color-scheme: light;
}

/* "Lantern Night": a warm night, the colour of a room lit by one lamp, not
   the violet-black of a code editor. The dark theme's faces are pale, so
   they take dark words. */
:root[data-theme="dark"] {
  --ground: #1A1316;  --surface: #231B1E;  --sunk: #2B2125;  --raised: #31272B;
  --ink: #F7EFE7;     --ink-soft: #BCABA5;
  --purple: #D88AB9;  --purple-deep: #A9608C; --purple-wash: #3A2230;
  --gold: #F5BC45;    --gold-deep: #C88F1E;   --gold-wash: #36291A; --gold-text: #F5BC45;
  --green: #77C994;   --green-deep: #4E9A6A;  --green-wash: #1C2B21;
  --sky: #74C6BF;     --sky-deep: #4A9993;    --sky-wash: #172B2A;
  --clay: #F09C76;    --clay-deep: #C0714F;   --clay-wash: #3A231A;
  --on-face: #1E1416; --on-gold: #1E1416;
  --green-face: #77C994; --green-edge: #4E9A6A;
  --sky-face: #74C6BF;   --sky-edge: #4A9993;
  --clay-face: #F09C76;  --clay-edge: #C0714F;
  --green-text: #77C994; --sky-text: #74C6BF; --clay-text: #F09C76;
  --line: #3A2E31;    --line-strong: #514246;
  --focus: #F5BC45;
  color-scheme: dark;
}

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

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

/* ---------------------------------------------------------------- locked

   The page does not move except by scrolling down. On a shared phone, held
   one-handed, half the accidental gestures are pinches and two-finger drags,
   and an app that slides off-centre and stays there reads as broken to someone
   who has no idea they did it — and no idea how to undo it.

   · touch-action: pan-y      one axis. Pinch-zoom and double-tap zoom cannot fire.
   · overscroll-behavior      no rubber-band past the ends, no pull-to-refresh.
   · overflow-x: clip         nothing can push the layout sideways.
   · text-size-adjust         stops mobile Safari reflowing type on rotate.

   Paired with `user-scalable=no` in every page's viewport tag.

   The trade: pinch-to-zoom is gone, which is a real accessibility loss. It is
   paid for by a 17px base, a 56px minimum touch target and a contrast floor
   around 14:1 — the page is built to be readable without zooming rather than
   readable once you have zoomed. */
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  touch-action: pan-y;
  overscroll-behavior: none;
  overflow-x: clip;
  height: 100%;
}

body {
  touch-action: pan-y;
  overscroll-behavior: none;
  overflow-x: clip;
  min-height: 100%;
  /* iOS shows a grey flash and a magnifier on long-press. Neither means
     anything here, and both look like a fault. */
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 500 17px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

h1, h2, h3 { line-height: 1.2; margin: 0 0 .5em; font-weight: 800; letter-spacing: -.015em; }
h1 { font-size: clamp(1.8rem, 5vw, 2.6rem); }
h2 { font-size: clamp(1.35rem, 3.4vw, 1.8rem); }
h3 { font-size: 1.15rem; }
p  { margin: 0 0 1em; }
a  { color: var(--purple); }

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

/* A dragged image or button is a UI that has come loose. Prose stays
   selectable — someone may want to copy an address or a filename out of it. */
img, svg, video, .btn, .tile, .res, .fchip, .chip, .top, .steps > li::before,
.et-btn, .et-tile, .et-chip, .et-badge {
  -webkit-user-drag: none;
  -webkit-user-select: none;
  user-select: none;
}
p, li, h1, h2, h3, code, input { -webkit-user-select: text; user-select: text; }

/* Taps register immediately instead of waiting out a double-tap that can no
   longer happen. */
.btn, .tile, .res, .fchip, .et-tile, .et-chip, a, button, select, input { touch-action: manipulation; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 1.1rem; }
.stack > * + * { margin-top: var(--gap); }
.muted { color: var(--ink-soft); }
.center { text-align: center; }
[hidden] { display: none !important; }

.sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

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

/* ============================================================ app chrome

   Shaped after the Bible app in the reference recording: a fixed bar of four
   destinations at the bottom, thumbs-first, so no screen needs its own way
   back. Anything deeper than a destination (a resource, a guided flow) keeps
   its own Back control. */

.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  height: calc(var(--tabbar) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  align-items: center;
  background: color-mix(in srgb, var(--surface) 96%, transparent);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--line);
}
.tabbar a {
  display: grid; justify-items: center; gap: 3px;
  padding: 8px 4px; min-height: 58px;
  color: var(--ink-soft); text-decoration: none;
  font-size: .72rem; font-weight: 700;
  border-radius: var(--r);
}
.tabbar a svg { width: 25px; height: 25px; }
.tabbar a[aria-current="page"] { color: var(--ink); }
.tabbar a[aria-current="page"] .pipe {
  background: var(--purple-wash); color: var(--purple);
}
.tabbar .pipe {
  display: grid; place-items: center;
  width: 58px; height: 32px; border-radius: var(--r-pill);
}
body.has-tabs { padding-bottom: calc(var(--tabbar) + env(safe-area-inset-bottom) + 1rem); }
html[dir="rtl"] .tabbar a { font-size: .78rem; }

/* ------------------------------------------------------------- greeting */
.greet { padding: 1.2rem 0 .3rem; display: flex; align-items: center; gap: .9rem; }
.greet-text { display: grid; gap: .15rem; min-width: 0; }
.greet-mascot { flex: none; display: flex; }

/* Quick picks under the greeting: big, bright, pressable, one per kind. */
.quick { display: grid; grid-template-columns: repeat(auto-fit, minmax(5.8rem, 1fr)); gap: .7rem; margin: .9rem 0 1.1rem; }
.quick-pick {
  --c: var(--purple); --w: var(--purple-wash);
  display: flex; flex-direction: column; align-items: flex-start; gap: .45rem;
  min-height: 6.5rem; padding: .85rem .9rem; border-radius: var(--r-lg);
  background: var(--w); color: var(--ink); text-decoration: none;
  border: 2px solid var(--c); box-shadow: 0 5px 0 var(--c);
  transition: transform .1s, box-shadow .1s;
}
.quick-pick:active { transform: translateY(5px); box-shadow: 0 0 0 var(--c); }
.quick-pick.sky   { --c: var(--sky);   --w: var(--sky-wash); }
.quick-pick.clay  { --c: var(--clay);  --w: var(--clay-wash); }
.quick-pick.green { --c: var(--green); --w: var(--green-wash); }
.quick-pick.gold  { --c: var(--gold);  --w: var(--gold-wash); }
.quick-ico {
  width: 2.6rem; height: 2.6rem; border-radius: .9rem; display: grid; place-items: center;
  background: var(--c); color: var(--on-face);
}
.quick-pick.green .quick-ico { background: var(--green-face); }
.quick-pick.sky .quick-ico   { background: var(--sky-face); }
.quick-pick.clay .quick-ico  { background: var(--clay-face); }
.quick-pick.gold .quick-ico  { color: var(--on-gold); }
.quick-ico svg { width: 22px; height: 22px; }
.quick-t { font-weight: 800; font-size: 1.02rem; line-height: 1.25; }
.greet h1 { font-size: clamp(1.9rem, 7vw, 2.4rem); margin: 0; }
.greet .eyebrow {
  font-size: .76rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--purple); margin: 0;
}
html[dir="rtl"] .greet .eyebrow { text-transform: none; letter-spacing: 0; font-size: .95rem; }
.greet .muted { margin: .25rem 0 0; }

/* ------------------------------------------------------------- list row

   One row, one thing, one action — thumbnail, two lines of text, and a pill
   that says what tapping it does. Repeated down the library and inside every
   shelf, so learning one row teaches the whole app. */
.row-list { display: grid; gap: 2px; }
.rowi {
  display: grid; grid-template-columns: 62px 1fr auto; gap: .85rem;
  align-items: center;
  padding: .7rem .2rem;
  color: inherit; text-decoration: none;
  border-radius: var(--r);
}
.rowi:hover { background: var(--sunk); }
.rowi:active { background: var(--raised); }
.rowi .art {
  width: 62px; height: 62px; border-radius: 14px; overflow: hidden;
  background: var(--sunk); position: relative;
  display: grid; place-items: center; color: var(--purple);
}
.rowi .art svg { width: 26px; height: 26px; opacity: .65; }
.rowi .art img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; max-width: none; }
.rowi .meta { min-width: 0; display: grid; gap: 2px; }
.rowi .kicker {
  font-size: .72rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-soft);
}
html[dir="rtl"] .rowi .kicker { text-transform: none; letter-spacing: 0; font-size: .82rem; }
.rowi .name { font-weight: 800; font-size: 1.02rem; line-height: 1.25; }
.rowi .sub { color: var(--ink-soft); font-size: .88rem; }
.rowi .go {
  flex: none; min-height: 40px; padding: .5rem 1.1rem;
  border-radius: var(--r-pill);
  background: var(--raised); color: var(--ink);
  font-weight: 800; font-size: .92rem;
  border: 1px solid var(--line-strong);
  display: inline-flex; align-items: center; gap: .4rem;
}
.rowi .go svg { width: 16px; height: 16px; }
.rowi:hover .go { background: var(--purple); border-color: var(--purple); color: var(--on-face); }

/* ------------------------------------------------------ batch downloads */

.bulk-start { margin-top: 1rem; }
.bulk-tools {
  display: flex; align-items: center; justify-content: space-between;
  gap: .7rem; flex-wrap: wrap; margin-top: 1rem;
  padding: .8rem; border: 2px solid var(--green); border-radius: var(--r);
  background: var(--green-wash);
}
.bulk-tools strong { font-size: 1.05rem; }
.bulk-tools .btn, .bulk-tools .et-btn { min-height: 48px; padding: .65rem 1rem; }
button.bulk-row {
  width: 100%; border: 2px solid transparent; background: transparent;
  color: inherit; font: inherit; text-align: start; cursor: pointer;
}
button.bulk-row:hover { border-color: var(--line-strong); }
button.bulk-row[aria-checked="true"] {
  border-color: var(--green); background: var(--green-wash);
}
.bulk-check {
  width: 42px; height: 42px; display: grid; place-items: center;
  border: 3px solid var(--line-strong); border-radius: 50%;
  color: transparent; background: var(--surface);
}
.bulk-check svg { width: 25px; height: 25px; stroke-width: 3; }
.bulk-row[aria-checked="true"] .bulk-check {
  border-color: var(--green); background: var(--green); color: var(--on-face);
}
.bulk-dock {
  position: fixed; z-index: 46; left: 0; right: 0;
  bottom: calc(var(--tabbar) + env(safe-area-inset-bottom));
  padding: .8rem max(1.1rem, calc((100% - var(--wrap)) / 2 + 1.1rem));
  background: color-mix(in srgb, var(--ground) 96%, transparent);
  border-top: 2px solid var(--line); backdrop-filter: blur(12px);
}
body.bulk-mode { padding-bottom: calc(var(--tabbar) + 92px + env(safe-area-inset-bottom)); }

/* ------------------------------------------------ shared language pages */

/* The same sunrise as the front page, so all 57 language pages read as one
   brand. Kandili sits in the top corner, out of the flow, so nothing moves
   when it arrives. */
.shared-hero {
  position: relative;
  margin: 1rem 0; padding: 1.25rem;
  border: 2px solid var(--line); border-bottom-width: 6px; border-radius: var(--r-lg);
  /* One warm hue fading into the page, not two colours fighting. */
  background: linear-gradient(180deg, var(--gold-wash) 0%, var(--surface) 100%);
}
.shared-mascot { position: absolute; top: .6rem; inset-inline-end: .8rem; pointer-events: none; }
.shared-mascot .et-kandili-slot { pointer-events: auto; }
.shared-hero h1, .shared-hero > p { padding-inline-end: 4.5rem; }
@media (min-width: 48rem) { .shared-mascot { top: 1rem; inset-inline-end: 1.4rem; } }
.shared-hero .eyebrow {
  display: inline-flex; padding: .25rem .65rem; margin: 0 0 .65rem;
  border-radius: var(--r-pill); background: var(--purple); color: var(--on-face);
  font-size: .8rem; font-weight: 800;
}
.shared-hero h1 { margin: 0; }
.shared-hero p { margin: .45rem 0 0; }
/* One row of round share targets with a word under each, the way the
   iPhone and Android share sheets lay them out, so the library itself starts
   on the first screen. The row has a fixed height from the first paint: the
   page ships plain-text buttons and shared-library.js dresses them, and
   nothing below may move when it does. */
.shared-hero .shared-actions {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  gap: .1rem; margin-inline: -.4rem; margin-top: 1rem; height: 5.6rem; overflow: hidden;
}
.shared-hero .shared-actions .btn {
  --face: var(--surface); --edge: var(--line-strong);
  display: flex; flex-direction: column; justify-content: flex-start; align-items: center; gap: .35rem;
  min-height: 0; height: 100%; padding: 0; margin: 0;
  background: none; border: 0; box-shadow: none; color: var(--ink);
  font-size: .78rem; font-weight: 700; line-height: 1.2; text-align: center;
  white-space: normal; overflow: hidden;
}
.shared-actions .btn:active { transform: none; box-shadow: none; }
.sa-ico {
  display: grid; place-items: center; flex: none;
  width: min(3.4rem, 13vw); height: min(3.4rem, 13vw); border-radius: 50%;
  background: var(--face); color: var(--ink);
  border: 2px solid var(--edge); box-shadow: 0 3px 0 var(--edge);
  transition: transform .32s cubic-bezier(.34, 1.56, .64, 1);
}
.sa-ico svg { width: 24px; height: 24px; }
.shared-actions .btn:active .sa-ico { transform: scale(.9); transition-duration: .08s; }
.shared-actions .btn.green { --face: var(--green-face); --edge: var(--green-edge); }
.shared-actions .btn.green .sa-ico { color: var(--on-face); }
.shared-actions .btn.fb { --face: #166FE5; --edge: #0B5BC4; }
.shared-actions .btn.fb .sa-ico { color: #fff; }
.sa-label { max-width: 100%; font-size: .68rem; letter-spacing: -.02em; overflow-wrap: normal; word-break: normal; hyphens: none; }
html[dir="rtl"] .shared-actions .btn { padding: 0; }
.send-library { margin-top: 1rem; }
.send-library[hidden] { display: none; }
.send-library .flabel { display: block; margin: 0 0 .4rem; }
.send-library-row { display: flex; flex-wrap: wrap; gap: .7rem; }
.send-library-row input { flex: 1 1 14rem; width: auto; }
.send-library-row .btn, .send-library-row .et-btn { flex: 0 1 9rem; }
.send-library .send-library-status:empty { display: none; }

/* Guide: the three how-to-share videos. */
.share-videos h2 { margin: 0 0 .3rem; }
.share-videos > p { margin: 0 0 .9rem; }
.share-video-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .7rem; }
.share-video-row figure { margin: 0; }
.share-video-row video { display: block; width: 100%; aspect-ratio: 9 / 16; border-radius: var(--r); background: #0b0714; }
.share-video-row figcaption { margin-top: .4rem; font-weight: 800; text-align: center; }
@media (max-width: 520px) { .share-video-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Read aloud, never shown: the design kit's sheet and toast label their icon
   buttons with it. Same rule as base.css, which app pages do not load. */
.et-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
/* Kit toasts rise above the tab bar, not behind it. */
body.has-tabs { --et-tabbar-h: var(--tabbar); }


/* Pass it on: the inline share card on every item, and the note a recipient
   sees saying who sent it. Prominent, but part of the page, never a pop-up. */
.pass-on { margin-top: 1.3rem; border-color: var(--green); display: grid; gap: .7rem; }
.pass-on h2 { display: flex; align-items: center; gap: .5rem; margin: 0; font-size: 1.25rem; }
.pass-on h2 svg { width: 24px; height: 24px; color: var(--green-deep); flex: none; }
.pass-on > p { margin: 0; }
.pass-on .flabel { display: block; margin: 0 0 .3rem; }
.pass-on input { width: 100%; }
.pass-names { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: .7rem; }
.pass-on .send-library-row input { flex: 1 1 12rem; width: auto; }
.pass-on .send-library-row .et-btn { flex: 1 1 12rem; }
.pass-on .send-library-status { margin: 0; font-weight: 600; }
.pass-on .send-library-status:empty { display: none; }
.pass-privacy { font-size: .9rem; }
.chain-banner { display: flex; gap: .9rem; align-items: flex-start; margin: 0 0 1rem;
  border-color: var(--green); background: var(--green-wash); }
.chain-banner .ico svg { width: 30px; height: 30px; color: var(--green-deep); }
.chain-banner h2 { margin: 0 0 .2rem; font-size: 1.2rem; }
.chain-banner p { margin: 0 0 .7rem; }

.share-language-grid { display: grid; gap: .8rem; margin-top: 1.2rem; }
.share-language-card {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; gap: 1rem;
}
.share-language-card h2, .share-language-card p { margin: 0; }
.share-language-card .btn, .share-language-card .et-btn { min-width: 10rem; }
@media (max-width: 680px) {
  .share-language-card { grid-template-columns: 1fr; }
  .share-language-card .btn-row { width: 100%; }
}

/* ---------------------------------------------------------------- shelves */
.shelf { display: grid; gap: .6rem; margin-top: 1.7rem; }
.shelf-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.shelf-head h2 { font-size: 1.32rem; margin: 0; }
.shelf-head a {
  font-size: .92rem; font-weight: 700; text-decoration: none; color: var(--purple);
  /* A word-sized link is still a thumb-sized target. */
  display: inline-flex; align-items: center; min-height: 48px; padding: 0 .4rem; margin: -.6rem -.4rem;
}
.rail-x {
  display: grid; grid-auto-flow: column; grid-auto-columns: 46%;
  gap: .7rem; overflow-x: auto; scroll-snap-type: x mandatory;
  /* Kept inside the page gutter: bleeding to the screen edge clipped the
     first poster, which is the one most likely to be tapped. */
  padding-bottom: .3rem;
  scrollbar-width: none;
}
.rail-x::-webkit-scrollbar { display: none; }
.rail-x > * { scroll-snap-align: start; }
@media (min-width: 40rem) { .rail-x { grid-auto-columns: 22%; } }
.poster { display: grid; gap: .45rem; color: inherit; text-decoration: none; }
.poster .art {
  aspect-ratio: 3 / 4; border-radius: 16px; overflow: hidden; position: relative;
  background: var(--sunk); display: grid; place-items: center; color: var(--purple);
}
.poster .art img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; max-width: none; }
.poster .art svg { width: 30px; height: 30px; opacity: .6; }
.poster .name { font-weight: 700; font-size: .93rem; line-height: 1.25; }
.poster .sub { color: var(--ink-soft); font-size: .8rem; }
.poster:active { opacity: .7; }

/* ------------------------------------------------------------ hero card */
.hero-card {
  position: relative; overflow: hidden;
  border-radius: var(--r-lg);
  background: var(--purple-wash);
  padding: 1.3rem;
  display: grid; gap: .5rem;
  color: inherit; text-decoration: none;
  min-height: 150px; align-content: end;
  border: 1px solid var(--line);
}
.hero-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; max-width: none; }
.hero-card .veil { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(30,20,22,.12), rgba(30,20,22,.86)); }
.hero-card > * { position: relative; }
.hero-card.shot { color: #fff; }
.hero-card .kicker { font-size: .74rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; opacity: .9; }
.hero-card h2 { font-size: 1.5rem; margin: 0; }
.hero-card .sub { opacity: .85; font-size: .93rem; }
/* The headline film: player on top, caption below, so nothing covers the controls. */
.hero-card.headline { padding: 0; gap: 0; min-height: 0; align-content: start; }
.hero-card.headline video { display: block; width: 100%; aspect-ratio: 16 / 9; background: #000; }
.hero-card.headline .cap { display: grid; gap: .35rem; padding: 1rem 1.3rem 1.2rem; }

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

.top {
  position: sticky; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--ground) 92%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 2px solid var(--line);
}
.top .wrap {
  display: flex; align-items: center; gap: .6rem;
  min-height: 64px;
}
/* The header is written by script, above <main>. Until it arrives, hold its
   64px + 2px border open so the page does not jump down when it lands — on a
   slow phone that jump moves the button someone is reaching for. header()
   adds .has-top in the same frame, which lets this go. */
body:not(.has-top):has(> main#main) { padding-top: 66px; }
.brand {
  display: flex; align-items: center; gap: .55rem;
  font-weight: 800; font-size: 1.05rem; color: var(--ink);
  text-decoration: none; margin-inline-end: auto;
}
.brand svg { width: 30px; height: 30px; flex: none; }

/* ----------------------------------------------------- the language hint

   An arrow under the language button. The one control a reader stranded in a
   language they cannot read must be able to find, so it points at itself and
   says so in every language in turn. */
.lang-hint {
  position: fixed; z-index: 45;
  display: flex; align-items: center; gap: .4rem;
  padding: .55rem .85rem;
  border-radius: var(--r-pill);
  background: var(--gold); color: var(--on-gold);
  font-weight: 800; font-size: .88rem; line-height: 1.3;
  white-space: nowrap; cursor: pointer;
  box-shadow: 0 10px 24px -10px rgba(0, 0, 0, .7);
  opacity: 0;
}
.lang-hint.in { animation: hint-say 2.6s ease-in-out; }
@keyframes hint-say {
  0%   { opacity: 0; transform: translateY(-4px); }
  14%  { opacity: 1; transform: none; }
  80%  { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateY(-2px); }
}
/* The little arrow, sitting under the button it points at. */
.lang-hint .beak {
  position: absolute; top: -7px; left: var(--beak, 50%);
  width: 14px; height: 14px; margin-left: -7px;
  background: var(--gold);
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
.lang-hint .say { display: block; }
/* Held still for anyone who asked for less motion. */
@media (prefers-reduced-motion: reduce) {
  .lang-hint, .lang-hint.in { opacity: 1; animation: none; }
}

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

/* The chunky button: a solid face sitting on a darker edge, which the press
   collapses. It reads as physically pressable at arm's length in sunlight,
   which a flat rectangle does not. */
.btn {
  --face: var(--purple);
  --edge: var(--purple-deep);
  display: inline-flex; align-items: center; justify-content: center;
  gap: .5rem;
  min-height: var(--tap);
  padding: .85rem 1.4rem;
  border: 0;
  border-radius: var(--r);
  background: var(--face);
  box-shadow: 0 var(--press) 0 var(--edge);
  color: var(--on-face);
  font: inherit; font-weight: 800; font-size: 1.02rem;
  text-decoration: none; text-align: center;
  cursor: pointer;
  transform: translateY(0);
  transition: transform .06s ease, box-shadow .06s ease, filter .15s ease;
}
.btn:hover { filter: brightness(1.06); }
.btn:active {
  transform: translateY(var(--press));
  box-shadow: 0 0 0 var(--edge);
}
.btn[disabled] { opacity: .45; pointer-events: none; }

.btn.green { --face: var(--green-face); --edge: var(--green-edge); }
.btn.gold  { --face: var(--gold);  --edge: var(--gold-deep); color: var(--on-gold); }
.btn.clay  { --face: var(--clay-face); --edge: var(--clay-edge); }
.btn.sky   { --face: var(--sky-face); --edge: var(--sky-edge); }
/* Facebook's own blue is 4.2:1 under white; its pressed shade clears 4.5. */
.btn.fb    { --face: #166FE5; --edge: #0B5BC4; color: #fff; }

.btn.ghost {
  --face: var(--surface); --edge: var(--line-strong);
  color: var(--ink);
  border: 2px solid var(--line-strong);
}
.btn.block { width: 100%; }
.btn.big   { min-height: 64px; font-size: 1.12rem; padding: 1rem 1.6rem; }
.btn svg   { width: 22px; height: 22px; flex: none; }

.btn-row { display: flex; flex-wrap: wrap; gap: .7rem; }
.btn-row .btn, .btn-row .et-btn { flex: 1 1 10rem; }

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

.card {
  background: var(--surface);
  border: 2px solid var(--line);
  border-radius: var(--r-lg);
  padding: 1.2rem;
}
.card.flat { box-shadow: none; }

/* A tile is a card you tap: the whole thing is the target. */
.tile {
  display: flex; align-items: center; gap: .9rem;
  background: var(--surface);
  border: 2px solid var(--line);
  border-bottom-width: 5px;
  border-radius: var(--r-lg);
  padding: 1rem 1.1rem;
  color: inherit; text-decoration: none;
  min-height: 84px;
  transition: transform .08s ease, border-color .15s ease, background .15s ease;
}
.tile:hover { border-color: var(--purple); background: var(--purple-wash); }
.tile:active { transform: translateY(3px); border-bottom-width: 2px; margin-bottom: 3px; }
.tile .ico {
  width: 54px; height: 54px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--r);
  background: var(--purple-wash); color: var(--purple);
}
.tile .ico svg { width: 30px; height: 30px; }
.tile > span:not(.ico):not(.chev) { min-width: 0; }
.tile .t { display: block; font-weight: 800; font-size: 1.08rem; }
.tile .s { display: block; color: var(--ink-soft); font-size: .92rem; }
.tile .chev { margin-inline-start: auto; color: var(--line-strong); flex: none; }

.grid {
  display: grid; gap: .8rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
}

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

.res {
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 2px solid var(--line);
  border-bottom-width: 5px;
  border-radius: var(--r-lg);
  overflow: hidden;
  color: inherit; text-decoration: none;
  transition: transform .08s ease, border-color .15s ease;
}
.res:hover { border-color: var(--purple); }
.res:active { transform: translateY(3px); border-bottom-width: 2px; margin-bottom: 3px; }
.thumb {
  aspect-ratio: 16 / 10;
  background: var(--sunk) center/cover no-repeat;
  display: grid; place-items: center;
  color: var(--purple);
}
.res .thumb { border-bottom: 2px solid var(--line); }
.thumb { position: relative; overflow: hidden; }
.thumb > svg { width: 46px; height: 46px; opacity: .5; }
.thumb > img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; max-width: none;
  object-fit: cover;
}
.res .body { padding: .85rem .95rem 1rem; display: flex; flex-direction: column; gap: .3rem; flex: 1; }
.res .name { font-weight: 800; font-size: 1.02rem; line-height: 1.3; }
.res .meta { color: var(--ink-soft); font-size: .88rem; }
.res .foot { margin-top: auto; padding-top: .55rem; display: flex; gap: .4rem; flex-wrap: wrap; }

/* ----------------------------------------------------------------- chips */

.chip {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .3rem .7rem;
  border-radius: var(--r-pill);
  background: var(--sunk); color: var(--ink-soft);
  border: 1.5px solid transparent;
  font-size: .82rem; font-weight: 700;
  white-space: nowrap;
}
.chip svg { width: 15px; height: 15px; }
.chip.offline { background: var(--green-wash); color: var(--green-text); }
.chip.online  { background: var(--gold-wash);  color: var(--gold-text); }
.chip.lang    { background: var(--purple-wash); color: var(--purple); }
.chip.stream  { background: var(--sky-wash); color: var(--sky-text); }

.player {
  width: 100%; aspect-ratio: 16 / 9;
  border-radius: var(--r-lg); background: #000;
  border: 2px solid var(--line);
}
.picker { display: flex; gap: .6rem; align-items: end; flex-wrap: wrap; margin-top: .7rem; }
.picker > label { width: 100%; margin: 0; }
.picker > select { flex: 1 1 12rem; }
.picker.two > div { flex: 1 1 9rem; }
.picker .flabel { margin: 0 0 .35rem; }
.pdf {
  width: 100%; height: 70vh; margin-top: 1rem;
  border: 2px solid var(--line); border-radius: var(--r-lg);
  background: var(--sunk);
}
.pdf-note { margin: .55rem 0 0; font-size: .9rem; }

.flabel {
  margin: 1rem 0 .45rem;
  font-size: .8rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-soft);
}
/* Uppercase-with-tracking is a Latin device; in Arabic script it breaks the
   joins, and there is no case to change. */
html[dir="rtl"] .flabel { text-transform: none; font-size: .95rem; }

/* A filter chip is a chip you press. */
.filters { display: flex; gap: .5rem; flex-wrap: wrap; }
.library-language { margin-top: 1rem; }
.library-language summary {
  display: flex; align-items: center; gap: .75rem;
  min-height: 48px; padding: .55rem .9rem;
  border: 2px solid var(--line-strong); border-radius: var(--r);
  background: var(--surface); color: var(--ink);
  font-weight: 700; cursor: pointer;
  list-style: none;
}
.library-language summary::-webkit-details-marker { display: none; }
.library-language summary strong {
  min-width: 0; margin-inline-start: auto; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; color: var(--purple);
}
.library-language summary::after {
  content: ''; width: .55rem; height: .55rem; flex: none;
  border-inline-end: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg); margin: -.25rem .2rem 0 0;
}
.library-language[open] summary::after { transform: rotate(225deg); margin: .25rem .2rem 0 0; }
.library-language .filters { padding: .8rem 0 .25rem; }
.library-source[hidden] { display: none; }
.fchip .fcount, .et-chip .fcount { font-weight: 500; opacity: .7; }
.library-search { margin-bottom: .9rem; }
.library-actions { margin-top: 1rem; }
.library-actions .bulk-start { margin-top: 1rem; }
.save-guide-entry {
  display: inline-flex; align-items: center; gap: .6rem; min-height: 48px;
  margin-top: .8rem; padding: .3rem 0; font-weight: 700; font-size: .9rem;
}
.save-guide-entry svg { width: 22px; height: 22px; flex: none; }
.save-guide-entry small { font-weight: 500; white-space: nowrap; }
.library-count { margin: 1rem 0 .6rem; }
@media (max-width: 39.99rem) {
  #type-filters {
    flex-wrap: nowrap; overflow-x: auto;
    scrollbar-width: none; scroll-snap-type: x proximity;
    padding-bottom: .2rem;
  }
  #type-filters::-webkit-scrollbar { display: none; }
  #type-filters .fchip { flex: none; scroll-snap-align: start; }
  .library-actions.has-bulk {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .5rem; margin-top: .8rem;
  }
  .library-actions.has-bulk > *, .library-actions.has-bulk .et-btn { min-width: 0; }
  .library-actions.has-bulk .et-btn {
    width: 100%; min-height: 56px; padding: .4rem .3rem;
    font-size: .78rem; line-height: 1.2; gap: .25rem;
  }
  .library-actions.has-bulk .et-btn svg { width: 18px; height: 18px; }
  .library-actions.has-bulk .bulk-start { margin-top: 0; }
  .library-count { margin: .55rem 0 .35rem; }
}
@media (max-width: 360px) {
  .library-search { margin-bottom: .45rem; }
  .library-kind { margin: .55rem 0 .3rem; }
  .library-language { margin-top: .6rem; }
  .library-actions.has-bulk { margin-top: .5rem; }
  .library-count { margin: .25rem 0 0; }
}
.fchip {
  display: inline-flex; align-items: center; justify-content: center;
  gap: .4rem;
  min-height: 46px;
  padding: .55rem 1rem;
  border-radius: var(--r-pill);
  border: 2px solid var(--line-strong);
  background: var(--surface); color: var(--ink);
  font: inherit; font-weight: 700; font-size: .95rem;
  cursor: pointer;
}
.fchip svg { width: 20px; height: 20px; flex: none; }
.fchip[aria-pressed="true"] {
  background: var(--purple); border-color: var(--purple-deep); color: var(--on-face);
}

/* ----------------------------------------------------------------- input */

.search { position: relative; }
.search svg {
  position: absolute; inset-inline-start: 1rem; top: 50%;
  transform: translateY(-50%); width: 22px; height: 22px;
  color: var(--ink-soft); pointer-events: none;
}
input[type="search"], input[type="text"], input[type="email"], select {
  width: 100%;
  min-height: var(--tap);
  /* Logical, not physical: in an RTL page the icon sits on the right and the
     text must start clear of it. `padding-left` would put the gap on the wrong
     side and the placeholder would run straight through the magnifier. */
  padding: .9rem 1rem;
  padding-inline-start: 3rem;
  border: 2px solid var(--line-strong);
  border-radius: var(--r);
  background: var(--surface); color: var(--ink);
  font: inherit; font-weight: 600;
  -webkit-appearance: none; appearance: none;
}
select, input[type="email"], .pass-on input[type="text"] { padding-inline-start: 1rem; }
input[type="search"]::-webkit-search-cancel-button { margin-inline-start: .5rem; }
input::placeholder { color: var(--ink-soft); font-weight: 500; }

/* ----------------------------------------------------------------- steps */

/* The share and view walkthroughs. Numbered, one action each, big enough to
   follow while holding two phones. */
.steps { list-style: none; margin: 0; padding: 0; counter-reset: s; }
.steps > li {
  counter-increment: s;
  position: relative;
  /* Logical: the number circle sits on the inline-start edge, which is the
     right in an RTL page. Physical padding-left leaves no room for it there
     and the heading runs underneath the number. */
  padding: 0 0 1.5rem;
  padding-inline-start: 3.4rem;
  border-inline-start: 3px solid var(--line);
  margin-inline-start: 1.35rem;
}
.steps > li:last-child { border-color: transparent; padding-bottom: 0; }
.steps > li::before {
  content: counter(s);
  position: absolute; inset-inline-start: -1.5rem; top: -.15rem;
  width: 2.9rem; height: 2.9rem;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--purple); color: var(--on-face);
  font-weight: 800; font-size: 1.1rem;
  box-shadow: 0 0 0 5px var(--ground);
}
.steps h3 { margin: .35rem 0 .3rem; }
.steps p  { margin: 0 0 .4rem; color: var(--ink-soft); }

/* ----------------------------------------------------------------- notes */

.note {
  /* A whole soft tint, not a coloured stripe down one side. */
  border: 2px solid color-mix(in srgb, var(--gold) 45%, transparent);
  border-radius: var(--r);
  background: var(--gold-wash);
  padding: .9rem 1.1rem;
}
.note.good { border-color: color-mix(in srgb, var(--green) 35%, transparent); background: var(--green-wash); }
.note.info { border-color: color-mix(in srgb, var(--sky) 35%, transparent);   background: var(--sky-wash); }
.note strong { display: block; margin-bottom: .15rem; }
.note strong svg { width: 20px; height: 20px; display: inline-block; vertical-align: -4px; }

/* --------------------------------------------------------------- sheets */

.sheet-back {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(30, 20, 22, .55);
  display: grid; align-items: end; justify-items: center;
}
.sheet {
  width: min(40rem, 100%);
  max-height: 88vh; overflow: auto;
  /* The sheet scrolls; the page behind it must not scroll with it. */
  overscroll-behavior: contain;
  background: var(--ground);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  border-top: 3px solid var(--line);
  padding: 1.4rem 1.1rem 2rem;
  animation: rise .18s ease-out;
}
@keyframes rise { from { transform: translateY(14px); opacity: .6 } }
@media (min-width: 40rem) {
  .sheet-back { align-items: center; }
  .sheet { border-radius: var(--r-lg); border: 3px solid var(--line); }
}
@media (prefers-reduced-motion: reduce) {
  .sheet { animation: none; }
  .meter > span.busy { animation: none; }
  * { transition-duration: .01ms !important; }
}

/* --------------------------------------------------------- guided flows

   Save and Nearby share one shape: a row of numbered dots, "Step 2 of 4", one
   question per screen, and a big bar while anything is moving. */

.stepper { display: flex; align-items: center; margin: .2rem 0 .4rem; }
.stepper .dot {
  width: 34px; height: 34px; flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 2.5px solid var(--line-strong);
  background: var(--surface); color: var(--ink-soft);
  font-weight: 800; font-size: .95rem;
}
.stepper .dot svg { width: 18px; height: 18px; }
.stepper .dot.on   { border-color: var(--purple); background: var(--purple); color: var(--on-face); }
.stepper .dot.done { border-color: var(--green-deep); background: var(--green-face); color: var(--on-face); }
.stepper .bar { flex: 1; height: 3px; background: var(--line); margin: 0 .3rem; border-radius: 2px; }
.step-of { margin: 0 0 .2rem; color: var(--ink-soft); font-size: .85rem; font-weight: 700; }

.meter {
  height: 22px; border-radius: var(--r-pill);
  background: var(--sunk); border: 2px solid var(--line);
  overflow: hidden;
}
.meter > span {
  display: block; height: 100%; width: 0;
  background: var(--green);
  border-radius: var(--r-pill);
  transition: width .2s linear;
}
/* No content-length: say "working" honestly instead of a bar that lies. */
.meter > span.busy {
  width: 40% !important;
  animation: sweep 1.2s ease-in-out infinite;
}
@keyframes sweep { 0% { transform: translateX(-100%) } 100% { transform: translateX(250%) } }
html[dir="rtl"] .meter > span.busy { animation-direction: reverse; }

.done-mark {
  width: 84px; height: 84px; margin: .6rem auto .4rem;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--green); color: var(--on-face);
  box-shadow: 0 5px 0 var(--green-deep);
}
.done-mark svg { width: 46px; height: 46px; stroke-width: 3; }

/* A pairing code people read aloud across a room. */
.code {
  font: 800 clamp(2.4rem, 12vw, 3.4rem)/1 ui-monospace, "SF Mono", Menlo, monospace;
  letter-spacing: .18em !important;
  text-align: center; direction: ltr; unicode-bidi: isolate;
  padding: 1rem .5rem;
  border: 3px dashed var(--purple);
  border-radius: var(--r-lg);
  background: var(--purple-wash); color: var(--purple);
}
.code-input {
  font: 800 2rem/1 ui-monospace, "SF Mono", Menlo, monospace !important;
  letter-spacing: .3em !important;
  text-align: center; direction: ltr;
  padding: .9rem !important;
}

.spin {
  display: inline-block; width: 1em; height: 1em; vertical-align: -.15em;
  margin-inline-end: .5em;
  border: 2.5px solid var(--line-strong); border-top-color: var(--purple);
  border-radius: 50%;
  animation: turn .8s linear infinite;
}
@keyframes turn { to { transform: rotate(360deg) } }

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

.foot-nav {
  margin-top: 3rem; padding: 1.6rem 0 2.5rem;
  border-top: 2px solid var(--line);
  color: var(--ink-soft); font-size: .92rem;
}

/* Chinese sets in the reader's own system face — Simplified for Mandarin,
   Traditional for Cantonese — and wants a little more line height than Latin
   because the characters are dense. */
html[lang="cmn"] body,
html[lang="yue"] body {
  font-family: "PingFang SC", "PingFang HK", "Hiragino Sans GB", "Microsoft YaHei",
               "Noto Sans CJK SC", "Source Han Sans SC", system-ui, sans-serif;
  line-height: 1.75;
}
html[lang="yue"] body { font-family: "PingFang HK", "PingFang TC", "Microsoft JhengHei",
               "Noto Sans CJK TC", "Source Han Sans TC", system-ui, sans-serif; }

/* Devanagari needs a face with the full headline and conjunct repertoire. */
html[lang="hi"] body,
html[lang="mr"] body,
html[lang="ne"] body {
  font-family: "Noto Sans Devanagari", "Kohinoor Devanagari", Mangal,
               "Nirmala UI", system-ui, sans-serif;
  line-height: 1.75;
}
html[lang="pa"] body {
  font-family: "Noto Sans Gurmukhi", "Gurmukhi MN", "Mukta Mahee", Raavi,
               "Nirmala UI", system-ui, sans-serif;
  line-height: 1.75;
}
html[lang="te"] body {
  font-family: "Noto Sans Telugu", "Kohinoor Telugu", "Telugu Sangam MN", Gautami,
               "Nirmala UI", system-ui, sans-serif;
  line-height: 1.8;
}
/* Chinese has no letter case and no word spaces: uppercase tracking on a label
   just pushes the characters apart. */
html[lang="cmn"] .kicker, html[lang="yue"] .kicker,
html[lang="cmn"] .flabel, html[lang="yue"] .flabel,
html[lang="cmn"] .eyebrow, html[lang="yue"] .eyebrow {
  text-transform: none; letter-spacing: 0; font-size: .82rem;
}
html[lang="hi"] .kicker, html[lang="hi"] .flabel, html[lang="hi"] .eyebrow,
html[lang="mr"] .kicker, html[lang="mr"] .flabel, html[lang="mr"] .eyebrow,
html[lang="ne"] .kicker, html[lang="ne"] .flabel, html[lang="ne"] .eyebrow,
html[lang="pa"] .kicker, html[lang="pa"] .flabel, html[lang="pa"] .eyebrow,
html[lang="te"] .kicker, html[lang="te"] .flabel, html[lang="te"] .eyebrow {
  text-transform: none; letter-spacing: 0; font-size: .86rem;
}

/* ============================================================ Arabic script

   Urdu and Sindhi join their letters. Several defaults that are invisible in
   Latin actively break them, so they are switched off wherever the interface
   is running in an Arabic script — and Latin runs opt back in via .latin.  */

html[dir="rtl"] body {
  font-family: "Noto Nastaliq Urdu", "Noto Naskh Arabic", "Geeza Pro",
               "Urdu Typesetting", serif;
  line-height: 2.1;                 /* Nastaliq hangs far below the baseline */
}
html[lang="snd"] body,
html[lang="ps"] body,
html[lang="ar"] body {
  font-family: "Noto Naskh Arabic", "Geeza Pro", "Traditional Arabic", serif;
  line-height: 1.95;                /* Naskh is shallower and sits tighter */
}

/* Any tracking at all pulls the joins apart and renders words as loose glyphs. */
html[dir="rtl"] * { letter-spacing: 0 !important; }

/* break-word would sever a word between two joined letters. */
html[dir="rtl"] body { overflow-wrap: normal; word-break: normal; }

/* Headings at body leading open a chasm; step them back down. */
html[dir="rtl"] h1 { line-height: 1.45; }
html[dir="rtl"] h2 { line-height: 1.55; }
html[dir="rtl"] h3 { line-height: 1.7; }

/* Descenders collide with control borders without extra room. */
html[dir="rtl"] .btn,
html[dir="rtl"] .fchip,
html[dir="rtl"] .et-chip { padding-top: 1.05rem; padding-bottom: 1.05rem; }

/* Catalogue data — titles, publisher names — stays English. Inside an RTL page
   it needs its own face and its own direction or it zig-zags between the two. */
.latin {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  direction: ltr;
  unicode-bidi: isolate;
  text-align: start;
  letter-spacing: normal !important;
  line-height: 1.4;
}

/* An element whose entire content is a numeral: "112", "06:00", "2:07:53".
   Without isolation the bidi algorithm renders "200+" as "+200". Only ever put
   this on elements that hold nothing but the number. */
.num { direction: ltr; unicode-bidi: isolate; display: inline-block; }

/* Chevrons and arrows point the other way in an RTL page. */
html[dir="rtl"] .flip { transform: scaleX(-1); }

/* ============================================================ first run

   A whole screen rather than a sheet: nothing behind it matters until a
   language is chosen, and it must not be dismissable by a stray tap.

   One screen at a time, like the best phone apps open: a top bar (back,
   progress, skip), the screen, and one big button pinned under the thumb.
   The page itself never scrolls; only the language list does, inside its
   screen. The same shell carries Kandili's questions
   (features/onboarding.js), so the whole first visit reads as one flow. */
html.welcoming, html.welcoming body { overflow: hidden; }
html.welcoming #et-hint { display: none; }
.welcome {
  position: fixed; inset: 0; z-index: 200;
  display: flex; height: 100%; height: 100dvh; overflow: hidden;
  background: var(--ground);
}
.welcome.ob-on > :not(.ob) { display: none; }
.ob { flex: 1; display: flex; min-width: 0; }
.ob-in {
  flex: 1; display: flex; flex-direction: column; min-height: 0;
  width: 100%; max-width: 30rem; margin: 0 auto;
  padding: max(.4rem, env(safe-area-inset-top)) 16px 0;
}
.et-kandili-slot { display: inline-block; flex: none; }
.et-kandili-slot > .et-lantern { width: 100%; height: auto; }

.ob-top { flex: none; display: flex; align-items: center; gap: .4rem; min-height: 56px; }
.ob-icon, .ob-skip {
  min-width: 56px; min-height: 56px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 16px; background: transparent; color: var(--ink-soft);
  font: inherit; font-weight: 700; cursor: pointer;
}
/* Hidden controls keep their place, so the progress bar never jumps. */
.ob-icon[hidden], .ob-skip[hidden] { display: inline-flex; visibility: hidden; }
.ob-skip { padding: 0 .8rem; font-size: 1rem; }
.ob-icon svg { width: 26px; height: 26px; }
.ob-icon:hover, .ob-skip:hover { background: var(--sunk); color: var(--ink); }
.ob-icon:focus-visible, .ob-skip:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.ob-barwrap { flex: 1; display: flex; }
.ob-bar {
  position: relative; flex: 1; height: 12px; border-radius: 999px;
  background: var(--sunk); overflow: hidden;
}
.ob-bar i {
  position: absolute; inset-block: 0; inset-inline-start: 0;
  border-radius: inherit; background: var(--purple);
  transition: width .45s cubic-bezier(.32, .72, 0, 1);
}

.ob-screens { flex: 1; min-height: 0; display: flex; }
.ob-screen {
  flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column;
  padding-block-start: .6rem;
  overflow-y: auto; overscroll-behavior: contain;   /* only on the smallest phones */
}
.ob-screen[hidden] { display: none; }
.ob-screen h1 { outline: none; }

/* Forward slides in from the end side, back from the start side. */
.ob-from-end   { animation: ob-from-end .42s cubic-bezier(.32, .72, 0, 1) both; }
.ob-from-start { animation: ob-from-start .42s cubic-bezier(.32, .72, 0, 1) both; }
@keyframes ob-from-end   { from { opacity: 0; transform: translateX(36px); } to { opacity: 1; transform: none; } }
@keyframes ob-from-start { from { opacity: 0; transform: translateX(-36px); } to { opacity: 1; transform: none; } }

.ob-foot { flex: none; padding: .7rem 0 max(1rem, env(safe-area-inset-bottom)); }
.ob-go { border-radius: var(--r-pill); }
.ob-go svg { width: 20px; height: 20px; }

/* Kandili talks: lantern on the start side, a speech bubble beside it. */
.ob-talk { flex: none; display: flex; align-items: flex-end; gap: .4rem; margin: .4rem 0 1rem; }
.ob-k { flex: none; }
.ob-bubble {
  position: relative; flex: 1; min-width: 0; margin-block-end: 1.2rem;
  padding: .8rem 1rem; border: 2px solid var(--line-strong); border-radius: 20px;
  background: var(--surface);
}
.ob-bubble::before {
  content: ""; position: absolute; inset-block-end: 16px; inset-inline-start: -9px;
  width: 14px; height: 14px; background: var(--surface);
  border-block-end: 2px solid var(--line-strong); border-inline-start: 2px solid var(--line-strong);
  transform: rotate(45deg);
}
html[dir="rtl"] .ob-bubble::before { transform: rotate(-45deg); }
.welcome .ob-q {
  margin: 0; text-align: start; font-family: var(--display); font-weight: 800;
  font-size: clamp(1.3rem, 5.6vw, 1.6rem); line-height: 1.3;
}
.ob-say { margin: .3rem 0 0; color: var(--ink-soft); line-height: 1.5; }
.ob-say:empty { display: none; }

/* ------------------------------------------------ 1 hello */
.tour-hello { justify-content: center; text-align: center; gap: .2rem; }
.tour-art { flex: none; margin: 0 0 1.1rem; }
.tour-art img {
  display: block; width: 100%; height: auto; max-height: 34vh;
  aspect-ratio: 971 / 465; object-fit: cover; object-position: 50% 50%;
  border-radius: var(--r-lg); border: 3px solid var(--surface);
  box-shadow: 0 10px 28px -14px rgba(42, 30, 34, .45);
}
.tour-brand {
  margin: 0 0 .35rem; font-family: var(--display); font-weight: 800;
  font-size: 1rem; letter-spacing: .02em; color: var(--ink-soft);
}
.tour-brand span { color: var(--purple); }
.tour-h {
  margin: 0 0 .6rem; font-weight: 900; line-height: 1.1;
  font-size: clamp(1.8rem, 8vw, 2.5rem);
}
.tour-h span { color: var(--purple); }
.tour-sub { margin: 0 auto .9rem; max-width: 26rem; color: var(--ink-soft); font-size: 1.06rem; line-height: 1.5; }
.tour-chips {
  display: flex; flex-wrap: wrap; justify-content: center; gap: .4rem;
  margin: 0; padding: 0; list-style: none;
}
.tour-chips li {
  display: inline-flex; align-items: center; gap: .4rem; padding: .3rem .75rem;
  border: 1px solid var(--line-strong); border-radius: 999px; background: var(--surface);
  font-weight: 700; font-size: .88rem;
}
.tour-chips li::before { content: ""; width: .45rem; height: .45rem; border-radius: 50%; background: var(--purple); }
.tour-own {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  align-self: center; min-height: 56px; margin-top: .4rem; padding: 0 1rem;
  border: 0; border-radius: 16px; background: transparent; color: var(--purple);
  font: inherit; font-weight: 800; font-size: 1.05rem; cursor: pointer;
}
.tour-own svg { width: 22px; height: 22px; }
.tour-own:hover { background: var(--purple-wash); }

/* ------------------------------------------- 2 how, 3 road */
.tour-how .tour-h, .tour-road .tour-h { font-size: clamp(1.6rem, 7vw, 2.1rem); }
.tour-how .tour-sub { margin: 0 0 .4rem; }

/* A path of stops joined by a line, like the roadmap: the line draws itself
   down from each stop to the next. */
.tour-path { list-style: none; margin: 1rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 1.5rem; }
.tp-stop { position: relative; display: flex; align-items: flex-start; gap: 1rem; }
.tp-stop:not(:last-child)::after {
  content: ""; position: absolute; z-index: 0;
  inset-inline-start: 26px; inset-block-start: 58px; height: calc(100% - 58px + 1.5rem);
  width: 4px; border-radius: 4px; background: var(--line-strong);
  transform-origin: top; animation: tp-draw .5s cubic-bezier(.32, .72, 0, 1) both;
}
.tp-stop:nth-child(2)::after { animation-delay: .18s; }
@keyframes tp-draw { from { transform: scaleY(0); } to { transform: none; } }
.tp-node {
  position: relative; z-index: 1; flex: none;
  width: 56px; height: 56px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface); border: 4px solid var(--c, var(--purple)); color: var(--c, var(--purple));
}
.tp-node > svg { width: 24px; height: 24px; }
.tp-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; padding-block-start: .3rem; }
.tp-txt strong { font-family: var(--display); font-weight: 800; font-size: 1.18rem; line-height: 1.3; }
.tp-txt > span:last-child { color: var(--ink-soft); line-height: 1.45; }
.tp-stop.plum { --c: var(--purple); }
.tp-stop.teal { --c: var(--sky); }
.tp-stop.gold { --c: var(--gold-deep); }
.tp-stop.gold .tp-node { background: var(--gold); border-color: var(--gold-deep); color: var(--on-gold); }

.tp-tag {
  align-self: flex-start; margin-block-end: .25rem; padding: .15rem .6rem; border-radius: 999px;
  font-size: .78rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  background: var(--sunk); color: var(--ink-soft);
}
/* Done: a filled green stop and a solid line. Here: Kandili, ringed. Next:
   an open stop at the end of a dotted line. */
.road .done { --c: var(--green); }
.road .done .tp-node { background: var(--green); color: var(--on-face); }
.road .done .tp-tag { background: var(--green-wash); color: var(--green-text); }
.road .done::after { background: var(--green); }
.road .here .tp-node {
  width: 60px; height: 60px; margin-inline-start: -2px;
  border-color: var(--purple); background: var(--purple-wash);
  box-shadow: 0 0 0 6px var(--purple-wash);
}
.road .here .tp-tag { background: var(--purple); color: var(--on-face); }
.road .here::after {
  background: none; border-inline-start: 4px dotted var(--line-strong); border-radius: 0;
  inset-block-start: 62px;
}
.road .soon { --c: var(--ink-soft); }
.road .soon .tp-node { border-style: dashed; }
.tour-partner {
  display: flex; align-items: center; gap: .8rem; margin-top: auto; min-height: 56px;
  padding: .75rem 1rem; border: 1px solid var(--line-strong); border-radius: var(--r);
  background: var(--surface); color: var(--ink); text-decoration: none;
}
.tour-road .tour-path { margin-bottom: 1.2rem; }
.tour-partner > span { flex: 1; display: flex; flex-direction: column; }
.tour-partner > span > span { color: var(--ink-soft); font-size: .92rem; line-height: 1.4; }
.tour-partner svg { width: 20px; height: 20px; flex: none; color: var(--ink-soft); }
.tour-partner:hover { border-color: var(--purple); }

/* ------------------------------------------------ 4 language */
.tour-lang { overflow: hidden; }
.welcome-search { flex: none; margin-bottom: .7rem; }
.welcome-list {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: .5rem; padding: 2px 2px 1rem;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 1.5rem), transparent);
          mask-image: linear-gradient(to bottom, #000 calc(100% - 1.5rem), transparent);
}
.welcome-lang {
  flex: none; display: flex; align-items: center; gap: .75rem; width: 100%;
  min-height: 4.1rem; padding: .7rem .9rem;
  text-align: start; color: inherit; font: inherit;
  background: var(--surface); border: 2px solid var(--line); border-radius: 16px;
  cursor: pointer; transition: border-color .15s, transform .1s, background .15s;
}
.welcome-lang:hover { border-color: var(--purple); }
.welcome-lang:active { transform: scale(.985); }
.welcome-lang:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.welcome-lang[aria-checked="true"] { border-color: var(--purple); background: var(--purple-wash); }
.welcome-lang[hidden] { display: none; }
.wl-text { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.wl-native { font-size: 1.22rem; font-weight: 700; line-height: 1.5; }
.wl-name { font-size: .84rem; color: var(--ink-soft); }
.wl-count { font-size: .8rem; color: var(--purple); line-height: 1.45; margin-top: .1rem; }
.wl-tick {
  flex: none; width: 28px; height: 28px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: 2px solid var(--line-strong); color: transparent;
}
.wl-tick svg { width: 16px; height: 16px; }
.welcome-lang[aria-checked="true"] .wl-tick { background: var(--purple); border-color: var(--purple); color: var(--on-face); }

/* Short phones (older Androids at 640px and less): tighten, never scroll. */
@media (max-height: 700px) {
  .tour-art img { max-height: 27vh; }
  .tour-art { margin-bottom: .7rem; }
  .tour-h { font-size: clamp(1.6rem, 7vw, 2rem); }
  .tour-sub { font-size: 1rem; margin-bottom: .6rem; }
  .tour-path { gap: 1.1rem; margin-top: .7rem; }
  .tp-stop:not(:last-child)::after { height: calc(100% - 58px + 1.1rem); }
  .tp-txt > span:last-child { font-size: .95rem; }
  .ob-talk { margin-bottom: .6rem; }
  .tour-partner { padding-block: .5rem; }
  .tour-partner > span > span { display: none; }
  .tour-road .tour-path { margin-bottom: .8rem; }
}
@media (max-height: 660px) {
  .tour-art img { max-height: 23vh; }
  .tour-chips li { padding: .2rem .6rem; font-size: .82rem; }
  .tour-path { gap: .9rem; }
  .tp-stop:not(:last-child)::after { height: calc(100% - 58px + .9rem); }
  .tour-how .tour-sub { font-size: .95rem; }
}
/* The smallest phones (iPhone SE size): drop the extras, keep the story. */
@media (max-height: 600px) {
  .tour-chips, .tour-how .tour-sub, .tour-partner { display: none; }
  .tour-h { font-size: 1.55rem; }
  .tp-node { width: 48px; height: 48px; }
  .tp-stop:not(:last-child)::after { inset-inline-start: 22px; inset-block-start: 50px; height: calc(100% - 50px + .9rem); }
  .road .here .tp-node { width: 54px; height: 54px; }
  .road .here::after { inset-block-start: 56px; }
}

@media (prefers-reduced-motion: reduce) {
  .ob-from-end, .ob-from-start, .tp-stop::after { animation: none; }
  .ob-bar i { transition: none; }
}
html[data-motion="reduce"] .ob-from-end, html[data-motion="reduce"] .ob-from-start,
html[data-motion="reduce"] .tp-stop::after { animation: none; }

/* ------------------------------------------------------------ generated covers
   Every item has one; a real cover image, when it loads, sits on top. */
.gen {
  position: absolute; inset: 0; display: flex; align-items: flex-end;
  color: rgba(255, 255, 255, .95); overflow: hidden;
}
.gen svg { position: absolute; inset: 0; width: 100% !important; height: 100% !important; opacity: 1 !important; }
.gen-title {
  position: relative; z-index: 1; width: 100%;
  padding: .7rem .75rem .8rem;
  font-weight: 800; font-size: 1.02rem; line-height: 1.3; color: #fff;
  text-shadow: 0 1px 8px rgba(0, 0, 0, .35);
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
  background: linear-gradient(to top, rgba(0, 0, 0, .38), transparent);
}
.gen-year {
  position: absolute; top: .6rem; inset-inline-end: .65rem; z-index: 1;
  font-weight: 800; font-size: .8rem; letter-spacing: .04em; color: #fff;
  background: rgba(0, 0, 0, .25); padding: .15rem .45rem; border-radius: 999px;
}
.poster .art img, .rowi .art img, .thumb > img { z-index: 2; }

/* Ethiopic faces are supplied by the device so the offline app needs no font download. */
html[lang="am"] body {
  font-family: "Noto Sans Ethiopic", Nyala, Kefa, "Abyssinica SIL", system-ui, sans-serif;
  line-height: 1.75;
}
html[lang="am"] .latin { font-family: inherit; }
html[lang="am"] .kicker, html[lang="am"] .flabel, html[lang="am"] .eyebrow {
  text-transform: none; letter-spacing: 0; font-size: .86rem;
}

/* Rounded display face for headlines (first-run tour, partner pages). */
.display { font-family: var(--display); letter-spacing: -.01em; }

/* ============================================================ delight

   Small, quick motion that makes the bright brand feel alive: things arrive
   with a little pop (never the headline or hero picture, which must paint
   at once for a fast first screen), cards lift under a finger, and finishing a save or a
   transfer gets a burst of sparks behind Kandili. Only transform and opacity
   animate, so a budget phone stays smooth, and all of it switches off under
   "reduce motion". */
@keyframes et-pop-in {
  from { opacity: 0; transform: translateY(10px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
@keyframes et-burst {
  0%   { opacity: 1; transform: translate(-50%, -50%) scale(.2); }
  70%  { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }
}

.quick-pick {
  animation: et-pop-in .45s cubic-bezier(.34, 1.56, .64, 1) both;
}
.quick-pick:nth-child(2) { animation-delay: .07s; }
.quick-pick:nth-child(3) { animation-delay: .14s; }

/* Lift under a pointer, sink under a thumb. */
.poster .art, .hero-card {
  transition: transform .16s cubic-bezier(.2, .8, .2, 1), box-shadow .16s;
}
@media (hover: hover) {
  .poster:hover .art { transform: translateY(-3px) rotate(-.6deg); }
  .quick-pick:hover { transform: translateY(-2px); }
}
.poster:active .art { transform: scale(.97); }

/* The done moment. One pseudo-element carries every spark as a shadow, so
   the burst costs no markup. */
.cheer {
  position: relative; display: flex; justify-content: center;
  margin: .6rem auto .8rem; isolation: isolate;
}
.cheer::before {
  content: ""; position: absolute; z-index: -1; left: 50%; top: 50%;
  width: 10px; height: 10px; border-radius: 50%; background: transparent;
  box-shadow:
      0 -78px 0 var(--gold),   55px -55px 0 var(--clay),  78px 0 0 var(--gold-deep),
     55px 55px 0 var(--gold),    0 78px 0 var(--clay),    -55px 55px 0 var(--gold-deep),
    -78px 0 0 var(--gold),   -55px -55px 0 var(--clay),
     30px -88px 0 -2px var(--glow), 88px -30px 0 -2px var(--gold),  88px 30px 0 -2px var(--glow),
     30px 88px 0 -2px var(--gold-deep), -30px 88px 0 -2px var(--glow), -88px 30px 0 -2px var(--gold),
    -88px -30px 0 -2px var(--glow), -30px -88px 0 -2px var(--gold-deep);
  opacity: 0;
  animation: et-burst .9s cubic-bezier(.2, .8, .2, 1) .15s both;
}
.cheer::after {
  content: ""; position: absolute; z-index: -2; left: 50%; top: 50%;
  width: 150px; height: 150px; border-radius: 50%;
  background: radial-gradient(circle, var(--gold-wash) 0%, transparent 70%);
  transform: translate(-50%, -50%);
}

@media (prefers-reduced-motion: reduce) {
  .quick-pick { animation: none; }
  .cheer::before { animation: none; opacity: 0; }
  .poster .art, .hero-card { transition: none; }
}
html[data-motion="reduce"] .quick-pick { animation: none; }
html[data-motion="reduce"] .cheer::before { animation: none; opacity: 0; }

/* ======================================================== motion and feel

   What makes the best phone apps feel solid is mostly three small things:
   every tap answers at once, pages hand over to each other instead of
   blinking, and nothing moves that the person did not cause. All of it is
   CSS, costs nothing on a 2 GB Android, and switches off for reduced motion. */

/* Page to page: a short cross-fade instead of a white blink. The header and
   tab bar are the same pixels on both pages, so they read as standing still.
   Browsers without cross-document view transitions (and file:// cards) just
   navigate as before. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: et-page-out 120ms cubic-bezier(.4, 0, 1, 1) both; }
::view-transition-new(root) { animation: et-page-in 220ms cubic-bezier(.2, .8, .2, 1) both; }
@keyframes et-page-out { to { opacity: 0; } }
@keyframes et-page-in { from { opacity: 0; } }

/* Tab bar: the pill under the icon dips when pressed and springs back. */
.tabbar a { transition: color .16s ease; }
.tabbar .pipe { transition: transform .32s cubic-bezier(.34, 1.56, .64, 1), background .2s ease; }
.tabbar a:active .pipe { transform: scale(.86); transition-duration: .08s; }

/* Filter chips, header buttons and list rows answer the finger too. */
.fchip { transition: transform .32s cubic-bezier(.34, 1.56, .64, 1), background .16s ease, border-color .16s ease; }
.fchip:active { transform: scale(.95); transition-duration: .08s; }
.rowi { transition: background .16s ease, transform .3s cubic-bezier(.34, 1.56, .64, 1); }

/* The header stays out of the way: one line, never a two-line logo. */
.brand > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.brand { min-width: 0; }
/* RTL's taller control padding is for descenders in body text; the header's
   round buttons already have the room, and doubling them crowded the bar. */
html[dir="rtl"] .top .fchip { padding-top: .35rem; padding-bottom: .35rem; }
.top .wrap { gap: .45rem; }
.top .fchip { padding-inline: .8rem; }
@media (max-width: 380px) { .brand { font-size: .98rem; gap: .4rem; } .top .fchip { padding-inline: .7rem; } }

/* On a phone, "save to your computer" is a Guide topic, not something to
   step over on every library and item page. */
@media (max-width: 40rem) and (pointer: coarse) {
  .save-guide-entry, .desk-only { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  .tabbar .pipe, .fchip, .rowi { transition: none; }
  .tabbar a:active .pipe, .fchip:active { transform: none; }
}
html[data-motion="reduce"] .tabbar a:active .pipe,
html[data-motion="reduce"] .fchip:active { transform: none; }

/* Library refine row: "From" and "Language" sit side by side as two small
   cards (a word on top, the choice under it), the way iPhone apps show a
   filter; the one that is open takes the whole width below. The two tools
   under them are quiet buttons: they matter, but the list is the page. */
.library-refine { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 1rem; }
.library-refine > .library-language { flex: 1 1 9rem; min-width: 0; margin-top: 0; }
.library-refine > .library-language[open] { flex-basis: 100%; order: 1; }
.library-refine summary { position: relative; flex-wrap: wrap; gap: 0 .5rem; padding-inline-end: 2.2rem; min-height: 56px; }
.library-refine summary > span { width: 100%; font-size: .74rem; font-weight: 700; color: var(--ink-soft); line-height: 1.2; }
.library-refine summary strong { margin-inline-start: 0; max-width: 100%; line-height: 1.3; }
.library-refine summary::after { border-inline-end: 0; border-right: 2px solid currentColor; position: absolute; inset-inline-end: .9rem; top: 50%; margin: -.4rem 0 0; }
.library-refine .library-language[open] summary::after { margin: -.1rem 0 0; }
.library-actions .et-btn--quiet svg { color: var(--purple); }
.library-actions #bulk-start svg { color: var(--green); }
