/* The site's stylesheet.
 *
 * Lifted out of index.html by build/split-index.mjs — it was 159 KB inline,
 * re-sent on every visit and impossible to cache on its own.
 */
:root {
    --bg: #f0f6ff;
    --card: var(--s-0);
    --card-2: #f2f4fb;
    --text: var(--i-0);
    --text-dim: var(--i-3);
    --accent: #a855f7;
    --accent-2: #22c55e;
    --border: var(--s-3);
    --danger: #ef4444;
    --shadow: 0 4px 18px rgba(99, 102, 241, 0.10);

    /* ── Surfaces and ink ────────────────────────────────────────────
       Every colour below is redefined for dark mode in one place, which is
       the only reason the stylesheet could be flipped at all: before this,
       475 literal hex values meant 475 places to get it wrong. */
    --s-0:#ffffff; --s-1:#f8fafc; --s-2:#f1f5f9; --s-3:#e2e8f0; --s-4:#cbd5e1;
    --i-0:#0f172a; --i-1:#334155; --i-2:#475569; --i-3:#64748b; --i-4:#94a3b8;
    --t-good:#f0fdf4; --t-good-2:#dcfce7; --t-good-b:#bbf7d0;
    --t-bad:#fee2e2; --t-bad-b:#fca5a5; --t-warn:#fffbeb; --t-warn-b:#fcd34d;
    --t-info:#e0f2fe; --t-info-b:#bae6fd;
    --t-brand:#eef2ff; --t-brand-2:#e0e7ff;
    /* Frosted surfaces sit over whatever scrolls beneath them, so they are
       translucent rather than a flat colour — which means a hex token cannot
       carry them and they need their own. */
    /* Text and icons that sit on a coloured surface or a photograph. It
       is white in both themes: the thing underneath it does not change,
       so neither can this. */
    /* Status text. Dark on a pale tint in light mode, light on a dark
       tint in dark mode — the tint flips, so the text must flip too. */
    --fg-good:#15803d; --fg-warn:#92400e; --fg-bad:#b91c1c;
    --fg-info:#0369a1; --fg-brand:#4338ca;
    --on-accent:#ffffff;
    --glass:rgba(255,255,255,0.75); --glass-strong:rgba(255,255,255,0.9);
    --glass-edge:rgba(255,255,255,0.6);
    /* The ground every page stands on — the sign-in page's, adopted site
       wide. A tinted wash from the top rather than a flat fill, so the white
       cards on it have something to sit against. Named --page-* and kept out
       of --bg on purpose: --bg is a flat colour dozens of rules composite
       against, and this is a gradient only the page itself should wear. */
    --page-1:#d9dcfb; --page-2:#e8ecfb; --page-top:#c3c9f7;

    /* Accessibility, applied by the settings panel. Type is scaled from one
       root value rather than by rewriting every font-size, so nothing can
       fall out of proportion with the rest. */
    --ui-scale: 1;
  }

  /* Dark mode. Three states, not two: an explicit choice stamps data-theme
     on <html>, and the default "follow my device" setting stamps nothing —
     so the media query has to hold the un-stamped case while still losing to
     an explicit light choice. */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      --bg:#0d1117; --card:#161b22; --card-2:#1c232c; --border:#2b333d;
      --text:#e6edf3; --text-dim:#8b98a5;
      --shadow: 0 4px 18px rgba(0, 0, 0, 0.45);
      --s-0:#161b22; --s-1:#1c232c; --s-2:#222b35; --s-3:#2b333d; --s-4:#3a4550;
      --i-0:#e6edf3; --i-1:#c9d4de; --i-2:#a9b6c2; --i-3:#8b98a5; --i-4:#6e7b88;
      /* The tinted panels keep their meaning but not their brightness: a
         mint-white "correct" card on a dark page is a flashbulb. */
      --t-good:#10231a; --t-good-2:#16311f; --t-good-b:#2b6b45;
      --t-bad:#2a1618; --t-bad-b:#7a2c2c; --t-warn:#282013; --t-warn-b:#7a5c1e;
      --t-info:#0f2530; --t-info-b:#2b5f79;
      --t-brand:#1a1f33; --t-brand-2:#252c47;
      --fg-good:#5fd68a; --fg-warn:#f0b45e; --fg-bad:#f08a8a; --fg-info:#6cc9f5; --fg-brand:#a3aaf5;
      --glass:rgba(22,27,34,0.78); --glass-strong:rgba(22,27,34,0.92);
      --glass-edge:rgba(255,255,255,0.09);
      --page-1:#191d2a; --page-2:#0e1017; --page-top:#232a3c;
    }
  }
  :root[data-theme="dark"] {
    --bg:#0d1117; --card:#161b22; --card-2:#1c232c; --border:#2b333d;
    --text:#e6edf3; --text-dim:#8b98a5;
    --shadow: 0 4px 18px rgba(0, 0, 0, 0.45);
    --s-0:#161b22; --s-1:#1c232c; --s-2:#222b35; --s-3:#2b333d; --s-4:#3a4550;
    --i-0:#e6edf3; --i-1:#c9d4de; --i-2:#a9b6c2; --i-3:#8b98a5; --i-4:#6e7b88;
    --t-good:#10231a; --t-good-2:#16311f; --t-good-b:#2b6b45;
    --t-bad:#2a1618; --t-bad-b:#7a2c2c; --t-warn:#282013; --t-warn-b:#7a5c1e;
    --t-info:#0f2530; --t-info-b:#2b5f79;
    --t-brand:#1a1f33; --t-brand-2:#252c47;
    --fg-good:#5fd68a; --fg-warn:#f0b45e; --fg-bad:#f08a8a; --fg-info:#6cc9f5; --fg-brand:#a3aaf5;
    --glass:rgba(22,27,34,0.78); --glass-strong:rgba(22,27,34,0.92);
    --glass-edge:rgba(255,255,255,0.09);
    --page-1:#191d2a; --page-2:#0e1017; --page-top:#232a3c;
  }

  /* High contrast pushes the ink to the ends of the scale and thickens every
     border. It is a separate switch from dark mode because the people who
     need it do not all want a dark page. */
  :root[data-contrast="high"] {
    --i-0:#000000; --i-1:#101010; --i-2:#1c1c1c; --i-3:#333333; --i-4:#4a4a4a;
    --s-3:#767676; --s-4:#5a5a5a;
  }
  /* On a dark ground the contrast boost has to run the other way, and it has
     to cover the un-stamped "follow my device" case as well as the explicit
     one — otherwise turning on high contrast at night makes the text darker. */
  :root[data-contrast="high"][data-theme="dark"] {
    --i-0:#ffffff; --i-1:#f2f2f2; --i-2:#e2e2e2; --i-3:#cccccc; --i-4:#b4b4b4;
    --s-3:#9a9a9a; --s-4:#b0b0b0;
  }
  @media (prefers-color-scheme: dark) {
    :root[data-contrast="high"]:not([data-theme="light"]) {
      --i-0:#ffffff; --i-1:#f2f2f2; --i-2:#e2e2e2; --i-3:#cccccc; --i-4:#b4b4b4;
      --s-3:#9a9a9a; --s-4:#b0b0b0;
    }
  }
  :root[data-contrast="high"] * { border-color: var(--s-3); }

  html { font-size: calc(16px * var(--ui-scale)); }

  /* Honour the system setting, and the app's own switch, with the same rule.
     Motion that cannot be turned off is not a preference, it is a symptom
     trigger for anyone with a vestibular disorder. */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: .001ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: .001ms !important;
      scroll-behavior: auto !important;
    }
  }
  :root[data-motion="off"] *, :root[data-motion="off"] *::before,
  :root[data-motion="off"] *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }

  * { box-sizing: border-box; }
  /* Thin, theme-colored scrollbar instead of the browser's default chunky
     gray one — Firefox via scrollbar-width/-color, Chrome/Edge/Safari via
     the -webkit- pseudo-elements (no standard equivalent exists yet). */
  * { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
  ::-webkit-scrollbar { width: 8px; height: 8px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; }
  ::-webkit-scrollbar-thumb:hover { background: var(--text-dim); }
  /* Keyboard-only focus ring: invisible to mouse/touch users (who get each
     control's own :focus style, e.g. border-color changes above), but a
     Tab-driven visit gets a real outline everywhere, including on the many
     controls above that suppress the default outline. */
  :focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }
  /* Every screen here is driven by fetch(), so a dropped connection would
     otherwise just look like the app silently doing nothing. */
  /* A flex item of <body> rather than a fixed overlay, so when it appears it
     pushes the header down instead of covering the site title. Collapsed to
     zero height (not display:none) so the reveal can animate. */
  .offline-banner {
    flex-shrink: 0;
    background: #b91c1c;
    color: var(--on-accent);
    font-size: 13px;
    font-weight: 600;
    text-align: center;
    overflow: hidden;
    max-height: 0;
    padding: 0 14px;
    transition: max-height .25s ease, padding .25s ease;
  }
  .offline-banner.show {
    max-height: 90px;
    padding: 8px 14px;
    padding-top: calc(8px + env(safe-area-inset-top, 0px));
  }
  .cookie-banner {
    position: fixed;
    left: 12px; right: 12px;
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    z-index: 60;
    background: rgba(30, 41, 59, 0.96);
    color: var(--on-accent);
    border-radius: 16px;
    padding: 14px 16px;
    display: flex;
    align-items: center;
    gap: 12px;
    box-shadow: 0 10px 30px rgba(0,0,0,.25);
    font-size: 12.5px;
    line-height: 1.5;
  }
  .cookie-banner span { flex: 1; }
  .cookie-banner button {
    flex-shrink: 0;
    background: linear-gradient(135deg, #4f46e5, #a855f7);
    color: var(--on-accent);
    border: none;
    border-radius: 999px;
    padding: 9px 18px;
    font-size: 12.5px;
    font-weight: 700;
    font-family: inherit;
    cursor: pointer;
  }
  @media (min-width: 1024px) {
    .cookie-banner { max-width: 480px; inset-inline-start: auto; inset-inline-end: 20px; bottom: 20px; }
  }
  body {
    margin: 0;
    font-family: "Vazirmatn", "Segoe UI", Tahoma, sans-serif;
    background:
      radial-gradient(120% 80% at 50% -10%, var(--page-top) 0%, transparent 60%),
      linear-gradient(180deg, var(--page-1), var(--page-2) 72%);
    color: var(--text);
    /* --app-height tracks visualViewport.height in JS, so this shrinks
       with the on-screen keyboard instead of the static 100vh, which is
       what used to make the whole page appear to jump when a keyboard
       opened. The page itself never scrolls; only <main> does, and only
       when its content actually overflows the available height. */
    height: var(--app-height, 100vh);
    overflow: hidden;
    display: flex;
    flex-direction: column;
  }
  /* Scoped to a direct child of body — the site's own single top header —
     so it never leaks onto a <header> nested inside a page's own content
     (e.g. the German course's self-study module uses its own <header>s for
     internal cards; without this scoping every bare-tag rule below would
     also restyle those). */
  body > header {
    padding: 20px 20px 6px;
    text-align: center;
    background: none;
    position: relative;
    /* Header chrome (back button, bell, language switcher, desktop nav
       order) always lays out the same physical way regardless of
       interface language — the back arrow must stay on the left exactly
       like the English layout, never mirroring under fa/ar. Inheriting
       direction:ltr here makes every inset-inline-start/end and flex
       order rule below resolve as fixed left/right, the same trick
       already used on .bottom-nav for the footer. */
    direction: ltr;
  }
  header h1 {
    margin: 0;
    font-size: 22px;
    font-weight: 800;
    letter-spacing: 0.5px;
    background: linear-gradient(135deg, #4f46e5, #a855f7);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
  /* Desktop-only nav/shell pieces — hidden unconditionally here so mobile
     never sees them; the desktop media query at the end of this stylesheet
     is the only place that reveals them. */
  .desktop-nav-links, .desktop-only { display: none; }
  .header-right-icons {
    position: absolute;
    top: 16px;
    inset-inline-end: 20px;
    display: flex;
    align-items: center;
    gap: 8px;
  }
  /* Present for screen readers, absent from the page. Used by the language
     button, whose visible content is a flag with no words in it. */
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* The current language reads as its flag, in a circle the same size as the
     bell and support buttons beside it. */
  .header-lang-btn {
    padding: 0;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    overflow: hidden;
    background: var(--card);
    border: 1px solid var(--border);
    cursor: pointer;
    box-shadow: var(--shadow);
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
  }
  .header-lang-btn:hover { border-color: var(--accent); }
  /* cover, not contain: a flag's aspect ratio is not 1:1, so fitting it
     inside the circle would leave bars of card colour top and bottom. */
  .header-lang-flag {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .lang-option-flag {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    object-fit: cover;
    flex: none;
  }
  .header-lang-btn iconify-icon { font-size: 15px; }
  .header-lang-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
    transform: translateY(-1px);
    box-shadow: var(--shadow), 0 6px 14px rgba(79, 70, 229, .18);
  }
  .header-lang-btn:active { transform: scale(.93); }
  .lang-dropdown-wrap { position: relative; }
  .lang-dropdown {
    position: fixed;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: var(--shadow), 0 16px 40px rgba(0, 0, 0, .22);
    padding: 8px;
    width: max-content;
    min-width: 150px;
    max-width: 220px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    z-index: 90;
    opacity: 0;
    transform: translateY(-8px) scale(.95);
    pointer-events: none;
    transition: opacity .16s ease, transform .16s ease;
  }
  .lang-dropdown.active {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
  }
  /* Support lives in the app bar now, beside the bell. Same shape as the
     bell so the pair reads as one set rather than two ideas. */
  .header-icon-btn {
    background: var(--card);
    border: 1px solid var(--border);
    color: var(--text-dim);
    width: 32px;
    height: 32px;
    border-radius: 50%;
    cursor: pointer;
    box-shadow: var(--shadow);
    font-size: 15px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    transition: color .2s ease, border-color .2s ease;
  }
  .header-icon-btn:hover { color: var(--accent); border-color: var(--accent); }

  /* A section heading with one control on the far side of it — where
     Reviews sits now that it has left the bottom bar. */
  .section-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin: 18px 0 8px;
  }
  .section-head h2 { margin: 0; font-size: 15px; }
  .section-head-btn {
    background: var(--card);
    border: 1px solid var(--border);
    color: var(--text-dim);
    width: 30px;
    height: 30px;
    border-radius: 50%;
    cursor: pointer;
    box-shadow: var(--shadow);
    font-size: 15px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    transition: color .2s ease, border-color .2s ease, transform .2s ease;
  }
  .section-head-btn:hover { color: var(--accent); border-color: var(--accent); }
  .section-head-btn:active { transform: scale(.92); }

  .header-bell-btn {
    position: relative;
    background: var(--card);
    border: 1px solid var(--border);
    color: var(--text-dim);
    width: 32px;
    height: 32px;
    border-radius: 50%;
    cursor: pointer;
    box-shadow: var(--shadow);
    font-size: 15px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease, color .15s ease;
  }
  .header-bell-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
    transform: translateY(-1px);
    box-shadow: var(--shadow), 0 6px 14px rgba(79, 70, 229, .18);
  }
  .header-bell-btn:active { transform: scale(.93); }
  .header-bell-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    background: #ef4444;
    color: var(--on-accent);
    font-size: 10px;
    font-weight: 700;
    min-width: 16px;
    height: 16px;
    border-radius: 999px;
    padding: 0 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    animation: header-bell-badge-pulse 1.8s ease-in-out infinite;
  }
  @keyframes header-bell-badge-pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.18); }
  }
  .header-back-btn {
    position: absolute;
    top: 16px;
    inset-inline-start: 20px;
    background: var(--card);
    border: 1px solid var(--border);
    color: var(--text-dim);
    font-size: 16px;
    font-weight: 700;
    width: 32px;
    height: 32px;
    transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease, color .15s ease;
    border-radius: 50%;
    cursor: pointer;
    box-shadow: var(--shadow);
    display: none;
    align-items: center;
    justify-content: center;
  }
  .header-back-btn.show { display: flex; }
  .header-back-btn iconify-icon { font-size: 18px; }
  .header-back-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
    transform: translateX(-2px);
    box-shadow: var(--shadow), 0 6px 14px rgba(79, 70, 229, .18);
  }
  .header-back-btn:active { transform: scale(.9); }
  /* On pages that have their own hero banner (home, category pages), the
     header floats over the banner instead of pushing it down — taken out
     of flow entirely so <main>'s existing negative-margin trick lands the
     banner at the true top of the page, with the back/lang buttons and
     the app title sitting on top of it. */
  body.has-banner > header {
    position: absolute; top: 0; left: 0; right: 0; z-index: 5; padding: 16px 20px;
  }
  /* On mobile the h1 normally uses background-clip:text with a purple
     gradient fill, which would be unreadable against the banner's own
     gradient/photo behind it — solid white with a shadow instead, only
     while it's floating over that banner. Desktop's own override (below,
     in the >=1024px block) keeps the gradient version since there the
     header sits on a plain background, not on top of the banner. */
  @media (max-width: 1023px) {
    body.has-banner header h1 {
      display: block;
      text-align: start;
      background: none;
      -webkit-background-clip: unset;
      background-clip: unset;
      color: var(--on-accent);
      text-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
    }
    /* Home has no back button, so its title keeps sitting to the side like
       before; only pages reached via the back button (category sub-pages)
       center it, since there it sits between a back arrow and the lang/bell
       icons rather than alone in the corner. */
    body.has-banner.has-back header h1 {
      text-align: center;
    }
    /* Profile's own header card already carries the avatar/name — the
       floating site title and language switcher would just be clutter on
       top of it; only the bell (unread notifications) stays useful there. */
    body.profile-banner header h1,
    body.profile-banner #header-lang-btn {
      display: none;
    }
    /* By default the banner's own heading just duplicates the site name
       already floating in the header above it — hidden via this class.
       Once an admin sets a real custom title (loadNews() in the JS below),
       that class is removed so the custom text shows same as any other
       category banner; only the redundant default stays hidden. */
    #home-banner-title.home-banner-title-default { display: none; }
  }
  body.full-page-mode .bottom-fixed-inner,
  body.full-page-mode .nav-center-btn,
  body.full-page-mode .nav-center-label { display: none; }
  main {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 480px;
    width: 100%;
    margin: 0 auto;
    /* Left/right cut 20% from the original 16px so every card on the site
       sits closer to the screen's own edges; top stays 16px (nothing asked
       to move the header down), bottom keeps its own much bigger value for
       the bottom dock. Every full-bleed banner below cancels this exact
       number, not a fixed 16px, so it still breaks out flush and no
       further than the true edge. */
    padding: 16px 12.8px;
    padding-bottom: 150px;
  }
  .step { display: none; }
  .step.active { display: block; }
  /* Short conversations/lists shouldn't leave a dead gap between the
     last item and the composer — fill the step's height and pin the
     content to the bottom, so any slack lands above it instead. */
  #step-support.active {
    display: flex;
    flex-direction: column;
    min-height: 100%;
  }
  #step-support .chat-messages { margin-top: auto; }

  .home-banner {
    position: relative;
    display: flex;
    align-items: flex-end;
    min-height: 220px;
    /* Breaks out of main's 16px padding on three sides so it starts flush
       at the very top of the page, right under the header, edge to edge. */
    margin: -16px -12.8px 16px -12.8px;
    border-radius: 0 0 22px 22px;
    overflow: hidden;
    background: linear-gradient(135deg, #4f46e5, #a855f7);
    color: var(--on-accent);
    box-shadow: 0 10px 24px rgba(168, 85, 247, 0.28);
  }
  .home-banner-bg {
    position: absolute; inset: 0;
    display: none;
  }
  .home-banner.has-image .home-banner-bg { display: block; }
  /* Two stacked layers, only one ever at opacity:1 — startBannerCarousel()
     crossfades between them on a timer instead of hard-cutting the image,
     which CSS can't animate directly (background-image isn't
     interpolatable between two different URLs). */
  .home-banner-bg-layer {
    position: absolute; inset: 0;
    background-size: cover; background-position: center;
    opacity: 0;
    transition: opacity 1s ease;
  }
  .home-banner-bg-layer.active { opacity: 1; }
  .home-banner-scrim {
    position: absolute; inset: 0;
    background: linear-gradient(180deg, rgba(0,0,0,0) 25%, rgba(0,0,0,0.62) 100%);
    display: none;
  }
  .home-banner.has-image .home-banner-scrim { display: block; }
  .home-banner.has-image .home-banner-icon { display: none; }
  .home-banner-content {
    position: relative; z-index: 1; width: 100%;
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 20px;
  }
  .home-banner-text h2 { margin: 0 0 4px; font-size: 17px; font-weight: 800; }
  .home-banner-text p { margin: 0; font-size: 13px; opacity: 0.9; }
  .home-banner-icon { font-size: 42px; opacity: 0.9; line-height: 1; flex-shrink: 0; }

  /* Same full-bleed hero treatment as .home-banner, reused as the header for
     every category page (the appointment list and the 15 "coming soon"
     placeholders) — the category's own icon sits on the banner instead of
     a plain text heading. */
  .category-banner {
    position: relative;
    display: flex;
    align-items: flex-end;
    min-height: 220px;
    margin: -16px -12.8px 16px -12.8px;
    border-radius: 0 0 22px 22px;
    overflow: hidden;
    background: linear-gradient(135deg, #4f46e5, #a855f7);
    color: var(--on-accent);
    box-shadow: 0 10px 24px rgba(168, 85, 247, 0.28);
  }
  .category-banner-bg {
    position: absolute; inset: 0; background-size: cover; background-position: center;
    display: none;
  }
  .category-banner.has-image .category-banner-bg { display: block; }
  .category-banner-scrim {
    position: absolute; inset: 0;
    background: linear-gradient(180deg, rgba(0,0,0,0) 25%, rgba(0,0,0,0.55) 100%);
    display: none;
  }
  .category-banner.has-image .category-banner-scrim { display: block; }
  .category-banner-content {
    position: relative; z-index: 1; width: 100%;
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 20px;
  }
  .category-banner-title { font-size: 18px; font-weight: 800; }
  .category-banner-subtitle { font-size: 13px; opacity: 0.9; margin-top: 4px; display: none; }
  .category-banner-subtitle.show { display: block; }

  .category-pager {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* Pulled 8px out past main's 16px padding on each side, so the row of
       icons sits closer to the edges of the screen than the text around it
       and has 16px more width to spend on the icons themselves. */
    margin: 18px -8px 4px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -ms-overflow-style: none;
    /* Mobile browsers disagree on what scrollLeft:0 means once the page
       is RTL (some treat it as the first page like LTR, others as the
       last) — forcing LTR scroll semantics here keeps "page 1" the one
       actually visible after a language switch on every browser, at the
       minor cost of the icon grid itself no longer mirroring in RTL. */
    direction: ltr;
  }
  .category-pager::-webkit-scrollbar { display: none; }
  .category-page {
    flex: 0 0 100%;
    scroll-snap-align: start;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(2, auto);
    gap: 12px 4px;
  }
  .category-item {
    background: none;
    border: none;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 2px;
    font-family: inherit;
  }
  .category-icon {
    position: relative;
    /* 66, down ~15% from the earlier 78 — still the point of the screen,
       just not quite so heavy against the rest of the home page. */
    width: 66px;
    height: 66px;
    border-radius: 19px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 21px;
    color: var(--accent);
    transition: transform .15s;
  }
  .category-item:active .category-icon { transform: scale(0.94); }
  .category-flag-icon { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
  /* The "coming soon" pill, its grey-out filter and the PRO corner are gone
     from the home tiles — every section is being worked on and the grid
     reads as one set. What opens is still decided by HOME_CATEGORY_LIVE_IDS
     in js/app/07-categories.js; a section that is not ready answers a tap
     with a toast. The Jobcenter list keeps its own Pro tag, which marks a
     form you have not paid for rather than one that is unfinished. */
  .category-label {
    font-size: 13px;
    font-weight: 700;
    /* --text, not --text-dim: these name the site's main destinations, so
       they are primary text. On the dark ground the dim token left them at
       5.7:1 against everything around them at 14:1, which read as a whole
       row of greyed-out labels. */
    color: var(--text);
    text-align: center;
    line-height: 1.3;
  }
  .category-dots {
    display: flex;
    justify-content: center;
    gap: 6px;
    margin-bottom: 14px;
  }
  .category-dots .dot {
    width: 6px;
    height: 6px;
    border-radius: 3px;
    /* Not --border: that is a colour meant to sit on a card, and against the
       page's own lavender ground it came out at 1.05:1 — a mark you cannot
       see is not a page indicator.
       The accent muted with the faintest ink instead: 2.7:1, quiet but
       present, and still the page's hue rather than a grey dropped into a
       purple screen. Both halves are theme tokens, so it re-mixes itself in
       dark mode. The active dot stays the full accent and four times as
       wide, so which page you are on reads by shape as well as colour. */
    background: color-mix(in oklab, var(--accent) 55%, var(--i-4));
    transition: background .2s, width .2s;
  }
  .category-dots .dot.active {
    width: 16px;
    background: var(--accent);
  }

  /* Small rotating strip below the category grid — same two-layer
     crossfade trick as .home-banner-bg, just a slimmer, plainer box (no
     scrim/text overlay, images only). */
  .home-secondary-banner {
    position: relative;
    height: 90px;
    border-radius: 16px;
    overflow: hidden;
    margin: 4px 0 20px;
    box-shadow: var(--shadow);
  }
  .home-secondary-banner-layer {
    position: absolute; inset: 0;
    background-size: cover; background-position: center;
    opacity: 0;
    transition: opacity 1s ease;
  }
  .home-secondary-banner-layer.active { opacity: 1; }

  .news-card {
    background: var(--card); border: 1px solid var(--border); border-radius: 18px;
    overflow: hidden; margin-bottom: 12px; box-shadow: var(--shadow);
  }
  .news-image { width: 100%; height: 220px; object-fit: cover; display: block; }
  .news-card-body { padding: 12px 14px; }
  .news-card-title { font-weight: 700; font-size: 14px; }
  .news-card-text { font-size: 13px; color: var(--text-dim); margin-top: 4px; line-height: 1.5; }
  .news-card-footer { display: flex; align-items: center; justify-content: space-between; margin-top: 10px; }
  .news-card-time { font-size: 11px; color: var(--text-dim); }
  .news-like-btn {
    background: none; border: none; cursor: pointer; font-size: 13px; color: var(--text-dim);
    display: flex; align-items: center; gap: 4px;
  }
  .news-like-btn.liked { color: #ef4444; }

  .embassy-list {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    margin-top: 8px;
  }
  /* Jobcenter forms only, not the other .embassy-list grids (country
     pickers etc.) — an id selector so it wins over both the 4-column
     default above and the desktop auto-fill override further down,
     regardless of viewport width. A vertical list instead of an icon grid,
     since each row now carries a form-code badge plus an explanation line
     ("HA — درخواست اصلی کمک") rather than just a bare abbreviation, and
     that much text needs the full row width to stay readable. */
  #jobcenter-category-list { display: flex; flex-direction: column; gap: 8px; }
  #jobcenter-category-list .embassy-card {
    flex-direction: row; align-items: center; justify-content: flex-start;
    min-height: unset; padding: 10px 12px; text-align: start; gap: 12px;
  }
  #jobcenter-category-list .embassy-card .flag-photo,
  #jobcenter-category-list .embassy-card .emblem-photo {
    width: 60px; flex-shrink: 0; aspect-ratio: 1 / 1;
    /* Overrides the base .flag-photo/.emblem-photo rules (bordered frame,
       10px padding, object-fit:contain) — every form here shares the same
       logo image, so it should fill this box edge-to-edge like a real app
       icon instead of floating small inside a padded card. */
    object-fit: cover; padding: 0; background: none; border: none; border-radius: 12px;
  }
  #jobcenter-category-list .embassy-card .jc-row-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  #jobcenter-category-list .embassy-card .name {
    font-size: 13px; -webkit-line-clamp: 1;
  }
  #jobcenter-category-list .embassy-card .jc-row-subtitle {
    font-size: 11.5px; color: var(--text-dim); line-height: 1.3;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .jc-free-badge {
    display: inline-block; align-self: flex-start; margin-top: 2px;
    font-size: 10px; font-weight: 700; color: var(--fg-good); background: var(--t-good-2);
    padding: 2px 8px; border-radius: 999px;
  }
  .jc-pro-badge {
    display: inline-block; align-self: flex-start; margin-top: 2px;
    font-size: 10px; font-weight: 700; color: #7c3aed; background: #ede9fe;
    padding: 2px 8px; border-radius: 999px;
  }
  .jc-access-banner {
    display: flex; align-items: center; gap: 6px; cursor: pointer; border: none;
    background: var(--t-good-2); color: #166534; border-radius: 999px;
    padding: 5px 10px; font-size: 11.5px; font-weight: 700;
    max-width: 100%; transition: opacity .15s;
  }
  .jc-access-banner:hover { opacity: 0.85; }
  .jc-access-banner span:last-child { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .jc-access-banner span:first-child {
    width: 16px; height: 16px; border-radius: 50%; background: #16a34a; color: var(--on-accent);
    display: flex; align-items: center; justify-content: center; font-size: 10px; flex-shrink: 0;
  }
  /* Not yet subscribed — a CTA rather than a status readout, so it needs a
     visibly different (not "success green") color to read as actionable. */
  .jc-access-banner.locked { background: #ede9fe; color: #7c3aed; }
  .jc-access-banner.locked span:first-child { background: #7c3aed; }
  .embassy-card {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 12px 6px;
    min-height: 84px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    text-align: center;
    cursor: pointer;
    box-shadow: var(--shadow);
    transition: border-color .15s, transform .15s;
  }
  .embassy-card:active { transform: scale(0.95); }
  .embassy-card:hover { border-color: var(--accent); }
  .embassy-card .code-badge {
    width: 30px; height: 20px; border-radius: 6px;
    background: var(--card-2); color: var(--accent);
    font-size: 10px; font-weight: 800; letter-spacing: 0.5px;
    display: flex; align-items: center; justify-content: center;
  }
  .embassy-card .name {
    font-size: 11px; font-weight: 700; line-height: 1.3;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .embassy-card .price { font-size: 10px; font-weight: 700; color: var(--accent-2); }

  /* Country cards (residence + target-country grids): a contained flag
     photo up top — same "icon box, label below" shape as the home screen's
     .category-icon/.category-label pair — with the name (and price, for
     target-country cards) as plain text underneath, not overlaid on the
     image. */
  .embassy-card.country-card {
    padding: 8px 6px 10px;
    justify-content: flex-start;
    gap: 8px;
  }
  .embassy-card.country-card .flag-photo {
    width: 100%;
    aspect-ratio: 3 / 2;
    border-radius: 10px;
    object-fit: cover;
    display: block;
    background: var(--card-2);
    border: 1px solid var(--border);
  }
  /* Emblems (embassy-branch city cards) aren't flag-shaped — contain
     instead of cover, with padding, so the coat of arms isn't cropped. */
  .embassy-card.country-card .emblem-photo {
    aspect-ratio: 1 / 1;
    object-fit: contain;
    padding: 10px;
  }

  /* Phrase topic cards: two per row instead of the four-up grid the other
     .embassy-list pickers use — each one carries a photo and a full topic
     name ("Probleme lösen und um Hilfe bitten"), neither of which fits in
     a quarter-width tile. An id selector so it beats both the 4-column
     default and the desktop auto-fill override, the same way
     #jobcenter-category-list does. */
  #german-phrases-picker-list { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  #german-phrases-picker-list .name { font-size: 12px; }

  /* Topic cards (German-language sub-topics): same card shape as the
     country/emblem cards, but with an iconify glyph in a tinted box
     instead of a photo — these are abstract topics (Grammar, Listening,
     ...) with no natural photo to show. */
  .embassy-card.country-card .topic-icon-box {
    width: 100%;
    aspect-ratio: 1 / 1;
    border-radius: 10px;
    background: var(--card-2);
    border: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 28px;
    color: var(--accent);
  }

  /* German-language level filter (A1–C1): picking a level doesn't change
     which of the 8 topic cards show below it — it just scopes the content
     each one leads to, so all 8 stay visible regardless of the pick. */
  .level-filter-row {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    margin: 0 0 4px;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .level-filter-row::-webkit-scrollbar { display: none; }
  .level-filter-chip {
    flex: none;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 8px 18px;
    font-size: 13px;
    font-weight: 700;
    color: var(--text-dim);
    cursor: pointer;
    box-shadow: var(--shadow);
    transition: background .15s, color .15s, border-color .15s;
  }
  .level-filter-chip.active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
  }
  .order-filter-icon-btn {
    position: relative;
    flex-shrink: 0;
    background: var(--card);
    border: 1px solid var(--border);
    color: var(--text-dim);
    width: 32px;
    height: 32px;
    border-radius: 50%;
    cursor: pointer;
    box-shadow: var(--shadow);
    font-size: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: color .15s ease, border-color .15s ease;
  }
  .order-filter-icon-btn:hover { color: var(--text); }
  .order-filter-active-dot {
    position: absolute;
    top: -2px;
    inset-inline-end: -2px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--accent);
    border: 2px solid var(--bg);
    display: none;
  }
  #order-filter-modal .form-card .level-filter-row {
    flex-wrap: wrap;
    overflow-x: visible;
    justify-content: center;
  }

  /* Embassy-branch city cards: one big card per row instead of a grid, so
     each one has room for an address line and a request-count strip below
     the name (see renderCityList()). */
  .city-list { display: flex; flex-direction: column; gap: 14px; margin-top: 8px; }
  .city-card {
    position: relative;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 18px;
    padding: 16px;
    box-shadow: var(--shadow);
    cursor: pointer;
    transition: border-color .15s, transform .15s, opacity .15s;
  }
  .city-card:active { transform: scale(0.98); }
  .city-card:hover { border-color: var(--accent); }
  .city-card.inactive { cursor: not-allowed; opacity: .65; }
  .city-card.inactive:hover { border-color: var(--border); }
  .city-status-badge {
    position: absolute; top: 14px; inset-inline-end: 16px;
    display: flex; align-items: center; gap: 5px;
    font-size: 10.5px; font-weight: 700; padding: 3px 10px; border-radius: 20px;
  }
  .city-status-badge .dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
  .city-status-badge.active { background: rgba(22, 163, 74, 0.12); color: var(--fg-good); }
  .city-status-badge.active .dot { background: #16a34a; }
  .city-status-badge.inactive { background: rgba(220, 38, 38, 0.12); color: var(--fg-bad); }
  .city-status-badge.inactive .dot { background: #dc2626; }
  .city-card-main { display: flex; align-items: center; gap: 14px; padding-inline-end: 80px; }
  .city-card-emblem {
    width: 56px; height: 56px; object-fit: contain; border-radius: 12px;
    background: var(--card-2); border: 1px solid var(--border); padding: 8px; flex-shrink: 0;
  }
  .city-card-info { min-width: 0; flex: 1; }
  .city-card-name { font-size: 16px; font-weight: 800; }
  .city-card-address { font-size: 12.5px; color: var(--text-dim); margin-top: 4px; line-height: 1.5; }
  .city-card-stats {
    display: flex; gap: 14px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);
  }
  .city-stat { flex: 1; display: flex; align-items: baseline; justify-content: center; gap: 4px; }
  .city-stat iconify-icon { align-self: center; font-size: 13px; color: var(--text-dim); }
  .city-stat b { font-size: 13px; color: var(--accent); font-weight: 800; }
  .city-stat span { font-size: 10.5px; color: var(--text-dim); }
  .city-stat.completed iconify-icon { color: var(--fg-good); }
  .city-stat.completed b { color: var(--fg-good); }

  /* Wizard breadcrumb chips (residence / target-country) shown above the
     next step's grid once that step is picked — tap one to jump back. */
  .selected-chip-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
  .selected-chip {
    display: flex; align-items: center; gap: 6px;
    background: var(--card-2); border: 1px solid var(--border); border-radius: 999px;
    padding: 6px 12px; font-size: 12px; font-weight: 700; color: var(--text);
    cursor: pointer;
  }
  .selected-chip .code-badge {
    width: 24px; height: 16px; border-radius: 5px;
    background: var(--card); color: var(--accent);
    font-size: 9px; font-weight: 800;
    display: flex; align-items: center; justify-content: center;
  }
  .selected-chip iconify-icon { font-size: 14px; color: var(--text-dim); }

  /* ── Top-up wizard ─────────────────────────────────────────────────── */
  /* Standard "country dropdown + phone field" shape — the select-style
     trigger shows the current pick (flag/name/dial code) and toggles a
     dropdown panel of every configured country below it, each row also
     showing its dial code, same as any phone-number country picker. */
  .topup-country-select {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    background: var(--card-2); border: 1px solid var(--border); border-radius: 10px;
    padding: 11px 12px; cursor: pointer;
  }
  .topup-country-select iconify-icon { color: var(--text-dim); font-size: 20px; flex-shrink: 0; }
  .topup-country-select-value { display: flex; align-items: center; gap: 8px; min-width: 0; }
  .topup-country-select-flag { width: 24px; height: 18px; object-fit: cover; border-radius: 3px; border: 1px solid var(--border); flex-shrink: 0; }
  .topup-country-select-dial { color: var(--text-dim); font-size: 13px; direction: ltr; }
  .topup-country-select-name.placeholder { color: var(--text-dim); font-weight: 500; }
  .topup-country-dropdown {
    position: relative;
    background: var(--card); border: 1px solid var(--border); border-radius: 10px;
    margin-top: 6px; box-shadow: var(--shadow); overflow: hidden;
  }
  .topup-country-search {
    margin: 0; border: none; border-bottom: 1px solid var(--border); border-radius: 0;
  }
  .topup-country-dropdown-list { max-height: 240px; overflow-y: auto; }
  .topup-country-dropdown-row {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 12px; cursor: pointer; font-size: 14px;
  }
  .topup-country-dropdown-row:hover { background: var(--card-2); }
  .topup-country-dropdown-row img { width: 24px; height: 18px; object-fit: cover; border-radius: 3px; border: 1px solid var(--border); flex-shrink: 0; }
  .topup-country-dropdown-row .dial { margin-inline-start: auto; color: var(--text-dim); font-size: 12.5px; direction: ltr; }
  .topup-custom-amount-toggle {
    display: block; background: none; border: none; color: var(--accent);
    font-size: 13px; font-weight: 700; padding: 14px 0 0; cursor: pointer; text-align: start;
  }
  .topup-price-readout { margin-top: 14px; }
  .topup-package-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
    margin-top: 8px;
  }
  .topup-package-card {
    background: var(--card);
    border: 2px solid var(--border);
    border-radius: 16px;
    padding: 14px 10px;
    text-align: center;
    cursor: pointer;
    box-shadow: var(--shadow);
    transition: border-color .15s, transform .15s;
  }
  .topup-package-card:active { transform: scale(0.97); }
  .topup-package-card.selected { border-color: var(--accent); }
  .topup-package-card .eur { font-size: 18px; font-weight: 800; color: var(--text); }
  .topup-package-card .local { font-size: 12.5px; color: var(--text-dim); margin-top: 4px; }
  .topup-custom-amount { margin-top: 10px; }
  /* A "detected operator" chip — logo (or a colored initial badge when the
     admin hasn't set a logo URL for it) + name, in its own bordered box
     with real spacing above so it never sits flush against the phone
     input or the box's own edge. */
  .topup-operator-row {
    display: flex; align-items: center; gap: 10px;
    margin-top: 14px; padding: 10px 12px;
    background: var(--card-2); border: 1px solid var(--border); border-radius: 10px;
    font-size: 14px;
  }
  .topup-operator-row .logo { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex-shrink: 0; border: 1px solid var(--border); }
  .topup-operator-row .logo-fallback {
    width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    background: var(--accent); color: var(--on-accent); font-weight: 800; font-size: 13px;
  }
  .topup-operator-row .name { font-weight: 700; color: var(--text); }
  .topup-operator-select { margin-top: 14px; }
  .topup-summary-row {
    display: flex; justify-content: space-between; align-items: center;
    padding: 10px 0; border-bottom: 1px solid var(--border); font-size: 14px;
  }
  .topup-summary-row:last-of-type { border-bottom: none; }
  .topup-summary-row > span:first-child { color: var(--text-dim); }
  .topup-summary-row > span:last-child { font-weight: 700; }

  .topup-history { margin-top: 18px; }
  .topup-history-title { font-size: 14px; font-weight: 800; color: var(--text); margin-bottom: 10px; }
  .topup-history-list { display: flex; flex-direction: column; gap: 10px; }
  .topup-history-card {
    display: flex; align-items: flex-start; gap: 12px;
    background: var(--card); border: 1px solid var(--border); border-radius: 14px;
    padding: 12px; box-shadow: var(--shadow);
  }
  .topup-history-flag { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; flex-shrink: 0; border: 1px solid var(--border); margin-top: 2px; }
  .topup-history-info { min-width: 0; flex: 1; }
  .topup-history-main { font-size: 13.5px; font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .topup-history-sub { font-size: 12px; color: var(--text-dim); margin-top: 2px; }
  .topup-history-code { font-size: 11.5px; margin-top: 4px; display: flex; align-items: center; gap: 6px; }
  .topup-history-copy {
    border: none; background: transparent; color: var(--text-dim); cursor: pointer;
    display: flex; align-items: center; padding: 2px; border-radius: 6px; font-size: 14px;
    transition: color .15s, background-color .15s;
  }
  .topup-history-copy:hover { color: var(--accent); background: var(--card-2); }
  .topup-history-amounts { display: flex; gap: 16px; margin-top: 8px; }
  .topup-history-amounts .amt-item { font-size: 11.5px; color: var(--text-dim); display: flex; flex-direction: column; gap: 2px; }
  .topup-history-amounts .amt-item b { font-size: 10.5px; font-weight: 600; color: var(--text-dim); }
  .topup-history-amounts .amt-item .amt-val { font-size: 13px; font-weight: 700; color: var(--text); }
  .topup-history-meta { font-size: 11.5px; color: var(--text-dim); margin-top: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .topup-history-side { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; flex-shrink: 0; margin-top: 2px; }
  .topup-history-status {
    font-size: 10.5px; font-weight: 700; padding: 3px 9px; border-radius: 20px; white-space: nowrap;
  }
  .topup-history-status.completed { background: rgba(22, 163, 74, 0.12); color: var(--fg-good); }
  .topup-history-status.pending { background: rgba(217, 119, 6, 0.12); color: var(--fg-warn); }
  .topup-history-status.awaiting { background: rgba(220, 38, 38, 0.12); color: var(--fg-bad); }
  .topup-history-status.rejected { background: rgba(220, 38, 38, 0.12); color: var(--fg-bad); }
  .topup-history-again {
    flex-shrink: 0; border: 1px solid var(--accent); color: var(--accent); background: transparent;
    border-radius: 10px; padding: 7px 14px; font-size: 13px; font-weight: 700; cursor: pointer;
    transition: background-color .15s, color .15s;
  }
  .topup-history-again:hover { background: var(--accent); color: var(--on-accent); }
  .topup-history-again:active { transform: scale(0.96); }
  .topup-history-again.pay { background: var(--accent); color: var(--on-accent); }
  .topup-history-again:disabled { opacity: .6; cursor: default; }

  .form-card {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 22px;
    padding: 20px;
    box-shadow: var(--shadow);
  }
  .form-card h2 { margin: 0 0 18px; font-size: 18px; }
  .price-tag {
    display: inline-block;
    background: rgba(239, 68, 68, 0.12);
    color: var(--fg-good);
    font-weight: 700;
    padding: 6px 14px;
    border-radius: 999px;
    font-size: 14px;
    white-space: nowrap;
  }
  .submit-row {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 22px;
  }
  .submit-row .submit-btn {
    flex: 1;
    width: auto;
    margin-top: 0;
  }
  label {
    display: block;
    font-size: 13px;
    color: var(--text-dim);
    margin: 14px 0 6px;
  }
  input, textarea, select {
    width: 100%;
    background: var(--card-2);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 11px 12px;
    color: var(--text);
    font-size: 16px;
    font-family: inherit;
  }
  input:focus, textarea:focus, select:focus {
    border-color: var(--accent);
  }
  /* Suppress only the plain mouse-click outline here — a keyboard-driven
     focus still falls through to the global :focus-visible ring above,
     instead of this higher-specificity rule silently cancelling it too. */
  input:focus:not(:focus-visible), textarea:focus:not(:focus-visible), select:focus:not(:focus-visible) {
    outline: none;
  }
  textarea { resize: vertical; min-height: 70px; }

  .phone-input-wrap {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--card-2);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0 12px;
  }
  .phone-input-wrap:focus-within { border-color: var(--accent); }
  .phone-input-wrap .phone-prefix { font-size: 16px; font-weight: 700; color: var(--text-dim); flex-shrink: 0; }
  .phone-input-wrap input {
    width: auto;
    flex: 1;
    min-width: 0;
    background: none;
    border: none;
    padding: 11px 0;
  }
  .phone-input-wrap input:focus:not(:focus-visible) { outline: none; }
  .phone-input-wrap input::placeholder { color: var(--text-dim); opacity: 0.7; }

  .field-error {
    display: none;
    color: var(--danger, #ef4444);
    font-size: 12px;
    margin: 6px 0 14px;
  }
  .field-error.show { display: block; }
  .field-invalid { border-color: var(--danger, #ef4444) !important; }
  .field-valid { border-color: #16a34a !important; }

  .field-hint {
    font-size: 12px;
    color: var(--text-dim);
    margin: 6px 0 14px;
    line-height: 1.5;
  }
  .field-hint-warn {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    font-size: 12px;
    color: #f59e0b;
    margin: 6px 0 14px;
    line-height: 1.5;
  }
  .field-hint-warn iconify-icon { flex-shrink: 0; font-size: 15px; margin-top: 1px; }

  .review-info-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    padding: 10px 0;
    border-bottom: 1px solid var(--border);
    font-size: 13.5px;
  }
  .review-info-row:last-child { border-bottom: none; }
  .review-info-row b { color: var(--text-dim); font-weight: 600; flex-shrink: 0; }
  .review-info-row span { font-weight: 700; text-align: end; word-break: break-word; }
  .review-info-price { color: var(--danger, #ef4444); }

  .submit-btn {
    width: 100%;
    margin-top: 22px;
    background: linear-gradient(135deg, #4f46e5, #a855f7);
    color: white;
    border: none;
    border-radius: 999px;
    padding: 14px;
    font-size: 16px;
    font-weight: 700;
    cursor: pointer;
    box-shadow: 0 6px 16px rgba(168, 85, 247, 0.25);
    transition: opacity .15s;
  }
  .submit-btn:hover { opacity: 0.9; }
  .submit-btn:disabled { opacity: 0.6; cursor: default; }

  .error-msg {
    color: var(--danger);
    font-size: 13px;
    margin-top: 10px;
    display: none;
  }

  /* Jobcenter form viewer — fullscreen PDF stage with just two action
     buttons up top (Save / Guide); title/subtitle/price already showed on
     the grid tile the user tapped, so they aren't repeated in here. The
     step breaks out of <main>'s normal padded flow via position:fixed so
     the PDF can run truly edge-to-edge, same trick .jc-guide-overlay
     already uses. */
  #step-jobcenter-form.active {
    display: flex;
    flex-direction: column;
    position: fixed;
    inset: 0;
    z-index: 40;
    background: var(--bg);
    padding: 0;
    max-width: none;
    margin: 0;
    touch-action: pan-x pan-y;
  }
  body.jc-fullscreen > header {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 50;
    background: none;
    /* The >=1024px navbar rule (further down, see "DESKTOP LAYOUT") sets
       its own backdrop-filter:blur(16px) — a separate property from
       background, so clearing just the background above left the blur
       itself still active, smearing the .jc-topbar buttons sitting right
       behind this (visually transparent, but still blurring) header. */
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    padding: 10px 20px;
    pointer-events: none;
  }
  body.jc-fullscreen header h1,
  body.jc-fullscreen .header-lang-btn,
  body.jc-fullscreen .header-bell-btn,
  body.jc-fullscreen .desktop-nav-links { display: none; }
  body.jc-fullscreen .header-back-btn { pointer-events: auto; }
  .jc-topbar {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: max(10px, env(safe-area-inset-top)) 14px 8px;
    padding-left: 64px;
    flex-shrink: 0;
    /* Unconstrained, the three flex:1 columns below stretch across the
       full viewport — fine on a phone, but on a wide desktop window that
       balloons each button to 500+px, and the pale secondary (Guide) style
       all but disappears against the page background at that width. Capped
       to the same reading width the PDF page itself uses — but width:100%
       alongside the cap is what actually matters: without it this is a
       shrink-to-fit block, so the flex:1 columns collapse to their own
       tiny content width (three ~60px pills hugging the left edge with a
       huge dead gap beside them) instead of the row actually filling out
       to a normal toolbar width. Left-anchored, not centered —
       .header-back-btn sits at a fixed inset-inline-start
       inside the (full-width, in fullscreen mode) header above this row,
       and this box's own padding-left is what clears it; centering the box
       would pull it out from under that padding and leave the back button
       stranded with a gap of empty space next to it. */
    width: 100%;
    max-width: 820px;
    /* Physical, not logical — .header-back-btn sits at a physical `left`
       regardless of page direction (see its own rule), but this document
       is RTL by default (Persian) and a direction-agnostic max-width block
       hugs the RTL *start* edge, i.e. the right, leaving this row stranded
       on the opposite side from the button it needs to clear. margin-right
       collapses onto the right instead, pinning this box to the left. */
    margin-right: auto;
    /* This row sits right under the header-back-btn, which now always
       floats at a fixed physical left position (see header{} above) —
       forcing the same direction:ltr here keeps Save/Guide/Text
       Guide/Language in that same left-to-right order and clear of the
       back button regardless of interface language, instead of the
       language button drifting under it when the page is RTL. */
    direction: ltr;
  }
  .jc-topbar-col { flex: 1; min-width: 0; }
  .jc-top-btn {
    width: 100%;
    border: 0;
    border-radius: 12px;
    padding: 12px 8px;
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
    box-shadow: var(--shadow);
    color: var(--on-accent);
    background: linear-gradient(135deg, #4f46e5, #a855f7);
  }
  .jc-top-btn.jc-guide-top-btn {
    background: var(--card-2);
    color: var(--text);
    border: 1px solid var(--border);
  }
  .jc-top-btn:disabled { opacity: 0.6; cursor: default; }
  .jc-topbar-lang-btn {
    flex: none;
    align-self: flex-start;
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--card-2);
    color: var(--text);
    font-size: 18px;
    cursor: pointer;
    box-shadow: var(--shadow);
    transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease, color .15s ease;
  }
  .jc-topbar-lang-btn:hover { color: var(--accent); border-color: var(--accent); transform: translateY(-1px); }
  .jc-topbar-lang-btn:active { transform: scale(.93); }
  .jc-topbar-hint {
    margin-top: 6px;
    font-size: 11px;
    color: var(--text-dim);
    text-align: center;
  }
  .jc-pdf-stage {
    position: relative;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-x pan-y;
    background: var(--card-2);
    padding-bottom: max(12px, env(safe-area-inset-bottom));
  }
  .jc-pdf-pages { transform-origin: top center; }
  .jc-pdf-page { position: relative; margin: 0 auto 10px; background: var(--s-0); }
  .jc-pdf-page canvas { display: block; width: 100%; height: 100%; }
  .jc-pdf-loading, .jc-pdf-error {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 40px 16px;
    color: var(--text-dim);
    font-size: 13px;
    text-align: center;
  }
  /* !important is required since pdf.js sets a per-field font-size/height
     inline. Below 16px, iOS Safari force-zooms the whole page on input
     focus (its built-in "don't let the user lose a tiny input" behavior). */
  .annotationLayer input,
  .annotationLayer textarea,
  .annotationLayer select {
    font-size: 8px !important;
    touch-action: manipulation;
  }
  /* Chrome/Safari give every <input> a built-in min-height (and default
     padding) that silently overrides any explicit height — pdf.js's own
     height:100% (matching the PDF field's real size) never actually
     applied because of it, which is why fields rendered oversized. */
  .annotationLayer input:not([type="checkbox"]):not([type="radio"]),
  .annotationLayer textarea,
  .annotationLayer select {
    height: 100% !important;
    min-height: 0 !important;
    padding: 0 !important;
    border-radius: 0 !important;
    -webkit-appearance: none !important;
    appearance: none !important;
  }
  /* iOS Safari's own rounded-corner field chrome and pale blue fill are
     both lighter/rounder than the site wants; dial the tint down without
     touching the :focus override below, which replaces it entirely. */
  .annotationLayer {
    --annotation-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,<svg width='1px' height='1px' xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' style='fill:rgba(0, 54, 255, 0.06);'/></svg>");
  }
  /* Same min-size floor hits checkbox/radio too (their real hit-area is
     often tiny — a few px, matching the PDF's own tap target). */
  .annotationLayer input[type="checkbox"],
  .annotationLayer input[type="radio"] {
    width: 100% !important;
    height: 100% !important;
    min-width: 0 !important;
    min-height: 0 !important;
    padding: 0 !important;
  }
  .annotationLayer input[type="radio"] {
    border-radius: 50%;
  }
  /* This PDF's AcroForm has no "checked" appearance graphic of its own, so
     without an explicit style a checked box/radio shows no visible mark. */
  .annotationLayer input[type="checkbox"]:checked,
  .annotationLayer input[type="radio"]:checked {
    background-color: #000 !important;
    background-image: none !important;
  }
  .annotationLayer :is(input, textarea, select):focus {
    background: rgba(255, 152, 0, 0.15) !important;
    outline: 2px solid #ff9800 !important;
    outline-offset: 1px;
  }
  /* Free-placed text boxes — for notes, or for PDFs with no fillable
     AcroForm fields at all. Independent of the real AcroForm layer above;
     saveJcPdf() bakes these into the PDF separately, via pdf-lib. */
  .jc-text-overlay { position: absolute; inset: 0; pointer-events: none; }
  .jc-text-field-wrap {
    position: absolute;
    min-width: 100px;
    min-height: 32px;
    resize: both;
    overflow: hidden;
    pointer-events: auto;
    z-index: 6;
  }
  .jc-text-field {
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    border: 1px dashed #1769e0;
    background: var(--glass-strong);
    padding: 5px;
    font-family: inherit;
    font-size: 16px;
    resize: none;
    color: #111;
  }
  .jc-text-field:focus { outline: 2px solid #1769e0; }
  .jc-text-field-del {
    position: absolute;
    top: -8px;
    inset-inline-end: -8px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: #ef4444;
    color: var(--on-accent);
    border: 0;
    font-size: 12px;
    line-height: 1;
    padding: 0;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .jc-edit-toolbar {
    position: fixed;
    left: 50%;
    bottom: max(12px, env(safe-area-inset-bottom));
    transform: translateX(-50%);
    z-index: 41;
    display: none;
    align-items: center;
    gap: 8px;
    background: var(--card);
    border: 1px solid var(--border);
    box-shadow: var(--shadow);
    border-radius: 999px;
    padding: 8px 14px;
    font-size: 13px;
    color: var(--text);
    white-space: nowrap;
  }
  .jc-edit-toolbar.active { display: flex; }
  .jc-edit-toolbar { display: none !important; }
  /* Floating "exit zoom" button — shown only while the PDF is pinch-zoomed
     in, in case a pinch-out gesture ever fails to bring it back to 1x. */
  .jc-pdf-zoom-reset {
    display: none;
    position: fixed;
    right: 12px;
    bottom: max(12px, env(safe-area-inset-bottom));
    z-index: 42;
    width: 44px;
    height: 44px;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: var(--card);
    box-shadow: var(--shadow);
    color: var(--text);
    font-size: 20px;
  }
  .jc-pdf-zoom-reset.visible { display: flex; }
  .jc-edit-toolbar button {
    background: var(--card-2);
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: 999px;
    padding: 8px 14px;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
  }

  /* Guide-audio popup — opens directly as a small floating bar (no bigger
     modal stage first), draggable anywhere on screen; the backdrop is
     transparent/click-through so the PDF underneath stays fully usable. */
  .jc-guide-overlay {
    position: fixed;
    inset: 0;
    background: transparent;
    display: none;
    align-items: flex-end;
    justify-content: flex-end;
    z-index: 80;
    padding: 16px;
    pointer-events: none;
  }
  .jc-guide-overlay.active { display: flex; }
  .jc-guide-audio-player {
    position: fixed;
    right: 16px;
    bottom: 16px;
    display: flex;
    flex-direction: column;
    background: linear-gradient(180deg, #22222c, #131318);
    border-radius: 14px;
    box-shadow: 0 6px 20px rgba(0,0,0,0.35);
    pointer-events: auto;
    width: 300px;
    max-width: calc(100vw - 32px);
  }
  .jc-guide-audio-player.dragging { transition: none; opacity: 0.9; }
  /* Dedicated grab strip — dragging is only initiated from here, so it
     never conflicts with tapping the buttons or seeking the track. */
  .jc-guide-drag-handle {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 34px;
    cursor: grab;
    touch-action: none;
  }
  .jc-guide-audio-player.dragging .jc-guide-drag-handle { cursor: grabbing; }
  .jc-guide-drag-handle span {
    width: 28px;
    height: 3px;
    border-radius: 2px;
    background: rgba(255, 255, 255, 0.25);
  }
  .jc-guide-audio-controls {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 2px 10px 8px;
  }
  .jc-guide-audio-empty {
    display: none;
    padding: 4px 16px 16px;
    font-size: 12.5px;
    color: #9ca3af;
    text-align: center;
  }
  .jc-guide-audio-player.empty .jc-guide-audio-controls { display: none; }
  .jc-guide-audio-player.empty .jc-guide-audio-empty { display: block; }
  .jc-guide-audio-btn {
    flex: none;
    width: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    color: var(--on-accent);
    font-size: 15px;
    cursor: pointer;
    padding: 0;
  }
  .jc-guide-audio-skip {
    font-size: 9px;
    font-weight: 700;
    width: 24px;
  }
  .jc-guide-audio-time {
    flex: none;
    color: #cfcfe0;
    font-size: 10px;
    font-variant-numeric: tabular-nums;
  }
  .jc-guide-audio-track {
    flex: 1;
    min-width: 0;
    height: 6px;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.15);
    position: relative;
    cursor: pointer;
  }
  .jc-guide-audio-fill {
    /* top/bottom/left, not inset:0 — with a right edge also pinned,
       direction:rtl would keep "right" fixed and let "left" float
       instead, anchoring the growing fill from the wrong side. Belt and
       braces alongside the player's own dir="ltr" above. */
    position: absolute;
    top: 0; bottom: 0; left: 0;
    width: 0%;
    border-radius: 3px;
    background: linear-gradient(90deg, #6366f1, #a855f7);
  }
  .jc-guide-audio-close {
    position: absolute;
    right: 4px;
    top: 50%;
    transform: translateY(-50%);
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    color: #cfcfe0;
    font-size: 24px;
    cursor: pointer;
    padding: 0;
  }

  .jc-text-guide-overlay {
    position: fixed;
    inset: 0;
    background: rgba(15, 15, 30, 0.5);
    display: none;
    align-items: flex-end;
    justify-content: center;
    z-index: 85;
  }
  .jc-text-guide-overlay.active { display: flex; }
  .jc-text-guide-sheet {
    width: 100%;
    max-width: 480px;
    max-height: 75vh;
    background: var(--card);
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.25);
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  .jc-text-guide-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 18px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
  }
  .jc-text-guide-title { font-size: 15px; font-weight: 800; color: var(--text); }
  .jc-text-guide-close {
    border: none;
    background: var(--card-2);
    color: var(--text-dim);
    width: 28px;
    height: 28px;
    border-radius: 50%;
    cursor: pointer;
    font-size: 14px;
    flex-shrink: 0;
  }
  .jc-text-guide-body {
    padding: 16px 18px;
    padding-bottom: max(16px, env(safe-area-inset-bottom));
    overflow-y: auto;
    font-size: 14px;
    line-height: 1.9;
    color: var(--text);
  }
  .jc-text-guide-body.empty { color: var(--text-dim); text-align: center; padding: 40px 18px; }
  .jc-text-guide-body p { margin: 0 0 10px; }
  .jc-text-guide-body h3, .jc-text-guide-body h4, .jc-text-guide-body h5 {
    margin: 18px 0 8px;
    font-weight: 800;
    color: var(--accent);
  }
  .jc-text-guide-body h3:first-child, .jc-text-guide-body h4:first-child, .jc-text-guide-body h5:first-child { margin-top: 0; }
  .jc-text-guide-body h3 { font-size: 17px; }
  .jc-text-guide-body h4 { font-size: 15px; }
  .jc-text-guide-body h5 { font-size: 14px; }
  .jc-text-guide-body hr { border: none; border-top: 1px solid var(--border); margin: 14px 0; }
  .jc-text-guide-body ul { margin: 0 0 10px; padding-inline-start: 20px; }
  .jc-text-guide-body li { margin-bottom: 4px; }
  .jc-text-guide-body strong { color: var(--text); font-weight: 800; }

  /* What is left of the old sign-in dialog. The card, its purple banner,
     the pill inputs and the pill submit went with it — signing in is a page
     now and css/den-auth.css draws it. These few rules are the ones the page
     still shares with the rest of the site. */
  .auth-header { text-align: center; }
  .auth-body { padding: 22px 22px 24px; }
  .auth-switch-line {
    text-align: center;
    margin-top: 16px;
    font-size: 13px;
    color: #666;
  }
  /* Google's button is a same-origin <iframe> it draws itself — width can
     only be set in px on init (see the JS below), so this wrapper just
     centers whatever size that ends up being rather than trying to stretch
     it, and gives it breathing room from the divider above it. */
  .auth-divider {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 16px 0 12px;
    font-size: 12px;
    color: #999;
  }
  .auth-divider::before, .auth-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: rgba(0,0,0,0.1);
  }
  .google-signin-btn-wrap { display: flex; justify-content: center; }
  /* Grid, not flex — the global `input { width: 100% }` reset was letting
     the checkbox's box model claim the whole row's width in flex layout
     (fixed once for most browsers by pinning flex-basis, but iOS Safari
     kept squeezing the label text into a sliver regardless — a WebKit-
     specific flex/label interaction, reproduced on-device and confirmed
     it wasn't just a caching artifact). A two-column grid has no such
     history of engine-specific quirks: the checkbox's track is always
     exactly 18px and the text's track is always whatever's left, full
     stop, in every browser. */
  .consent-row {
    display: grid;
    grid-template-columns: 18px 1fr;
    align-items: start;
    gap: 12px;
    margin: 4px 0 14px;
    font-size: 12px;
    color: var(--text-dim);
    line-height: 1.5;
    cursor: pointer;
  }
  .consent-row input {
    width: 18px;
    height: 18px;
    margin: 2px 0 0;
    padding: 0;
    /* Without these, an unchecked native checkbox follows the OS/browser
       dark theme and paints itself solid black instead of an empty box —
       color-scheme:light pins it to the light checkbox chrome regardless
       of the visitor's system theme, and accent-color keeps the checked
       fill on-brand instead of the browser default blue. */
    color-scheme: light;
    accent-color: var(--accent);
  }
  .consent-row span { min-width: 0; }
  .consent-row a { color: var(--accent); font-weight: 600; text-decoration: underline; }
  .auth-switch-line button {
    background: none;
    border: none;
    color: #7c3aed;
    font-weight: 700;
    cursor: pointer;
    font-size: 13px;
    padding: 0;
  }

  .modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.6);
    display: none;
    align-items: flex-start;
    justify-content: center;
    padding: 16px;
    padding-top: 8vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    z-index: 50;
  }
  .modal-overlay.active { display: flex; }
  .modal-overlay .form-card {
    max-width: 400px;
    width: 100%;
    position: relative;
  }
  .modal-close {
    position: absolute;
    top: 10px;
    inset-inline-start: 14px;
    background: none;
    border: none;
    color: var(--text-dim);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
  }
  .modal-close:hover { color: var(--text); }
  .welcome-card { text-align: center; padding: 28px 22px 24px; }
  .welcome-emoji {
    font-size: 40px;
    width: 72px;
    height: 72px;
    margin: 0 auto 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: linear-gradient(135deg, rgba(79, 70, 229, .14), rgba(168, 85, 247, .14));
  }
  .welcome-card h2 { margin: 0 0 10px; }
  .welcome-body {
    margin: 0 0 22px;
    font-size: 14px;
    line-height: 1.7;
    color: var(--text-dim);
  }
  .welcome-card .submit-btn { margin-top: 0; }
  .welcome-cookie-note {
    margin: 14px 0 0;
    font-size: 11px;
    line-height: 1.6;
    color: var(--text-dim);
    opacity: .8;
  }

  /* position:fixed is deliberate, not a leftover: making this a normal flex
     sibling of <main> was tried and reverted — with no fixed anchor, iOS
     Safari's native "scroll the focused input into view above the keyboard"
     behavior took over on a real device and dragged the whole page (header
     included) up past the visible area instead of just the keyboard opening
     in place. Fixed positioning keeps this bar pinned to the viewport
     regardless of that, and JS below closes the gap this used to leave
     behind by measuring the bar's live position instead of guessing it. */
  .bottom-fixed {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 40;
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  .bottom-fixed-inner {
    position: relative;
    background: var(--glass);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    /* Rounded where it meets the page, square where it meets the bottom of
       the screen — a rounded bottom edge leaves two slivers of page showing
       under the corners instead of sitting flush. */
    border-radius: 28px 28px 0 0;
    box-shadow: 0 -10px 30px rgba(99, 102, 241, 0.10);
    overflow: hidden;
  }
  /* direction:ltr keeps Home/Reviews/Support/Profile in the same physical
     left-to-right order regardless of the page's own dir — same pattern
     already used on .compose-bar for the identical class of bug (a send
     button that flipped sides between RTL and LTR languages). */
  .bottom-nav {
    border-top: 1px solid var(--glass-edge);
    display: flex;
    direction: ltr;
  }
  .nav-center-spacer { flex: 1; }
  .nav-center-label {
    position: absolute;
    bottom: 9px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 10.5px;
    font-weight: 700;
    color: var(--accent);
    white-space: nowrap;
    z-index: 45;
    pointer-events: none;
  }
  .nav-center-btn {
    position: absolute;
    bottom: 26px;
    left: 50%;
    transform: translateX(-50%);
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: linear-gradient(135deg, #4f46e5, #a855f7);
    color: var(--on-accent);
    border: 4px solid var(--bg);
    box-shadow: 0 8px 20px rgba(168, 85, 247, 0.35);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    cursor: pointer;
    z-index: 45;
  }
  .lang-option {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    text-align: inherit;
    background: none;
    border: 1px solid transparent;
    color: var(--text);
    font-size: 13.5px;
    font-weight: 600;
    padding: 10px 14px;
    border-radius: 10px;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
  }
  .lang-option:hover { background: var(--card-2); }
  .lang-option.active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
  }
  .compose-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    direction: ltr;
    margin: 8px 12px 12px;
    padding: 0;
  }
  .compose-bar textarea {
    flex: 1;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 13px 16px;
    color: var(--text);
    font-size: 16px;
    font-family: inherit;
    resize: none;
    min-height: 47px;
    max-height: 68px;
    line-height: 1.2;
    box-shadow: var(--shadow);
  }
  .compose-bar textarea:focus { border-color: var(--accent); }
  .compose-bar textarea:focus:not(:focus-visible) { outline: none; }
  .compose-send-btn {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    /* The site's own indigo-to-purple, not the blue this started as: it is
       chrome, so it should read as part of the page rather than as a second
       brand sitting inside it. */
    background: linear-gradient(135deg, #6366f1, #a855f7);
    border: none;
    color: white;
    font-size: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    flex-shrink: 0;
  }
  .compose-bar .compose-send-btn {
    width: 44px;
    height: 44px;
    font-size: 16px;
  }
  .nav-btn {
    flex: 1;
    background: none;
    border: none;
    color: var(--text-dim);
    padding: 8px 4px;
    font-size: 11px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    cursor: pointer;
    transition: color .15s;
  }
  .nav-btn .nav-icon {
    font-size: 19px;
    line-height: 1;
    width: 40px;
    height: 26px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    transition: background .2s;
  }
  .nav-btn.active { color: var(--accent); font-weight: 700; }
  .nav-btn.active .nav-icon { background: rgba(168, 85, 247, 0.14); }
  /* #nav-profile-icon is the dock's own .tab__icon span (see dock.css) —
     for every other tab that's a small glyph, but once there's a real
     avatar photo it should read as a photo, not a tiny picture floating
     inside the dock's big round "bead". So it grows to fill most of the
     bead itself, with the tab's own accent colour (--acc, set per-tab in
     the markup) left showing through a couple of pixels as a ring. */
  /* Always laid out at the full 44px — only ever scaled, never resized —
     so the <img> bitmap is never re-decoded/repainted mid-transition. A
     width/height transition on the container was doing exactly that: at
     every intermediate size the browser had to re-rasterize the photo,
     which showed up as a one-frame flash right as the bead's own rise
     animation kicked in. transform is GPU-composited and has no such
     repaint step. The scale is appended after .tab__icon's own
     translate/translateY (dock.css) rather than replacing it, so the
     icon keeps centering itself and rising into the bead exactly as
     every other tab's glyph does. */
  #nav-profile-icon.has-avatar {
    width: 44px; height: 44px; border-radius: 50%; overflow: hidden;
    background: var(--acc); padding: 2.5px; box-sizing: border-box;
    transform: translate(-50%, -50%) translateY(calc(var(--t) * var(--rise) * -1)) scale(.636);
    transition: transform .18s ease;
  }
  #nav-profile-icon.has-avatar img {
    width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 50%;
  }
  /* Full scale only once this tab is actually the selected one — resting on
     the bar next to the other (smaller, 24px) tab glyphs, a full-size photo
     read as oversized; risen into the bead, that same size is what makes it
     read as a photo instead of a tiny picture. */
  .tab[aria-selected="true"] #nav-profile-icon.has-avatar {
    transform: translate(-50%, -50%) translateY(calc(var(--t) * var(--rise) * -1)) scale(1);
  }

  .review-card {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 20px;
    padding: 14px 16px;
    margin-bottom: 8px;
    display: flex;
    gap: 10px;
    align-items: flex-start;
    box-shadow: var(--shadow);
  }
  .review-card .review-avatar {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    background: var(--accent);
    color: white;
    font-weight: 700;
    font-size: 15px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }
  /* Blue for a man's name, pink for a woman's — see review_gender() in
     reviews_list.php. A name the list doesn't recognise keeps the accent
     above, so the wall never looks like it guessed. */
  .review-card .review-avatar.g-m { background: #3b82f6; }
  .review-card .review-avatar.g-f { background: #ec4899; }
  .review-card .review-body { flex: 1; min-width: 0; }
  /* Name and star rating share one header row (name on the start side,
     stars on the end side) instead of stacking — matches how the admin
     panel's own review cards already pair a name with its stars. */
  .review-card-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
  .review-card .review-name {
    font-weight: 700;
    font-size: 14px;
    background: none;
    border: none;
    color: var(--text);
    padding: 0;
    cursor: pointer;
    font-family: inherit;
  }
  .review-card .review-name:hover { color: var(--accent); text-decoration: underline; }
  .review-card .review-comment { font-size: 13px; color: var(--text); margin-top: 6px; line-height: 1.7; }
  .review-card .review-dates { font-size: 11px; color: var(--text-dim); margin-top: 8px; display: flex; gap: 12px; flex-wrap: wrap; }
  /* Same dot treatment as an order's own stage tracker (see .stage-item
     .dot) — completed gets the green checkmark of a finished stage
     (.dot.final), reviewing/processing get the plain colored dot of a
     stage that's merely reached (.dot.active), just recolored per status
     so the three read apart from each other at a glance. */
  .review-duration-badge { display: inline-flex; align-items: center; gap: 6px; font-weight: 700; }
  .review-duration-badge.status-completed { color: var(--accent-2); }
  .review-duration-badge.status-reviewing { color: var(--fg-warn); }
  .review-duration-badge.status-processing { color: #ea580c; }
  .review-duration-dot {
    width: 15px; height: 15px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  }
  .review-duration-badge.status-completed .review-duration-dot { background: var(--accent-2); }
  .review-duration-dot iconify-icon { font-size: 10px; color: var(--on-accent); }
  /* Reviewing/processing aren't finished yet, so there's nothing to put a
     checkmark badge around — a plain clock icon in the status's own color
     instead of a filled dot, so it reads as "still moving" rather than
     "done" at a glance. */
  .review-duration-badge.status-reviewing .review-duration-dot,
  .review-duration-badge.status-processing .review-duration-dot {
    width: auto; height: auto; background: none;
  }
  .review-duration-badge.status-reviewing .review-duration-dot iconify-icon,
  .review-duration-badge.status-processing .review-duration-dot iconify-icon {
    font-size: 14px; color: currentColor;
  }
  .review-card .review-stars { flex-shrink: 0; }
  .review-card .review-image { display: block; max-width: 160px; border-radius: 12px; margin-top: 8px; }
  .empty-msg { color: var(--text-dim); font-size: 13px; text-align: center; padding: 24px 0; }
  .coming-soon-box {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 60px 20px 40px;
    gap: 14px;
  }
  .coming-soon-icon {
    width: 74px;
    height: 74px;
    border-radius: 50%;
    background: linear-gradient(135deg, rgba(79, 70, 229, .14), rgba(168, 85, 247, .14));
    display: flex;
    align-items: center;
    justify-content: center;
    color: #8b5cf6;
    font-size: 34px;
  }
  .coming-soon-title {
    font-size: 18px;
    font-weight: 800;
    background: linear-gradient(135deg, #4f46e5, #a855f7);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
  .coming-soon-sub {
    font-size: 13px;
    color: var(--text-dim);
    max-width: 280px;
    line-height: 1.6;
  }

  /* ── Einbürgerungstest practice quiz ─────────────────────────────────── */
  .cq-progress-text, .cq-score-text { font-size: 13px; font-weight: 700; color: var(--text-dim); }
  .cq-progress-bar { height: 6px; border-radius: 999px; background: var(--card-2); overflow: hidden; margin-bottom: 14px; }
  .cq-progress-fill { height: 100%; background: linear-gradient(90deg, #4f46e5, #a855f7); transition: width .25s; }
  .cq-question-card {
    background: var(--card); border: 1px solid var(--border); border-radius: 18px;
    padding: 16px; box-shadow: var(--shadow);
  }
  .cq-question-image-wrap { margin-bottom: 12px; border-radius: 12px; overflow: hidden; background: var(--card-2); }
  .cq-question-image { display: block; width: 100%; max-height: 220px; object-fit: contain; background: var(--s-0); }
  .cq-question-text { font-size: 15px; font-weight: 700; line-height: 1.6; margin-bottom: 14px; }
  .cq-options-list { display: flex; flex-direction: column; gap: 10px; }
  .cq-option-btn {
    display: flex; align-items: center; gap: 10px; text-align: start;
    background: var(--card-2); border: 2px solid transparent; border-radius: 12px;
    padding: 12px 14px; font-size: 13.5px; font-family: inherit; color: var(--text);
    cursor: pointer; transition: border-color .15s, background .15s;
  }
  .cq-option-btn:active { transform: scale(.98); }
  .cq-option-btn.cq-correct { border-color: #16a34a; background: rgba(22,163,74,.12); }
  .cq-option-btn.cq-wrong { border-color: #dc2626; background: rgba(220,38,38,.1); }
  .cq-option-btn:disabled { cursor: default; }
  /* Image-option questions (coat-of-arms picks, etc.) — a 2x2 grid of
     images instead of the text-row list above. */
  .cq-options-list.cq-options-image { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  .cq-options-image .cq-option-btn { flex-direction: column; padding: 10px; background: var(--card-2); }
  .cq-options-image .cq-option-img { width: 100%; height: 90px; object-fit: contain; background: var(--s-0); border-radius: 8px; }
  .cq-results-card { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 50px 20px 30px; }
  .cq-results-icon { font-size: 56px; margin-bottom: 8px; }
  .cq-results-title { font-size: 20px; font-weight: 800; margin-bottom: 8px; }
  .cq-results-score { font-size: 14px; color: var(--text-dim); margin-bottom: 24px; }
  .cq-results-home-btn { background: var(--card-2); color: var(--text); margin-top: 10px; }
  /* "Practical Sentences" pronunciation drill — one sentence on screen at a
     time (see step-german-phrases' own comment). Laid out as a fixed column
     so nothing below the card moves as the learner works: the result box
     keeps its height while empty, and the mic stays under the thumb. */
  /* "Show more" under the review list — outlined rather than filled, so it
     reads as a way to keep reading and not as the page's main action. */
  /* Avatar-colour picker in the profile form. Three plain segments rather
     than a dropdown — the whole choice is visible at a glance, and the two
     coloured dots say what it actually changes. */
  .gender-field { margin: 2px 0 12px; }
  .gender-label { font-size: 12px; font-weight: 700; color: var(--text-dim); margin-bottom: 7px; }
  .gender-row { display: flex; gap: 8px; }
  .gender-opt {
    flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px;
    padding: 10px 6px; border-radius: 10px; border: 1px solid var(--border);
    background: var(--card-2); color: var(--text);
    font-family: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
  }
  .gender-opt.selected { border-color: var(--accent); background: var(--card); color: var(--accent); }
  .gender-dot { width: 12px; height: 12px; border-radius: 50%; flex: none; }
  .gender-dot.dot-m { background: #3b82f6; }
  .gender-dot.dot-f { background: #ec4899; }
  .reviews-more-wrap { display: flex; justify-content: center; margin: 6px 0 4px; }
  .reviews-more-btn {
    width: auto; padding: 11px 26px; background: none; border: 1px solid var(--border);
    color: var(--text); font-weight: 700;
  }
  .reviews-more-btn:hover { background: var(--card-2); }
  .pp-wrap { max-width: 460px; margin: 0 auto; }
  .pp-picker-hint { font-size: 13px; color: var(--text-dim); line-height: 1.6; margin: -6px 0 14px; }
  /* Section title line, with the two list buttons at its trailing end. */
  .pp-detail-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0 12px; }
  .pp-detail-head h2 { flex: 1 1 auto; min-width: 0; margin: 0; font-size: 16px; }
  .pp-progress { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
  .pp-progress-bar { flex: 1; height: 6px; border-radius: 3px; background: var(--card-2); overflow: hidden; }
  .pp-progress-fill { height: 100%; background: var(--accent); border-radius: 3px; transition: width .45s cubic-bezier(.4, 0, .2, 1); }
  .pp-progress-count { flex: none; font-size: 12px; font-weight: 700; color: var(--text-dim); }
  .pp-card {
    background: var(--card); border: 1px solid var(--border); border-radius: 16px;
    padding: 22px 18px; text-align: center;
    animation: pp-enter .4s cubic-bezier(.2, .7, .3, 1) both;
    transition: border-color .2s;
  }
  /* Each new sentence slides in from the side the language reads from, so the
     deck feels like it advances rather than blinking in place. */
  @keyframes pp-enter { from { opacity: 0; transform: translateX(22px) scale(.97); } to { opacity: 1; transform: none; } }
  [dir="rtl"] .pp-card { animation-name: pp-enter-rtl; }
  @keyframes pp-enter-rtl { from { opacity: 0; transform: translateX(-22px) scale(.97); } to { opacity: 1; transform: none; } }
  .pp-card.ok { border-color: #2e7d32; animation: pp-pop .5s ease; }
  @keyframes pp-pop { 0% { transform: scale(1); } 35% { transform: scale(1.035); } 100% { transform: scale(1); } }
  .pp-card.bad { border-color: #c62828; animation: pp-shake .42s; }
  @keyframes pp-shake {
    0%, 100% { transform: translateX(0); } 18% { transform: translateX(-8px); }
    38% { transform: translateX(8px); } 58% { transform: translateX(-5px); } 78% { transform: translateX(5px); }
  }
  .pp-de { font-size: 20px; font-weight: 800; line-height: 1.45; }
  .pp-translation { font-size: 14px; color: var(--text-dim); margin-top: 10px; line-height: 1.5; }
  .pp-listen { display: flex; justify-content: center; gap: 10px; margin-top: 16px; }
  .pp-mic-wrap { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-top: 22px; }
  .pp-mic {
    position: relative; width: 72px; height: 72px; padding: 0; border-radius: 50%;
    border: 1px solid var(--border); background: var(--card); color: var(--accent);
    display: flex; align-items: center; justify-content: center;
    font-size: 32px; cursor: pointer; transition: background .15s, color .15s, transform .15s;
  }
  .pp-mic:hover { background: var(--card-2); }
  .pp-mic:active { transform: scale(.94); }
  .pp-mic.listening { background: var(--accent); border-color: var(--accent); color: var(--on-accent); animation: pp-pulse 1.2s infinite; }
  @keyframes pp-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.08); } }
  /* Halo expanding out of the mic while it is open — the one cue that says
     "it is hearing you right now" even before any words come back. */
  .pp-mic.listening::after {
    content: ''; position: absolute; inset: -9px; border-radius: 50%;
    border: 2px solid var(--accent); animation: pp-ring 1.5s ease-out infinite; pointer-events: none;
  }
  @keyframes pp-ring { 0% { transform: scale(.92); opacity: .65; } 100% { transform: scale(1.4); opacity: 0; } }
  .pp-wave { display: none; align-items: flex-end; gap: 3px; height: 20px; }
  .pp-wave.on { display: flex; }
  .pp-wave i { display: block; width: 3px; height: 5px; border-radius: 2px; background: var(--accent); animation: pp-bar .85s ease-in-out infinite; }
  .pp-wave i:nth-child(2) { animation-delay: .12s; }
  .pp-wave i:nth-child(3) { animation-delay: .24s; }
  .pp-wave i:nth-child(4) { animation-delay: .36s; }
  .pp-wave i:nth-child(5) { animation-delay: .48s; }
  @keyframes pp-bar { 0%, 100% { height: 5px; } 50% { height: 20px; } }
  .pp-hint { font-size: 13px; color: var(--text-dim); text-align: center; }
  /* Result box. Kept in the layout with visibility rather than display, so
     the mic and Skip below it never jump when the first answer comes back. */
  .pp-box {
    margin-top: 18px; padding: 12px 14px; min-height: 62px; box-sizing: border-box;
    border: 1px solid var(--border); border-radius: 12px; background: var(--card);
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
    transition: border-color .2s, background .2s;
  }
  .pp-box.ok { border-color: #2e7d32; background: rgba(46, 125, 50, .12); }
  .pp-box.bad { border-color: #c62828; background: rgba(198, 40, 40, .12); }
  .pp-box-label { font-size: 12px; font-weight: 700; color: var(--text-dim); }
  .pp-heard { font-size: 15px; font-weight: 700; line-height: 1.5; }
  .pp-box.ok .pp-heard { color: var(--fg-good); }
  /* Live transcript, still being revised by the recogniser — dimmed so it
     reads as "what I have so far", not as the verdict. */
  .pp-heard.interim { font-weight: 600; opacity: .65; color: var(--text-dim); }
  /* Word-by-word verdict on a failed attempt: the words that belonged to the
     sentence stay neutral, the ones that did not are struck through, so an
     extra word or a second run at the sentence is visible as the reason. */
  .pp-word { display: inline-block; margin: 0 2px; }
  .pp-word.bad { color: var(--fg-bad); text-decoration: line-through; }
  .pp-actions { margin-top: 16px; }
  /* One button, two jobs: grey "skip this one" until the sentence has been
     said correctly, green "next" afterwards. Nothing advances on its own, so
     this is the only way forward and it has to read as the way forward. */
  .pp-skip { background: var(--card-2); color: var(--text); transition: background .25s, color .25s; }
  .pp-skip.next { background: linear-gradient(135deg, #2e7d32, #43a047); color: var(--on-accent); }
  /* Checklist button in the progress row, with the finished count on it. */
  .pp-list-btn {
    position: relative; flex: none; width: 34px; height: 34px; padding: 0; border-radius: 10px;
    border: 1px solid var(--border); background: var(--card-2); color: var(--text);
    display: flex; align-items: center; justify-content: center; font-size: 19px; cursor: pointer;
  }
  .pp-list-btn:hover { color: var(--accent); }
  .pp-list-badge {
    position: absolute; top: -6px; inset-inline-end: -6px; min-width: 17px; height: 17px;
    padding: 0 4px; box-sizing: border-box; border-radius: 999px; background: #2e7d32; color: var(--on-accent);
    font-size: 10px; font-weight: 800; line-height: 17px; text-align: center;
  }
  .pp-star-badge {
    position: absolute; top: -6px; inset-inline-end: -6px; min-width: 17px; height: 17px;
    padding: 0 4px; box-sizing: border-box; border-radius: 999px; background: #f5a623; color: #3a2a00;
    font-size: 10px; font-weight: 800; line-height: 17px; text-align: center;
  }
  .pp-list-btn.pp-star-btn:hover { color: #f5a623; }
  /* Star toggle beside the speaker on the card. Shares the round speaker
     shape so the two read as one pair of controls on the sentence. */
  .phrase-star { color: var(--text-dim); }
  .phrase-star.on { color: #f5a623; border-color: #f5a623; }
  .pp-list-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
  .pp-list-count { flex: 1; min-width: 0; font-size: 13px; font-weight: 700; color: var(--text-dim); }
  .pp-list-reset {
    flex: none; width: 32px; height: 32px; padding: 0; border-radius: 9px;
    border: 1px solid var(--border); background: var(--card-2); color: var(--text-dim);
    display: flex; align-items: center; justify-content: center; font-size: 18px; cursor: pointer;
  }
  .pp-list-reset:hover { color: var(--fg-bad); border-color: #c62828; }
  .pp-list-confirm {
    margin-bottom: 12px; padding: 10px 12px; border-radius: 10px;
    border: 1px solid #c62828; background: rgba(198, 40, 40, .12);
    font-size: 13px; line-height: 1.6;
  }
  .pp-confirm-text { display: block; margin-bottom: 8px; }
  .pp-confirm-actions { display: flex; gap: 8px; }
  .pp-list-confirm button {
    flex: 1; padding: 7px 10px; border-radius: 8px; border: 1px solid var(--border);
    font-family: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
  }
  .pp-confirm-yes { background: #c62828; border-color: #c62828; color: var(--on-accent); }
  .pp-confirm-no { background: var(--card-2); color: var(--text); }
  .pp-list-empty { padding: 26px 10px; text-align: center; font-size: 13px; color: var(--text-dim); line-height: 1.7; }
  .pp-list-star { flex: none; font-size: 17px; color: #f5a623; line-height: 1; }
  .pp-list-row {
    display: flex; align-items: flex-start; gap: 10px; width: 100%; box-sizing: border-box;
    margin-bottom: 8px; padding: 10px 12px; border-radius: 10px; text-align: start;
    border: 1px solid var(--border); background: var(--card-2); color: var(--text);
    font-family: inherit; font-size: 13px; cursor: pointer;
  }
  .pp-list-row.done { border-color: #2e7d32; background: rgba(46, 125, 50, .12); }
  /* The sentence the drill is sitting on, so the list says "you are here". */
  .pp-list-row.current { outline: 2px solid var(--accent); outline-offset: -2px; }
  .pp-list-mark { flex: none; font-size: 19px; color: var(--text-dim); line-height: 1; }
  .pp-list-row.done .pp-list-mark { color: var(--fg-good); }
  .pp-list-text { flex: 1; min-width: 0; }
  .pp-list-de { display: block; font-weight: 700; }
  .pp-list-tr { display: block; margin-top: 3px; color: var(--text-dim); }
  .pp-done { text-align: center; padding: 30px 10px; max-width: 460px; margin: 0 auto; }
  .pp-done-icon { font-size: 56px; margin-bottom: 10px; animation: pp-cheer .6s cubic-bezier(.2, .8, .3, 1.4) both; }
  @keyframes pp-cheer { from { opacity: 0; transform: scale(.4) rotate(-12deg); } to { opacity: 1; transform: none; } }
  .pp-done-title { font-size: 20px; font-weight: 800; margin-bottom: 8px; }
  .pp-done-score { font-size: 14px; color: var(--text-dim); margin-bottom: 22px; }
  /* Everything above is decoration; a learner who has asked the OS to stop
     moving things gets the same drill without any of it. */
  @media (prefers-reduced-motion: reduce) {
    .pp-card, .pp-card.ok, .pp-card.bad, .pp-done-icon { animation: none; }
    .pp-mic.listening, .pp-mic.listening::after, .pp-wave i { animation: none; }
    .pp-progress-fill { transition: none; }
  }
  /* Speaker button beside the sentence — same control as before, so it keeps
     its class name and its round 34px shape. */
  .phrase-speak {
    flex: none; width: 34px; height: 34px; padding: 0; border-radius: 50%;
    border: 1px solid var(--border); background: transparent; color: var(--accent);
    display: flex; align-items: center; justify-content: center;
    font-size: 18px; cursor: pointer; transition: background .15s, color .15s;
  }
  .phrase-speak:hover { background: var(--card-2); }
  .phrase-speak.speaking { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
  /* Hub screen — state chosen, dispatches to study/marked/exam modes. */
  .cq-hub-wrap { display: flex; flex-direction: column; gap: 14px; max-width: 420px; margin: 28px auto 0; }
  .cq-hub-state-title { text-align: center; font-size: 17px; font-weight: 800; margin: 0 0 6px; }
  .cq-hub-row { display: flex; flex-direction: column; gap: 6px; }
  .cq-hub-lang-label { font-size: 12px; font-weight: 700; color: var(--text-dim); }
  .cq-hub-btn {
    display: flex; align-items: center; gap: 10px; width: 100%; box-sizing: border-box;
    padding: 14px 16px; border-radius: 14px; border: 1px solid var(--border);
    background: var(--card-2); color: var(--text); font-size: 14px; font-weight: 700;
    font-family: inherit; cursor: pointer; text-align: start;
  }
  .cq-hub-btn iconify-icon { font-size: 20px; flex-shrink: 0; }
  .cq-hub-btn span:not(.cq-hub-count) { flex: 1; }
  .cq-hub-count { background: var(--card); border-radius: 999px; padding: 2px 9px; font-size: 12px; font-weight: 800; }
  .cq-hub-btn-primary { background: linear-gradient(90deg, #4f46e5, #a855f7); color: var(--on-accent); border-color: transparent; }
  .cq-hub-btn-primary .cq-hub-count { background: rgba(255,255,255,.22); color: var(--on-accent); }
  select.cq-hub-btn { appearance: none; -webkit-appearance: none; }
  /* Translate icon + inline translation box, shown in study/marked modes. */
  .cq-translate-btn {
    display: none; align-items: center; justify-content: center; width: 30px; height: 30px;
    border-radius: 999px; border: 1px solid var(--border); background: var(--card-2); color: var(--text);
    font-size: 16px; cursor: pointer; flex-shrink: 0; padding: 0;
  }
  .cq-translate-btn.show { display: flex; }
  .cq-translation-box { margin: -2px 0 14px; padding: 10px 12px; border-radius: 10px; background: var(--card-2); font-size: 13px; line-height: 1.7; color: var(--text-dim); }
  .cq-translation-box .cq-translation-q { font-weight: 700; color: var(--text); margin-bottom: 6px; }
  .cq-translation-box .cq-translation-options { margin: 0; padding-inline-start: 18px; }

  /* ── Reviews: rating summary + star widgets ─────────────────────────── */
  .star-row { display: inline-flex; gap: 2px; }
  .star-row iconify-icon { font-size: 15px; color: #f59e0b; }
  .star-row iconify-icon.star-empty { color: var(--border); }

  .review-summary {
    display: flex;
    gap: 18px;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 20px;
    padding: 16px;
    margin-bottom: 14px;
    box-shadow: var(--shadow);
  }
  .review-summary-score {
    flex-shrink: 0;
    width: 92px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 4px;
  }
  .review-summary-avg { font-size: 30px; font-weight: 800; color: var(--text); line-height: 1; }
  .review-summary-score .star-row iconify-icon { font-size: 13px; }
  .review-summary-count { font-size: 11px; color: var(--text-dim); }
  .review-summary-bars { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; justify-content: center; }
  .review-summary-bar-row { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--text-dim); }
  .review-summary-bar-label { flex-shrink: 0; width: 46px; font-weight: 700; }
  .review-summary-bar-track { flex: 1; height: 6px; border-radius: 999px; background: var(--card-2); overflow: hidden; }
  .review-summary-bar-fill { display: block; height: 100%; border-radius: 999px; }
  /* One color per rating tier, reused for both the breakdown bars here and
     each review card's own star row (see .review-card.rating-N below) — a
     glance at the color says roughly how good the review was before
     reading a word of it. */
  .review-summary-bar-row.rating-5 .review-summary-bar-label { color: var(--fg-good); }
  .review-summary-bar-row.rating-5 .review-summary-bar-fill { background: #16a34a; }
  .review-summary-bar-row.rating-4 .review-summary-bar-label { color: #65a30d; }
  .review-summary-bar-row.rating-4 .review-summary-bar-fill { background: #84cc16; }
  .review-summary-bar-row.rating-3 .review-summary-bar-label { color: var(--fg-warn); }
  .review-summary-bar-row.rating-3 .review-summary-bar-fill { background: #f59e0b; }
  .review-summary-bar-row.rating-2 .review-summary-bar-label { color: #ea580c; }
  .review-summary-bar-row.rating-2 .review-summary-bar-fill { background: #f97316; }
  .review-summary-bar-row.rating-1 .review-summary-bar-label { color: var(--danger, #ef4444); }
  .review-summary-bar-row.rating-1 .review-summary-bar-fill { background: var(--danger, #ef4444); }

  /* Rating filter chips below the write-review button — same per-tier
     colors as the breakdown bars above, applied only once a chip is picked
     so the row isn't a wall of color while "All" is active. */
  .rating-filter-chip.rating-5.active { background: #16a34a; border-color: #16a34a; }
  .rating-filter-chip.rating-4.active { background: #84cc16; border-color: #84cc16; }
  .rating-filter-chip.rating-3.active { background: #f59e0b; border-color: #f59e0b; }
  .rating-filter-chip.rating-2.active { background: #f97316; border-color: #f97316; }
  .rating-filter-chip.rating-1.active { background: var(--danger, #ef4444); border-color: var(--danger, #ef4444); }

  .review-write-btn {
    width: 100%;
    padding: 12px;
    border-radius: 14px;
    border: none;
    background: linear-gradient(135deg, #4f46e5, #a855f7);
    color: var(--on-accent);
    font-weight: 700;
    font-size: 14px;
    cursor: pointer;
    margin-bottom: 14px;
  }
  /* The shared .level-filter-row's own bottom margin (4px) is too tight
     right before the review cards — this row sits between the write-review
     button and the cards, so it needs breathing room on both sides. */
  #review-rating-filter-row { margin-bottom: 14px; }

  /* ── Write-a-review popup: order picker + star/comment screens ───────── */
  .write-review-stars-label { font-size: 13px; font-weight: 700; margin-bottom: 8px; color: var(--text); }
  .write-review-stars { display: flex; gap: 6px; margin-bottom: 20px; }
  .write-review-stars iconify-icon { font-size: 34px; color: var(--border); cursor: pointer; transition: color .1s; }
  .write-review-stars iconify-icon.filled { color: #f59e0b; }
  .review-order-item {
    display: flex; align-items: center; gap: 10px;
    background: var(--card-2); border: 1px solid var(--border); border-radius: 14px;
    padding: 10px 12px; margin-bottom: 8px; cursor: pointer; text-align: start;
    width: 100%; font-family: inherit;
  }
  .review-order-item:hover { border-color: var(--accent); }
  .review-order-item img { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
  .review-order-item-text { min-width: 0; flex: 1; }
  .review-order-item-title-row { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
  .review-order-item-title { font-size: 13px; font-weight: 700; color: var(--text); }
  .review-order-item-code { font-size: 12px; font-weight: 700; color: var(--fg-brand); direction: ltr; unicode-bidi: isolate; }
  .review-order-item-meta { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
  /* Lets the customer tell, at a glance, which order in the picker is
     already finished vs. still moving through the pipeline — same
     green/yellow/orange language as the completed review's own badge. */
  .review-order-item-status {
    display: inline-flex; align-items: center; gap: 4px; font-size: 10px; font-weight: 700;
    padding: 2px 8px; border-radius: 999px; margin-top: 4px;
  }
  .review-order-item-status.status-completed { background: rgba(34,197,94,0.14); color: var(--fg-good); }
  .review-order-item-status.status-reviewing { background: rgba(245,158,11,0.14); color: var(--fg-warn); }
  .review-order-item-status.status-processing { background: rgba(249,115,22,0.14); color: #ea580c; }
  .review-back-btn {
    position: absolute; top: 10px; inset-inline-end: 14px;
    background: none; border: none; color: var(--text-dim); font-size: 13px; font-weight: 700;
    cursor: pointer; padding: 0;
  }
  #write-review-comment-input {
    width: 100%; box-sizing: border-box; min-height: 100px; resize: vertical;
    padding: 11px 12px; border-radius: 10px; border: 1px solid var(--border);
    /* 16px, not smaller — iOS Safari auto-zooms the page on focus for any
       field under 16px, which is the "page zooms in while typing" bug. */
    font-size: 16px; font-family: inherit; color: var(--text); background: var(--card-2);
    margin-bottom: 16px;
  }

  .reply-toggle-btn {
    background: none;
    border: none;
    color: var(--text-dim);
    font-size: 11px;
    cursor: pointer;
    padding: 0;
    margin-top: 8px;
  }
  .reply-toggle-btn:hover { color: var(--accent); }

  .review-replies {
    margin-top: 10px;
    padding-inline-start: 16px;
    border-inline-start: 2px solid var(--border);
  }
  .reply-item {
    display: flex;
    gap: 8px;
    margin-top: 8px;
    align-items: flex-start;
  }
  .reply-item .reply-avatar {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--card-2);
    color: var(--text);
    font-weight: 700;
    font-size: 11px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }
  .reply-item .reply-name { font-weight: 700; font-size: 12px; }
  .reply-item .reply-comment { font-size: 12px; color: var(--text); margin-top: 2px; line-height: 1.6; }

  .reply-form {
    display: none;
    gap: 6px;
    margin-top: 10px;
  }
  .reply-form.active { display: flex; }
  .reply-form textarea {
    flex: 1;
    min-height: 36px;
    font-size: 16px;
    padding: 6px 10px;
    resize: vertical;
  }
  .reply-form button {
    font-size: 12px;
    padding: 0 14px;
    white-space: nowrap;
    background: var(--accent);
    color: white;
    border: none;
    border-radius: 8px;
    cursor: pointer;
  }

  .order-row {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 20px;
    padding: 14px 16px;
    margin-bottom: 10px;
    box-shadow: var(--shadow);
    transition: border-color .3s, box-shadow .3s;
  }
  .order-row.order-row-highlight {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(168,85,247,0.25);
    animation: orderRowPulse 1.1s ease-in-out 2;
  }
  @keyframes orderRowPulse {
    0%, 100% { box-shadow: 0 0 0 3px rgba(168,85,247,0.25); }
    50% { box-shadow: 0 0 0 7px rgba(168,85,247,0.08); }
  }
  .order-row-head { display: flex; align-items: flex-start; gap: 12px; }
  .order-cat-icon { width: 36px; height: 36px; object-fit: contain; flex-shrink: 0; border: none; background: transparent; }
  .order-cat-icon-fallback {
    display: flex; align-items: center; justify-content: center;
    background: var(--accent); color: var(--on-accent); font-weight: 800; font-size: 14px;
  }
  .order-row-info { min-width: 0; flex: 1; }
  .order-cat-label { font-size: 11px; font-weight: 700; color: var(--accent); text-transform: uppercase; letter-spacing: .02em; }
  .order-row .order-embassy { font-weight: 700; font-size: 14px; margin-top: 2px; }
  .order-row .order-meta { font-size: 12px; color: var(--text-dim); margin-top: 6px; display: flex; flex-direction: column; gap: 4px; }
  .order-row .order-status {
    display: inline-block;
    margin-top: 8px;
    font-size: 11px;
    padding: 3px 10px;
    border-radius: 999px;
  }
  .order-status.pending { background: var(--card-2); color: var(--text-dim); }
  .order-status.received { background: rgba(34,197,94,0.15); color: var(--accent-2); }

  /* Colored status accent bar across the top of each order card — same
     idea and colors as the admin panel's Requests cards, [data-status] only
     (set in buildOrderRow(), not on the separate Payments-list rows that
     also reuse .order-row) so this doesn't spill onto payment history. */
  .order-row[data-status] { position: relative; overflow: hidden; padding-top: 18px; }
  .order-row[data-status]::before {
    content: ""; position: absolute; left: 0; right: 0; top: 0; height: 4px;
    background: var(--border);
  }
  .order-row[data-status="submitted"]::before { background: linear-gradient(90deg,#4338ca,#6366f1); }
  .order-row[data-status="reviewing"]::before { background: linear-gradient(90deg,#d97706,#f59e0b); }
  .order-row[data-status="processing"]::before { background: linear-gradient(90deg,#1e40af,#3b82f6); }
  .order-row[data-status="completed"]::before { background: linear-gradient(90deg,#16a34a,#22c55e); }
  .order-row[data-status="rejected"]::before { background: linear-gradient(90deg,#b91c1c,#ef4444); }
  .order-row[data-status="needs_info"]::before { background: linear-gradient(90deg,#9d174d,#db2777); }

  .toast {
    position: fixed;
    left: 50%;
    bottom: 90px;
    transform: translateX(-50%) translateY(20px);
    background: rgba(30, 41, 59, 0.92);
    color: var(--on-accent);
    padding: 10px 20px;
    border-radius: 999px;
    font-size: 13px;
    z-index: 60;
    opacity: 0;
    pointer-events: none;
    transition: opacity .2s, transform .2s;
    white-space: nowrap;
  }
  .toast.show {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
  .center-success-toast {
    position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.85);
    display: flex; flex-direction: column; align-items: center; gap: 12px;
    background: var(--card); border-radius: 20px; padding: 28px 34px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.18);
    z-index: 90; opacity: 0; pointer-events: none;
    transition: opacity .2s, transform .2s;
  }
  .center-success-toast.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  .center-success-icon {
    width: 56px; height: 56px; border-radius: 50%;
    background: #16a34a; color: var(--on-accent);
    display: flex; align-items: center; justify-content: center;
    font-size: 26px; font-weight: 800;
  }
  .center-success-toast.show .center-success-icon { animation: paySuccessPop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); }
  .center-success-text { font-size: 14px; font-weight: 700; color: var(--text); white-space: nowrap; }

  .sheet-overlay {
    position: fixed;
    top: 0; left: 0; right: 0;
    /* Same fix as body's height (see the --app-height comment there): a
       plain inset:0 sizes this against the browser's static layout
       viewport, which doesn't shrink when the keyboard opens. That
       mismatch is what made the sheet (and whatever input inside it —
       e.g. the payment amount field) jump an extra, unwanted amount on
       top of the keyboard itself opening. Tracking --app-height instead
       keeps this overlay's height in sync with the real visible area. */
    height: var(--app-height, 100vh);
    background: rgba(15, 15, 30, 0.4);
    display: none;
    align-items: flex-end;
    justify-content: center;
    z-index: 70;
  }
  .sheet-overlay.active { display: flex; }
  .chat-sheet {
    background: var(--card);
    width: 100%;
    max-width: 480px;
    /* Capped against --app-height too, not just 78vh — otherwise once the
       keyboard shrinks the overlay above, a static 78vh can end up taller
       than the space actually left for it. */
    max-height: min(78vh, calc(var(--app-height, 100vh) - 20px));
    border-radius: 24px 24px 0 0;
    display: flex;
    flex-direction: column;
    box-shadow: 0 -10px 40px rgba(0,0,0,0.2);
    transform: translateY(100%);
    transition: transform .25s ease;
  }
  .sheet-overlay.active .chat-sheet {
    transform: translateY(0);
  }
  .chat-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 18px;
    font-weight: 700;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
  }
  .chat-messages {
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  /* Physical side is decided here, on the outer row, via text-align: right/left
     (the physical keywords, NOT start/end) — this is immune to dir=rtl on the
     page and to dir=auto on the message text, because text-align positions its
     inline-level children as a block, independent of what direction the text
     inside those children happens to be. The old approach (flex cross-axis
     auto-margins on the bubble itself) worked in desktop testing but proved
     unreliable on real iOS Safari, so this row-level text-align replaces it.
     .msg-line is itself display:inline-flex — still an inline-level box, so
     it inherits that same reliable row-level positioning while letting the
     avatar and bubble sit side by side inside it. It's forced direction:ltr
     because flex-direction (unlike text-align:left/right) IS relative to the
     page's own direction — on this RTL (Persian) page, row-reverse would
     otherwise flip which side of the bubble the avatar lands on. */
  .chat-msg-row.user { text-align: right; }
  .chat-msg-row.support { text-align: left; }
  .msg-line { display: inline-flex; align-items: flex-end; gap: 8px; max-width: 92%; text-align: start; direction: ltr; }
  .chat-msg-row.user .msg-line { flex-direction: row-reverse; }
  .msg-avatar-col { display: flex; flex-direction: column; align-items: center; gap: 3px; flex-shrink: 0; }
  .msg-sender-name {
    font-size: 11px; font-weight: 700; color: var(--accent);
    max-width: 70px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .chat-bubble {
    display: inline-block;
    max-width: 200px;
    padding: 9px 13px;
    border-radius: 16px;
    font-size: 14px;
    line-height: 1.6;
    word-break: break-word;
    text-align: start;
  }
  /* Tail points toward the adjacent avatar now that the two sit side by
     side — the customer's own avatar leads on the right, so its tail is
     bottom-right; support's avatar leads on the left, tail bottom-left. */
  .chat-bubble.user {
    background: linear-gradient(135deg, #4f46e5, #a855f7);
    color: var(--on-accent);
    border-bottom-right-radius: 4px;
  }
  .chat-bubble.support {
    background: #dcf6dc;
    color: var(--text);
    border-bottom-left-radius: 4px;
  }
  .chat-bubble .msg-time { font-size: 11px; margin-top: 4px; opacity: .8; }
  .chat-avatar {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--accent);
    color: var(--on-accent);
    font-size: 13px;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    overflow: hidden;
  }
  .chat-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .chat-ticks {
    font-size: 11px;
    margin-top: 4px;
    text-align: left;
    opacity: 0.85;
  }
  .chat-ticks.blue { color: #60d3ff; }

  .pay-method {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    background: var(--card-2);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 14px;
    margin-bottom: 10px;
    cursor: pointer;
    transition: border-color .15s;
  }
  .pay-method:hover { border-color: var(--accent); }
  .pay-method .pay-icon {
    width: 48px; height: 48px; border-radius: 12px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
  }
  .pay-method .pay-icon iconify-icon { font-size: 26px; }
  .pay-method .pay-icon.icon-apple { background: var(--card); border: 1px solid var(--border); }
  .pay-method .pay-icon.icon-apple iconify-icon { color: var(--text); }
  .pay-method .pay-icon.icon-crypto { background: rgba(247,147,26,0.14); }
  .pay-method .pay-icon.icon-crypto iconify-icon { color: #f7931a; }
  /* These logos are already self-contained badges with their own fill
     color (PaysafeCard's blue square, Wunschgutschein's red circle) — no
     extra card framing around them, so they render at full box size
     instead of shrinking further inside added padding. */
  .pay-method .pay-icon.icon-paysafecard img,
  .pay-method .pay-icon.icon-wunschgutschein img { width: 48px; height: 48px; object-fit: contain; }
  .pay-method .pay-icon.icon-paysafecard img { border-radius: 12px; }
  .pay-method .pay-icon.icon-wunschgutschein img { border-radius: 50%; }
  .pay-method .pay-name { font-weight: 700; font-size: 14px; }
  .pay-method .pay-desc { font-size: 12px; color: var(--text-dim); margin-top: 2px; }
  .pay-details { width: 100%; margin-top: 4px; cursor: default; }

  .notif-item {
    background: var(--card-2); border: 1px solid var(--border); border-radius: 16px;
    padding: 12px 14px; margin-bottom: 10px; position: relative;
  }
  .notif-item.unread { border-color: var(--accent); }
  .notif-item.clickable { cursor: pointer; transition: transform .15s, border-color .15s; }
  .notif-item.clickable:hover { border-color: var(--accent); transform: translateY(-1px); }
  .notif-item.clickable:active { transform: translateY(0); }
  .notif-item-title { font-weight: 700; font-size: 13.5px; }
  .notif-item.cat-success .notif-item-title { color: var(--accent-2, #22c55e); }
  .notif-item.cat-danger .notif-item-title { color: var(--danger, #ef4444); }
  .notif-item.cat-warning .notif-item-title { color: var(--fg-warn); }
  .notif-item.cat-info .notif-item-title { color: var(--accent); }
  .notif-item-body { font-size: 12.5px; color: var(--text-dim); margin-top: 4px; line-height: 1.5; }
  .notif-item-time { font-size: 11px; color: var(--text-dim); margin-top: 6px; opacity: .8; }
  .pay-value-row {
    display: flex; align-items: center; gap: 8px; background: var(--card);
    border-radius: 12px; padding: 8px 10px;
  }
  .pay-value-text { flex: 1; font-size: 13px; font-family: monospace; word-break: break-all; }
  .pay-copy-btn {
    background: var(--accent); color: var(--on-accent); border: none; border-radius: 999px;
    padding: 5px 12px; font-size: 12px; font-weight: 700; cursor: pointer; flex-shrink: 0;
  }
  .pay-open-link {
    display: block; text-align: center; background: var(--accent); color: var(--on-accent);
    border-radius: 999px; padding: 9px; font-size: 13px; font-weight: 700; text-decoration: none;
  }
  .pay-holder-row { font-size: 12.5px; color: var(--text-dim); margin-bottom: 6px; }
  /* The amount actually owed, as opposed to .pay-holder-row's other use
     (an account holder's name) — this one deserves to stand out since
     it's the number the customer is about to act on. */
  .pay-price-row { font-size: 18px; font-weight: 800; color: var(--accent-2); margin-bottom: 10px; }
  /* Only used when a guide button sits opposite the price (see
     buildPayGuideBtn) — the plain .pay-price-row above still applies on
     its own otherwise, so this only overrides the spacing/layout, not the
     price's own look. */
  .pay-price-row-wrap { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
  .pay-price-row-wrap .pay-price-row { margin-bottom: 0; }
  .pay-field-label { font-size: 12px; color: var(--text-dim); margin: 10px 0 4px; display: block; }
  /* Opens the admin's "how to buy" text/image in a centered popup — only
     rendered at all when the admin has actually set something (see
     buildPayGuideBtn). */
  .pay-guide-btn {
    display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
    background: var(--card-2); border: 1px solid var(--border); border-radius: 999px;
    padding: 7px 14px; font-size: 12.5px; font-weight: 700; color: #f59e0b;
    cursor: pointer; font-family: inherit;
  }
  .pay-guide-btn iconify-icon { font-size: 15px; }
  /* Populated by openPayGuideModal — one column when a method has a single
     reference image, two side by side when it has both. */
  /* One shared frame around both images (a thin divider line between them,
     not two separate rounded boxes) — the border/radius/overflow-clip live
     on the grid itself, not on each img. */
  .pay-guide-images-grid {
    display: grid; grid-template-columns: 1fr; gap: 1px; margin-bottom: 12px;
    border: 1px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--border);
  }
  /* Fixed box + contain (never cover — cropping a gift card can hide the
     actual code/price printed on it) so two images with different source
     dimensions still line up at the same height side by side, letterboxed
     on whichever axis needs it instead of cutting content off. */
  .pay-guide-images-grid img { width: 100%; aspect-ratio: 4 / 5; object-fit: contain; display: block; }
  /* Real retailer logos (code-defined, see PAY_GUIDE_STORES) rather than
     just naming the stores in the free-text paragraph below — recognizing
     the logo on the shelf matters more here than reading the name. */
  .pay-guide-stores-heading { font-weight: 800; font-size: 13.5px; color: var(--text); margin-bottom: 8px; }
  .pay-guide-stores-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
  .pay-guide-store-chip {
    display: flex; align-items: center; gap: 6px; background: var(--card-2);
    border: 1px solid var(--border); border-radius: 999px; padding: 5px 10px;
  }
  .pay-guide-store-chip img { width: 18px; height: 18px; object-fit: contain; display: block; }
  .pay-guide-store-chip iconify-icon { font-size: 16px; color: var(--text); }
  .pay-guide-store-chip span { font-size: 12px; font-weight: 700; color: var(--text); }
  .pay-photo-reminder {
    display: flex; align-items: flex-start; gap: 6px; margin: 8px 0 0;
    font-size: 12px; line-height: 1.6; color: #f59e0b;
  }
  .pay-photo-reminder iconify-icon { font-size: 14px; flex-shrink: 0; margin-top: 1px; color: #f59e0b; }

  /* Accepted-vs-rejected gift card pair, shown on the Apple Gift Card
     payment page above the code field — same green/red accept-reject
     language as everywhere else in the app. */
  .gift-compare-row { display: flex; gap: 12px; margin: 14px 0 4px; }
  .gift-compare-card {
    flex: 1; position: relative; border-radius: 16px; padding: 18px 10px 14px;
    display: flex; flex-direction: column; align-items: center; gap: 8px;
    border: 2px solid; text-align: center;
  }
  .gift-compare-card.accept { background: rgba(34,197,94,0.08); border-color: #22c55e; }
  .gift-compare-card.reject { background: rgba(239,68,68,0.08); border-color: #ef4444; }
  .gift-compare-badge {
    position: absolute; top: -10px; inset-inline-end: -10px; width: 26px; height: 26px;
    border-radius: 50%; display: flex; align-items: center; justify-content: center;
    color: var(--on-accent); font-size: 16px; box-shadow: 0 2px 6px rgba(0,0,0,.2);
  }
  .gift-compare-card.accept .gift-compare-badge { background: #22c55e; }
  .gift-compare-card.reject .gift-compare-badge { background: #ef4444; }
  .gift-compare-icon {
    width: 48px; height: 48px; border-radius: 12px; background: var(--card);
    border: 1px solid var(--border); display: flex; align-items: center; justify-content: center;
  }
  .gift-compare-icon iconify-icon { font-size: 26px; color: var(--text); }
  .gift-compare-name { font-size: 12.5px; font-weight: 700; color: var(--text); }
  .gift-compare-tag { font-size: 11px; font-weight: 700; }
  .gift-compare-card.accept .gift-compare-tag { color: var(--fg-good); }
  .gift-compare-card.reject .gift-compare-tag { color: var(--fg-bad); }
  .pay-amount-input {
    width: 100%; padding: 9px 12px; border-radius: 12px; border: 1px solid var(--border);
    /* 16px, not smaller — iOS Safari auto-zooms the page on focus for any
       field under 16px, which is the "page zooms in while typing" bug. */
    font-size: 16px; font-family: inherit; color: var(--text); background: var(--card);
  }
  .pay-screenshot-drop {
    position: relative; border: 1px dashed var(--border); border-radius: 12px;
    background: var(--card); min-height: 70px; display: flex; align-items: center;
    justify-content: center; cursor: pointer; overflow: hidden;
  }
  .pay-screenshot-placeholder { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 12px; color: var(--text-dim); font-size: 12px; }
  .pay-screenshot-placeholder .icon { font-size: 22px; }
  .pay-screenshot-preview { width: 100%; max-height: 160px; object-fit: contain; display: none; }
  .pay-submit-btn {
    width: 100%; margin-top: 12px; background: linear-gradient(135deg, var(--accent), #8b5cf6);
    color: var(--on-accent); border: none; border-radius: 999px; padding: 10px; font-size: 13px; font-weight: 700; cursor: pointer;
  }
  .pay-submit-btn:disabled { opacity: .6; cursor: default; }
  .pay-submit-msg { display: block; margin-top: 8px; font-size: 12px; text-align: center; }
  .pay-success {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 10px; padding: 20px 0 8px;
  }
  .pay-success-icon {
    width: 56px; height: 56px; border-radius: 50%;
    background: #16a34a; color: var(--on-accent);
    display: flex; align-items: center; justify-content: center;
    font-size: 26px; font-weight: 800;
    animation: paySuccessPop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
  }
  .pay-success-text { font-size: 13px; font-weight: 700; color: var(--text); }
  /* Bigger standalone variant for #pay-success-modal (the small one above
     stays for the account-type screenshot flow's own inline confirmation). */
  .pay-success-icon-lg {
    width: 72px; height: 72px; margin: 26px auto 0; font-size: 34px;
  }
  .pay-success-receipt { border-top: 1px solid var(--border); padding-top: 14px; margin-bottom: 22px; }
  .pay-success-row {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    font-size: 13px; padding: 7px 0;
  }
  .pay-success-row + .pay-success-row { border-top: 1px dashed var(--border); }
  .pay-success-row-label { color: var(--text-dim); }
  .pay-success-row-value { font-weight: 700; color: var(--text); }
  @keyframes paySuccessPop {
    from { transform: scale(0); opacity: 0; }
    to { transform: scale(1); opacity: 1; }
  }

  .pay-status-pill {
    display: inline-block; font-size: 11px; font-weight: 700; padding: 3px 10px;
    border-radius: 999px; margin-top: 6px;
  }
  .pay-status-pill.pending { background: #fef3c7; color: var(--fg-warn); }
  .pay-status-pill.confirmed { background: var(--t-good-2); color: #166534; }
  .pay-status-pill.cancelled { background: var(--t-bad); color: var(--fg-bad); }
  .pay-cancel-reason { font-size: 12px; color: var(--fg-bad); margin-top: 6px; line-height: 1.5; }
  .order-row-with-thumb { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
  .order-row-with-thumb .order-row-text { flex: 1; min-width: 0; }
  .pay-support-btn {
    display: inline-flex; width: fit-content; align-items: center; gap: 6px; margin-top: 10px;
    border: 1px solid var(--border); background: var(--card-2); color: var(--accent);
    border-radius: 10px; padding: 6px 12px; font-size: 12px; font-weight: 700; cursor: pointer;
    transition: border-color .15s;
  }
  .pay-support-btn:hover { border-color: var(--accent); }
  .pay-support-btn iconify-icon { font-size: 15px; }
  .pay-screenshot-thumb { width: 48px; height: 48px; object-fit: cover; border-radius: 10px; cursor: pointer; flex-shrink: 0; }

  .avatar-circle {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: var(--accent);
    color: white;
    font-weight: 700;
    font-size: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto 10px;
    overflow: hidden;
  }
  .avatar-circle img { width: 100%; height: 100%; object-fit: cover; }
  .avatar-upload-wrap { position: relative; display: inline-block; cursor: pointer; }
  .avatar-edit-badge {
    position: absolute; bottom: 6px; right: -2px; background: var(--card);
    border: 1px solid var(--border); border-radius: 50%; width: 22px; height: 22px;
    display: flex; align-items: center; justify-content: center; font-size: 11px;
    box-shadow: var(--shadow);
  }
  .profile-header-card {
    background: linear-gradient(135deg, var(--accent), #8b5cf6);
    border-radius: 0 0 22px 22px;
    padding: 22px 20px;
    text-align: center;
    /* Breaks out of main's 16px padding on three sides, same trick as
       .home-banner/.category-banner, so it starts flush at the very top of
       the page instead of floating below the header with a gap. */
    margin: -16px -12.8px 16px -12.8px;
    box-shadow: 0 10px 24px rgba(168, 85, 247, 0.28);
  }
  .profile-header-card .avatar-circle {
    width: 68px; height: 68px; font-size: 26px; margin: 0 auto 10px;
    border: 3px solid rgba(255,255,255,0.5);
  }
  .profile-header-name { font-weight: 800; font-size: 16px; color: var(--on-accent); margin-top: 4px; }
  .profile-header-email { font-size: 12px; color: rgba(255,255,255,0.85); margin-top: 2px; }

  .sheet-input {
    width: 100%; padding: 12px 14px; border-radius: 14px; border: 1px solid var(--border);
    font-size: 14px; font-family: inherit; margin-bottom: 10px; color: var(--text); background: var(--card);
  }
  .sheet-input:focus { border-color: var(--accent); }
  .sheet-input:focus:not(:focus-visible) { outline: none; }
  .sheet-error { color: var(--danger, #ef4444); font-size: 12px; margin-bottom: 10px; }
  .sheet-success { color: var(--accent-2, #22c55e); font-size: 13px; margin-bottom: 10px; }
  .sheet-submit-btn {
    width: 100%; padding: 13px; border: none; border-radius: 999px; cursor: pointer;
    background: linear-gradient(135deg, var(--accent), #8b5cf6); color: var(--on-accent); font-size: 15px; font-weight: 700;
  }
  .forgot-link {
    display: block; text-align: center; margin-top: 12px; font-size: 13px;
    color: var(--text-dim); text-decoration: underline; background: none; border: none; cursor: pointer;
  }

  .profile-nav-row {
    width: 100%; display: flex; align-items: center; justify-content: space-between;
    background: var(--card); border: 1px solid var(--border); border-radius: 16px;
    padding: 10px 16px; margin-bottom: 12px; cursor: pointer; box-shadow: var(--shadow);
    font-weight: 700; font-size: 14px; color: var(--text); font-family: inherit;
  }
  .profile-nav-row:hover { border-color: var(--accent); }
  .profile-nav-row-left { display: flex; align-items: center; gap: 10px; }
  .profile-nav-icon {
    width: 36px; height: 36px; border-radius: 50%; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center; font-size: 16px;
  }
  /* Two shades of the site's own brand tint rather than a blue one and a
     green one. The chips are decoration — nothing about "change password" is
     more successful than "edit profile" — so a green that means nothing was
     only pulling the row out of the page's colour family. */
  .profile-nav-icon.icon-blue { background: var(--t-brand-2); }
  .profile-nav-icon.icon-green { background: var(--t-brand); }
  .profile-nav-row .chevron {
    color: var(--text-dim); font-size: 15px; font-weight: 400;
    transform: scaleX(var(--chevron-flip, 1));
    /* ‹ is itself a bidi-mirrored character — inside an RTL paragraph the
       browser silently mirrors it to › on its own, on top of (and
       fighting) the explicit flip below. Isolating it as its own
       always-LTR run stops that auto-mirroring so the one flip we ask
       for here is the only one that happens. */
    display: inline-block;
    direction: ltr;
    unicode-bidi: isolate;
  }
  html[dir="ltr"] .profile-nav-row .chevron { --chevron-flip: -1; }

  .logout-btn-bottom {
    width: 100%;
    margin-top: 24px;
    background: rgba(239, 68, 68, 0.08);
    border: 1px solid rgba(239, 68, 68, 0.25);
    color: var(--danger);
    font-weight: 700;
    font-size: 14px;
    padding: 13px;
    border-radius: 999px;
    cursor: pointer;
  }
  .logout-btn-bottom:hover { background: rgba(239, 68, 68, 0.15); }
  .legal-links-row {
    display: flex; justify-content: center; gap: 8px;
    margin-top: 14px; font-size: 12px; color: var(--text-dim);
  }
  .legal-links-row a { color: var(--text-dim); text-decoration: underline; }

  /* Home screen's own footer — the only screen (besides Profile's small
     .legal-links-row) that ever showed a copyright/legal line, and even
     that one required navigating into Profile first. This one sits at the
     bottom of the category/news browsing screen so a first-time desktop
     visitor sees it without having to log in. */
  .home-footer {
    margin-top: 28px; padding-top: 18px;
    border-top: 1px solid var(--border);
    display: flex; flex-direction: column; align-items: center; gap: 8px;
    text-align: center;
  }
  .home-footer-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; font-size: 13px; }
  .home-footer-links a { color: var(--text-dim); text-decoration: none; }
  .home-footer-links a:hover { color: var(--accent); text-decoration: underline; }
  .home-footer-links span { color: var(--border); }
  .home-footer-copy { font-size: 12px; color: var(--text-dim); opacity: 0.8; }

  /* Each stage is one flex:1 column (dot stacked over its own label), so a
     label is always exactly centered under its dot. The connecting line is
     a separate element behind the dots, spanning from the first dot's
     center to the last dot's center (12.5%..87.5%, since there are always
     exactly 4 equal-width columns) — decoupled from dot layout entirely,
     which is what broke the old dot-line-dot-line flex row: it positioned
     dots differently than the labels' 4-equal-columns row underneath it.
     .stage-row is a plain flex row with no forced direction, so in RTL
     (Farsi/Arabic) it auto-reverses — stage 1 renders on the physical
     right, matching that reading direction, same as the dots/labels below
     it. The line must follow that same reversal, so it uses logical
     inset-inline-* (which flips with dir) instead of left/right (which
     don't) — otherwise the fill renders under the wrong two dots in RTL
     while looking fine in LTR. */
  .stage-tracker { position: relative; margin-top: 14px; }
  .stage-track-line, .stage-track-fill {
    position: absolute; top: 5px; inset-inline-start: 12.5%; inset-inline-end: 12.5%; height: 2px; background: var(--border);
  }
  .stage-track-fill { inset-inline-end: auto; background: var(--accent-2); transition: width .2s; }
  .stage-row { display: flex; position: relative; }
  .stage-item { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 0; }
  .stage-item .dot { width: 12px; height: 12px; border-radius: 50%; background: var(--border); flex-shrink: 0; position: relative; z-index: 1; }
  .stage-item .dot.done { background: var(--accent-2); }
  /* Current stage gets its own color (yellow) — done stages are green, the
     traversed line up to here is green, only this one dot is yellow, and
     everything still ahead stays gray. */
  .stage-item .dot.active { background: #f59e0b; }
  /* Reaching the last stage — the whole thing is actually finished, not
     merely "in progress on the final step" — gets its own green
     checkmark instead of the plain done dot, so completion reads as
     complete at a glance. */
  .stage-item .dot.final {
    width: 15px; height: 15px; display: flex; align-items: center; justify-content: center;
  }
  .stage-item .dot.final iconify-icon { font-size: 10px; color: var(--on-accent); }
  .stage-item .stage-label { font-size: 10px; color: var(--text-dim); text-align: center; }
  .order-code-line { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
  /* The "#" is a neutral character — inside an RTL paragraph the bidi
     algorithm can reorder it away from the LTR code that follows it (e.g.
     Persian/Arabic), making them look detached. Isolating this span as its
     own always-LTR run keeps "#" glued to the code regardless of the
     surrounding text direction. */
  .ref-code { color: var(--fg-brand); font-weight: 700; direction: ltr; unicode-bidi: isolate; }

  /* Rejected/needs-info replace the stage tracker entirely — the request
     isn't progressing through the normal stages, so showing them would be
     misleading. */
  .order-status-box { margin-top: 10px; border-radius: 14px; padding: 10px 12px; }
  .order-status-box.rejected { background: rgba(239,68,68,0.08); }
  .order-status-box.needs-info { background: rgba(168,85,247,0.08); }
  .status-box-title { font-size: 12px; font-weight: 700; }
  .order-status-box.rejected .status-box-title { color: var(--danger, #ef4444); }
  .order-status-box.needs-info .status-box-title { color: var(--accent); }
  .status-box-reason { font-size: 13px; color: var(--text); margin-top: 4px; line-height: 1.5; }
  .order-reply-row { display: flex; gap: 6px; margin-top: 8px; }
  .order-reply-input {
    flex: 1; border: 1px solid var(--border); border-radius: 12px; padding: 8px 10px;
    font-size: 13px; font-family: inherit; resize: none; min-height: 38px; background: var(--card);
  }
  .order-reply-send {
    background: var(--accent); color: var(--on-accent); border: none; border-radius: 12px;
    padding: 0 14px; font-size: 13px; font-weight: 700; cursor: pointer; flex-shrink: 0;
  }
  .order-reply-attach {
    background: var(--card); border: 1px solid var(--border); border-radius: 12px;
    padding: 0 12px; font-size: 16px; cursor: pointer; flex-shrink: 0; line-height: 1;
  }
  .order-reply-preview-row { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
  .order-reply-preview-img { width: 48px; height: 48px; border-radius: 10px; object-fit: cover; border: 1px solid var(--border); }
  .order-reply-preview-remove {
    background: var(--card); border: 1px solid var(--border); border-radius: 50%;
    width: 22px; height: 22px; font-size: 11px; cursor: pointer; color: var(--danger, #ef4444);
  }
  .order-reply-sent-msg { font-size: 11px; color: var(--accent-2); margin-top: 6px; }

  /* Shown under a still-"submitted" order — payment hasn't been made yet,
     so it's parked here until the customer pays for it. */
  .order-pay-hint {
    margin-top: 10px; border-radius: 14px; padding: 10px 12px; background: rgba(245,158,11,0.1);
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
  }
  .order-pay-hint-text { font-size: 12.5px; color: var(--text); line-height: 1.5; }
  .order-pay-hint-btn {
    background: var(--accent); color: var(--on-accent); border: none; border-radius: 999px;
    padding: 7px 14px; font-size: 12px; font-weight: 700; cursor: pointer; flex-shrink: 0;
  }

  /* Image cropper modal (openImageCropper()) — lets the user see and control
     exactly which part of their photo becomes the avatar, instead of a
     surprise object-fit:cover crop after upload. */
  .imgcrop-overlay {
    position: fixed; inset: 0; background: rgba(0,0,0,0.75);
    display: flex; align-items: center; justify-content: center;
    z-index: 9999; padding: 20px;
  }
  .imgcrop-card {
    background: var(--card); border-radius: 18px; padding: 18px;
    display: flex; flex-direction: column; align-items: center; gap: 14px;
    max-width: 92vw;
  }
  .imgcrop-stage {
    position: relative; overflow: hidden; border-radius: 12px;
    background: #000; touch-action: none; cursor: grab;
  }
  .imgcrop-stage:active { cursor: grabbing; }
  .imgcrop-img {
    position: absolute; top: 0; left: 0; transform-origin: top left;
    max-width: none; max-height: none; user-select: none; -webkit-user-drag: none;
  }
  .imgcrop-hint { font-size: 12px; color: var(--text-dim); text-align: center; }
  .imgcrop-zoom { width: 100%; max-width: 340px; }
  .imgcrop-actions { display: flex; gap: 10px; width: 100%; max-width: 340px; }
  .imgcrop-actions button {
    flex: 1; padding: 10px; border-radius: 999px; border: none; font-size: 13px; font-weight: 700; cursor: pointer;
  }
  .imgcrop-cancel { background: var(--card-2); color: var(--text); }
  .imgcrop-confirm { background: linear-gradient(135deg, #4f46e5, #a855f7); color: var(--on-accent); }

  /* ══════════════════════════════════════════════════════════════════
     DESKTOP LAYOUT — everything below only ever applies at >=1024px.
     Mobile (everything above) is completely untouched: no rule here
     overrides anything outside this block, and nothing above reaches
     in here. This is purely an additive layer that turns the phone-
     width app shell into a real desktop site: a top navbar instead of
     the bottom tab bar, a wide centered canvas instead of a 480px
     column, and grids instead of single-file stacks wherever the
     content is a list of same-shaped cards.
     ══════════════════════════════════════════════════════════════════ */
  @media (min-width: 1024px) {
    /* ── Shell: header becomes a real navbar, bottom tab bar retires ── */
    body > header {
      text-align: start;
      display: flex;
      align-items: center;
      gap: 28px;
      padding: 16px 40px;
      background: var(--glass-strong);
      backdrop-filter: blur(16px);
      -webkit-backdrop-filter: blur(16px);
      box-shadow: 0 2px 16px rgba(99, 102, 241, 0.08);
    }
    /* The mobile "float over the hero banner" trick is a phone-only
       space-saving move; on desktop the navbar is permanent chrome and
       the banner simply sits below it in normal flow. Same selector,
       same specificity as the mobile rules — later in the file wins. */
    body.has-banner > header { position: relative; top: auto; left: auto; right: auto; z-index: auto; }
    body.has-banner header .site-title { display: block; }
    .site-title { font-size: 21px; order: 1; flex-shrink: 0; }
    .header-back-btn { position: static; order: 0; }
    .header-right-icons { position: static; order: 3; margin-inline-start: auto; }
    .desktop-nav-links {
      display: flex;
      align-items: center;
      gap: 4px;
      order: 2;
      flex: 1;
      justify-content: center;
    }
    .desktop-nav-link {
      display: flex;
      align-items: center;
      gap: 7px;
      background: none;
      border: none;
      color: var(--text-dim);
      font-family: inherit;
      font-size: 14px;
      font-weight: 600;
      padding: 9px 16px;
      border-radius: 999px;
      cursor: pointer;
      transition: background .15s, color .15s;
    }
    .desktop-nav-link iconify-icon { font-size: 18px; }
    .desktop-nav-link:hover { background: var(--card-2); color: var(--text); }
    .desktop-nav-link.active { background: rgba(168, 85, 247, 0.12); color: var(--accent); font-weight: 700; }

    #bottom-fixed .bottom-fixed-inner,
    #bottom-fixed .nav-center-btn,
    #bottom-fixed .nav-center-label { display: none; }
    #bottom-fixed { max-width: 1300px; margin: 0 auto; }
    #bottom-fixed .compose-bar { max-width: 780px; margin: 0 auto 18px; }

    /* ── Canvas: a real desktop-width content column ──
       main itself stays full-width (not max-width + margin:auto) so its
       own scrollbar — main is what actually scrolls, not body — lands at
       the true browser edge instead of floating wherever a narrower box
       happened to end. The centered-column look comes from padding
       instead, via a shared var so the banners' edge-to-edge break-out
       can cancel exactly whatever that padding currently is. */
    main {
      max-width: none;
      --content-pad: max(40px, calc((100% - 1300px) / 2));
      padding-top: 28px;
      padding-bottom: 60px;
      padding-inline: var(--content-pad);
    }
    main::-webkit-scrollbar { width: 10px; }
    main::-webkit-scrollbar-track { background: transparent; }
    main::-webkit-scrollbar-thumb { background: rgba(100, 116, 139, 0.25); border-radius: 999px; }
    main::-webkit-scrollbar-thumb:hover { background: rgba(100, 116, 139, 0.4); }
    .home-banner, .category-banner {
      margin: -28px calc(-1 * var(--content-pad)) 24px calc(-1 * var(--content-pad));
      min-height: 260px;
      border-radius: 0 0 28px 28px;
    }
    .home-banner-text h2 { font-size: 24px; }
    .home-banner-text p { font-size: 15px; }
    .category-banner-title { font-size: 24px; }
    .home-banner-icon { font-size: 56px; }

    /* ── Grids: category tiles flow as one wide grid instead of a
       horizontally-paged phone carousel — dots become meaningless once
       there's no horizontal paging, so they're hidden alongside it. ── */
    .category-pager { display: flex; flex-direction: column; overflow: visible; }
    .category-page {
      flex: none;
      grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
      grid-template-rows: none;
      gap: 18px 10px;
    }
    .category-dots { display: none; }
    .category-icon { width: 73px; height: 73px; border-radius: 21px; font-size: 25px; }
    .category-item:hover .category-icon { transform: translateY(-3px); }
    .category-label { font-size: 14px; font-weight: 700; }

    .embassy-list { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 14px; }
    .embassy-card { min-height: 100px; padding: 16px 10px; }
    .embassy-card:hover { transform: translateY(-2px); box-shadow: 0 10px 20px rgba(99, 102, 241, 0.14); }

    #news-feed {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
      gap: 18px;
    }
    .news-card { margin-bottom: 0; }
    .news-card:hover { box-shadow: 0 8px 24px rgba(99, 102, 241, 0.16); }

    #dashboard-orders, #payments-list {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
      gap: 14px;
      align-items: start;
    }
    #dashboard-orders .order-row, #payments-list .order-row { margin-bottom: 0; }

    /* ── Reading-width content: forms and chat stay comfortably narrow
       and centered even though the canvas around them is wide — the
       same technique news sites use for article body text. ── */
    .form-card { max-width: 640px; margin: 0 auto; }
    .modal-overlay .form-card { margin: 0; }
    #step-support, #step-reviews { max-width: 760px; margin: 0 auto; width: 100%; }

    .submit-btn, .lang-option { cursor: pointer; }
  }
/* ── Display, accessibility and data screens ───────────────────────── */
.a11y-lead,.pv-lead{font-size:13.5px;color:var(--i-3);line-height:1.8;margin:0 0 18px}
.a11y-group{background:var(--s-0);border:1px solid var(--s-3);border-radius:12px;
  padding:14px 15px;margin-bottom:11px}
.a11y-label{font-size:14px;font-weight:700;color:var(--i-1);margin-bottom:10px}
.a11y-opts{display:flex;gap:7px;flex-wrap:wrap}
.a11y-opt{flex:1 1 auto;min-width:64px;border:1px solid var(--s-4);background:var(--s-0);
  color:var(--i-2);border-radius:9px;height:40px;padding:0 12px;font-size:13.5px;
  font-family:inherit;cursor:pointer}
.a11y-opt.on{border-color:var(--accent);background:var(--t-brand-2);color: var(--fg-brand);font-weight:700}
.a11y-sample{margin-top:11px;padding:11px 13px;border-radius:9px;background:var(--s-2);
  color:var(--i-0);font-size:15px;line-height:1.9}
.a11y-note{margin-top:9px;font-size:12.5px;color:var(--i-3);line-height:1.7}
.pv-list{background:var(--s-0);border:1px solid var(--s-3);border-radius:12px;
  padding:6px 15px;margin-bottom:16px}
.pv-row{display:flex;align-items:center;justify-content:space-between;
  padding:10px 0;border-bottom:1px solid var(--s-2);font-size:14px;color:var(--i-2)}
.pv-row:last-child{border-bottom:0}
.pv-row b{color:var(--i-0);font-weight:700}
.pv-btn{width:100%;border:1px solid var(--s-4);background:var(--s-0);color:var(--i-1);
  border-radius:11px;height:46px;font-size:14.5px;font-weight:700;font-family:inherit;
  cursor:pointer;margin-bottom:10px}
.pv-btn:hover{border-color:var(--accent)}
.pv-btn.danger{border-color:#ef4444;color: var(--fg-bad);background:var(--s-0)}
.pv-btn.danger:hover{background:var(--t-bad)}
.pv-danger{margin-top:22px;border:1px solid #ef4444;border-radius:12px;
  padding:15px;background:var(--t-bad)}
.pv-danger-h{font-size:15px;font-weight:700;color: var(--fg-bad);margin-bottom:7px}
.pv-danger-p{font-size:13.5px;color:var(--i-2);line-height:1.85;margin:0 0 12px}
.pv-input{width:100%;box-sizing:border-box;border:1px solid var(--s-4);border-radius:9px;
  height:44px;padding:0 12px;font-size:15px;font-family:inherit;margin-bottom:10px;
  background:var(--s-0);color:var(--i-0)}
.pv-msg{font-size:13px;color: var(--fg-bad);line-height:1.7;min-height:18px}

  /* ── The German course ──────────────────────────────────────────────
     Built from js/german.js. Uses the page's tokens throughout, so it
     follows the theme and the text-size setting like every other screen. */
  .de-wrap { display: flex; flex-direction: column; gap: 8px; padding: 4px 0 28px; }
  /* Module, unit and alphabet views have no banner under the floating header,
     so they need head room or their first line slides beneath it. */
  .de-wrap.de-inner { padding-top: 62px; }

  /* The hero is this page's banner. It bleeds past main's 16px padding to
     both edges and to the top, and carries enough head room for the floating
     header — back arrow, bell and language — to sit on it. The extra 4px on
     top cancels .de-wrap's own 4px top padding too — main's padding alone
     left a 4px sliver of page showing above the banner. */
  .de-hero {
    margin: -20px -12.8px 4px -12.8px;
    padding: 76px 20px 22px;
    border-radius: 0 0 24px 24px;
    color: #fff;
    background: linear-gradient(135deg, #1f2544 0%, #4f46e5 55%, #a855f7 100%);
    box-shadow: 0 10px 24px rgba(168, 85, 247, .28);
  }

  /* The photo banner this replaced. */
  #step-deutsch .category-banner { display: none; }
  .de-hero-badge {
    display: inline-block; padding: 5px 11px; border-radius: 999px;
    background: rgba(255,255,255,.16); font-size: 11.5px; font-weight: 800;
  }
  .de-hero-title { margin: 12px 0 4px; font-size: 21px; font-weight: 900; }
  .de-hero-sub { margin: 0; font-size: 13px; line-height: 1.8; opacity: .9; }
  .de-hero-stats { display: flex; gap: 8px; margin-top: 18px; }
  .de-hero-stats > div {
    flex: 1; background: rgba(255,255,255,.13); border-radius: 13px;
    padding: 9px 6px; text-align: center;
  }
  .de-hero-stats b { display: block; font-size: 17px; font-weight: 900; }
  .de-hero-stats span { font-size: 10px; opacity: .85; }

  .de-section-title { margin: 4px 0 0; font-size: 15px; font-weight: 900; color: var(--text); }

  .de-levels { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; }
  .de-level {
    flex: 0 0 auto; padding: 9px 16px; border-radius: 999px; cursor: pointer;
    border: 1px solid var(--border); background: var(--card); color: var(--text);
    font-family: inherit; font-size: 13px; font-weight: 800;
  }
  .de-level.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

  .de-modules { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .de-module {
    display: flex; flex-direction: column; gap: 4px; text-align: start; cursor: pointer;
    padding: 13px; border-radius: 17px; border: 1px solid var(--border);
    background: var(--card); color: var(--text); font-family: inherit;
    box-shadow: var(--shadow);
  }
  .de-module-top { display: flex; align-items: center; justify-content: space-between; }
  .de-module-no { font-size: 11px; font-weight: 900; color: var(--accent); }
  .de-module-icon { font-size: 19px; }
  .de-module-title { font-size: 13.5px; font-weight: 900; }
  .de-module-sub { font-size: 11px; line-height: 1.6; color: var(--text-dim); min-height: 34px; }
  .de-module-foot {
    display: flex; justify-content: space-between;
    font-size: 10.5px; font-weight: 800; color: var(--text-dim);
  }

  .de-bar { height: 5px; border-radius: 4px; background: var(--card-2); overflow: hidden; margin-top: 4px; }
  .de-bar-fill { height: 5px; border-radius: 4px; background: var(--accent-2); }

  .de-bar-head { display: flex; align-items: center; gap: 11px; }
  .de-bar-title { font-size: 15.5px; font-weight: 900; color: var(--text); }
  .de-bar-sub { font-size: 11.5px; color: var(--text-dim); margin-top: 2px; }

  /* The unit list as one connected trail — a node per unit, a stem running
     from it down to the next, sequentially unlocked — rather than a flat
     stack of equal cards, inspired by the reference app's chapter path. */
  .de-units { display: flex; flex-direction: column; gap: 8px; }
  .de-unit {
    display: flex; align-items: stretch; gap: 11px; width: 100%; box-sizing: border-box;
    padding: 12px; border-radius: 15px; cursor: pointer; text-align: start;
    border: 1px solid var(--border); background: var(--card); color: var(--text);
    font-family: inherit;
  }
  .de-unit.done { border-color: var(--accent-2); }
  .de-unit.locked { opacity: .6; cursor: default; }
  .de-unit-node { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; }
  .de-unit-no {
    width: 36px; height: 36px; flex-shrink: 0; border-radius: 50%;
    background: var(--card-2); color: var(--accent);
    display: flex; align-items: center; justify-content: center;
    font-size: 13px; font-weight: 900;
  }
  .de-unit.done .de-unit-no { background: var(--t-good-2); color: #15803d; }
  .de-unit.locked .de-unit-no { color: var(--text-dim); font-size: 15px; }
  .de-unit-stem { flex: 1; width: 2px; min-height: 10px; margin-top: 4px; background: var(--border); }
  .de-unit.done .de-unit-stem { background: var(--accent-2); }
  .de-unit-body { display: flex; flex-direction: column; gap: 2px; flex: 1; justify-content: center; }
  .de-unit-title { font-size: 13px; font-weight: 800; }
  .de-unit-goal { font-size: 11px; line-height: 1.6; color: var(--text-dim); }

  /* One chapter's lessons at a time — the chevron on the banner (below)
     shows which way it opens; this is just its collapsible body. */
  .de-chapter-lessons { display: flex; flex-direction: column; gap: 8px; }
  .de-chapter-lessons[hidden] { display: none; }

  /* The card that closes out a chapter, after its last lesson — locked
     until every lesson above it is done, same rule as a locked lesson
     node. There is no exam to take yet, so tapping it unlocked says so
     instead of pretending to open something. */
  .de-exam-card {
    display: flex; align-items: center; gap: 14px; width: 100%; box-sizing: border-box;
    padding: 16px 18px; border-radius: 18px; cursor: pointer; text-align: start;
    border: 0; font-family: inherit; color: #fff;
    background: linear-gradient(135deg, #8a6d47, #6a5236);
    box-shadow: var(--shadow);
  }
  .de-exam-card.locked { background: linear-gradient(135deg, #9a9186, #7d766c); opacity: .75; }
  .de-exam-icon { font-size: 30px; flex-shrink: 0; filter: drop-shadow(0 4px 8px rgba(0, 0, 0, .3)); }
  .de-exam-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
  .de-exam-title { display: flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 900; }
  .de-exam-title iconify-icon { font-size: 15px; }
  .de-exam-dot { width: 6px; height: 6px; border-radius: 50%; background: #fff; flex-shrink: 0; }
  .de-exam-sub { font-size: 11.5px; line-height: 1.6; color: rgba(255, 255, 255, .85); }

  /* "گیم" — the same lesson list as "شروع", but a big round avatar per
     lesson (the level's own art, done ones checked off) instead of a small
     numbered circle, matching the reference app's own Home screen. */
  .de-game-lesson {
    display: flex; align-items: center; gap: 12px; width: 100%; box-sizing: border-box;
    padding: 10px 14px 10px 10px; border-radius: 16px; cursor: pointer; text-align: start;
    border: 1px solid var(--border); background: var(--card); color: var(--text);
    font-family: inherit;
  }
  .de-game-lesson.done { border-color: var(--accent-2); }
  .de-game-lesson.locked { opacity: .6; cursor: default; }
  .de-game-lesson-avatar {
    width: 52px; height: 52px; flex-shrink: 0; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-size: 24px; color: #fff; box-shadow: 0 4px 10px rgba(0, 0, 0, .18);
  }
  .de-game-lesson.locked .de-game-lesson-avatar { filter: grayscale(1); opacity: .8; }
  .de-game-lesson.done .de-game-lesson-avatar { font-size: 22px; }
  .de-game-lesson-body { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
  .de-game-lesson-title { font-size: 13.5px; font-weight: 800; }
  .de-game-lesson-goal {
    font-size: 11px; line-height: 1.5; color: var(--text-dim);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  .de-goal {
    padding: 13px; border-radius: 15px; background: var(--card-2);
    font-size: 12.5px; line-height: 1.9; color: var(--text-dim);
  }

  .de-steps { display: flex; flex-direction: column; gap: 8px; }
  .de-step { border: 1px solid var(--border); border-radius: 15px; background: var(--card); overflow: hidden; }
  .de-step.done { border-color: #16a34a; }
  .de-step.done .de-step-no { background: var(--t-good-2, #dcfce7); color: #16a34a; }
  .de-step-head {
    display: flex; align-items: center; gap: 10px; width: 100%; box-sizing: border-box;
    padding: 13px; cursor: pointer; text-align: start;
    background: none; border: 0; color: var(--text); font-family: inherit;
  }
  .de-step-head iconify-icon { margin-inline-start: auto; font-size: 20px; color: var(--text-dim); transition: transform .18s; }
  .de-step-head.open iconify-icon { transform: rotate(180deg); }
  .de-step-no {
    width: 26px; height: 26px; flex-shrink: 0; border-radius: 9px;
    background: var(--card-2); color: var(--accent);
    display: flex; align-items: center; justify-content: center;
    font-size: 12px; font-weight: 900;
  }
  .de-step-name { font-size: 13px; font-weight: 800; }
  /* Completion lives here, separate from the A–F badge, so finishing a
     card never costs it its own letter. */
  .de-step-check {
    width: 20px; height: 20px; flex-shrink: 0; border-radius: 50%;
    border: 2px solid var(--border); background: var(--card);
    display: flex; align-items: center; justify-content: center;
    font-size: 13px; color: #fff;
  }
  .de-step-check.done { border-color: #16a34a; background: #16a34a; }
  .de-step-body { padding: 0 13px 14px; display: flex; flex-direction: column; gap: 9px; }
  .de-step-body[hidden] { display: none; }
  .de-step-lead { margin: 0; font-size: 12.5px; line-height: 1.9; color: var(--text-dim); }
  .de-grammar {
    padding: 10px 12px; border-radius: 11px; background: var(--card-2);
    font-size: 12.5px; font-weight: 700;
  }

  .de-word {
    display: flex; align-items: center; gap: 9px;
    padding: 9px 11px; border-radius: 12px; background: var(--card-2);
    font-size: 13px; font-weight: 700;
  }
  .de-speak {
    width: 34px; height: 34px; flex-shrink: 0; border-radius: 10px; cursor: pointer;
    border: 0; background: var(--accent); color: var(--on-accent);
    display: flex; align-items: center; justify-content: center; font-size: 17px;
  }

  .de-practice {
    display: flex; align-items: center; justify-content: center; gap: 7px;
    padding: 12px; border-radius: 13px; cursor: pointer; border: 0;
    background: #e2603f; color: #fff; font-family: inherit; font-size: 13px; font-weight: 900;
  }
  .de-practice[disabled] { opacity: .6; cursor: default; }
  .de-practice-out { padding: 10px 12px; border-radius: 11px; font-size: 12.5px; line-height: 1.7; background: var(--card-2); }
  .de-practice-out.good { background: var(--t-good-2); color: #15803d; font-weight: 800; }
  .de-practice-out.warn { background: var(--t-warn-2, #fef3c7); color: #92400e; font-weight: 800; }

  /* A topic point with no German behind it: no speaker, no false affordance. */
  .de-word-plain { background: transparent; padding: 4px 2px; font-weight: 700; color: var(--text-dim); }
  .de-pending {
    padding: 10px 12px; border-radius: 11px; font-size: 12px; line-height: 1.8;
    background: var(--card-2); color: var(--text-dim);
  }

  .de-write {
    width: 100%; box-sizing: border-box; padding: 11px; border-radius: 12px;
    border: 1px solid var(--border); background: var(--card-2); color: var(--text);
    font-family: inherit; font-size: 13px; line-height: 1.9; resize: vertical;
  }

  .de-finish {
    padding: 14px; border-radius: 15px; cursor: pointer; border: 0;
    background: var(--accent); color: var(--on-accent);
    font-family: inherit; font-size: 14px; font-weight: 900;
  }
  .de-finish.done { background: var(--accent-2); }

  /* The alphabet flow: the selected-letter card, the tips, the six steps. */
  .de-selected {
    display: flex; align-items: center; gap: 11px;
    padding: 13px; border-radius: 17px;
    border: 1px solid var(--border); background: var(--card);
  }
  .de-selected-big {
    width: 58px; height: 58px; flex-shrink: 0; border-radius: 17px;
    background: var(--card-2); color: var(--accent);
    display: flex; align-items: center; justify-content: center;
    font-size: 19px; font-weight: 900;
  }
  .de-selected-copy { flex: 1; }
  .de-selected-name { font-size: 14px; font-weight: 900; color: var(--text); }
  .de-selected-sub { font-size: 11px; color: var(--text-dim); margin-top: 4px; line-height: 1.6; }
  .de-selected-read {
    display: flex; align-items: center; gap: 5px; flex-shrink: 0; cursor: pointer;
    padding: 11px 13px; border-radius: 13px; border: 0;
    background: var(--accent); color: var(--on-accent);
    font-family: inherit; font-size: 12px; font-weight: 900;
  }

  .de-tips { padding: 13px; border-radius: 17px; background: #FFF9E9; border: 1px solid #E9DFAF; }
  .de-tips-title { font-size: 13px; font-weight: 900; color: #4a4327; }
  .de-tips-body { font-size: 11.5px; line-height: 1.9; color: #665F43; margin-top: 5px; }

  .de-dots { display: flex; gap: 7px; justify-content: space-between; }
  .de-dot {
    flex: 1; height: 34px; border-radius: 11px; background: var(--card-2);
    display: flex; align-items: center; justify-content: center;
    font-size: 12px; font-weight: 900; color: var(--text-dim);
  }
  .de-dot.on { background: #1f2544; color: #fff; }
  .de-dot.done { background: var(--accent-2); color: #fff; }

  .de-flow-card {
    padding: 16px; border-radius: 20px;
    border: 1px solid var(--border); background: var(--card);
    display: flex; flex-direction: column; gap: 9px;
  }
  .de-flow-count { font-size: 11px; font-weight: 900; color: var(--accent); }
  .de-flow-title { margin: 0; font-size: 19px; font-weight: 900; color: var(--text); }
  .de-flow-body { margin: 0; font-size: 12.5px; line-height: 1.9; color: var(--text-dim); }
  .de-flow-example {
    padding: 14px; border-radius: 16px; background: var(--card-2);
    display: flex; flex-direction: column; align-items: center; gap: 10px;
  }
  .de-flow-example-text { font-size: 16px; font-weight: 900; color: var(--text); text-align: center; }
  .de-flow-audio {
    display: flex; align-items: center; gap: 6px; cursor: pointer;
    padding: 12px 20px; border-radius: 13px; border: 0;
    background: var(--accent); color: var(--on-accent);
    font-family: inherit; font-size: 12.5px; font-weight: 900;
  }

  .de-choices { display: flex; gap: 7px; }
  .de-choice {
    flex: 1; min-height: 46px; padding: 8px; cursor: pointer;
    border-radius: 12px; border: 1px solid var(--border);
    background: var(--card); color: var(--text);
    font-family: inherit; font-size: 12.5px; font-weight: 800;
  }
  .de-choice.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

  .de-mic {
    padding: 14px; border-radius: 16px; background: #FFF6F3;
    display: flex; flex-direction: column; align-items: center; gap: 9px;
  }
  .de-mic-word { font-size: 24px; font-weight: 900; color: var(--text); }
  .de-mic-help { font-size: 11.5px; line-height: 1.8; color: var(--text-dim); text-align: center; }
  .de-repeats { display: flex; gap: 10px; }
  .de-repeat {
    width: 34px; height: 34px; border-radius: 12px; background: #E9DEDA;
    display: flex; align-items: center; justify-content: center;
    font-weight: 900; color: var(--text-dim);
  }
  .de-repeat.done { background: var(--accent-2); color: #fff; }

  .de-check {
    padding: 12px; border-radius: 13px; cursor: pointer; border: 0;
    background: #1f2544; color: #fff;
    font-family: inherit; font-size: 12.5px; font-weight: 900;
  }

  .de-flow-nav { display: flex; gap: 8px; }
  .de-flow-back {
    min-width: 86px; padding: 13px; border-radius: 14px; cursor: pointer;
    border: 1px solid var(--border); background: var(--card-2); color: var(--text);
    font-family: inherit; font-size: 12.5px; font-weight: 900;
  }
  .de-flow-next {
    flex: 1; padding: 13px; border-radius: 14px; cursor: pointer; border: 0;
    background: var(--accent-2); color: #fff;
    font-family: inherit; font-size: 12.5px; font-weight: 900;
  }
  /* Dimmed rather than disabled: a disabled button eats the tap, and the
     tap is how the learner finds out what is still missing. */
  .de-flow-next.locked { opacity: .5; }

  /* The "آموزش" card's own teaser for a picture exercise, and the exercise's
     own full screen — deliberately numbered rather than lettered, since it's
     a story followed scene by scene, not one of the unit's A–F cards.
     .de-story-images only appears once a real (non-book) picture is
     uploaded — until then the exercise works fine without it. */
  .de-story-teaser {
    padding: 10px 12px; border-radius: 11px; background: var(--card-2);
    font-size: 13px; font-weight: 800;
  }
  /* Both story screens share this: the title in German by default, its own
     translate toggle right opposite it rather than buried in the body. */
  .de-story-title-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
  .de-story-title-row .de-bar-title { flex: 1; min-width: 0; }
  /* The solved-pairs reveal list: a title row (listen + the German
     sentence + its own translate toggle) with the Persian line appearing
     underneath only once asked for, not all at once. */
  .de-reveal-row {
    display: flex; flex-direction: column; gap: 7px;
    padding: 10px 12px; border-radius: 13px; background: var(--card-2);
  }
  .de-reveal-head { display: flex; align-items: center; gap: 9px; }
  .de-reveal-title { flex: 1; min-width: 0; font-size: 13px; font-weight: 800; color: var(--text); text-align: left; }
  .de-reveal-fa { font-size: 12.5px; line-height: 1.8; color: var(--text-dim); }

  /* A one-shot confetti eruption from the bottom — fired once, the first
     time a dailyPart card's own exercise is actually finished, not on
     every later visit. A high z-index on purpose: this has to sit above
     everything else the app shell stacks on top of the page content. */
  .de-confetti-burst { position: fixed; inset: 0; pointer-events: none; z-index: 99999; overflow: hidden; }
  .de-confetti-piece {
    position: absolute; bottom: 0; width: 8px; height: 14px; border-radius: 2px;
    opacity: 0;
    animation: de-confetti-erupt 1.7s cubic-bezier(.15, .6, .4, 1) forwards;
    animation-delay: var(--delay, 0s);
  }
  @keyframes de-confetti-erupt {
    0% { opacity: 1; transform: translate(0, 0) rotate(0deg); }
    55% { opacity: 1; transform: translate(calc(var(--x, 0) * .5), var(--rise, -60vh)) rotate(calc(var(--r, 360deg) * .6)); }
    100% { opacity: 0; transform: translate(var(--x, 0), 6vh) rotate(var(--r, 360deg)); }
  }
  /* Reduced motion still gets a visible signal — completing a section is
     real feedback, not décor — just a fade instead of movement. */
  .de-confetti-piece.reduced {
    bottom: 40%; animation: de-confetti-fade 1.4s ease forwards; animation-delay: var(--delay, 0s);
  }
  @keyframes de-confetti-fade {
    0% { opacity: 0; }
    20% { opacity: 1; }
    80% { opacity: 1; }
    100% { opacity: 0; }
  }
  .de-story-images {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 9px;
    margin: 2px 0 4px;
  }
  .de-story-images img {
    display: block; width: 100%; aspect-ratio: 4 / 5; object-fit: cover;
    object-position: top; border-radius: 16px; border: 1px solid var(--border);
    background: var(--card-2);
  }
  .de-story-placeholder {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
    padding: 30px 16px; margin: 2px 0 4px; border-radius: 16px;
    border: 1px dashed var(--border); background: var(--card-2); color: var(--text-dim);
  }
  .de-story-placeholder iconify-icon { font-size: 34px; }
  .de-story-placeholder span { font-size: 12px; font-weight: 700; }
  .de-story-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .de-story-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 7px; }
  .de-story-item {
    display: flex; align-items: center; gap: 7px; min-height: 48px; padding: 8px;
    cursor: pointer; border-radius: 13px; border: 1px solid var(--border);
    background: var(--card); color: var(--text); font: inherit; font-size: 11px;
    font-weight: 800; line-height: 1.45; text-align: start;
  }
  .de-story-item.on { border-color: var(--accent); background: var(--card-2); }
  .de-story-number {
    display: inline-flex; flex: 0 0 24px; width: 24px; height: 24px;
    align-items: center; justify-content: center; border-radius: 8px;
    background: var(--accent); color: var(--on-accent); font-size: 11px; font-weight: 900;
  }
  .de-story-card {
    display: flex; flex-direction: column; gap: 9px; padding: 15px;
    border: 1px solid var(--border); border-radius: 18px; background: var(--card);
  }
  .de-story-kicker { color: var(--accent); font-size: 11px; font-weight: 900; }
  .de-story-sentence { color: var(--text); font-size: 17px; font-weight: 900; text-align: left; }
  .de-story-translation { color: var(--text-dim); font-size: 12.5px; font-weight: 700; line-height: 1.8; }
  .de-story-card .de-flow-audio { align-self: flex-start; }
  @media (max-width: 390px) {
    .de-story-list { grid-template-columns: 1fr; }
  }

  /* Vocabulary flashcards — a unit lesson's first step. */
  .de-vocab-list { display: flex; flex-direction: column; gap: 9px; }
  .de-vocab-card { padding: 12px; border-radius: 15px; border: 1px solid var(--border); background: var(--card); }
  .de-vocab-top { display: flex; align-items: center; gap: 9px; }
  .de-vocab-de { font-size: 15px; font-weight: 900; color: var(--text); }
  .de-vocab-fa { margin-inline-start: auto; font-size: 12px; font-weight: 700; color: var(--text-dim); }
  .de-vocab-example { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--border); display: flex; flex-direction: column; gap: 3px; }
  .de-vocab-example span:first-child { font-size: 12.5px; font-weight: 700; color: var(--text); }
  .de-vocab-example-fa { font-size: 11.5px; color: var(--text-dim); }
  /* Which of the stage's N words this is — distinct from .de-flow-count
     above it (which stage of the whole lesson), so the two never read as
     the same number repeated twice. */
  .de-vocab-progress { text-align: center; font-size: 11px; font-weight: 800; color: var(--text-dim); }

  /* Matching pairs. */
  .de-match-grid { display: flex; gap: 9px; }
  .de-match-col { flex: 1; display: flex; flex-direction: column; gap: 7px; }
  .de-match-item {
    padding: 11px 8px; border-radius: 12px; cursor: pointer;
    border: 1px solid var(--border); background: var(--card); color: var(--text);
    font-family: inherit; font-size: 12.5px; font-weight: 800; text-align: center;
    transition: transform .12s ease, background .18s ease, border-color .18s ease;
  }
  .de-match-item:active { transform: scale(.96); }
  .de-match-item.picked { background: #1f2544; border-color: #1f2544; color: #fff; }
  .de-match-item.matched { background: var(--t-good-2, #dcfce7); border-color: #16a34a; color: #14532d; cursor: default; }
  .de-match-item.mistake {
    background: #fee2e2; border-color: #ef4444; color: #991b1b;
    animation: de-match-shake .34s ease;
  }
  @keyframes de-match-shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-5px); }
    75% { transform: translateX(5px); }
  }
  @media (prefers-reduced-motion: reduce) {
    .de-match-item.mistake { animation: none; }
  }

  /* Word order. Forced ltr: the sentence being built is German, so the
     chips must accumulate left-to-right regardless of the page's own rtl,
     the same fix used for phone codes and chat bubbles elsewhere here. */
  .de-order-answer {
    min-height: 46px; padding: 10px; border-radius: 13px; background: var(--card-2);
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
    direction: ltr; justify-content: flex-start;
  }
  .de-order-token {
    padding: 7px 10px; border-radius: 9px; background: var(--accent); color: var(--on-accent);
    font-size: 12.5px; font-weight: 800;
  }
  .de-order-placeholder { color: var(--text-dim); font-size: 12.5px; }
  .de-order-pool { display: flex; flex-wrap: wrap; gap: 7px; direction: ltr; justify-content: flex-start; }
  .de-order-chip {
    padding: 9px 12px; border-radius: 11px; cursor: pointer;
    border: 1px solid var(--border); background: var(--card); color: var(--text);
    font-family: inherit; font-size: 12.5px; font-weight: 800;
  }
  .de-order-chip.used { opacity: .35; cursor: default; }
  .de-flow-nav-inline { display: flex; gap: 8px; }
  .de-order-reset {
    padding: 12px; border-radius: 13px; cursor: pointer;
    border: 1px solid var(--border); background: var(--card-2); color: var(--text);
    font-family: inherit; font-size: 12.5px; font-weight: 900;
  }

  /* The lesson-complete card, and the badge on a review unit. */
  .de-lesson-done { align-items: center; text-align: center; }
  .de-lesson-done-icon { font-size: 40px; }
  .de-review-badge {
    align-self: flex-start; padding: 5px 11px; border-radius: 999px;
    background: var(--card-2); color: var(--accent); font-size: 11px; font-weight: 900;
  }

  /* "شروع" — a rotating hero banner (cross-fading through the level
     illustrations, like the site's own home-page banner), a swipeable
     A1–C1 level carousel, a "you are here" banner, then the flat
     chapter-by-chapter path below it. */
  /* Flush against the very top of the page, edge to edge — the same
     breakout (cancelling main's own 16px padding) .de-hero and the site's
     own .home-banner both use, not a card sitting inside the page margin.
     The extra 4px on top also cancels .de-wrap's own 4px top padding, which
     main's padding alone left showing as a sliver above the banner. */
  .de-start-banner {
    position: relative; height: 148px; margin: -20px -12.8px 4px -12.8px;
    border-radius: 0 0 20px 20px; overflow: hidden; box-shadow: var(--shadow);
  }
  .de-start-banner-layer {
    position: absolute; inset: 0;
    opacity: 0; transition: opacity 1s ease;
  }
  .de-start-banner-layer.active { opacity: 1; }
  /* The art bleeds in big, off one edge, the way a real cover photo would —
     not a small centred icon — and a bottom scrim (same treatment as the
     site's own home banner) sits over both fading layers for depth. */
  .de-start-banner-layer svg {
    position: absolute; inset-inline-end: -8%; top: 50%; transform: translateY(-50%);
    width: 60%; max-width: 210px;
    filter: drop-shadow(0 10px 20px rgba(0, 0, 0, .35));
  }
  .de-start-banner-scrim {
    position: absolute; inset: 0; z-index: 1; pointer-events: none;
    background: linear-gradient(180deg, rgba(0, 0, 0, 0) 30%, rgba(0, 0, 0, .5) 100%);
  }

  /* Points / streak / this month, at a glance — the same job the reference
     app's own top-bar row does, right under the banner instead of pinned
     above it since this page has no header bar of its own to share with. */
  .de-start-stats { display: flex; gap: 8px; padding: 0 2px; }
  .de-start-stat {
    flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px;
    padding: 8px; border-radius: 13px; background: var(--card); border: 1px solid var(--border);
    font-size: 13px; font-weight: 900; color: var(--text);
  }
  .de-start-stat iconify-icon { font-size: 17px; }
  .de-start-stat:nth-child(1) iconify-icon { color: #f59e0b; }
  .de-start-stat:nth-child(2) iconify-icon { color: #ef4444; }
  .de-start-stat:nth-child(3) iconify-icon { color: #2563eb; }

  .de-level-carousel {
    /* Bottom/top padding sized to the cards' own box-shadow (0 8px 18px) —
       an auto-scrolling container clips vertically too, so too little of
       this and the shadow gets sliced off in a visible straight line. */
    display: flex; gap: 6px; overflow-x: auto; padding: 10px 2px 24px;
    scroll-snap-type: x mandatory; scroll-padding: 0 15%;
    scrollbar-width: none; -ms-overflow-style: none; /* Firefox / old Edge */
  }
  .de-level-carousel::-webkit-scrollbar { display: none; } /* Chrome / Safari */

  /* The alphabet shortcut, right under the carousel — German 0 sits outside
     the A1–C1 cards by design, but its progress is real (letters actually
     finished), so it still earns a visible entry point here. */
  .de-alpha-shortcut {
    display: flex; align-items: center; gap: 12px; text-align: start;
    /* Matches the chapter banner's own sizing below it: a flex box needs an
       explicit width since (unlike a plain block) it won't stretch to fill
       the container on its own, and 100% plus this same 2px+2px margin
       overflowed the card past its neighbours by 4px. */
    width: calc(100% - 4px); margin: 8px 2px 0; padding: 14px 16px; border-radius: 17px;
    background: var(--card); border: 1px solid var(--border); box-shadow: var(--shadow);
    font-family: inherit; cursor: pointer;
  }
  .de-alpha-shortcut-icon {
    font-size: 22px; flex-shrink: 0; width: 44px; height: 44px; border-radius: 13px;
    background: var(--card-2); display: flex; align-items: center; justify-content: center;
  }
  .de-alpha-shortcut-text { display: flex; flex-direction: column; gap: 5px; min-width: 0; flex: 1; }
  .de-alpha-shortcut-title { font-size: 13.5px; font-weight: 900; color: var(--text); }
  .de-alpha-shortcut-sub { font-size: 11px; color: var(--text-dim); }

  /* Leitner's own entry card on "مدیریت" — same shape as the alphabet
     shortcut above (it plays the same "open a whole feature from a card"
     role), plus a due-count badge since that's the one number worth
     seeing before you tap in. */
  .de-leitner-entry {
    display: flex; align-items: center; gap: 12px; text-align: start;
    width: calc(100% - 4px); margin: 0 2px; padding: 14px 16px; border-radius: 17px;
    background: var(--card); border: 1px solid var(--border); box-shadow: var(--shadow);
    font-family: inherit; cursor: pointer;
  }
  .de-leitner-entry-icon {
    font-size: 20px; flex-shrink: 0; width: 44px; height: 44px; border-radius: 13px;
    background: var(--card-2); color: var(--accent);
    display: flex; align-items: center; justify-content: center;
  }
  .de-leitner-entry-text { display: flex; flex-direction: column; gap: 5px; min-width: 0; flex: 1; }
  .de-leitner-entry-title { font-size: 13.5px; font-weight: 900; color: var(--text); }
  .de-leitner-entry-sub { font-size: 11px; color: var(--text-dim); }
  .de-leitner-entry-badge {
    flex-shrink: 0; min-width: 22px; height: 22px; padding: 0 6px; border-radius: 999px;
    background: #ef4444; color: #fff; font-size: 11.5px; font-weight: 900;
    display: flex; align-items: center; justify-content: center;
  }

  /* The whole card's background IS the level's picture — the gradient plus
     its hand-drawn scene bleeding off one corner — not a neutral card with
     a small icon tile stuck on it. Text sits on top in white. 25% shorter
     than the earlier two-tone (photo-strip + white body) version. */
  .de-level-card {
    flex: 0 0 88%; scroll-snap-align: center; cursor: pointer; text-align: start;
    position: relative; overflow: hidden; min-height: 160px;
    display: flex; flex-direction: column; justify-content: flex-end; gap: 6px;
    padding: 16px 18px; border-radius: 22px; border: 0; color: #fff;
    font-family: inherit; opacity: .5; transform: scale(.94);
    transition: opacity .22s ease, transform .22s ease;
    box-shadow: 0 8px 18px rgba(0, 0, 0, .22);
  }
  .de-level-card.on { opacity: 1; transform: scale(1); }
  .de-level-card-art {
    position: absolute; inset-inline-end: -16px; top: -16px;
    width: 104px; height: 104px; opacity: .95; pointer-events: none;
  }
  .de-level-card-art svg { width: 100%; height: 100%; display: block; filter: drop-shadow(0 6px 12px rgba(0, 0, 0, .28)); }
  .de-level-card-body { position: relative; z-index: 1; display: flex; flex-direction: column; gap: 6px; }
  .de-level-card-name { font-size: 20px; font-weight: 900; }
  .de-level-card-progress { font-size: 11px; font-weight: 800; opacity: .9; }
  .de-level-card .de-bar { background: rgba(255, 255, 255, .25); }
  .de-level-card .de-bar-fill { background: #fff; }
  .de-level-card-desc { margin-top: 2px; font-size: 10.5px; line-height: 1.55; opacity: .92; }

  /* The banner at the start of every chapter — same idea as the level
     card: its background IS the level's picture, not a neutral card with
     a boxed icon on it. The level badge in its corner is the only place
     "A1" (etc.) appears now that the page header no longer repeats it. */
  .de-path-banner {
    position: relative; overflow: hidden; display: flex; align-items: center;
    margin: 8px 2px 0; padding: 12px 16px; min-height: 48px; border-radius: 18px; color: #fff;
    border: 1px solid var(--border); box-shadow: var(--shadow);
    /* It doubles as the chapter accordion's toggle button now. */
    width: calc(100% - 4px); text-align: start; cursor: pointer; font-family: inherit;
  }
  .de-path-banner-chevron {
    position: absolute; bottom: 7px; inset-inline-end: 10px; z-index: 2;
    font-size: 18px; color: rgba(255, 255, 255, .85);
    transition: transform .2s;
  }
  .de-path-banner.open .de-path-banner-chevron { transform: rotate(180deg); }
  .de-path-banner-level {
    /* Same corner as the art (inset-inline-end), not a hardcoded physical
       side — pinned to "left" it sat clear of the title in RTL by luck, but
       collided with it once the page ran left-to-right. */
    position: absolute; top: 8px; inset-inline-end: 8px; z-index: 2;
    padding: 2px 7px; border-radius: 999px; background: rgba(255, 255, 255, .25); color: #fff;
    font-size: 9.5px; font-weight: 900; backdrop-filter: blur(2px);
  }
  .de-path-banner-art {
    position: absolute; inset-inline-end: -18px; bottom: -27px;
    width: 94px; height: 94px; opacity: .9; pointer-events: none;
  }
  .de-path-banner-art svg { width: 100%; height: 100%; display: block; filter: drop-shadow(0 6px 12px rgba(0, 0, 0, .28)); }
  .de-path-banner-text { position: relative; z-index: 1; display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; max-width: 100%; }
  .de-path-banner-title { font-size: 13px; font-weight: 900; color: #fff; }
  .de-path-banner-sub { font-size: 11px; line-height: 1.45; color: rgba(255, 255, 255, .85); }

  /* "مدیریت" — points are a plain formula on real completions, the streak
     and calendar come from a real per-day activity log; nothing here is
     invented per visit. */
  .de-mgmt-profile { display: flex; align-items: center; gap: 12px; }
  .de-mgmt-avatar {
    width: 46px; height: 46px; flex-shrink: 0; border-radius: 50%;
    background: linear-gradient(135deg, #1f2544, #a855f7);
    color: #fff; display: flex; align-items: center; justify-content: center; font-size: 22px;
  }
  .de-mgmt-profile-name { font-size: 15px; font-weight: 900; color: var(--text); }
  .de-mgmt-profile-head { padding: 4px 2px; }
  .de-mgmt-profile-head small { display: block; margin-top: 3px; color: var(--text-dim); font-size: 11px; }
  .de-profile-passport { display: block; width: 100%; height: 156px; padding: 0; border: 0; border-radius: 21px; perspective: 1000px; cursor: pointer; background: transparent; text-align: start; font-family: inherit; }
  .de-profile-passport-inner { position: relative; display: block; width: 100%; height: 100%; transition: transform .7s cubic-bezier(.2,.75,.2,1); transform-style: preserve-3d; }
  .de-profile-passport.flipped .de-profile-passport-inner { transform: rotateY(180deg); }
  .de-profile-passport-face { position: absolute; inset: 0; box-sizing: border-box; display: flex; align-items: center; justify-content: center; gap: 9px; padding: 18px; border-radius: 21px; color: #fff; backface-visibility: hidden; -webkit-backface-visibility: hidden; overflow: hidden; box-shadow: 0 15px 30px rgba(57,46,135,.18); }
  .de-profile-passport-front { flex-direction: column; background: linear-gradient(135deg,#293b87,#7048b6); }
  .de-profile-passport-front::after { content:''; position:absolute; width:170px; height:170px; border-radius:50%; background:rgba(255,255,255,.12); inset-inline-end:-52px; top:-82px; }
  .de-profile-passport-front iconify-icon,.de-profile-passport-front b,.de-profile-passport-front small,.de-profile-passport-front em { position:relative; z-index:1; }
  .de-profile-passport-front iconify-icon { font-size:31px; }
  .de-profile-passport-front b { font-size:22px; }
  .de-profile-passport-front small,.de-profile-passport-front em { font-size:11px; font-style:normal; opacity:.87; }
  .de-profile-passport-front em { margin-top:4px; padding:4px 9px; border:1px solid rgba(255,255,255,.22); border-radius:999px; background:rgba(255,255,255,.11); }
  .de-profile-passport-back { transform:rotateY(180deg); justify-content:space-around; background:linear-gradient(135deg,#3a2d74,#256a93); }
  .de-profile-passport-back > span { display:flex; flex-direction:column; align-items:center; gap:5px; text-align:center; }
  .de-profile-passport-back b { font-size:18px; font-weight:950; }
  .de-profile-passport-back small { font-size:10px; opacity:.82; line-height:1.5; }

  .de-mgmt-goal {
    display: flex; align-items: center; gap: 12px; padding: 14px 16px;
    border-radius: 17px; color: #fff; border: 0; text-align: start;
    font-family: inherit; cursor: pointer;
    background: linear-gradient(120deg, #f59e0b, #f97316);
  }
  .de-mgmt-goal.done { background: linear-gradient(120deg, #16a34a, #22c55e); }
  .de-mgmt-goal iconify-icon { font-size: 26px; flex-shrink: 0; }
  .de-mgmt-goal-title { font-size: 13.5px; font-weight: 900; }
  .de-mgmt-goal-sub { margin-top: 2px; font-size: 11.5px; line-height: 1.6; opacity: .92; }

  .de-mgmt-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .de-mgmt-card {
    display: block; width: 100%; padding: 14px; border-radius: 17px; border: 1px solid var(--border);
    background: var(--card); box-shadow: var(--shadow); text-align: start;
    font-family: inherit; cursor: pointer;
  }
  .de-mgmt-card iconify-icon { font-size: 18px; }
  .de-mgmt-value { margin-top: 4px; font-size: 19px; font-weight: 900; color: var(--text); }
  .de-mgmt-label { margin-top: 3px; font-size: 11px; line-height: 1.6; color: var(--text-dim); }
  .de-mgmt-points iconify-icon { color: #f59e0b; }
  .de-mgmt-streak iconify-icon { color: #f97316; }
  .de-mgmt-level { grid-column: 1 / -1; }
  .de-voice-setting {
    width: 100%; display: grid; grid-template-columns: 48px 1fr 24px; align-items: center; gap: 12px;
    padding: 14px; border: 1px solid color-mix(in srgb,#2563eb 28%,var(--border)); border-radius: 18px;
    color: var(--text); background: linear-gradient(135deg,color-mix(in srgb,#2563eb 8%,var(--card)),var(--card));
    text-align: start; cursor: pointer; font-family: inherit; box-shadow: 0 8px 22px rgba(37,99,235,.07);
    transition: transform .18s ease, box-shadow .18s ease;
  }
  .de-voice-setting:active { transform: scale(.985); }
  .de-voice-setting-icon { display: grid; place-items: center; width: 48px; height: 48px; border-radius: 15px; color: #fff; background: linear-gradient(145deg,#2563eb,#4338ca); box-shadow: 0 8px 18px rgba(37,99,235,.24); }
  .de-voice-setting-icon iconify-icon { font-size: 25px; }
  .de-voice-setting > span:nth-child(2) { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .de-voice-setting b { font-size: 14px; font-weight: 950; }
  .de-voice-setting em { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #2563eb; font-size: 12px; font-style: normal; font-weight: 800; }
  .de-voice-setting small { color: var(--text-dim); font-size: 11px; line-height: 1.55; }

  /* A real month grid: header (month name + prev/next), a weekday row,
     then a 7-column grid where each date sits under its actual weekday —
     empty cells pad out the first week so day 1 lands in the right column. */
  .de-mgmt-calendar {
    padding: 16px; border-radius: 18px; border: 1px solid var(--border);
    background: var(--card); box-shadow: var(--shadow);
  }
  .de-cal-head { display: flex; align-items: center; justify-content: center; gap: 14px; }
  .de-cal-month { font-size: 14px; font-weight: 900; color: var(--text); min-width: 120px; text-align: center; }
  .de-cal-nav {
    width: 30px; height: 30px; border-radius: 50%; cursor: pointer; border: 0;
    background: var(--card-2); color: var(--text); display: flex; align-items: center; justify-content: center;
  }
  .de-cal-nav iconify-icon { font-size: 17px; }
  .de-cal-weekdays, .de-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
  .de-cal-weekdays { margin-top: 14px; }
  .de-cal-weekdays span { text-align: center; font-size: 10.5px; font-weight: 800; color: var(--text-dim); }
  .de-cal-grid { margin-top: 6px; }
  .de-cal-cell {
    aspect-ratio: 1; border-radius: 10px; background: var(--card-2);
    display: flex; align-items: center; justify-content: center;
    font-size: 11px; font-weight: 800; color: var(--text-dim);
    font-variant-numeric: tabular-nums;
  }
  .de-cal-cell.empty { background: transparent; }
  .de-cal-cell.active { background: var(--t-good-2); color: #15803d; }
  .de-cal-cell.today { box-shadow: inset 0 0 0 2px var(--accent); }

  /* Achievements — real progress on real milestones. */
  .de-ach-list { display: flex; flex-direction: column; gap: 9px; }
  .de-ach-row {
    display: flex; align-items: center; gap: 12px;
    padding: 12px; border-radius: 15px; border: 1px solid var(--border); background: var(--card);
  }
  .de-ach-row.done { border-color: var(--accent-2); }
  .de-ach-badge {
    width: 46px; height: 46px; flex-shrink: 0; border-radius: 13px;
    display: flex; align-items: center; justify-content: center; color: #fff; font-size: 21px;
  }
  .de-ach-info { flex: 1; min-width: 0; }
  .de-ach-title { font-size: 12.5px; font-weight: 900; color: var(--text); }
  .de-ach-desc { margin-top: 1px; font-size: 11px; color: var(--text-dim); }
  .de-ach-progress-row { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
  .de-ach-progress-row .de-bar { flex: 1; }
  .de-ach-frac { font-size: 10.5px; font-weight: 800; color: var(--text-dim); font-variant-numeric: tabular-nums; }

  /* Invite friends — a real share action, not a fake referral system. */
  .de-invite-card {
    padding: 18px; border-radius: 18px; text-align: center; color: #fff;
    background: linear-gradient(135deg, #4f46e5, #a855f7);
    display: flex; flex-direction: column; align-items: center; gap: 8px;
  }
  .de-invite-icon {
    width: 44px; height: 44px; border-radius: 50%; background: rgba(255, 255, 255, .18);
    display: flex; align-items: center; justify-content: center; font-size: 22px;
  }
  .de-invite-title { font-size: 14px; font-weight: 900; }
  .de-invite-sub { font-size: 11.5px; line-height: 1.7; opacity: .92; }
  .de-invite-card .de-flow-next { width: 100%; background: #fff; color: #1f2544; margin-top: 4px; }
  .de-friend-challenge { display:grid; grid-template-columns:46px 1fr; gap:11px; align-items:center; padding:14px; border:1px solid color-mix(in srgb,#4f46e5 30%,var(--border)); border-radius:18px; background:linear-gradient(135deg,color-mix(in srgb,#4f46e5 10%,var(--card)),var(--card)); box-shadow:var(--shadow); }
  .de-friend-challenge-icon { width:46px; height:46px; display:grid; place-items:center; border-radius:15px; color:#fff; background:linear-gradient(145deg,#3348a0,#7252bd); }
  .de-friend-challenge-icon iconify-icon { font-size:23px; }
  .de-friend-challenge b { display:block; color:var(--text); font-size:13px; font-weight:950; }
  .de-friend-challenge p { margin:3px 0 0; color:var(--text-dim); font-size:11px; line-height:1.6; }
  .de-friend-challenge button { grid-column:1 / -1; min-height:38px; border:0; border-radius:12px; color:#fff; background:#3348a0; font:800 12px inherit; cursor:pointer; }
  .de-friend-challenge-management { margin-top:2px; }
  .de-game-empty { min-height:calc(100vh - 170px); }

  /* "رقابت" — a real tier and real points, no invented opponents. */
  .de-learning-pulse {
    --pulse: #5b8def; position: relative; overflow: hidden; isolation: isolate;
    display: grid; grid-template-columns: 1fr auto; gap: 14px; padding: 16px;
    border-radius: 22px; color: #fff;
    background: linear-gradient(135deg, color-mix(in srgb,var(--pulse) 88%,#101936), color-mix(in srgb,var(--pulse) 48%,#0b1026));
    box-shadow: 0 15px 34px color-mix(in srgb,var(--pulse) 24%,transparent);
  }
  .de-learning-pulse::before { content: ''; position: absolute; z-index: -1; width: 180px; height: 180px; inset-inline-end: -65px; top: -95px; border-radius: 50%; background: rgba(255,255,255,.14); }
  .de-learning-pulse-main { display: flex; align-items: center; gap: 11px; min-width: 0; }
  .de-learning-level { display: grid; place-items: center; width: 48px; height: 48px; flex: 0 0 48px; border-radius: 15px; color: var(--pulse); background: #fff; font-size: 13px; font-weight: 950; box-shadow: 0 7px 16px rgba(0,0,0,.16); }
  .de-learning-pulse-main div { display: flex; flex-direction: column; min-width: 0; }
  .de-learning-pulse-main b { font-size: 14px; font-weight: 950; }
  .de-learning-pulse-main small { margin-top: 3px; font-size: 11px; opacity: .82; }
  .de-learning-pulse-stats { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(3,1fr); gap: 7px; }
  .de-learning-pulse-stats span { display: grid; grid-template-columns: auto auto; justify-content: center; align-items: center; gap: 2px 5px; min-height: 48px; border: 1px solid rgba(255,255,255,.17); border-radius: 14px; background: rgba(255,255,255,.1); backdrop-filter: blur(8px); }
  .de-learning-pulse-stats iconify-icon { grid-row: 1 / 3; font-size: 20px; }
  .de-learning-pulse-stats b { font-size: 14px; font-weight: 950; line-height: 1; }
  .de-learning-pulse-stats small { font-size: 9px; opacity: .75; }
  .de-learning-pulse > button { position: relative; z-index: 2; width: 38px; height: 38px; border: 1px solid rgba(255,255,255,.24); border-radius: 12px; color: #fff; background: rgba(255,255,255,.13); cursor: pointer; }
  .de-learning-pulse > button iconify-icon { font-size: 19px; }

  .de-comp-tabs { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .de-comp-tab {
    display: flex; flex-direction: column; align-items: center; gap: 6px;
    padding: 16px 10px; border-radius: 17px; cursor: pointer;
    border: 1px solid var(--border); background: var(--card); color: var(--text-dim);
    font-family: inherit; font-size: 14px; font-weight: 850;
    box-shadow: var(--shadow);
  }
  .de-comp-tab iconify-icon { font-size: 24px; }
  .de-comp-tab.on { border-color: var(--accent); color: var(--accent); background: var(--card-2); }

  .de-tier-carousel {
    display: flex; gap: 10px; overflow-x: auto; padding: 6px 2px 20px;
    scroll-snap-type: x proximity;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .de-tier-carousel::-webkit-scrollbar { display: none; }
  .de-tier-card {
    flex: 0 0 30%; scroll-snap-align: center;
    display: flex; flex-direction: column; align-items: center; gap: 6px;
    padding: 14px 8px; border-radius: 17px; border: 1px solid var(--border); background: var(--card);
    opacity: .55; transition: opacity .18s ease, transform .18s ease;
  }
  .de-tier-card.reached { opacity: 1; }
  .de-tier-card.on { border-color: var(--accent); box-shadow: var(--shadow); transform: scale(1.04); }
  .de-tier-card-icon { font-size: 30px; color: var(--text-dim); }
  .de-tier-card-name { font-size: 13px; font-weight: 850; color: var(--text); text-align: center; }
  .de-comp-tier-next-note { text-align: center; font-size: 13px; line-height: 1.7; color: var(--text-dim); }

  .de-comp-you-row {
    display: flex; align-items: center; gap: 12px;
    padding: 14px 16px; border-radius: 17px;
    background: var(--t-good-2); border: 1px solid #16a34a;
  }
  .de-comp-you-info { flex: 1; min-width: 0; }
  .de-comp-you-name { font-size: 13.5px; font-weight: 900; color: #14532d; }
  .de-comp-you-badge { margin-top: 1px; font-size: 10.5px; font-weight: 800; color: #15803d; }
  .de-comp-you-row .de-comp-row-points { color: #15803d; }
  .de-comp-row-points { font-size: 16px; font-weight: 900; }

  .de-comp-card {
    display: flex; align-items: center; gap: 12px;
    padding: 15px; border-radius: 18px; border: 1px solid var(--border); background: var(--card);
    box-shadow: 0 8px 22px rgba(15,23,42,.055); transition: transform .18s ease, box-shadow .18s ease;
  }
  .de-comp-action { width: 100%; color: inherit; text-align: start; cursor: pointer; font-family: inherit; }
  .de-comp-action:active { transform: translateY(1px) scale(.99); }
  .de-comp-card:hover { transform: translateY(-2px); box-shadow: 0 13px 28px rgba(15,23,42,.09); }
  .de-comp-card.muted { opacity: .7; }
  .de-comp-card iconify-icon { font-size: 22px; color: var(--accent); flex-shrink: 0; }
  .de-comp-card-body { flex: 1; }
  .de-comp-card-title { font-size: 14px; font-weight: 900; color: var(--text); }
  .de-comp-card-desc { margin-top: 3px; font-size: 13px; line-height: 1.7; color: var(--text-dim); }
  .de-comp-check {
    width: 26px; height: 26px; flex-shrink: 0; border-radius: 50%;
    background: var(--card-2); display: flex; align-items: center; justify-content: center;
    font-weight: 900; color: var(--text-dim);
  }
  .de-comp-check.done { background: var(--t-good-2); color: #15803d; }

  /* The current calendar month's own points goal — a round, honest number
     (50 finished units), not a fabricated "event" with a made-up mascot
     story the way the reference app's monthly card has one. */
  .de-comp-monthly {
    padding: 16px 18px; border-radius: 18px; color: #fff;
    background: linear-gradient(135deg, #2563eb, #4f46e5);
    box-shadow: 0 8px 18px rgba(37, 99, 235, .28);
  }
  .de-comp-monthly-head { display: flex; align-items: center; gap: 8px; }
  .de-comp-monthly-head iconify-icon { font-size: 20px; }
  .de-comp-monthly-title { font-size: 14px; font-weight: 900; }
  .de-comp-monthly-desc { margin-top: 4px; font-size: 12px; line-height: 1.6; opacity: .9; }
  .de-comp-monthly .de-bar { margin-top: 10px; background: rgba(255, 255, 255, .25); }
  .de-comp-monthly .de-bar-fill { background: #fff; }
  .de-comp-monthly-count { margin-top: 6px; font-size: 11.5px; font-weight: 800; opacity: .9; }
  .de-comp-monthly-btn {
    display: block; width: 100%; margin-top: 12px; padding: 10px; border-radius: 12px;
    border: 0; background: rgba(255, 255, 255, .18); color: #fff;
    font-family: inherit; font-size: 12.5px; font-weight: 900; cursor: pointer;
  }

  /* The course's own detail popup — tapping a stat opens this instead of
     just sitting there, the way the reference app's own top-bar icons and
     profile cards do. */
  .de-detail-overlay {
    position: fixed; inset: 0; z-index: 60;
    display: flex; align-items: flex-end; justify-content: center;
    background: rgba(0, 0, 0, .5);
  }
  .de-detail-sheet {
    width: 100%; max-width: 480px; max-height: 80vh; overflow-y: auto;
    background: var(--card); border-radius: 22px 22px 0 0;
    box-shadow: 0 -8px 24px rgba(0, 0, 0, .25);
    padding: 18px 18px calc(18px + env(safe-area-inset-bottom, 0px));
  }
  .de-detail-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
  .de-detail-title { font-size: 15px; font-weight: 900; color: var(--text); }
  .de-detail-close {
    width: 30px; height: 30px; flex-shrink: 0; border-radius: 50%; border: 0;
    background: var(--card-2); color: var(--text-dim); font-size: 17px; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
  }
  .de-detail-body { margin-top: 14px; display: flex; flex-direction: column; gap: 10px; }
  .de-detail-body p { margin: 0; font-size: 12.5px; line-height: 1.8; color: var(--text-dim); }
  .de-detail-stat {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 14px; border-radius: 14px; background: var(--card-2);
  }
  .de-detail-stat iconify-icon { font-size: 24px; flex-shrink: 0; }
  .de-detail-stat-value { font-size: 15px; font-weight: 900; color: var(--text); }
  .de-detail-stat-label { margin-top: 1px; font-size: 11px; color: var(--text-dim); }

  /* "لایتنر" — real spaced repetition on real vocabulary. */
  .de-leitner-hero { display:grid; grid-template-columns:52px 1fr; gap:13px; align-items:center; padding:17px; border-radius:20px; color:#fff; background:linear-gradient(135deg,#293b87,#7048b6); box-shadow:0 14px 28px rgba(57,46,135,.2); }
  .de-leitner-hero > span { display:grid; place-items:center; width:52px; height:52px; border-radius:17px; background:rgba(255,255,255,.14); }
  .de-leitner-hero iconify-icon { font-size:27px; }
  .de-leitner-hero b { font-size:15px; font-weight:950; }
  .de-leitner-hero p { margin:4px 0 0; font-size:12px; line-height:1.7; opacity:.86; }
  .de-leitner-overview { display:grid; grid-template-columns:repeat(3,1fr); gap:8px; }
  .de-leitner-box { min-width:0; text-align:center; box-shadow:none; background:var(--card-2); }
  .de-leitner-box iconify-icon { font-size:21px; }
  .de-leitner-box .de-mgmt-value { font-size:18px; }
  .de-leitner-box .de-mgmt-label { font-size:10px; }
  .de-leitner-review-btn { width:100%; min-height:52px; justify-content:center; border-radius:16px; color:#fff; background:#3348a0; box-shadow:0 10px 20px rgba(51,72,160,.2); }
  .de-leitner-card { align-items:center; text-align:center; cursor:pointer; min-height:220px; justify-content:center; border:1px solid color-mix(in srgb,#3348a0 28%,var(--border)); background:linear-gradient(145deg,color-mix(in srgb,#3348a0 7%,var(--card)),var(--card)); box-shadow:0 14px 28px rgba(51,72,160,.1); }
  .de-leitner-word { font-size:31px; font-weight:950; color:var(--text); letter-spacing:.02em; }
  .de-leitner-fa { margin-top:10px; font-size:17px; font-weight:850; color:#3348a0; }
  .de-leitner-example { margin-top:18px; padding:14px 16px 0; border-top:1px dashed color-mix(in srgb,#3348a0 25%,var(--border)); font-size:14px; font-weight:750; color:var(--text); }
  .de-leitner-example-fa { margin-top:5px; font-size:12px; line-height:1.7; color:var(--text-dim); }

  /* The German course's own bottom bar — a second instance of the exact
     same liquid dock (js/dock.js + css/dock.css), not a re-skin of the
     site's and not a differently-styled bar of its own. Its own fixed
     wrapper mirrors #bottom-fixed/.bottom-fixed-inner exactly (same glass
     plate, same rounded top) so the only thing that differs is which
     element it is and which six tabs/accents it carries. No `display`
     override here on purpose — the site's own [hidden]{display:none} bug
     (an unconditional `display` on the same selector beating it on a
     specificity tie) taught not to repeat that on this wrapper. */
  .de-dock-fixed {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  .de-dock-fixed .bottom-fixed-inner { max-width: 480px; margin: 0 auto; }

  /* The German dock's own gradient defs (unique ids, since SVG url(#id)
     references must be document-unique) — everything else about .dock is
     shared with the site's instance via the plain classes in dock.css. */
  .dock--german .dock__fill { fill: url(#germanDockPlate); stroke: url(#germanDockRim); }

  @media (min-width: 1024px) {
    .de-dock-fixed { display: none; }
  }

  .de-letter.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
  .de-letter.on .de-letter-glyph { color: var(--on-accent); }
  .de-letter.finished { border-color: var(--accent-2); }
  .de-letter iconify-icon { font-size: 13px; color: var(--accent-2); }
  .de-letter.on iconify-icon { color: var(--on-accent); }

  .de-letters { display: grid; grid-template-columns: repeat(5, 1fr); gap: 7px; }
  .de-letter {
    display: flex; flex-direction: column; align-items: center; gap: 2px; cursor: pointer;
    padding: 11px 6px; border-radius: 14px;
    border: 1px solid var(--border); background: var(--card); color: var(--text);
    font-family: inherit;
  }
  .de-letter-glyph { font-size: 15px; font-weight: 900; letter-spacing: -.3px; }
  .de-letter-name { font-size: 10.5px; color: var(--text-dim); }
  .de-letter-word { font-size: 11px; font-weight: 700; color: var(--accent); }

  @media (min-width: 620px) {
    .de-modules { grid-template-columns: repeat(4, 1fr); }
  }

  
  /* ── Movement ────────────────────────────────────────────────────────
     Every animation below answers something the learner just did. They are
     all switched off under prefers-reduced-motion at the end of this block. */

  .de-letter { transition: transform .12s ease, background .15s ease, border-color .15s ease; }
  .de-letter:active { transform: scale(.92); }
  .de-letter.on { animation: de-pop .28s cubic-bezier(.34, 1.56, .64, 1); }

  @keyframes de-pop {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.12); }
    100% { transform: scale(1); }
  }

  /* A step that opened: it slides up as it arrives, so a tap that changed
     the whole card does not look like a page that flickered. */
  .de-flow-card.enter { animation: de-rise .3s cubic-bezier(.22, .61, .36, 1); }
  @keyframes de-rise {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
  }

  /* A step that will not open yet. */
  .de-flow-card.shake { animation: de-shake .34s ease; }
  @keyframes de-shake {
    10%, 90% { transform: translateX(-2px); }
    20%, 80% { transform: translateX(4px); }
    30%, 50%, 70% { transform: translateX(-7px); }
    40%, 60% { transform: translateX(7px); }
  }

  /* Right and wrong on a choice, marked rather than merely highlighted. */
  .de-choice { position: relative; transition: transform .12s ease, background .18s ease, border-color .18s ease; }
  .de-choice:active { transform: scale(.96); }
  .de-choice.right {
    background: var(--t-good-2, #dcfce7); border-color: #16a34a; color: #14532d;
    animation: de-pop .32s cubic-bezier(.34, 1.56, .64, 1);
  }
  .de-choice.wrong {
    background: #fee2e2; border-color: #ef4444; color: #7f1d1d;
    animation: de-shake .34s ease;
  }
  .de-mark {
    position: absolute; inset-inline-end: 7px; top: 6px;
    font-size: 13px; font-weight: 900;
    animation: de-tick .34s cubic-bezier(.34, 1.56, .64, 1);
  }
  @keyframes de-tick {
    from { opacity: 0; transform: scale(.3) rotate(-25deg); }
    to   { opacity: 1; transform: none; }
  }

  /* The microphone, while it is open. */
  .de-practice.recording { animation: de-breathe 1.1s ease-in-out infinite; }
  @keyframes de-breathe {
    0%, 100% { box-shadow: 0 0 0 0 rgba(226, 96, 63, .55); }
    50%      { box-shadow: 0 0 0 12px rgba(226, 96, 63, 0); }
  }

  .de-listening { display: flex; align-items: flex-end; justify-content: center; gap: 5px; height: 30px; }
  .de-listening[hidden] { display: none; }
  .de-listening span {
    width: 5px; border-radius: 3px; background: #e2603f;
    animation: de-bars .9s ease-in-out infinite;
  }
  .de-listening span:nth-child(1) { animation-delay: 0s; }
  .de-listening span:nth-child(2) { animation-delay: .12s; }
  .de-listening span:nth-child(3) { animation-delay: .24s; }
  .de-listening span:nth-child(4) { animation-delay: .36s; }
  .de-listening span:nth-child(5) { animation-delay: .48s; }
  @keyframes de-bars {
    0%, 100% { height: 7px; opacity: .55; }
    50%      { height: 26px; opacity: 1; }
  }

  /* A repeat that was accepted. */
  .de-repeat { transition: background .2s ease, color .2s ease; }
  .de-repeat.done { animation: de-pop .3s cubic-bezier(.34, 1.56, .64, 1); }

  /* The step dots, and the bar under a module. */
  .de-dot { transition: background .22s ease, color .22s ease; }
  .de-dot.done { animation: de-pop .3s cubic-bezier(.34, 1.56, .64, 1); }
  .de-bar-fill { transition: width .45s cubic-bezier(.22, .61, .36, 1); }

  /* The answer line under a step. */
  .de-practice-out { animation: de-rise .26s ease; }

  .de-module { transition: transform .12s ease, box-shadow .2s ease; }
  .de-module:active { transform: scale(.97); }
  .de-level { transition: transform .12s ease, background .18s ease; }
  .de-level:active { transform: scale(.95); }
  .de-flow-next, .de-practice, .de-check, .de-selected-read {
    transition: transform .12s ease, opacity .18s ease;
  }
  .de-flow-next:active, .de-practice:active, .de-check:active, .de-selected-read:active {
    transform: scale(.97);
  }

  @media (prefers-reduced-motion: reduce) {
    .de-letter, .de-letter.on, .de-flow-card.enter, .de-flow-card.shake,
    .de-choice, .de-choice.right, .de-choice.wrong, .de-mark,
    .de-practice.recording, .de-listening span, .de-repeat.done,
    .de-dot.done, .de-practice-out, .de-module, .de-level {
      animation: none !important;
      transition: none !important;
    }
    .de-listening span { height: 16px; }
  }

  /* ── The profile hero and its four cards ────────────────────────────
   *
   * Taken from the profile screen of the Android app (app/(main)/profile.tsx
   * in the hamdast_native project): the same deep gradient behind the avatar,
   * the same amber member badge under the name, and the same 2×2 grid of
   * small white cards beneath it. Measurements are the app's own.
   *
   * The one thing not copied is the numbers. The app fills its four cards
   * with fixed sample text — "۳ درخواست", "۴ فایل", "سطح A2", "تاییدشده" —
   * which is fine in a mock and would be a lie on a live account, so each
   * card here reads a value the site actually knows (see loadProfile()).
   */
  .profile-header-card {
    background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 55%, #3b0764 100%);
    /* Square along the top: it is the first thing on the page and meets the
       app bar, so a rounded top edge leaves two slivers of page above it.
       Rounded below, where it does meet the page. */
    border-radius: 0 0 28px 28px;
    /* Pulled out to the page's edges — main's own padding would inset it and
       leave the same slivers down the sides. */
    margin: -16px -12.8px 0;
    padding: 18px 24px 20px;
    text-align: center;
    overflow: hidden;
    box-shadow: 0 6px 16px rgba(15, 23, 42, 0.2);
  }
  .profile-header-card .avatar-upload-wrap { position: relative; display: inline-block; }
  .profile-header-card .avatar-circle {
    width: 72px;
    height: 72px;
    border-radius: 30px;
    background: rgba(255, 255, 255, 0.2);
    /* the app's ring: a translucent band around the picture, not a border on it */
    box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.1), 0 0 0 6px rgba(255, 255, 255, 0.3);
    color: #fff;
    font-size: 32px;
    font-weight: 900;
  }
  .profile-header-card .avatar-edit-badge {
    position: absolute;
    inset-inline-start: -4px;
    bottom: -2px;
    width: 30px;
    height: 30px;
    border-radius: 12px;
    background: #10b981;
    border: 2px solid #fff;
    display: grid;
    place-items: center;
    font-size: 13px;
  }
  .profile-header-name { color: #fff; font-size: 19px; font-weight: 900; margin-top: 12px; }
  .profile-header-email { color: #cbd5e1; font-size: 11.5px; margin-top: 4px; }
  .profile-vip-badge {
    display: inline-flex;
    flex-direction: row-reverse;
    align-items: center;
    gap: 6px;
    margin-top: 10px;
    padding: 6px 12px;
    border-radius: 16px;
    background: rgba(245, 158, 11, 0.2);
    border: 1px solid rgba(245, 158, 11, 0.4);
    color: #fde68a;
    font-size: 10.5px;
    font-weight: 800;
  }
  .profile-vip-badge > span:first-child { color: #f59e0b; font-size: 12px; }

  .stats-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    /* Space below as well as above: without it the last card sat right on
       top of the first menu row and the two blocks read as one. */
    margin: 16px 0 22px;
  }
  .stat-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 14px;
    border-radius: 20px;
    background: var(--card);
    border: 1px solid var(--border);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04);
    font-family: inherit;
    cursor: pointer;
    transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
  }
  .stat-card:hover { border-color: var(--accent); box-shadow: 0 6px 14px rgba(79, 70, 229, .12); }
  .stat-card:active { transform: scale(.97); }
  .stat-icon { font-size: 20px; line-height: 1; }
  .stat-number { font-size: 16px; font-weight: 900; color: var(--text); margin-top: 4px; text-align: center; }
  .stat-label { font-size: 10px; color: var(--text-dim); text-align: center; }

  /* ── Featured services on the home screen ──────────────────────────
   * The app's three slider cards, laid out as a stack rather than a swipe:
   * on a page that already has a horizontal category pager, a second thing
   * to swipe sideways would fight it. Gradients and wording are the app's.
   */
  /* One row you swipe, not a stack. Each card is a little under the full
     width so the next one always shows at the edge — that sliver is the only
     thing telling you there is more, since the row has no arrows. */
  .feature-cards {
    display: flex;
    gap: 12px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: 12.8px;
    /* Bleed to the page edges so the first and last card can sit against
       them, then pad back in so they still line up with everything else. */
    margin: 0 -12.8px 4px;
    padding: 4px 12.8px 10px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .feature-cards::-webkit-scrollbar { display: none; }
  .feature-card {
    position: relative;
    flex: 0 0 86%;
    scroll-snap-align: start;
    min-height: 168px;
    display: flex;
    padding: 0;
    border: 0;
    border-radius: 22px;
    color: #fff;
    font-family: inherit;
    cursor: pointer;
    overflow: hidden;
    box-shadow: 0 10px 24px -12px rgba(15, 23, 42, 0.55);
    transition: transform .18s ease, box-shadow .18s ease;
  }
  .feature-card:hover { transform: translateY(-2px); box-shadow: 0 16px 30px -14px rgba(15, 23, 42, 0.6); }
  .feature-card:active { transform: translateY(0) scale(.99); }
  /* The banner photo, when the category has one. It is the same image the
     admin panel already sets for that category's own screen, so changing it
     there changes it here — there is no second place to keep in step. */
  .feature-bg {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    opacity: 0;
    transition: opacity .3s ease;
  }
  .feature-card.has-image .feature-bg { opacity: .34; }
  .feature-body {
    position: relative;
    z-index: 1;
    display: grid;
    gap: 6px;
    justify-items: start;
    align-content: center;
    text-align: start;
    width: 100%;
    padding: 18px;
  }
  .feature-card--driving { background: linear-gradient(135deg, #172554 0%, #1d4ed8 55%, #0f172a 100%); }
  .feature-card--appointment { background: linear-gradient(135deg, #4a044e 0%, #a21caf 55%, #1e1b4b 100%); }
  .feature-card--german { background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 55%, #4c1d95 100%); }
  .feature-card--jobcenter { background: linear-gradient(135deg, #064e3b 0%, #047857 55%, #0f172a 100%); }
  .feature-card--citizenship { background: linear-gradient(135deg, #7c2d12 0%, #c2410c 55%, #1e1b4b 100%); }

  .feature-tag {
    display: inline-flex;
    flex-direction: row-reverse;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.16);
    border: 1px solid rgba(255, 255, 255, 0.22);
    font-size: 10.5px;
    font-weight: 800;
  }
  .feature-title { font-size: 15px; font-weight: 900; line-height: 1.45; }
  .feature-sub { font-size: 11.5px; line-height: 1.6; color: rgba(255, 255, 255, 0.78); }
  .feature-cta {
    display: inline-flex;
    flex-direction: row-reverse;
    align-items: center;
    gap: 8px;
    margin-top: 6px;
    padding: 9px 16px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.95);
    color: #0f172a;
    font-size: 12.5px;
    font-weight: 800;
    transition: background .18s ease;
  }
  .feature-card:hover .feature-cta { background: #fff; }
  /* The arrow points the way the language reads, so it is mirrored for the
     two left-to-right interface languages rather than the two right-to-left
     ones — the glyph itself points left. */
  [dir="ltr"] .feature-arrow { display: inline-block; transform: scaleX(-1); }
