@font-face {
  font-family: "IRANSansXFaNum";
  src: url("IRANSansXFaNum-Light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IRANSansXFaNum";
  src: url("IRANSansXFaNum-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IRANSansXFaNum";
  src: url("IRANSansXFaNum-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IRANSansXFaNum";
  src: url("IRANSansXFaNum-DemiBold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IRANSansXFaNum";
  src: url("IRANSansXFaNum-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IRANSansXFaNum";
  src: url("IRANSansXFaNum-Black.woff2") format("woff2");
  font-weight: 900;
  font-style: normal;
  font-display: swap;
}

#deTrackerApp {
  --de-navy-950: #0f172a;
  --de-navy-900: #172033;
  --de-navy-800: #22304a;

  /* Primary/success moved from the original soft cyan/emerald pair to
     a slightly deeper, more saturated blue/green — validated against
     five real app screens as mockups (quiz session, lesson list,
     dialogue, dashboard home, flashcard) before being applied here.
     Chosen deliberately for "bold but not childish": Duolingo/Drops-
     inspired energy (saturated, rounded, generous color) tempered for
     an adult-learner tool — no game-chrome (streaks, XP), color used
     for wayfinding (which tab, correct/wrong) rather than decoration. */
  --de-primary: #0B7BEA;
  --de-primary-dark: #04509A;
  /* 3.17.0: split out from --de-primary-dark specifically for the
     three call sites that use it as a FILL (button-hover background,
     active-tab gradient end, login-submit gradient) paired with white
     text — identical to --de-primary-dark in light mode (zero visual
     change), but dark mode needs a different value for this role than
     it needs for --de-primary-dark's other job (text sitting ON a
     soft background) — see the dark-mode override block for why one
     token can't serve both. */
  --de-primary-hover-fill: #04509A;
  --de-primary-soft: #E6F1FB;
  --de-primary-ring: rgba(11, 123, 234, 0.18);
  /* RGB triplets — exist so a color can be alpha-composited via
     rgba(var(--de-x-rgb), N) instead of a hardcoded rgba() literal.
     Added during the 3.16.0 token-integration pass specifically to
     retire ~20 leftover rgba(0, 174, 239, …) uses of the *pre-3.12.0*
     primary color (#00aeef) that never got updated when the palette
     moved to #0B7BEA — those become rgba(var(--de-primary-rgb), …). */
  --de-primary-rgb: 11, 123, 234;
  --de-danger-rgb: 239, 68, 68;
  --de-success-rgb: 9, 148, 104;
  --de-warning-rgb: 245, 158, 11;
  --de-heading-rgb: 15, 23, 42;

  --de-accent: #099468;
  --de-accent-soft: #E1F5EE;

  --de-text: #334155;
  --de-text-light: #64748b;
  --de-text-faint: #94a3b8;
  --de-heading: #0f172a;

  /* Borders now do real visual work (1.5-2px, more visible) rather
     than the original thin near-invisible hairlines — cards read as
     solid, tappable shapes instead of glass panes, matching the
     validated mockups' bold-outline button/card style. */
  --de-border: rgba(148, 163, 184, 0.32);
  --de-border-strong: #0B7BEA;
  --de-border-soft: rgba(255, 255, 255, 0.55);

  /* Cards moved from translucent glass-morphism (rgba white over a
     gradient backdrop) to solid white — the validated mockups use flat
     solid cards with a visible border, not a frosted-glass effect, so
     the surface tokens follow that rather than layering opacity. */
  --de-bg: linear-gradient(180deg, #f8fbff 0%, #f4f7fb 48%, #eef4f8 100%);
  --de-card: #ffffff;
  --de-card-strong: #ffffff;
  --de-card-soft: #fbfdff;
  /* 3.16.0: the identical two-stop white→pale-blue gradient used by
     .de-mini-stat/.de-plan-box/.de-chart-card (the handful of
     components the 3.12.0 pass deliberately kept as soft gradient
     cards rather than flattening) was hardcoded 3 separate times —
     one token, one place to redefine for dark mode later. */
  --de-card-gradient: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
  /* Warm off-white for the reading-passage text card specifically —
     a deliberate slight tint (not plain --de-card) since the German
     text panel sits under a purple header and reads better slightly
     warm than stark white. */
  --de-reading-card-bg: #fffdf9;

  --de-danger: #ef4444;
  --de-danger-dark: #b91c1c;
  --de-danger-soft: #fee2e2;
  --de-warning: #f59e0b;
  --de-warning-soft: #fef3c7;
  --de-success: #099468;
  --de-success-dark: #126248;
  --de-success-soft: #E1F5EE;
  --de-neutral: #64748b;

  /* Grammar-category palette for vocabulary — moved from the original
     muted/"professional" set to the brighter, more distinguishable
     palette validated in the mockups (same five hues used there for
     icon chips: blue, teal/green, amber, purple, coral), extended to
     cover all eight grammar categories. Still distinguishable, still
     not tied to success/danger "correct/wrong" semantics (so a
     feminine-noun badge doesn't read as an error) — just bolder and
     friendlier than the original, matching the rest of this pass.
     -soft tints are the pale card/badge background; the bold value is
     for icon chips, borders, and text-on-soft-background. */
  --de-cat-masc: #04509A;
  --de-cat-masc-soft: #E6F1FB;
  --de-cat-fem: #993556;
  --de-cat-fem-soft: #FBEAF0;
  --de-cat-neut: #0F6E56;
  --de-cat-neut-soft: #E1F5EE;
  --de-cat-plural: #854F0B;
  --de-cat-plural-soft: #FAEEDA;
  --de-cat-verb: #04509A;
  --de-cat-verb-soft: #E6F1FB;
  --de-cat-adj: #534AB7;
  --de-cat-adj-soft: #EEEDFE;
  --de-cat-adv: #993C1D;
  --de-cat-adv-soft: #FAECE7;
  --de-cat-other: #5f5e5a;
  --de-cat-other-soft: #F1EFE8;

  /* 3.13.0 (SVG icon pass) — dedicated colors for the 5 main lesson
     tabs (grammar/vocab/dialogue/quiz/reading), the only icons in the
     app that get a colored chip background per the agreed rule
     ("color only where it does wayfinding work"). Deliberately its
     own token set rather than reusing --de-cat-* — those are already
     reserved for grammatical gender/part-of-speech meaning, and
     borrowing one for an unrelated UI role was a mistake caught once
     already (the flashcard "hard" button, see project notes). */
  --de-tab-grammar: #534AB7;
  --de-tab-grammar-soft: #EEEDFE;
  --de-tab-vocabulary: #854F0B;
  --de-tab-vocabulary-soft: #FAEEDA;
  --de-tab-dialogue: #0F6E56;
  --de-tab-dialogue-soft: #E1F5EE;
  --de-tab-quiz: #04509A;
  --de-tab-quiz-soft: #E6F1FB;
  --de-tab-reading: #993C1D;
  --de-tab-reading-soft: #FAECE7;

  /* 3.16.0 token-integration pass — colors that already existed in the
     CSS (added ad hoc across several sessions) but were never given a
     token, plus two role tokens for "text/icon sitting on a strong
     fill" (buttons, active tab, chat bubble B, colored badges) so
     dark mode has one lever for that role instead of ~15 bare
     #ffffff literals. Same computed values as before — see the
     3.16.0 changelog for the full routing list. */
  --de-on-fill: #ffffff;
  --de-on-fill-muted: rgba(255, 255, 255, 0.75);
  /* Light mode: identical to --de-on-fill (success/warning fills are
     dark enough here that white already works — see the 3.17.0
     changelog's contrast check). Dark mode redefines these to a dark
     hue-tinted color instead, since dark mode's brighter success/
     warning fills fail white-text contrast — spec §2.5/§2.7. */
  --de-on-success-fill: var(--de-on-fill);
  --de-on-warning-fill: var(--de-on-fill);
  --de-sheen-inset: rgba(255, 255, 255, 0.95);
  --de-sheen-wash: rgba(255, 255, 255, 0.22);

  --de-reading-accent: #7c5cd4;
  --de-reading-accent-dark: #4c3899;

  --de-dialogue-b-soft: #D3F4E8;
  --de-dialogue-b-tick: #7FD8FF;

  --de-vocab-easy-bg: #CEF4E6;
  --de-vocab-easy-border: rgba(var(--de-success-rgb), 0.65);
  --de-vocab-hard-bg: #FFF0B2;
  --de-vocab-hard-border: rgba(var(--de-warning-rgb), 0.65);
  /* 3.19.0: generalized from the vocab-specific name — this is really
     "dark text that reads on a warning-soft background", a role the
     content-generation pipeline (Grammar tab HTML) also needs for its
     rule/tip/highlight boxes. --de-vocab-hard-text kept as an alias so
     nothing already using it needs to change. */
  --de-on-warning-soft: #92400e;
  --de-vocab-hard-text: var(--de-on-warning-soft);

  --de-space-1: 4px;
  --de-space-2: 8px;
  --de-space-3: 12px;
  --de-space-4: 16px;
  --de-space-5: 20px;
  --de-space-6: 28px;
  --de-space-8: 40px;

  /* Radius bumped up across the board (was 14/20/28) — the validated
     mockups lean into generous rounding on every tappable element
     (buttons, option chips, cards), not just the largest containers. */
  --de-radius-sm: 16px;
  --de-radius: 20px;
  --de-radius-lg: 20px;
  --de-radius-pill: 9999px;
  --de-radius-tab: 14px;      /* دکمه‌های تب: مربع با گوشه نرم */
  --de-radius-tabnav: 18px;   /* قاب بیرونی منوی تب */


  /* Shadows lightened/shortened — solid-card-with-border is now the
     primary way surfaces read as "raised," so heavy diffuse shadows
     (which made sense against the old glass-morphism look) would be
     redundant weight on top of a visible border. */
  --de-shadow-sm: 0 4px 14px rgba(var(--de-heading-rgb), 0.04);
  --de-shadow: 0 8px 24px rgba(var(--de-heading-rgb), 0.06);
  --de-shadow-hover: 0 12px 32px rgba(var(--de-heading-rgb), 0.10);

  --de-hero-gradient: linear-gradient(135deg, #0f172a 0%, #172554 55%, #1e3a8a 100%);
  --de-topband-line: linear-gradient(90deg, #85B7EB 0%, #0B7BEA 50%, #099468 100%);

  --de-transition: all 280ms cubic-bezier(0.4, 0, 0.2, 1);
  --de-font: IRANSansXFaNum, Vazirmatn, Tahoma, Arial, sans-serif;

  direction: rtl;
  width: 100%;
  max-width: 100%;
  margin: 0 auto;
  padding: var(--de-space-6) 0 var(--de-space-8);
  color: var(--de-text);
  background: var(--de-bg);
  font-family: var(--de-font);
  font-size: 15px;
  line-height: 1.85;
  text-align: right;
  box-sizing: border-box;
  -webkit-font-smoothing: antialiased;
  color-scheme: light;
  position: relative;
  overflow: hidden;
  /* --- English track aliases -------------------------------------
     Authored grammar HTML references plugin colour tokens by name
     (var(--en-primary), var(--en-card) and so on), exactly as the
     English content-generation specification instructs. Since
     3.31.0 the English page is served by this stylesheet, which
     defines only --de-* — so every one of those references
     resolved to nothing and the grammar cards lost their colours,
     backgrounds and borders and collapsed to plain text.
  
     Aliasing rather than rewriting the lessons: the HTML is already
     authored and the spec will keep producing --en-* names. These
     are declared in both theme blocks and point at --de-* by
     reference, so they follow light/dark automatically and can
     never drift from the values they mirror.
  
     Kept until the deferred cosmetic pass renames the whole token
     set to something track-neutral; at that point both prefixes
     become aliases of the new names. */
  --en-navy-950: var(--de-navy-950);
  --en-navy-900: var(--de-navy-900);
  --en-navy-800: var(--de-navy-800);
  --en-primary: var(--de-primary);
  --en-primary-dark: var(--de-primary-dark);
  --en-primary-hover-fill: var(--de-primary-hover-fill);
  --en-primary-soft: var(--de-primary-soft);
  --en-primary-ring: var(--de-primary-ring);
  --en-primary-rgb: var(--de-primary-rgb);
  --en-danger-rgb: var(--de-danger-rgb);
  --en-success-rgb: var(--de-success-rgb);
  --en-warning-rgb: var(--de-warning-rgb);
  --en-heading-rgb: var(--de-heading-rgb);
  --en-accent: var(--de-accent);
  --en-accent-soft: var(--de-accent-soft);
  --en-text: var(--de-text);
  --en-text-light: var(--de-text-light);
  --en-text-faint: var(--de-text-faint);
  --en-heading: var(--de-heading);
  --en-border: var(--de-border);
  --en-border-strong: var(--de-border-strong);
  --en-border-soft: var(--de-border-soft);
  --en-bg: var(--de-bg);
  --en-card: var(--de-card);
  --en-card-strong: var(--de-card-strong);
  --en-card-soft: var(--de-card-soft);
  --en-card-gradient: var(--de-card-gradient);
  --en-reading-card-bg: var(--de-reading-card-bg);
  --en-danger: var(--de-danger);
  --en-danger-dark: var(--de-danger-dark);
  --en-danger-soft: var(--de-danger-soft);
  --en-warning: var(--de-warning);
  --en-warning-soft: var(--de-warning-soft);
  --en-success: var(--de-success);
  --en-success-dark: var(--de-success-dark);
  --en-success-soft: var(--de-success-soft);
  --en-neutral: var(--de-neutral);
  --en-cat-masc: var(--de-cat-masc);
  --en-cat-masc-soft: var(--de-cat-masc-soft);
  --en-cat-fem: var(--de-cat-fem);
  --en-cat-fem-soft: var(--de-cat-fem-soft);
  --en-cat-neut: var(--de-cat-neut);
  --en-cat-neut-soft: var(--de-cat-neut-soft);
  --en-cat-plural: var(--de-cat-plural);
  --en-cat-plural-soft: var(--de-cat-plural-soft);
  --en-cat-verb: var(--de-cat-verb);
  --en-cat-verb-soft: var(--de-cat-verb-soft);
  --en-cat-adj: var(--de-cat-adj);
  --en-cat-adj-soft: var(--de-cat-adj-soft);
  --en-cat-adv: var(--de-cat-adv);
  --en-cat-adv-soft: var(--de-cat-adv-soft);
  --en-cat-other: var(--de-cat-other);
  --en-cat-other-soft: var(--de-cat-other-soft);
  --en-tab-grammar: var(--de-tab-grammar);
  --en-tab-grammar-soft: var(--de-tab-grammar-soft);
  --en-tab-vocabulary: var(--de-tab-vocabulary);
  --en-tab-vocabulary-soft: var(--de-tab-vocabulary-soft);
  --en-tab-dialogue: var(--de-tab-dialogue);
  --en-tab-dialogue-soft: var(--de-tab-dialogue-soft);
  --en-tab-quiz: var(--de-tab-quiz);
  --en-tab-quiz-soft: var(--de-tab-quiz-soft);
  --en-tab-reading: var(--de-tab-reading);
  --en-tab-reading-soft: var(--de-tab-reading-soft);
  --en-on-fill: var(--de-on-fill);
  --en-on-fill-muted: var(--de-on-fill-muted);
  --en-on-success-fill: var(--de-on-success-fill);
  --en-on-warning-fill: var(--de-on-warning-fill);
  --en-sheen-inset: var(--de-sheen-inset);
  --en-sheen-wash: var(--de-sheen-wash);
  --en-reading-accent: var(--de-reading-accent);
  --en-reading-accent-dark: var(--de-reading-accent-dark);
  --en-dialogue-b-soft: var(--de-dialogue-b-soft);
  --en-dialogue-b-tick: var(--de-dialogue-b-tick);
  --en-vocab-easy-bg: var(--de-vocab-easy-bg);
  --en-vocab-easy-border: var(--de-vocab-easy-border);
  --en-vocab-hard-bg: var(--de-vocab-hard-bg);
  --en-vocab-hard-border: var(--de-vocab-hard-border);
  --en-on-warning-soft: var(--de-on-warning-soft);
  --en-vocab-hard-text: var(--de-vocab-hard-text);
  --en-space-1: var(--de-space-1);
  --en-space-2: var(--de-space-2);
  --en-space-3: var(--de-space-3);
  --en-space-4: var(--de-space-4);
  --en-space-5: var(--de-space-5);
  --en-space-6: var(--de-space-6);
  --en-space-8: var(--de-space-8);
  --en-radius-sm: var(--de-radius-sm);
  --en-radius: var(--de-radius);
  --en-radius-lg: var(--de-radius-lg);
  --en-radius-pill: var(--de-radius-pill);
  --en-radius-tab: var(--de-radius-tab);
  --en-radius-tabnav: var(--de-radius-tabnav);
  --en-shadow-sm: var(--de-shadow-sm);
  --en-shadow: var(--de-shadow);
  --en-shadow-hover: var(--de-shadow-hover);
  --en-hero-gradient: var(--de-hero-gradient);
  --en-topband-line: var(--de-topband-line);
  --en-transition: var(--de-transition);
  --en-font: var(--de-font);
  /* No German counterpart: English marks nouns with one badge
     colour where German splits by grammatical gender. */
  --en-cat-noun: #3b82c4;
  --en-cat-noun-soft: #e0ebf6;
}

/* =========================================================================
   DARK THEME — 3.17.0
   ---------------------------------------------------------------------
   One override block, redefining the same semantic tokens the whole app
   already reads through (584+ var() call sites from the 3.16.0 token
   pass) — no component-by-component selectors, no duplicated rules.
   Activated via [data-theme="dark"] on #deTrackerApp, set by JS at
   startup (localStorage preference, falling back to
   prefers-color-scheme) — see setThemePreference()/applyTheme() in
   german-study-tracker.js and the inline flash-guard script in
   german-study-progress-tracker.php.

   Per the approved Dark Mode Design Specification: this is not a
   mechanical "invert light mode" — several tokens have a genuinely
   different VALUE here than a darkened version of their light-mode hex
   would produce, and two tokens (--de-on-success-fill/-warning-fill,
   new this pass) exist because success/warning fills need dark text in
   dark mode, not white — light mode's --de-on-fill doesn't work for
   those two roles once the fill itself gets bright enough to read
   against a dark page. See CHANGES-3.17.0-DARK-MODE.md for the full
   contrast validation this block was checked against.
   ========================================================================= */
#deTrackerApp[data-theme="dark"] {
  /* Elevation ramp — one consistent hue (222°, matching --de-heading's
     own hue) stepped by lightness only. --de-card and --de-card-strong
     were identical in light mode (nothing needed to tell them apart on
     white); here they're the ramp's actual reason to exist. */
  --de-bg: #0F121A;
  --de-card-soft: #131822;
  --de-card: #191F2C;
  --de-card-strong: #21293B;
  --de-card-gradient: #191F2C;
  --de-reading-card-bg: #221C16;

  /* Text — the two ends of the ramp swap roles (see spec §1.2);
     text-light/text-faint intentionally reuse each OTHER's light-mode
     literal rather than get new values (see spec §1.3/§2.2). */
  --de-heading: #ECF0F4;
  --de-text: #C9D1D9;
  --de-text-light: #94a3b8;
  --de-text-faint: #64748b;
  --de-neutral: #94a3b8;

  /* Borders — same hue as light mode's hairline, lower alpha (reads
     more present against a dark surface at equal alpha, so needs
     less). --de-border-strong stays pinned to primary, as in light
     mode. --de-border-soft's job changes along with the gradient
     cards it was built for — see --de-sheen-inset below. */
  --de-border: rgba(148, 163, 184, 0.22);
  --de-border-strong: #137FEC;
  --de-border-soft: rgba(255, 255, 255, 0.08);

  /* Primary — re-derived, not lightened-by-formula. #137FEC is the
     balance point between "readable as a border/icon against the dark
     page" and "white button text still reads on top of it" — see spec
     §2.4 for the actual contrast scan. The hover/emphasis value goes
     LIGHTER than the base (opposite of light mode's darker hover) —
     dimming further on hover barely registers against a dark surface,
     see spec §1's stated reversal. */
  --de-primary: #137FEC;
  --de-primary-dark: #50A1F1;
  --de-primary-hover-fill: #2F8FEE;
  --de-primary-soft: #1B334B;
  --de-primary-ring: rgba(19, 127, 236, 0.28);
  --de-primary-rgb: 19, 127, 236;
  --de-danger-rgb: 230, 55, 55;
  --de-success-rgb: 61, 214, 166;
  --de-warning-rgb: 242, 169, 44;

  --de-accent: #3DD6A6;
  --de-accent-soft: #1B4B3C;

  /* Danger — same fill-vs-white-text balance as primary (white on-fill
     text stays correct for danger; see spec §2.5). */
  --de-danger: #E63737;
  --de-danger-dark: #EE7777;
  --de-danger-soft: #4B1B1B;

  /* Success / warning — NOT the same shape of trade-off as primary/
     danger. White text fails against any success/warning fill bright
     enough to read on a dark page (spec §2.5's scan), so these are
     tuned purely for "legible against dark card bg" and "legible as
     text on their own -soft background" — both want brightness, with
     no white-text ceiling pulling them back down. Their on-fill text
     is a new dedicated pair (below), not --de-on-fill. */
  --de-success: #3DD6A6;
  --de-success-dark: #68DFB9;
  --de-success-soft: #1B4B3C;
  --de-warning: #F2A92C;
  --de-warning-soft: #4B3A1B;

  /* Grammar-category colors — each pair re-derived per the -soft
     formula in spec §2.6 (dark saturated fill + bright text of the
     same hue), not a darkened version of the light-mode pale tint.
     Checked as a full set for mutual distinguishability (spec §4.4),
     not just each pair against its own background. */
  --de-cat-masc: #65A8EC;
  --de-cat-masc-soft: #1B334B;
  --de-cat-fem: #EC6592;
  --de-cat-fem-soft: #4B1B2B;
  --de-cat-neut: #65ECCA;
  --de-cat-neut-soft: #1B4B3F;
  --de-cat-plural: #ECAF65;
  --de-cat-plural-soft: #4B351B;
  --de-cat-verb: #65A8EC;
  --de-cat-verb-soft: #1B334B;
  --de-cat-adj: #8F77EE;
  --de-cat-adj-soft: #241B4B;
  --de-cat-adv: #EC8665;
  --de-cat-adv-soft: #4B271B;
  --de-cat-other: #B4B1A7;
  --de-cat-other-soft: #333128;

  /* Tab colors — same hues as their matching --de-cat-* (already true
     in light mode; the coincidence just gets carried through). */
  --de-tab-grammar: #8F77EE;
  --de-tab-grammar-soft: #241B4B;
  --de-tab-vocabulary: #ECAF65;
  --de-tab-vocabulary-soft: #4B351B;
  --de-tab-dialogue: #65ECCA;
  --de-tab-dialogue-soft: #1B4B3F;
  --de-tab-quiz: #65A8EC;
  --de-tab-quiz-soft: #1B334B;
  --de-tab-reading: #EC8665;
  --de-tab-reading-soft: #4B271B;

  /* On-fill text. --de-on-fill stays white — still correct for
     primary/danger fills. The new pair below is success/warning's own
     on-fill color (spec §2.5/§2.7) — dark, hue-tinted near-black
     rather than plain black, so it doesn't look like a rendering
     error sitting on a saturated fill. */
  --de-on-fill: #ffffff;
  --de-on-fill-muted: rgba(255, 255, 255, 0.75);
  --de-on-success-fill: #0F2921;
  --de-on-warning-fill: #291F0F;

  /* Sheens/glass — the strong inset highlight and the white-to-pale-
     blue card gradient were a light-mode-only "glass card" technique
     (spec §4.6); a bright white sheen on a dark card reads as glare,
     not polish, so it's dropped to near-zero rather than dimmed to a
     "dark version" of itself. --de-card-gradient above was already
     flattened to a solid — .de-mini-stat/.de-plan-box/.de-chart-card
     fall back to the same flat elevated-card look as everything else,
     one fewer visual language to maintain. --de-sheen-wash (the
     smaller active-state tint on colored fills) is a different effect
     at a different scale and just gets a modest reduction. */
  --de-sheen-inset: rgba(255, 255, 255, 0.04);
  --de-sheen-wash: rgba(255, 255, 255, 0.14);

  /* Reading purple + dialogue-B green + flashcard rating colors — all
     re-derived per the same -soft formula (spec §4.1/§4.2/§4.3). */
  --de-reading-accent: #9B77EE;
  --de-reading-accent-dark: #5D3CAA;
  --de-dialogue-b-soft: #1C4A39;
  --de-dialogue-b-tick: #7FD8FF;
  --de-vocab-easy-bg: #1B4B3C;
  --de-vocab-hard-bg: #4B3A1B;
  --de-on-warning-soft: #F2A92C;
  --de-vocab-hard-text: var(--de-on-warning-soft);

  /* Shadows — per spec §1.1/§3, elevation comes from surface lightness
     + border now, not shadow (shadows barely register on a dark
     background regardless). Card/button-level shadow is dropped
     entirely; a small amount is kept only for things that float above
     the rest of the UI (modals, popovers), where a depth cue still
     earns its keep. These are literal rgba(0,0,0,…) rather than
     reusing --de-heading-rgb — that triplet doubling as "the shadow
     tint" was a light-mode coincidence (heading text and shadow tint
     happened to share a value), not a relationship worth preserving
     once heading text turns light. */
  --de-shadow-sm: none;
  --de-shadow: 0 6px 20px rgba(0, 0, 0, 0.32);
  --de-shadow-hover: 0 10px 28px rgba(0, 0, 0, 0.38);

  /* Hero card — per direction, folded into the elevation system
     (--de-card-strong) with a subtle primary-hued gradient for brand
     warmth, instead of a separate near-black block that loses its
     whole reason for being once the rest of the page is dark too
     (spec §4.5, approved option). Topband accent line just swaps its
     three stops for their dark-mode equivalents. */
  --de-hero-gradient: linear-gradient(135deg, #21293B 0%, #1B3352 100%);
  --de-topband-line: linear-gradient(90deg, #65A8EC 0%, #137FEC 50%, #3DD6A6 100%);

  color-scheme: dark;
  /* --- English track aliases -------------------------------------
     Authored grammar HTML references plugin colour tokens by name
     (var(--en-primary), var(--en-card) and so on), exactly as the
     English content-generation specification instructs. Since
     3.31.0 the English page is served by this stylesheet, which
     defines only --de-* — so every one of those references
     resolved to nothing and the grammar cards lost their colours,
     backgrounds and borders and collapsed to plain text.
  
     Aliasing rather than rewriting the lessons: the HTML is already
     authored and the spec will keep producing --en-* names. These
     are declared in both theme blocks and point at --de-* by
     reference, so they follow light/dark automatically and can
     never drift from the values they mirror.
  
     Kept until the deferred cosmetic pass renames the whole token
     set to something track-neutral; at that point both prefixes
     become aliases of the new names. */
  --en-navy-950: var(--de-navy-950);
  --en-navy-900: var(--de-navy-900);
  --en-navy-800: var(--de-navy-800);
  --en-primary: var(--de-primary);
  --en-primary-dark: var(--de-primary-dark);
  --en-primary-hover-fill: var(--de-primary-hover-fill);
  --en-primary-soft: var(--de-primary-soft);
  --en-primary-ring: var(--de-primary-ring);
  --en-primary-rgb: var(--de-primary-rgb);
  --en-danger-rgb: var(--de-danger-rgb);
  --en-success-rgb: var(--de-success-rgb);
  --en-warning-rgb: var(--de-warning-rgb);
  --en-heading-rgb: var(--de-heading-rgb);
  --en-accent: var(--de-accent);
  --en-accent-soft: var(--de-accent-soft);
  --en-text: var(--de-text);
  --en-text-light: var(--de-text-light);
  --en-text-faint: var(--de-text-faint);
  --en-heading: var(--de-heading);
  --en-border: var(--de-border);
  --en-border-strong: var(--de-border-strong);
  --en-border-soft: var(--de-border-soft);
  --en-bg: var(--de-bg);
  --en-card: var(--de-card);
  --en-card-strong: var(--de-card-strong);
  --en-card-soft: var(--de-card-soft);
  --en-card-gradient: var(--de-card-gradient);
  --en-reading-card-bg: var(--de-reading-card-bg);
  --en-danger: var(--de-danger);
  --en-danger-dark: var(--de-danger-dark);
  --en-danger-soft: var(--de-danger-soft);
  --en-warning: var(--de-warning);
  --en-warning-soft: var(--de-warning-soft);
  --en-success: var(--de-success);
  --en-success-dark: var(--de-success-dark);
  --en-success-soft: var(--de-success-soft);
  --en-neutral: var(--de-neutral);
  --en-cat-masc: var(--de-cat-masc);
  --en-cat-masc-soft: var(--de-cat-masc-soft);
  --en-cat-fem: var(--de-cat-fem);
  --en-cat-fem-soft: var(--de-cat-fem-soft);
  --en-cat-neut: var(--de-cat-neut);
  --en-cat-neut-soft: var(--de-cat-neut-soft);
  --en-cat-plural: var(--de-cat-plural);
  --en-cat-plural-soft: var(--de-cat-plural-soft);
  --en-cat-verb: var(--de-cat-verb);
  --en-cat-verb-soft: var(--de-cat-verb-soft);
  --en-cat-adj: var(--de-cat-adj);
  --en-cat-adj-soft: var(--de-cat-adj-soft);
  --en-cat-adv: var(--de-cat-adv);
  --en-cat-adv-soft: var(--de-cat-adv-soft);
  --en-cat-other: var(--de-cat-other);
  --en-cat-other-soft: var(--de-cat-other-soft);
  --en-tab-grammar: var(--de-tab-grammar);
  --en-tab-grammar-soft: var(--de-tab-grammar-soft);
  --en-tab-vocabulary: var(--de-tab-vocabulary);
  --en-tab-vocabulary-soft: var(--de-tab-vocabulary-soft);
  --en-tab-dialogue: var(--de-tab-dialogue);
  --en-tab-dialogue-soft: var(--de-tab-dialogue-soft);
  --en-tab-quiz: var(--de-tab-quiz);
  --en-tab-quiz-soft: var(--de-tab-quiz-soft);
  --en-tab-reading: var(--de-tab-reading);
  --en-tab-reading-soft: var(--de-tab-reading-soft);
  --en-on-fill: var(--de-on-fill);
  --en-on-fill-muted: var(--de-on-fill-muted);
  --en-on-success-fill: var(--de-on-success-fill);
  --en-on-warning-fill: var(--de-on-warning-fill);
  --en-sheen-inset: var(--de-sheen-inset);
  --en-sheen-wash: var(--de-sheen-wash);
  --en-reading-accent: var(--de-reading-accent);
  --en-reading-accent-dark: var(--de-reading-accent-dark);
  --en-dialogue-b-soft: var(--de-dialogue-b-soft);
  --en-dialogue-b-tick: var(--de-dialogue-b-tick);
  --en-vocab-easy-bg: var(--de-vocab-easy-bg);
  --en-vocab-easy-border: var(--de-vocab-easy-border);
  --en-vocab-hard-bg: var(--de-vocab-hard-bg);
  --en-vocab-hard-border: var(--de-vocab-hard-border);
  --en-on-warning-soft: var(--de-on-warning-soft);
  --en-vocab-hard-text: var(--de-vocab-hard-text);
  --en-space-1: var(--de-space-1);
  --en-space-2: var(--de-space-2);
  --en-space-3: var(--de-space-3);
  --en-space-4: var(--de-space-4);
  --en-space-5: var(--de-space-5);
  --en-space-6: var(--de-space-6);
  --en-space-8: var(--de-space-8);
  --en-radius-sm: var(--de-radius-sm);
  --en-radius: var(--de-radius);
  --en-radius-lg: var(--de-radius-lg);
  --en-radius-pill: var(--de-radius-pill);
  --en-radius-tab: var(--de-radius-tab);
  --en-radius-tabnav: var(--de-radius-tabnav);
  --en-shadow-sm: var(--de-shadow-sm);
  --en-shadow: var(--de-shadow);
  --en-shadow-hover: var(--de-shadow-hover);
  --en-hero-gradient: var(--de-hero-gradient);
  --en-topband-line: var(--de-topband-line);
  --en-transition: var(--de-transition);
  --en-font: var(--de-font);
  /* No German counterpart: English marks nouns with one badge
     colour where German splits by grammatical gender. */
  --en-cat-noun: #65ABEC;
  --en-cat-noun-soft: #1B344B;
}

/* --- Deliberate component-level exceptions (spec-justified, not
   scattered per-component patching) ---
   Two rules genuinely can't be expressed as a token redefinition:
   1. The hero card's border/shadow were hardcoded rgba() literals
      (not tokens) tuned for a dark block sitting on a light page —
      that relationship doesn't carry over now that the hero is part
      of the elevation system (see --de-hero-gradient above and spec
      §4.5). Border becomes a primary-tinted brand accent instead of a
      white rim; the dramatic drop-shadow is dropped, consistent with
      §1.1/§3's "shadow only where it earns its keep" rule — a card
      that's already part of the elevation ramp doesn't need one.
   2. .de-table th's near-black header fill was hardcoded to sit on a
      light table body — on a dark table body it would blend in
      instead of standing out as a header row, so it steps up to
      --de-card-strong (the same "elevated" surface used everywhere
      else) instead. */
#deTrackerApp[data-theme="dark"] .de-topband {
  border-color: rgba(19, 127, 236, 0.35);
  box-shadow: none;
}

#deTrackerApp[data-theme="dark"] .de-table th {
  background: var(--de-card-strong);
}

/* Book icon chip: light mode pairs --de-primary-soft with the plain
   --de-primary as icon color (0B7BEA on E6F1FB, easily AA). Dark
   mode's --de-primary is deliberately dimmer than that (tuned for
   white button text on top of it, not for itself-as-text-on-soft —
   see the --de-primary comment above), so it under-shoots contrast
   here; --de-primary-dark is the correct role for this job in dark
   mode. */
#deTrackerApp[data-theme="dark"] .de-lesson-book-icon {
  color: var(--de-primary-dark);
}

/* .de-table-wrap, .de-login-panel, and the login form's text inputs
   all used a translucent WHITE wash (rgba(255,255,255,N)) over the
   light page background — a valid "frosted glass" look specifically
   because the page behind them was light. On a dark page that same
   wash reads as a washed-out light-gray box, not glass — these three
   weren't part of the 3.16.0 token pass (each was a singleton, no
   shared token to route through), so they need explicit dark
   treatment here rather than a token redefinition. All three switch
   to the same flat elevated-card language the rest of dark mode
   already uses (no translucency, solid surface + border), consistent
   with dropping the glass/sheen technique in dark mode generally
   (see --de-sheen-inset above). */
#deTrackerApp[data-theme="dark"] .de-table-wrap {
  background: var(--de-card);
  border-color: var(--de-border);
}

#deTrackerApp[data-theme="dark"] .de-login-panel {
  background: var(--de-card-strong);
  border-color: var(--de-border);
}

#deTrackerApp[data-theme="dark"] .de-login-form input[type="text"],
#deTrackerApp[data-theme="dark"] .de-login-form input[type="password"] {
  background: var(--de-card-soft);
  border-color: var(--de-border);
  color: var(--de-text);
}

/* Same reasoning as .de-table-wrap above — the row divider and hover
   tint were light literal colors (a light-gray divider line, a
   near-white hover wash), both invisible or wrong on a dark table
   body. */
#deTrackerApp[data-theme="dark"] .de-table td {
  border-bottom-color: var(--de-border);
}

#deTrackerApp[data-theme="dark"] .de-table tbody tr:hover {
  background: var(--de-card-soft);
}

/* Toast/alert messages — same "translucent pale tint assumed a light
   page" issue as the login panel/table above, just with a colored
   (not white) tint this time. Dark mode routes to the matching -soft
   token instead (already correctly dark-tinted there), keeping the
   same danger-dark/primary-dark text that was already theme-correct. */
#deTrackerApp[data-theme="dark"] .de-message-error {
  background: var(--de-danger-soft);
  border-color: rgba(var(--de-danger-rgb), 0.3);
}

#deTrackerApp[data-theme="dark"] .de-message-info {
  background: var(--de-primary-soft);
  border-color: rgba(var(--de-primary-rgb), 0.3);
}

/* Same pale-gradient-assumes-light-page issue as the messages above. */
#deTrackerApp[data-theme="dark"] .de-total-box {
  background: linear-gradient(135deg, var(--de-primary-soft), var(--de-success-soft));
}

/* Main tab nav — the wrapper pill and each inactive tab's hover both
   used a translucent WHITE literal (rgba(255,255,255,N)), the exact
   same "assumed a light page behind it" issue as the login panel/
   table/messages above — except here the affected text was the tab
   label itself, so it read as genuinely illegible rather than just an
   odd-looking surface. Light mode keeps its existing frosted-white
   look untouched (it already reads fine there); dark mode gets a real
   bordered "box per tab" instead of trying to force the same
   translucent-glass technique to work on a dark page. */
#deTrackerApp[data-theme="dark"] .de-tabs-nav {
  background: var(--de-card-soft);
  border-color: var(--de-border);
  box-shadow: none;
}

#deTrackerApp[data-theme="dark"] .de-tab-btn {
  background: var(--de-card);
  border-color: var(--de-border);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp[data-theme="dark"] .de-tab-btn:hover {
    color: var(--de-heading);
    background: var(--de-card-strong);
    border-color: var(--de-border-strong);
  }
}

#deTrackerApp::before,
#deTrackerApp::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  z-index: 0;
  filter: blur(90px);
  opacity: 0.55;
}

#deTrackerApp::before {
  width: 360px;
  height: 360px;
  background: radial-gradient(circle, rgba(var(--de-primary-rgb), 0.14) 0%, transparent 72%);
  top: 4%;
  right: -100px;
}

#deTrackerApp::after {
  width: 340px;
  height: 340px;
  background: radial-gradient(circle, rgba(16, 185, 129, 0.1) 0%, transparent 72%);
  bottom: 8%;
  left: -110px;
}

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

#deTrackerApp button,
#deTrackerApp input,
#deTrackerApp select,
#deTrackerApp textarea {
  font: inherit;
  color: inherit;
}

/* Every interactive answer element in the quiz/dialogue screens
   (multiple-choice options, reorder chips, situational choices, quiz
   choice buttons) is a real <button>, for keyboard/AT accessibility —
   but many WordPress themes apply their own global button styling
   (text-transform: uppercase and/or extra letter-spacing is a very
   common theme default for buttons/CTAs) that would otherwise leak
   into these, turning authored German text like "Hobbys sind Sport,
   Musik und seine ..." into "HOBBYS SIND SPORT, MUSIK UND SEINE ...".
   Explicitly resetting both here — on the same #deTrackerApp button
   rule other browser/theme button defaults are already normalized on
   — guarantees quiz/dialogue text always renders exactly as authored,
   regardless of which theme the shortcode is embedded in. */
#deTrackerApp button {
  cursor: pointer;
  text-transform: none;
  letter-spacing: normal;
}

#deTrackerApp h1,
#deTrackerApp h2,
#deTrackerApp h3,
#deTrackerApp h4,
#deTrackerApp p {
  margin: 0;
}

#deTrackerApp h1,
#deTrackerApp h2,
#deTrackerApp h3,
#deTrackerApp h4 {
  color: var(--de-heading);
  font-weight: 800;
  line-height: 1.4;
}

#deTrackerApp a {
  color: var(--de-primary);
  text-decoration: none;
  transition: var(--de-transition);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp a:hover {
    color: var(--de-primary-dark);
  }
}

#deTrackerApp :focus-visible {
  outline: 2px solid var(--de-primary);
  outline-offset: 2px;
}

#deTrackerApp .de-loading,
#deTrackerApp .de-error-state {
  min-height: 280px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 1;
}

#deTrackerApp .de-loading {
  flex-direction: column;
  gap: var(--de-space-3);
  color: var(--de-text-light);
}

#deTrackerApp .de-loading-spinner {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 4px solid rgba(var(--de-primary-rgb), 0.12);
  border-top-color: var(--de-primary);
  border-right-color: var(--de-accent);
  animation: de-spin 0.8s linear infinite;
}

@keyframes de-spin {
  to {
    transform: rotate(360deg);
  }
}

#deTrackerApp .de-error-state .de-card {
  text-align: center;
  max-width: 420px;
}

#deTrackerApp .de-error-state h3 {
  margin-bottom: var(--de-space-2);
}

#deTrackerApp .de-error-state p {
  color: var(--de-text-light);
  margin-bottom: var(--de-space-4);
}

#deTrackerApp .de-dashboard-shell {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-6);
  animation: de-fade-in 450ms cubic-bezier(0.16, 1, 0.3, 1) both;
  position: relative;
  z-index: 1;
}

/* Applied while the lesson-detail screen is open (see the JS render
   function), so the two aren't both present in the document at once —
   .de-lesson-screen is a normal in-flow section now (see its own
   comment block), not an overlay that visually covers this on its
   own. display:none rather than not rendering the shell at all: its
   DOM nodes (and anything bound to them) stay intact underneath,
   ready to reappear exactly as they were once the lesson screen
   closes. */
#deTrackerApp .de-dashboard-shell--hidden {
  display: none;
}

@keyframes de-fade-in {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ===================== TOP BAND ===================== */
#deTrackerApp .de-topband {
  position: relative;
  overflow: hidden;
  background: var(--de-hero-gradient);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--de-radius-lg);
  padding: var(--de-space-6) var(--de-space-8);
  color: var(--de-on-fill);
  box-shadow: 0 24px 60px rgba(15, 23, 42, 0.24);
}

#deTrackerApp .de-topband::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-start: 0;
  height: 4px;
  background: var(--de-topband-line);
  pointer-events: none;
}

#deTrackerApp .de-topband::after {
  content: "";
  position: absolute;
  inset-block-start: -25%;
  inset-inline-end: -8%;
  width: 360px;
  height: 360px;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.11) 0%, rgba(255, 255, 255, 0) 72%);
  pointer-events: none;
}

#deTrackerApp .de-topband-main {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--de-space-4);
  margin-bottom: var(--de-space-6);
  z-index: 2;
}

#deTrackerApp .de-title {
  font-size: 28px;
  font-weight: 900;
  color: var(--de-on-fill);
  margin: 0;
  letter-spacing: 0;
  text-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}

#deTrackerApp .de-account {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--de-radius);
  padding: 10px 18px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.08);
  /* 3.21.0: now a real <button> (opens the account panel) instead of
     a plain div — resets the browser's button defaults so it still
     looks exactly like the same quiet header chip it always was. */
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  transition: background-color 160ms ease, border-color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-account:hover {
    background: rgba(255, 255, 255, 0.16);
    border-color: rgba(255, 255, 255, 0.28);
  }
}

#deTrackerApp .de-account:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.6);
  outline-offset: 2px;
}

#deTrackerApp .de-account-label {
  font-size: 11px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.64);
}

#deTrackerApp .de-account-name {
  font-size: 14px;
  font-weight: 700;
  color: var(--de-on-fill);
}

/* Header quick theme toggle — a compact on/off switch (sun/moon),
   always reachable without opening settings. Sits on the topband,
   which is a dark surface in both app themes (see the hero-card notes
   elsewhere), so — like .de-account/.de-chip next to it — it reuses
   the same translucent-white treatment in both light and dark mode
   rather than needing its own dark-mode override.
   Desktop: shown next to the account block. Mobile: shown next to the
   title instead (see .de-topband-title-row below) — two render
   locations for the same buildThemeQuickToggle() markup/action,
   toggled via display, not two separate controls or states. */
#deTrackerApp .de-theme-quick-toggle {
  appearance: none;
  border: none;
  background: transparent;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
  border-radius: var(--de-radius-pill);
}

#deTrackerApp .de-theme-quick-toggle:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.6);
  outline-offset: 2px;
}

#deTrackerApp .de-theme-quick-toggle-track {
  position: relative;
  display: block;
  width: 46px;
  height: 26px;
  border-radius: var(--de-radius-pill);
  background: rgba(255, 255, 255, 0.16);
  border: 1px solid rgba(255, 255, 255, 0.2);
  transition: background-color 200ms ease, border-color 200ms ease;
}

#deTrackerApp .de-theme-quick-toggle-thumb {
  position: absolute;
  top: 2px;
  /* Physical (not logical) positioning is deliberate here — a light/
     dark switch reads as "off = left, on = right" the way a physical
     toggle does, independent of page text direction, the same way OS-
     level theme switches work regardless of the system language. */
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #ffffff;
  color: var(--de-warning);
  transition: transform 220ms cubic-bezier(0.34, 1.56, 0.64, 1), background-color 200ms ease, color 200ms ease;
}

#deTrackerApp .de-theme-quick-toggle-thumb svg {
  width: 13px;
  height: 13px;
}

#deTrackerApp .de-theme-quick-toggle--dark .de-theme-quick-toggle-track {
  background: rgba(0, 0, 0, 0.32);
  border-color: rgba(255, 255, 255, 0.14);
}

#deTrackerApp .de-theme-quick-toggle--dark .de-theme-quick-toggle-thumb {
  transform: translateX(20px);
  background: var(--de-primary);
  color: #ffffff;
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-theme-quick-toggle:hover .de-theme-quick-toggle-track {
    border-color: rgba(255, 255, 255, 0.4);
  }
}

#deTrackerApp .de-topband-title-row {
  display: flex;
  align-items: center;
  gap: var(--de-space-3);
}

#deTrackerApp .de-account-row {
  display: flex;
  align-items: center;
  gap: var(--de-space-3);
}

/* Same buildThemeQuickToggle() markup rendered in two places (see the
   JS comment) — only one is ever visible at a time, swapped by this
   breakpoint rather than by any JS/state branching. */
#deTrackerApp .de-theme-quick-toggle-slot--mobile {
  display: none;
}

#deTrackerApp .de-summary-chips {
  position: relative;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--de-space-4);
  z-index: 2;
}

#deTrackerApp .de-chip {
  position: relative;
  background: rgba(255, 255, 255, 0.16);
  border-radius: var(--de-radius);
  padding: var(--de-space-4) var(--de-space-5);
  display: flex;
  flex-direction: column;
  gap: 8px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  overflow: hidden;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.08);
  transition: var(--de-transition);
}

#deTrackerApp .de-chip::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-start: 0;
  height: 3px;
  background: rgba(255, 255, 255, 0.65);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-chip:hover {
    transform: translateY(-6px);
    background: var(--de-sheen-wash);
    border-color: rgba(255, 255, 255, 0.24);
    box-shadow: 0 18px 40px rgba(15, 23, 42, 0.2);
  }
}

#deTrackerApp .de-chip-icon {
  font-size: 20px;
  line-height: 1;
  margin-bottom: 2px;
  opacity: 0.95;
}

#deTrackerApp .de-chip--indigo::before,
#deTrackerApp .de-chip--teal::before,
#deTrackerApp .de-chip--rose::before,
#deTrackerApp .de-chip--violet::before,
#deTrackerApp .de-chip--green::before {
  background: linear-gradient(90deg, rgba(255,255,255,0.95), rgba(255,255,255,0.38));
}

#deTrackerApp .de-chip-label {
  font-size: 12.5px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.74);
}

/* 3.38.0 — the study range beside the lesson count, so "12 / 44"
   says which 44. Quieter than the value it annotates. */
#deTrackerApp .de-chip-note {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.6);
}

#deTrackerApp .de-chip-value {
  font-size: 24px;
  font-weight: 900;
  color: var(--de-on-fill);
  letter-spacing: 0;
}

/* ===================== TABS ===================== */
#deTrackerApp .de-tabs-nav {
  display: flex;
  gap: 6px;
  background: rgba(255, 255, 255, 0.58);
  border: 1px solid rgba(255, 255, 255, 0.58);
  border-radius: var(--de-radius-tabnav);
  padding: 6px;
  box-shadow: var(--de-shadow-sm);
  overflow-x: auto;
  scrollbar-width: none;
}

#deTrackerApp .de-tabs-nav::-webkit-scrollbar {
  display: none;
}

#deTrackerApp .de-tab-btn {
  position: relative;
  flex: 1 1 auto;
  white-space: nowrap;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--de-radius-tab);
  padding: 11px 22px;
  font-size: 14px;
  font-weight: 700;
  color: var(--de-text-light);
  cursor: pointer;
  transition: background-color .22s ease, color .22s ease,
              box-shadow .22s ease, transform .18s ease, border-color .22s ease;
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-tab-btn:hover {
    color: var(--de-heading);
    background: rgba(255, 255, 255, 0.7);
    border-color: rgba(15, 23, 42, 0.08);
    transform: translateY(-1px);
  }
}

#deTrackerApp .de-tab-btn:focus-visible {
  outline: 2px solid rgba(var(--de-primary-rgb), 0.45);
  outline-offset: 2px;
}

#deTrackerApp .de-tab-btn.active {
  background: linear-gradient(135deg, var(--de-primary) 0%, var(--de-primary-hover-fill) 100%);
  color: var(--de-on-fill);
  border-color: transparent;
  transform: none;
}

#deTrackerApp .de-tab-btn.active::after {
  content: "";
  position: absolute;
  inset-inline: 26%;
  bottom: 5px;
  height: 2px;
  border-radius: 2px;
  background: var(--de-border-soft);
}

#deTrackerApp .de-tab-btn:active {
  transform: translateY(0) scale(.985);
}

@media (max-width: 600px) {
  #deTrackerApp {
    --de-radius-tab: 10px;
    --de-radius-tabnav: 14px;
  }
  #deTrackerApp .de-tab-btn {
    padding: 10px 14px;
    font-size: 13px;
  }
}

@media (prefers-reduced-motion: reduce) {
  #deTrackerApp .de-tab-btn { transition: none; }
  #deTrackerApp .de-tab-btn:hover { transform: none; }
}


#deTrackerApp .de-tab-panels {
  display: block;
  overflow: visible;
}

#deTrackerApp .de-tab-panel {
  display: none;
  flex-direction: column;
  gap: var(--de-space-5);
  overflow: visible;
  min-height: 0;
}

#deTrackerApp .de-tab-panel.active {
  display: flex;
  animation: de-fade-in 300ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* ===================== CARDS ===================== */
#deTrackerApp .de-card {
  background: var(--de-card);
  border: 1.5px solid var(--de-border);
  border-radius: var(--de-radius-lg);
  padding: var(--de-space-6);
  box-shadow: var(--de-shadow-sm);
  transition: var(--de-transition);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-card:hover {
    transform: translateY(-2px);
    border-color: var(--de-primary);
    box-shadow: var(--de-shadow);
  }
}

#deTrackerApp .de-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--de-space-3);
  margin-bottom: var(--de-space-5);
  padding-bottom: var(--de-space-4);
  border-bottom: 1.5px solid var(--de-border);
}

#deTrackerApp .de-card-head h2 {
  font-size: 18px;
  position: relative;
  padding-inline-start: 16px;
  font-weight: 800;
}

#deTrackerApp .de-card-head h2::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 6px;
  height: 18px;
  border-radius: var(--de-radius-pill);
  background: var(--de-primary);
}

#deTrackerApp .de-card-head h3 {
  font-size: 15px;
  color: var(--de-text-light);
}

#deTrackerApp .de-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--de-space-4);
}

#deTrackerApp .de-toolbar-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 150px;
}

#deTrackerApp .de-toolbar-group label,
#deTrackerApp .de-field label {
  font-size: 13px;
  font-weight: 700;
  color: var(--de-text-light);
  margin-bottom: 2px;
}

#deTrackerApp .de-toolbar-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--de-space-2);
  margin-inline-start: auto;
}

/* ===================== BUTTONS ===================== */
#deTrackerApp .de-btn {
  appearance: none;
  border: 1.5px solid var(--de-border-strong);
  border-radius: var(--de-radius-sm);
  padding: 11px 20px;
  font-size: 13.5px;
  font-weight: 700;
  background: var(--de-card);
  color: var(--de-text);
  transition: var(--de-transition);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-btn:hover {
    transform: translateY(-1px);
  }
}

#deTrackerApp .de-btn:active {
  transform: scale(0.98);
}

#deTrackerApp .de-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: none !important;
}

/* Solid fill, not a gradient — the validated mockups use a flat brand
   color on primary buttons (see the quiz/dialogue/dashboard mockups),
   not the original diagonal gradient. */
/* 3.15.0: dropped the "pressed 3D" offset shadow used here since
   3.13.0 — per direction, the whole app is going flat/minimal, no
   shadow separating a button from the page underneath it. Kept the
   solid fill and the plain `:active` fallback from the shared .de-btn
   rule (scale(0.98)) for tactile feedback without any shadow. */
#deTrackerApp .de-btn-primary {
  background: var(--de-primary);
  border-color: transparent;
  color: var(--de-on-fill);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-btn-primary:hover {
    /* Overrides the generic .de-btn:hover whitish background (same
       specificity, but this rule comes later) so the label stays
       white-on-brand instead of fading to white-on-white. */
    background: var(--de-primary-hover-fill);
    color: var(--de-on-fill);
  }
}

#deTrackerApp .de-btn-secondary {
  background: var(--de-card);
  border-color: var(--de-border-strong);
  color: var(--de-text);
}


@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-btn-secondary:hover {
    border-color: var(--de-primary);
    color: var(--de-primary-dark);
  }
}

#deTrackerApp .de-btn-ghost {
  background: transparent;
  border-color: var(--de-border);
  color: var(--de-text-light);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-btn-ghost:hover {
    background: var(--de-card-soft);
    color: var(--de-text);
  }
}

#deTrackerApp .de-btn-danger {
  background: var(--de-danger-soft);
  border-color: rgba(var(--de-danger-rgb), 0.24);
  color: var(--de-danger-dark);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-btn-danger:hover {
    background: var(--de-danger);
    color: var(--de-on-fill);
  }
}

#deTrackerApp .de-btn-inline {
  padding: 6px 12px;
  font-size: 12.5px;
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-btn-inline:hover {
    border-color: var(--de-primary);
    color: var(--de-primary);
  }

  #deTrackerApp .de-btn-inline-danger:hover {
    border-color: var(--de-danger);
    color: var(--de-danger-dark);
  }
}

/* ===================== LAYOUT GRID ===================== */
#deTrackerApp .de-main-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: var(--de-space-6);
  align-items: start;
}

#deTrackerApp .de-main-col,
#deTrackerApp .de-side-col {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-6);
  min-width: 0;
}

#deTrackerApp .de-form-card {
  position: relative;
}

#deTrackerApp .de-form {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-5);
}

#deTrackerApp .de-form-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--de-space-4);
}

#deTrackerApp .de-form-grid-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

#deTrackerApp .de-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

#deTrackerApp .de-field input,
#deTrackerApp .de-field select,
#deTrackerApp .de-field textarea,
#deTrackerApp .de-toolbar select {
  border: 1.5px solid var(--de-border-strong);
  border-radius: var(--de-radius-sm);
  padding: 12px var(--de-space-3);
  background: var(--de-card);
  font-size: 14px;
  line-height: 1.8;
  min-height: 48px;
  color: var(--de-text);
  transition: var(--de-transition);
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-field input:hover,
  #deTrackerApp .de-field select:hover,
  #deTrackerApp .de-field textarea:hover,
  #deTrackerApp .de-toolbar select:hover {
    border-color: var(--de-primary);
  }
}

#deTrackerApp .de-field input:focus,
#deTrackerApp .de-field select:focus,
#deTrackerApp .de-field textarea:focus,
#deTrackerApp .de-toolbar select:focus {
  outline: none;
  border-color: var(--de-primary);
  box-shadow: 0 0 0 4px var(--de-primary-ring);
  background: var(--de-card);
}

#deTrackerApp .de-field input[readonly] {
  background: var(--de-card-soft);
  color: var(--de-text-light);
}

#deTrackerApp .de-field textarea {
  resize: vertical;
  min-height: 100px;
}

#deTrackerApp .de-field-help {
  font-size: 12px;
  color: var(--de-text-faint);
}

/* ===================== SKILL CARDS ===================== */
#deTrackerApp .de-skill-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--de-space-3);
}

#deTrackerApp .de-skill-card {
  background: var(--de-card);
  border: 1.5px solid var(--de-border);
  border-radius: var(--de-radius);
  padding: var(--de-space-3) var(--de-space-4);
  display: flex;
  flex-direction: column;
  gap: var(--de-space-2);
  border-top: 4px solid var(--de-primary);
  transition: var(--de-transition);
  box-shadow: var(--de-shadow-sm);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-skill-card:hover {
    box-shadow: var(--de-shadow);
    transform: translateY(-2px);
  }
}

/* Each skill genuinely gets its own color now — previously every
   variant here fell through to the same primary blue regardless of
   which class was applied, so the six skills (Hören/Lesen/Sprechen/
   Schreiben/Wortschatz/Grammatik) were visually indistinguishable from
   each other. Reuses the same six-color set already established for
   vocabulary categories/icon chips elsewhere in this redesign, rather
   than inventing a separate palette just for this grid. */
#deTrackerApp .de-skill-card--indigo  { border-top-color: var(--de-cat-adj); }
#deTrackerApp .de-skill-card--teal    { border-top-color: var(--de-cat-neut); }
#deTrackerApp .de-skill-card--amber   { border-top-color: var(--de-cat-plural); }
#deTrackerApp .de-skill-card--rose    { border-top-color: var(--de-cat-fem); }
#deTrackerApp .de-skill-card--violet  { border-top-color: var(--de-cat-masc); }
#deTrackerApp .de-skill-card--green   { border-top-color: var(--de-cat-adv); }

#deTrackerApp .de-skill-card-title {
  font-size: 13.5px;
  font-weight: 800;
  color: var(--de-text);
  display: flex;
  align-items: center;
  gap: 8px;
}

#deTrackerApp .de-skill-card-icon {
  display: inline-flex;
  line-height: 1;
}

#deTrackerApp .de-skill-card .de-field input {
  background: var(--de-card);
}

#deTrackerApp .de-total-preview {
  justify-content: center;
}

#deTrackerApp .de-total-box {
  border: 1.5px dashed var(--de-primary);
  border-radius: var(--de-radius-sm);
  padding: 12px var(--de-space-4);
  background: linear-gradient(135deg, rgba(223, 245, 254, 0.8), rgba(236, 253, 245, 0.82));
  text-align: center;
  color: var(--de-text);
}

#deTrackerApp .de-total-box strong {
  color: var(--de-primary-dark);
  font-size: 18px;
}

#deTrackerApp .de-form-actions {
  display: flex;
  gap: var(--de-space-3);
  flex-wrap: wrap;
}

#deTrackerApp .de-form-messages:empty {
  display: none;
}

#deTrackerApp .de-message {
  border-radius: var(--de-radius-sm);
  padding: var(--de-space-3) var(--de-space-4);
  font-size: 14px;
  line-height: 1.7;
}

#deTrackerApp .de-message-error {
  background: rgba(254, 226, 226, 0.9);
  color: var(--de-danger-dark);
  border: 1px solid rgba(var(--de-danger-rgb), 0.15);
}

#deTrackerApp .de-message-info {
  background: rgba(223, 245, 254, 0.84);
  color: var(--de-primary-dark);
  border: 1px solid rgba(var(--de-primary-rgb), 0.16);
}

/* ===================== MINI STATS ===================== */
#deTrackerApp .de-mini-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--de-space-3);
}

#deTrackerApp .de-mini-stat {
  position: relative;
  background: var(--de-card-gradient);
  border: 1px solid rgba(148, 163, 184, 0.34);
  border-top: 3px solid var(--de-primary);
  border-radius: var(--de-radius);
  padding: var(--de-space-4);
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: var(--de-transition);
  box-shadow:
    0 10px 26px rgba(15, 23, 42, 0.055),
    inset 0 1px 0 var(--de-sheen-inset);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-mini-stat:hover {
    transform: translateY(-3px);
    background: var(--de-card);
    border-color: rgba(var(--de-primary-rgb), 0.34);
    box-shadow:
      0 16px 34px rgba(15, 23, 42, 0.09),
      0 0 0 3px rgba(var(--de-primary-rgb), 0.045);
  }
}

#deTrackerApp .de-mini-stat-icon {
  font-size: 18px;
  line-height: 1;
}

#deTrackerApp .de-mini-stat--indigo,
#deTrackerApp .de-mini-stat--amber,
#deTrackerApp .de-mini-stat--teal,
#deTrackerApp .de-mini-stat--rose,
#deTrackerApp .de-mini-stat--violet {
  border-top-color: var(--de-primary);
}

#deTrackerApp .de-mini-stat span {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--de-text-light);
}

#deTrackerApp .de-mini-stat strong {
  font-size: 19px;
  font-weight: 900;
  color: var(--de-heading);
}

/* ===================== PLAN BOX ===================== */
#deTrackerApp .de-plan-grid {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-2);
}

#deTrackerApp .de-plan-box {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--de-space-2);
  padding: var(--de-space-3) var(--de-space-4);
  border-radius: var(--de-radius-sm);
  background: var(--de-card-gradient);
  border: 1px solid rgba(148, 163, 184, 0.34);
  border-inline-start: 4px solid rgba(var(--de-primary-rgb), 0.42);
  transition: var(--de-transition);
  box-shadow:
    0 8px 20px rgba(15, 23, 42, 0.045),
    inset 0 1px 0 var(--de-sheen-inset);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-plan-box:hover {
    background: var(--de-card);
    border-color: rgba(var(--de-primary-rgb), 0.32);
    border-inline-start-color: var(--de-primary);
    transform: translateX(-4px);
    box-shadow:
      0 14px 30px rgba(15, 23, 42, 0.08),
      0 0 0 3px rgba(var(--de-primary-rgb), 0.045);
  }
}

#deTrackerApp .de-plan-box span {
  font-size: 13.5px;
  color: var(--de-text-light);
}

#deTrackerApp .de-plan-box strong {
  font-size: 14.5px;
  font-weight: 800;
}

#deTrackerApp .de-text-success {
  color: var(--de-success);
}

#deTrackerApp .de-text-danger {
  color: var(--de-danger);
}

#deTrackerApp .de-text-neutral {
  color: var(--de-neutral);
}

/* ===================== LESSON PANELS (accordion) =====================
   Book vs. Lesson visual hierarchy — revision 3.

   Revision 1 tried color alone (a very faint primary-tinted wash on
   the book vs. a very faint gray wash on the tray) — both landed a
   hair's-width from plain white and were hard to tell apart. Revision
   2 switched to a lightness/depth model instead: white book, visibly
   *darker* recessed tray, white items raised on top of it via shadow.
   That worked, but wasn't the specific look wanted — the tray's dark
   tint away from the plugin's own brand color, its use of shadow-based
   depth over surface color, and the resulting close visual similarity
   between the book and lesson card colors, all read as murkier than
   the direct blue-vs-white the plugin's own brand color could give.

   This revision keeps the same class names and DOM structure (both
   "پیشرفت درس‌ها" and "خلاصه دروس" still share buildBookAccordionList()
   in the JS, so this one stylesheet change covers both automatically)
   but switches strategy again — back to color, this time strong enough
   to actually carry it:

   1. The book card carries a soft, direct tint of the plugin's own
      brand blue (#00aeef at low opacity) — not white, not a vague
      neutral gray — so it visibly reads as "the branded container."
   2. Its lesson tray (.de-lesson-book-body), once expanded, is plain
      white — the opposite surface from the book around it. The color
      contrast alone (soft blue vs. white) marks the boundary; no
      shadow trickery needed to sell where one stops and the other
      starts.
   3. Each lesson row is also white, sitting on the white tray, so it's
      set apart by its own border/shadow/status-colored edge rather
      than by background contrast against the tray (item vs. tray are
      the same white — that boundary was never the one this design
      needed to solve; book vs. tray is).
   4. Each lesson chip also gets a status-colored left edge (RTL: right
      edge), reusing the same not-started/in-progress/completed colors
      already used by its badge and progress bar — a second, redundant
      cue so status is scannable even before reading the badge text.
   ========================================================================== */
#deTrackerApp .de-lesson-panels {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-4);
}

/* On desktop, collapsed book cards have plenty of room to sit side by
   side instead of stacking full-width one under another — a grid gives
   that, while a book that's expanded spans the full row (grid-column:
   1 / -1) so its lesson list isn't squeezed into a narrow column.
   Mobile stays a single-column stack (defined above), since there's no
   spare width to arrange cards side by side there anyway. */
@media (min-width: 783px) {
  #deTrackerApp .de-lesson-panels {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    align-items: start;
  }

  #deTrackerApp .de-lesson-book.expanded {
    grid-column: 1 / -1;
  }
}

/* Book card: solid white with a colored icon chip, not the old
   translucent-blue-wash background — matches the approved lesson-list
   mockup, where the book itself reads as a plain card and its status
   comes through the icon chip + progress bar, not a tinted background. */
#deTrackerApp .de-lesson-book {
  background: var(--de-card);
  border: 1.5px solid var(--de-border);
  border-radius: var(--de-radius);
  padding: var(--de-space-4) var(--de-space-5);
  min-width: 0;
  transition: var(--de-transition);
  box-shadow: var(--de-shadow-sm);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-lesson-book:hover {
    border-color: var(--de-primary);
    box-shadow: var(--de-shadow);
  }
}

/* An expanded book is "the open parent" — a firmer border marks it as
   the container currently holding a tray open below it, without going
   back to a tinted background (that's reserved for the icon chip now). */
#deTrackerApp .de-lesson-book.expanded {
  border-color: var(--de-primary);
  box-shadow: var(--de-shadow);
}

/* --- Book header as its own toggle button, styled like a lesson-item
   row (per design: the book card should look like its own lesson
   cards) but unstyled as a <button> so it stays keyboard/AT friendly. --- */
#deTrackerApp .de-lesson-book-toggle {
  display: block;
  width: 100%;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  text-align: inherit;
  font: inherit;
  color: inherit;
}

/* Colored icon chip, replacing the chevron as the row's leading
   glyph — the chevron moved to the trailing (RTL: left) edge instead,
   next to the title, matching the icon-chip pattern used throughout
   the rest of this redesign (dashboard stat cards, tab icons). */
#deTrackerApp .de-lesson-book-icon {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 12px;
  background: var(--de-primary-soft);
  color: var(--de-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

#deTrackerApp .de-lesson-book-toggle .de-lesson-row {
  position: relative;
}

/* Book title is a step larger/bolder than a lesson title (14px/700 —
   see .de-lesson-meta strong below) — a small typographic cue that
   the book is the higher level in the hierarchy, not just another
   item at the same rank. */
#deTrackerApp .de-lesson-book-toggle .de-lesson-meta strong {
  font-size: 15px;
  font-weight: 800;
}

/* 3.13.0: swapped the text chevron "▾" for a plus icon that rotates
   45° into an × on expand — a single glyph that reads as both
   "expand" and "collapse" depending on rotation, clearer than a
   chevron flipping 180° (which still looks like the same arrow, just
   upside down) about what tapping it will do next. */
#deTrackerApp .de-lesson-book-chevron {
  flex-shrink: 0;
  display: inline-flex;
  color: var(--de-text-light);
  transition: transform 220ms cubic-bezier(0.4, 0, 0.2, 1), color 220ms ease;
}

#deTrackerApp .de-lesson-book-chevron--expanded {
  transform: rotate(45deg);
  color: var(--de-primary);
}

/* The "tray" the lessons sit inside once a book expands — kept as a
   very light neutral wash (not pure white, not the book's own primary
   tint) so it still reads as a distinct sub-container without
   competing with the colored status cues on each lesson row inside it. */
#deTrackerApp .de-lesson-book-body {
  margin-top: var(--de-space-4);
  padding: var(--de-space-3);
  border: 1.5px solid var(--de-border);
  border-radius: var(--de-radius-sm);
  background: var(--de-card-soft);
  animation: de-fade-in 220ms ease both;
}

#deTrackerApp .de-lesson-list {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-3);
}

@media (min-width: 783px) {
  #deTrackerApp .de-lesson-list {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    align-items: start;
  }
}

/* Solid white card with a visible border — status now reads primarily
   through the leading status-circle glyph (see .de-lesson-status-circle
   below) rather than a colored inline-start edge, matching the
   approved lesson-list mockup's checkmark/play/number circles. The
   border itself stays neutral so the status circle is the one clear
   signal, not competing with a second colored accent on the same row. */
#deTrackerApp .de-lesson-item {
  background: var(--de-card);
  border: 1.5px solid var(--de-border);
  border-radius: var(--de-radius-sm);
  padding: var(--de-space-3);
  transition: var(--de-transition);
  overflow: visible;
}

#deTrackerApp .de-lesson-item--in-progress {
  border-color: var(--de-primary);
  border-width: 2px;
  background: var(--de-primary-soft);
}

/* 3.13.0: completed rows previously fell back to the same neutral
   border as an untouched row — the status circle was the only signal
   that anything had happened here. A light success tint (mirroring
   how --in-progress uses --de-primary-soft) makes a finished lesson
   read as "done" at a glance, not just "not currently active". */
#deTrackerApp .de-lesson-item--completed {
  border-color: var(--de-success);
  background: var(--de-success-soft);
}

/* Not-started rows sit back visually (per the mockup) — same card
   shape, just quieter, so the current/completed rows are what draw
   the eye first when scanning a book's lesson list. 3.13.0: 0.6 read
   as too faded against the now-more-saturated in-progress/completed
   rows above; 0.78 keeps them legible while still clearly secondary. */
#deTrackerApp .de-lesson-item--not-started {
  opacity: 0.78;
}

/* Hover feedback on desktop for every lesson row, in both "پیشرفت
   دروس" (read-only) and "خلاصه دروس" (clickable). */
@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-lesson-item:hover {
    transform: translateY(-1px);
    box-shadow: var(--de-shadow-sm);
  }
}

#deTrackerApp .de-lesson-row {
  display: flex;
  align-items: center;
  gap: var(--de-space-3);
  margin-bottom: 10px;
}

/* Status circle — the primary status signal on each lesson row now
   (checkmark/play glyph/lesson number), replacing the old colored
   side-edge. Colors mirror the badge/progress-bar palette so all three
   status cues (circle, badge, progress fill) can never drift out of
   sync with each other. */
#deTrackerApp .de-lesson-status-circle {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: var(--de-radius-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 800;
  background: var(--de-card-soft);
  color: var(--de-text-light);
}

#deTrackerApp .de-lesson-status-circle--completed {
  background: var(--de-success);
  color: var(--de-on-success-fill);
  font-size: 15px;
}

#deTrackerApp .de-lesson-status-circle--in-progress {
  background: var(--de-primary);
  color: var(--de-on-fill);
  font-size: 12px;
}

#deTrackerApp .de-lesson-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}

/* 3.18.0 — book/lesson names are always German/English titles (e.g.
   "Starten wir A1", "Menschen"), not Persian sentences — right-
   aligning them (the page's default, inherited from #deTrackerApp)
   read as visually unbalanced for genuinely Latin-script content.
   Scoped to this one class rather than a blanket [dir="ltr"] rule,
   since plenty of other dir="ltr" spots in this app (German text
   inline with a Persian translation, form inputs) are deliberately
   still right-aligned as a block — only the character order inside
   them needs to be LTR, not their position in the row. */
#deTrackerApp .de-text-ltr {
  direction: ltr;
  text-align: left;
  unicode-bidi: isolate;
}

#deTrackerApp .de-lesson-meta strong {
  font-size: 14px;
  color: var(--de-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

#deTrackerApp .de-lesson-meta span {
  font-size: 12px;
  color: var(--de-text-light);
}

#deTrackerApp .de-lesson-badge {
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 800;
  padding: 4px 12px;
  border-radius: var(--de-radius-pill);
  white-space: nowrap;
}

#deTrackerApp .de-lesson-badge--not-started {
  background: var(--de-card-soft);
  color: var(--de-text-light);
}

#deTrackerApp .de-lesson-badge--in-progress {
  background: var(--de-primary-soft);
  color: var(--de-primary-dark);
}

#deTrackerApp .de-lesson-badge--completed {
  background: var(--de-success-soft);
  color: var(--de-success);
}

#deTrackerApp .de-lesson-item--completed .de-lesson-meta strong {
  color: var(--de-success);
}

#deTrackerApp .de-lesson-progress {
  height: 8px;
  border-radius: var(--de-radius-pill);
  background: var(--de-card-soft);
  overflow: hidden;
}

#deTrackerApp .de-lesson-progress-fill {
  height: 100%;
  border-radius: inherit;
  background: var(--de-primary-soft);
  transition: width 400ms cubic-bezier(0.4, 0, 0.2, 1);
}

#deTrackerApp .de-lesson-item--in-progress .de-lesson-progress-fill {
  background: var(--de-primary);
}

#deTrackerApp .de-lesson-item--completed .de-lesson-progress-fill {
  background: var(--de-success);
}

/* ===================== CHARTS ===================== */
#deTrackerApp .de-chart-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--de-space-5);
}

#deTrackerApp .de-chart-card {
  background: var(--de-card-gradient);
  border: 1px solid rgba(148, 163, 184, 0.32);
  border-radius: var(--de-radius);
  padding: var(--de-space-4) var(--de-space-5);
  min-width: 0;
  transition: var(--de-transition);
  box-shadow:
    0 8px 22px rgba(15, 23, 42, 0.045),
    inset 0 1px 0 var(--de-sheen-inset);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-chart-card:hover {
    box-shadow:
      0 16px 34px rgba(15, 23, 42, 0.085),
      0 0 0 3px rgba(var(--de-primary-rgb), 0.04);
    background: var(--de-card);
    border-color: rgba(var(--de-primary-rgb), 0.28);
  }
}

#deTrackerApp .de-chart-card-wide {
  grid-column: 1 / -1;
}

#deTrackerApp .de-chart-card h3 {
  font-size: 14.5px;
  font-weight: 800;
  margin-bottom: var(--de-space-4);
}

#deTrackerApp .de-chart-wrap {
  position: relative;
  height: 250px;
}

#deTrackerApp .de-chart-card-wide .de-chart-wrap {
  height: 230px;
}

#deTrackerApp .de-chart-empty {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--de-text-faint);
  font-size: 13.5px;
  padding: 0 var(--de-space-4);
}

/* ===================== TABLE ===================== */
#deTrackerApp .de-table-wrap {
  overflow-x: auto;
  border: 1px solid rgba(255, 255, 255, 0.74);
  border-radius: var(--de-radius);
  background: rgba(255, 255, 255, 0.66);
  box-shadow: var(--de-shadow-sm);
}

#deTrackerApp .de-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
}

#deTrackerApp .de-table th {
  text-align: right;
  padding: var(--de-space-4) var(--de-space-3);
  background: rgba(15, 23, 42, 0.96);
  color: var(--de-on-fill);
  font-size: 12.5px;
  font-weight: 800;
  border-bottom: 2px solid rgba(var(--de-primary-rgb), 0.7);
  white-space: nowrap;
}

#deTrackerApp .de-table td {
  padding: var(--de-space-4) var(--de-space-3);
  border-bottom: 1px solid rgba(226, 232, 240, 0.72);
  vertical-align: middle;
}

#deTrackerApp .de-table tbody tr:last-child td {
  border-bottom: none;
}

#deTrackerApp .de-table tbody tr {
  transition: background-color 280ms cubic-bezier(0.4, 0, 0.2, 1);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-table tbody tr:hover {
    background: rgba(248, 250, 252, 0.88);
  }
}

#deTrackerApp .de-table-lesson {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

#deTrackerApp .de-table-lesson span {
  color: var(--de-text-light);
  font-size: 12px;
}

#deTrackerApp .de-note-cell {
  max-width: 220px;
  color: var(--de-text-light);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

#deTrackerApp .de-row-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

#deTrackerApp .de-empty-state {
  text-align: center;
  padding: var(--de-space-8) var(--de-space-4);
  color: var(--de-text-light);
}

/* ===================== LOGIN ===================== */
#deTrackerApp.de-tracker-login {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 480px;
  position: relative;
  z-index: 1;
}

#deTrackerApp .de-login-panel {
  position: relative;
  width: 100%;
  max-width: 400px;
  background: rgba(255, 255, 255, 0.74);
  border: 1px solid rgba(255, 255, 255, 0.76);
  border-radius: var(--de-radius-lg);
  padding: var(--de-space-6) var(--de-space-8);
  box-shadow: var(--de-shadow-hover);
  overflow: hidden;
}

#deTrackerApp .de-login-panel::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-start: 0;
  height: 4px;
  background: var(--de-topband-line);
}

#deTrackerApp .de-login-header {
  text-align: center;
  margin-bottom: var(--de-space-6);
}

#deTrackerApp .de-login-header h2 {
  font-size: 21px;
  font-weight: 900;
  margin-bottom: var(--de-space-2);
}

#deTrackerApp .de-login-header p {
  color: var(--de-text-light);
  font-size: 14px;
}

#deTrackerApp .de-login-form form {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-4);
}

#deTrackerApp .de-login-form p {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
}

#deTrackerApp .de-login-form label {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--de-text-light);
}

#deTrackerApp .de-login-form input[type="text"],
#deTrackerApp .de-login-form input[type="password"] {
  border: 1px solid rgba(148, 163, 184, 0.18);
  border-radius: var(--de-radius-sm);
  padding: 12px var(--de-space-3);
  font-size: 14px;
  line-height: 1.8;
  width: 100%;
  background: rgba(255, 255, 255, 0.72);
  transition: var(--de-transition);
}

#deTrackerApp .de-login-form input[type="text"]:focus,
#deTrackerApp .de-login-form input[type="password"]:focus {
  outline: none;
  border-color: var(--de-primary);
  box-shadow: 0 0 0 4px var(--de-primary-ring);
}

#deTrackerApp .de-login-form input[type="checkbox"] {
  margin-inline-end: 8px;
  accent-color: var(--de-primary);
  width: 16px;
  height: 16px;
}

#deTrackerApp .de-login-form .login-remember label {
  display: flex;
  align-items: center;
  font-weight: 500;
  cursor: pointer;
}

#deTrackerApp .de-login-form .login-submit input[type="submit"] {
  width: 100%;
  border: none;
  border-radius: var(--de-radius-sm);
  padding: 12px var(--de-space-4);
  background: linear-gradient(135deg, var(--de-primary) 0%, var(--de-primary-hover-fill) 100%);
  color: var(--de-on-fill);
  font-weight: 800;
  font-size: 14.5px;
  cursor: pointer;
  transition: var(--de-transition);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-login-form .login-submit input[type="submit"]:hover {
    transform: translateY(-2px);
  }
}

#deTrackerApp .de-login-links {
  display: flex;
  justify-content: space-between;
  gap: var(--de-space-3);
  margin-top: var(--de-space-5);
  font-size: 13.5px;
}

/* ===================== ACCORDION / DETAILS ===================== */
#deTrackerApp details {
  overflow: visible;
  min-height: 0;
}

#deTrackerApp details > summary {
  cursor: pointer;
  list-style: none;
}

#deTrackerApp details > summary::-webkit-details-marker {
  display: none;
}

#deTrackerApp .de-accordion,
#deTrackerApp .de-accordion-item,
#deTrackerApp .de-accordion-panel,
#deTrackerApp .de-collapse,
#deTrackerApp .de-collapse-inner {
  min-height: 0;
  overflow: visible;
}

#deTrackerApp .de-accordion-content,
#deTrackerApp .de-accordion-body,
#deTrackerApp .de-collapse-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 300ms cubic-bezier(0.4, 0, 0.2, 1), padding 300ms ease, opacity 250ms ease;
}

#deTrackerApp .is-open > .de-accordion-content,
#deTrackerApp .is-open > .de-accordion-body,
#deTrackerApp .is-expanded > .de-accordion-content,
#deTrackerApp .is-expanded > .de-accordion-body,
#deTrackerApp [aria-expanded="true"] + .de-accordion-content,
#deTrackerApp [aria-expanded="true"] + .de-accordion-body,
#deTrackerApp details[open] > .de-accordion-content,
#deTrackerApp details[open] > .de-accordion-body {
  max-height: 5000px;
  overflow: visible;
}

/* ===================== RESPONSIVE ===================== */
@media (max-width: 1100px) {
  #deTrackerApp .de-main-grid {
    grid-template-columns: 1fr;
  }

  #deTrackerApp .de-summary-chips {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  #deTrackerApp .de-chart-grid {
    grid-template-columns: 1fr;
  }

  /* "خلاصه امروز و دوره اخیر" duplicates numbers already visible in
     the top summary chips (de-summary-chips) and the charts tab — on
     mobile/tablet, where it stacks below the form instead of sitting
     in its own sidebar column, it's just extra scrolling for little
     new information. Desktop (above this same breakpoint, where the
     two-column grid above still applies) keeps it, since there it
     sits in the spare side column at no scroll cost. display: none
     avoids the mini-stat DOM entirely at this width, not just visual
     hiding — buildQuickSummary() still runs either way, but it costs
     five small divs either way, so nothing is meaningfully saved by
     also skipping the call in JS. */
  #deTrackerApp .de-quick-summary {
    display: none;
  }
}

@media (max-width: 780px) {
  #deTrackerApp .de-form-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  #deTrackerApp .de-skill-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  #deTrackerApp .de-mini-stats {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 640px) {
  #deTrackerApp {
    font-size: 14px;
    padding: var(--de-space-4) 0 var(--de-space-6);
  }

  #deTrackerApp .de-topband {
    padding: var(--de-space-5) var(--de-space-4);
    border-radius: 22px;
  }

  #deTrackerApp .de-topband-main {
    flex-direction: column;
    align-items: flex-start;
  }

  #deTrackerApp .de-topband-title-row {
    width: 100%;
    justify-content: space-between;
  }

  #deTrackerApp .de-theme-quick-toggle-slot--mobile {
    display: flex;
  }

  #deTrackerApp .de-theme-quick-toggle-slot--desktop {
    display: none;
  }

  #deTrackerApp .de-title {
    font-size: 22px;
  }

  #deTrackerApp .de-account-row,
  #deTrackerApp .de-account {
    align-items: flex-start;
    width: 100%;
  }

  #deTrackerApp .de-summary-chips {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--de-space-3);
  }

  #deTrackerApp .de-toolbar-actions {
    margin-inline-start: 0;
    width: 100%;
  }

  #deTrackerApp .de-toolbar-actions .de-btn {
    flex: 1 1 auto;
  }

  #deTrackerApp .de-card,
  #deTrackerApp .de-login-panel {
    padding: var(--de-space-5) var(--de-space-4);
    border-radius: 22px;
  }

  #deTrackerApp .de-form-grid,
  #deTrackerApp .de-form-grid-2,
  #deTrackerApp .de-skill-grid {
    grid-template-columns: 1fr;
  }

  #deTrackerApp .de-login-links {
    flex-direction: column;
    align-items: center;
    gap: var(--de-space-2);
  }

  /* Tabs: on narrow screens a single scrollable row can't fit 4 labels
     without clipping/overflowing the frame, so switch to a contained
     2x2 grid instead of a horizontally-scrolling flex row. */
  #deTrackerApp .de-tabs-nav {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px;
    overflow: visible;
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
  }

  #deTrackerApp .de-tab-btn {
    flex: none;
    width: 100%;
    min-width: 0;
    padding: 10px 8px;
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: center;
  }
}

@media (max-width: 360px) {
  #deTrackerApp .de-tab-btn {
    font-size: 12px;
    padding: 9px 4px;
  }
}


/* ==========================================================================
   Mobile performance: content-visibility + a narrower transition list
   --------------------------------------------------------------------------
   This section used to also flatten glass-morphism (drop backdrop-filter,
   swap translucent cards for solid ones, lighten box-shadows) specifically
   below 782px — that's no longer needed here: the visual refresh that
   introduced the current bold/rounded card style made solid backgrounds,
   visible borders, and light shadows the *default* everywhere (see the
   --de-card/--de-shadow-sm redefinition and the blanket backdrop-filter
   removal earlier in this file), not just a mobile-only fallback. What's
   left below are the genuinely independent performance wins that redesign
   didn't already cover: a narrower `transition` list (watching only the
   properties that actually animate, not everything `all` implies),
   dropping box-shadow specifically from the long lesson-accordion list
   (up to ~12 .de-lesson-item rows compositing at once on a low-end phone),
   and `content-visibility: auto` so far-off-screen rows in the 78-lesson
   list skip layout/paint work until scrolled near. */

@media (prefers-reduced-motion: reduce) {
  #deTrackerApp,
  #deTrackerApp * {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

@media (max-width: 782px) {
  /* Book vs. tray hierarchy relies on color contrast (soft blue book vs.
     white tray), unaffected by dropping shadows. Item vs. tray previously
     leaned partly on shadow; with that removed here for performance, the
     border is strengthened slightly to keep rows visually separated on a
     white tray without needing a shadow. */
  #deTrackerApp .de-lesson-book-body {
    box-shadow: none;
  }

  #deTrackerApp .de-lesson-item {
    box-shadow: none;
    border-color: rgba(148, 163, 184, 0.45);
  }

  /* `transition: all` forces the browser to watch every animatable
     property on the element (including ones that trigger layout) even
     though only color/shadow/transform actually change here. */
  #deTrackerApp .de-tab-btn,
  #deTrackerApp .de-btn,
  #deTrackerApp .de-card,
  #deTrackerApp .de-skill-card {
    transition: background-color 160ms ease, box-shadow 160ms ease, transform 160ms ease;
  }

  /* Same narrowing for the book/lesson accordion, minus box-shadow and
     transform — both are only ever animated by the (hover: hover) rules
     elsewhere, which are already scoped out on touch devices, so watching
     them here would cost paint-readiness checks for a transition that can
     never actually fire on mobile. */
  #deTrackerApp .de-lesson-book,
  #deTrackerApp .de-lesson-item {
    transition: background-color 160ms ease, border-color 160ms ease;
  }

  /* Decorative blurred blobs behind the header are pure paint cost with
     no functional purpose on a small viewport. */
  #deTrackerApp::before,
  #deTrackerApp::after {
    display: none;
  }

  /* Long, mostly off-screen lists (78 lesson rows across 8 books) don't
     need layout/paint work done for rows the user hasn't scrolled to yet.
     `content-visibility: auto` skips that work until a row is near the
     viewport, and `contain-intrinsic-size` keeps scrollbar length stable
     so it doesn't jump as rows attach. Supported browsers only; falls
     back to normal rendering everywhere else. */
  #deTrackerApp .de-lesson-item {
    content-visibility: auto;
    contain-intrinsic-size: 0 96px;
  }

  #deTrackerApp .de-table tbody tr {
    content-visibility: auto;
    contain-intrinsic-size: 0 56px;
  }
}


/* ==========================================================================
   Study Plan Settings (Feature 2: personalized scheduling)
   ========================================================================== */

#deTrackerApp .de-card-head-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--de-space-3);
  flex-wrap: wrap;
}

#deTrackerApp .de-btn-small {
  padding: 8px 14px;
  font-size: 13px;
}

#deTrackerApp .de-plan-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border-radius: var(--de-radius-pill);
  font-size: 13px;
  font-weight: 600;
  margin-bottom: var(--de-space-4);
}

#deTrackerApp .de-plan-badge--custom {
  background: var(--de-success-soft);
  color: var(--de-success-dark);
}

#deTrackerApp .de-plan-badge--default {
  background: rgba(100, 116, 139, 0.12);
  color: var(--de-text-light);
}

#deTrackerApp .de-field-hint {
  display: block;
  font-size: 12.5px;
  color: var(--de-text-faint);
  margin-top: 4px;
  line-height: 1.6;
}

/* Section-level guide text (above the "پیشرفت درس‌ها" / "خلاصه دروس"
   accordions) needs more presence than a small form-field hint: a bit
   bigger, a bit darker, and enough breathing room before the cards
   below so it doesn't read as crowded against them. Kept as an
   addition on top of .de-field-hint rather than changing that class,
   since .de-field-hint is also used for small helper text under form
   inputs elsewhere and shouldn't grow there. */
#deTrackerApp .de-section-hint {
  font-size: 14px;
  font-weight: 500;
  color: var(--de-text-light);
  line-height: 1.85;
  margin-top: 6px;
  margin-bottom: var(--de-space-4);
}

@media (max-width: 640px) {
  #deTrackerApp .de-section-hint {
    font-size: 13px;
  }
}

/* Overlay + panel. Deliberately a plain fixed-position element (not
   <dialog>) so it inherits the same RTL/font/focus-ring rules as the
   rest of the plugin's custom-built UI instead of browser dialog
   defaults. */
#deTrackerApp .de-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.55);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: var(--de-space-5) var(--de-space-3);
  overflow-y: auto;
  /* Flatsome's sticky header (and some of its builder elements) uses a
     stacking context with a z-index well above typical page content —
     60 was high enough for the plugin's own cards but not for the
     theme's sticky header, so the top of the modal was rendering behind
     it. Pushed well above any theme/header z-index so the modal always
     sits on top, regardless of scroll position or sticky state. */
  z-index: 999999;
}

#deTrackerApp .de-modal {
  width: 100%;
  max-width: 640px;
  margin-top: var(--de-space-6);
  background: var(--de-card-strong);
  border-radius: var(--de-radius-lg);
  box-shadow: var(--de-shadow-hover);
  border: 1px solid var(--de-border);
  overflow: hidden;
}

@media (max-width: 782px) {
  /* Same reasoning as the other mobile rules above: no blur
     compositing needed for a solid card. */
  #deTrackerApp .de-modal {
    margin-top: var(--de-space-3);
    border-radius: var(--de-radius);
  }
}

#deTrackerApp .de-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--de-space-4) var(--de-space-5);
  border-bottom: 1px solid var(--de-border);
}

#deTrackerApp .de-modal-head h2 {
  margin: 0;
  font-size: 18px;
  color: var(--de-heading);
}

#deTrackerApp .de-modal-close {
  border: none;
  background: transparent;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  color: var(--de-text-light);
  padding: 6px;
  border-radius: var(--de-radius-pill);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-modal-close:hover {
    background: rgba(100, 116, 139, 0.12);
  }
}

#deTrackerApp .de-modal-body {
  padding: var(--de-space-5);
  display: flex;
  flex-direction: column;
  gap: var(--de-space-4);
  max-height: 65vh;
  overflow-y: auto;
}

#deTrackerApp .de-modal-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--de-space-3);
  padding: var(--de-space-4) var(--de-space-5);
  border-top: 1px solid var(--de-border);
  flex-wrap: wrap;
}

#deTrackerApp .de-modal-actions-end {
  display: flex;
  gap: var(--de-space-2);
}

/* --- Account panel (3.21.0) --- */
#deTrackerApp .de-account-modal {
  max-width: 420px;
}

#deTrackerApp .de-account-overview {
  padding: var(--de-space-3) var(--de-space-4);
  margin-bottom: var(--de-space-4);
  background: var(--de-card-soft);
  border: 1px solid var(--de-border);
  border-radius: var(--de-radius-sm);
}

#deTrackerApp .de-account-overview-name {
  font-size: 15px;
  font-weight: 800;
  color: var(--de-heading);
}

#deTrackerApp .de-account-overview-email {
  font-size: 13px;
  color: var(--de-text-light);
  text-align: left;
  margin-top: 2px;
}

/* Single full-width action instead of the usual cancel/confirm pair —
   .de-modal-actions' default space-between has nothing to space a
   lone child against, so it's overridden here rather than adding a
   variant class just for one modal. */
#deTrackerApp .de-account-modal .de-modal-actions {
  justify-content: stretch;
}

#deTrackerApp .de-account-logout-btn {
  width: 100%;
  text-decoration: none;
}

/* 3.22.0 — app-password section. Separated from the change-password
   form above by a rule rather than another card, so the modal doesn't
   turn into a stack of competing boxes. */
#deTrackerApp .de-app-password-field,
#deTrackerApp .de-app-password-result {
  margin-top: var(--de-space-4);
  padding-top: var(--de-space-4);
  border-top: 1px solid var(--de-border);
}

#deTrackerApp .de-app-password-hint {
  font-size: 12px;
  line-height: 1.8;
  color: var(--de-text-light);
  margin: 0 0 var(--de-space-2);
}

/* Monospace and letter-spacing because this is a 24-character random
   string the student has to compare against what they typed into the
   phone — proportional text makes that needlessly hard. */
#deTrackerApp .de-app-password-result input {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px;
  letter-spacing: 1px;
  text-align: left;
}

#deTrackerApp .de-schedule-overrides {
  border-top: 1px dashed var(--de-border-strong);
  padding-top: var(--de-space-4);
}

#deTrackerApp .de-schedule-override-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--de-space-3);
}

@media (max-width: 480px) {
  #deTrackerApp .de-schedule-override-grid {
    grid-template-columns: 1fr;
  }
}


/* ==========================================================================
   Lesson Detail (grammar / vocabulary flashcards / dialogue reveal-quiz)
   ========================================================================== */

/* Lesson rows are now <button> elements (so the whole card is a single
   accessible, keyboard-reachable click target) instead of <div>s — reset
   the native button chrome while keeping every visual rule already
   defined for .de-lesson-item above. */
#deTrackerApp .de-lesson-item--clickable {
  display: block;
  width: 100%;
  text-align: right;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

#deTrackerApp .de-lesson-item--clickable:focus-visible {
  outline: 2px solid var(--de-primary);
  outline-offset: 2px;
}

#deTrackerApp .de-lesson-card-nav {
  display: flex;
  gap: var(--de-space-2);
  padding: var(--de-space-3) var(--de-space-5) 0;
  flex-wrap: wrap;
}

#deTrackerApp .de-lesson-card-btn {
  flex: 1;
  min-width: 0;
  padding: 10px 12px;
  border-radius: var(--de-radius-tab);
  border: 1px solid var(--de-border);
  background: transparent;
  color: var(--de-text-light);
  font-family: var(--de-font);
  font-size: 13.5px;
  cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

#deTrackerApp .de-lesson-card-btn.active {
  background: var(--de-primary);
  color: var(--de-on-fill);
  border-color: var(--de-primary);
}

/* Colored chip is the one place icons carry per-item color (the 5
   main tabs = wayfinding). On the active tab the chip drops to a
   translucent white treatment instead of keeping its own hue, since
   its colored soft-tint background would clash sitting directly on
   the solid primary-blue active fill. */
#deTrackerApp .de-tab-icon-chip {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: 7px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

#deTrackerApp .de-tab-icon-chip--grammar { background: var(--de-tab-grammar-soft); color: var(--de-tab-grammar); }
#deTrackerApp .de-tab-icon-chip--vocabulary { background: var(--de-tab-vocabulary-soft); color: var(--de-tab-vocabulary); }
#deTrackerApp .de-tab-icon-chip--dialogue { background: var(--de-tab-dialogue-soft); color: var(--de-tab-dialogue); }
#deTrackerApp .de-tab-icon-chip--quiz { background: var(--de-tab-quiz-soft); color: var(--de-tab-quiz); }
#deTrackerApp .de-tab-icon-chip--reading { background: var(--de-tab-reading-soft); color: var(--de-tab-reading); }

#deTrackerApp .de-lesson-card-btn.active .de-tab-icon-chip {
  background: var(--de-sheen-wash);
  color: var(--de-on-fill);
}

/* Plain inline icon (no chip) — sits in front of a feedback line or
   badge label and inherits color from its wrapping text (success/
   danger/etc.), per the "color = wayfinding only" rule: everywhere
   outside the 5 main tabs, icons carry meaning through color alone,
   not an added colored background. */
#deTrackerApp .de-inline-icon {
  display: inline-flex;
  vertical-align: -3px;
  margin-inline-end: 4px;
}

#deTrackerApp .de-inline-icon svg {
  display: block;
}

#deTrackerApp .de-lesson-modal-body {
  min-height: 260px;
}

/* ==========================================================================
   Lesson Content Screen (replaces the old floating modal for lesson
   detail). Still driven by a URL hash (see the JS router) so the
   browser's Back button and deep-linking/refresh work normally — that
   part is unchanged. The container itself, however, is now a normal
   in-flow section rather than a position:fixed, full-viewport overlay
   with its own internal overflow-y:auto scroll area.

   Root cause fixed here (see the UI/UX refinement audit, section 5):
   the old fixed-overlay + internal-scroll structure created a SECOND,
   independent scroll context stacked on top of the page's own scroll
   — nothing ever hid the underlying page while the overlay was open,
   so its scrollbar stayed live at the same time as the overlay's own
   internal one. Making this a normal block-flow section instead means
   there's only ever one scrollable thing: the page itself. Long
   vocabulary/dialogue lists now simply make the page taller, exactly
   like the rest of the dashboard already does.

   This also fixes the mobile "scroll jumps to the top after answering
   a dialogue step" issue (audit section 9): that jump was `.de-lesson-
   screen-body`'s own scrollTop resetting to 0 every time renderApp()
   rebuilt the DOM subtree it lived in. With no internal scroll
   container left, there's nothing with its own scrollTop to reset —
   the page's own scroll position is untouched by re-rendering content
   within it.

   .de-dashboard-shell is hidden (see .de-dashboard-shell--hidden,
   applied in the JS render function while lessonDetail.open is true)
   so this section visually replaces it instead of both being stacked
   in the document at once.
   ========================================================================== */
#deTrackerApp .de-lesson-screen {
  background: var(--de-bg);
  min-height: 100vh;
}

#deTrackerApp .de-lesson-screen-header {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-2);
  padding: var(--de-space-4) var(--de-space-5);
  border-bottom: 1px solid var(--de-border);
  background: var(--de-card-strong);
}

/* Title is the clear visual anchor of the screen: centered, larger,
   and given room to wrap onto two lines for longer lesson names
   instead of truncating, since it no longer shares its row with the
   back button. */
#deTrackerApp .de-lesson-screen-title {
  margin: 0;
  font-size: 19px;
  font-weight: 800;
  color: var(--de-heading);
  text-align: center;
  line-height: 1.5;
}

/* Back action sits on its own row beneath the title, aligned to the
   same side text starts from in RTL (flex-start), matching the
   spec's mockup. Appearance now comes entirely from the shared
   .de-btn/.de-btn-secondary/.de-btn-small system (unified button
   design pass) — no bespoke border/background/radius here anymore. */
#deTrackerApp .de-lesson-screen-header-actions {
  display: flex;
  justify-content: flex-start;
}

#deTrackerApp .de-lesson-screen .de-lesson-card-nav {
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
  box-sizing: border-box;
}

#deTrackerApp .de-lesson-screen-body {
  padding: var(--de-space-5);
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
  box-sizing: border-box;
}

@media (max-width: 640px) {
  #deTrackerApp .de-lesson-screen-header {
    padding: var(--de-space-3) var(--de-space-4);
  }

  #deTrackerApp .de-lesson-screen-title {
    font-size: 16px;
  }

  #deTrackerApp .de-lesson-screen-body {
    padding: var(--de-space-4);
  }
}

#deTrackerApp .de-lesson-grammar {
  line-height: 2;
}

#deTrackerApp .de-lesson-grammar p {
  margin: 0 0 var(--de-space-3);
}

#deTrackerApp .de-lesson-grammar table {
  width: 100%;
  border-collapse: collapse;
  margin: var(--de-space-3) 0;
}

#deTrackerApp .de-lesson-grammar th,
#deTrackerApp .de-lesson-grammar td {
  border: 1px solid var(--de-border-strong);
  padding: 8px 10px;
  text-align: right;
}

/* --- Reading comprehension (تب درک مطلب) --- */

#deTrackerApp .de-lesson-reading {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-4);
}

/* Deliberately its own look, not a reuse of .de-lesson-grammar's
   rule/table/tip cards — a reading passage is continuous prose to
   read comfortably, so it gets one "page" card with a small branded
   header strip instead. Indigo/violet rather than the app's primary
   blue (grammar) or green (success) on purpose, so درک مطلب reads as
   its own section at a glance, not a variant of another tab. */
#deTrackerApp .de-reading-text-card {
  background: var(--de-reading-card-bg);
  border: 1px solid var(--de-border);
  border-radius: var(--de-radius);
  overflow: hidden;
  box-shadow: 0 3px 16px rgba(76, 29, 149, .10);
}

#deTrackerApp .de-reading-text-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 18px;
  background: linear-gradient(135deg, var(--de-reading-accent), var(--de-reading-accent-dark));
  color: var(--de-on-fill);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .2px;
}

#deTrackerApp .de-reading-text-card-icon {
  display: inline-flex;
  align-items: center;
  color: var(--de-on-fill);
}

/* Always dir="ltr" from the markup (a Reading Comprehension text is
   German by definition, §3.5 of the Specification) — text-align here
   just makes sure the browser actually left-aligns it instead of
   inheriting the app's own RTL default, which dir alone doesn't
   guarantee in every browser. font-weight bumped from the app's
   normal body text (500 vs. ~400) — German passages read a little
   thin at the default weight against the surrounding RTL UI, this
   keeps it feeling substantial without tipping into "bold heading"
   territory. */
#deTrackerApp .de-lesson-reading-text {
  padding: var(--de-space-5) var(--de-space-4);
  font-size: 15.5px;
  font-weight: 500;
  line-height: 2.15;
  color: var(--de-heading);
  text-align: left;
}

#deTrackerApp .de-lesson-reading-text p {
  margin: 0 0 var(--de-space-3);
}

#deTrackerApp .de-lesson-reading-text p:last-child {
  margin-bottom: 0;
}

#deTrackerApp .de-reading-quiz-start {
  display: flex;
  justify-content: center;
  padding-top: var(--de-space-2);
}

/* --- Reading session/results: text stays reachable via a toggle --- */

#deTrackerApp .de-reading-session {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-3);
}

#deTrackerApp .de-reading-text-toggle {
  align-self: flex-start;
}

/* --- Reading passage list (multiple named passages per lesson) --- */

#deTrackerApp .de-reading-passage-list {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-2);
}

#deTrackerApp .de-reading-passage-card {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 14px 16px;
  background: var(--de-card);
  border: 1px solid var(--de-border);
  border-radius: var(--de-radius);
  cursor: pointer;
  text-align: right;
  font-family: var(--de-font);
  transition: var(--de-transition);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-reading-passage-card:hover {
    border-color: var(--de-reading-accent);
    box-shadow: 0 3px 12px rgba(76, 29, 149, .10);
  }
}

#deTrackerApp .de-reading-passage-card-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: linear-gradient(135deg, var(--de-reading-accent), var(--de-reading-accent-dark));
  color: var(--de-on-fill);
}

#deTrackerApp .de-reading-passage-card-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

#deTrackerApp .de-reading-passage-card-text strong {
  font-size: 14.5px;
  color: var(--de-heading);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

#deTrackerApp .de-reading-passage-card-text span {
  font-size: 12.5px;
  color: var(--de-text-light);
}

#deTrackerApp .de-reading-passage-card-arrow {
  flex-shrink: 0;
  font-size: 18px;
  color: var(--de-text-faint);
}

#deTrackerApp .de-reading-back {
  align-self: flex-start;
}

/* --- Flashcards (vocabulary) --- */

#deTrackerApp .de-flashcard-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--de-space-3);
}

#deTrackerApp .de-flashcard-progress {
  font-size: 13px;
  color: var(--de-text-light);
}

#deTrackerApp .de-flashcard {
  width: 100%;
  max-width: 380px;
  height: 180px;
  border: none;
  background: transparent;
  cursor: pointer;
  perspective: 1200px;
  padding: 0;
}

#deTrackerApp .de-flashcard-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transition: transform 420ms cubic-bezier(0.4, 0, 0.2, 1);
  transform-style: preserve-3d;
}

#deTrackerApp .de-flashcard.flipped .de-flashcard-inner {
  transform: rotateY(180deg);
}

#deTrackerApp .de-flashcard-face {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--de-space-4);
  border-radius: var(--de-radius);
  backface-visibility: hidden;
  font-size: 20px;
  font-weight: 600;
  text-align: center;
  box-shadow: var(--de-shadow-sm);
  border: 1.5px solid var(--de-border);
  /* Long unbroken German compound words (or long Persian phrases)
     would otherwise overflow past this absolutely-positioned face
     instead of wrapping (audit H-2). */
  overflow-wrap: break-word;
  word-break: break-word;
  /* The flip effect (both faces absolutely positioned inside a
     relatively-positioned .de-flashcard-inner) needs .de-flashcard's
     height to be a fixed value, not content-driven — absolutely
     positioned children don't contribute to their parent's height, so
     switching to min-height wouldn't actually let the card grow for
     longer content, it would just clip in a different place. For the
     rare case of an unusually long word plus its category badge (spec
     6) together needing more vertical room than the fixed height on a
     small phone, overflow: hidden clips cleanly at the card's own
     rounded edge (responsive audit, section 11) instead of spilling
     visibly past its border into the surrounding page — contained and
     still readable in practice (the badge/word text wraps first, via
     the rule above, well before this would ever be reached), rather
     than a hard data-loss risk. */
  overflow: hidden;
}

#deTrackerApp .de-flashcard-front {
  background: var(--de-card);
  color: var(--de-heading);
}

/* Solid brand blue by default (was the dark navy --de-hero-gradient) —
   the bolder/brighter palette this redesign introduced reads oddly
   against a near-black card, so the flashcard back now falls back to
   the same primary blue used everywhere else, not a separate dark
   theme of its own. Category colors (below) still override this once
   a word's grammar category is known. */
#deTrackerApp .de-flashcard-back {
  background: var(--de-primary);
  color: var(--de-on-fill);
  transform: rotateY(180deg);
}

/* Category-color reveal — same idea as the vocabulary list card,
   replacing the default blue with the resolved category's solid color
   once flipped. No :hover state exists on the flashcard at all, so
   unlike the list card there's no competing rule to out-order here. */
#deTrackerApp .de-flashcard-back--cat-masc   { background: var(--de-cat-masc); }
#deTrackerApp .de-flashcard-back--cat-fem    { background: var(--de-cat-fem); }
#deTrackerApp .de-flashcard-back--cat-neut   { background: var(--de-cat-neut); }
#deTrackerApp .de-flashcard-back--cat-plural { background: var(--de-cat-plural); }
#deTrackerApp .de-flashcard-back--cat-verb   { background: var(--de-cat-verb); }
#deTrackerApp .de-flashcard-back--cat-adj    { background: var(--de-cat-adj); }
#deTrackerApp .de-flashcard-back--cat-adv    { background: var(--de-cat-adv); }
#deTrackerApp .de-flashcard-back--cat-other  { background: var(--de-cat-other); }

/* Same reasoning as the vocabulary list card: the inline der/die/das
   article coloring would be invisible against a same-colored solid
   background, and is redundant anyway once the whole card already
   conveys the category. */
#deTrackerApp .de-flashcard-back[class*="de-flashcard-back--cat-"] .de-article-der,
#deTrackerApp .de-flashcard-back[class*="de-flashcard-back--cat-"] .de-article-die,
#deTrackerApp .de-flashcard-back[class*="de-flashcard-back--cat-"] .de-article-das {
  color: inherit;
}

#deTrackerApp .de-flashcard-nav {
  display: flex;
  gap: var(--de-space-2);
}

#deTrackerApp .de-vocab-rate {
  display: flex;
  gap: var(--de-space-2);
  width: 100%;
}

#deTrackerApp .de-vocab-rate .de-btn {
  flex: 1;
}

/* Soft amber/green fills, distinct from the plain secondary/primary
   buttons this used to reuse — matches the approved flashcard mockup,
   which gives these two a semantic (not just structural) color, but
   keeps them muted since self-rating isn't an objective correct/wrong
   judgment the way quiz feedback is. Uses --de-warning (not a
   vocabulary-category color) since this has nothing to do with grammar
   category — reusing --de-cat-adv here would visually conflate "this
   word is hard to remember" with "this word is an adverb".
   3.15.0: both fills bumped a bit more saturated (dedicated values,
   not the shared -soft tokens, so badges/cards elsewhere aren't
   affected) and given a subtle resting border — previously
   border-color was transparent until hover, so the buttons had no
   edge separating them from the page until touched. */
#deTrackerApp .de-vocab-rate-hard {
  background: var(--de-vocab-hard-bg);
  border-color: var(--de-vocab-hard-border);
  color: var(--de-vocab-hard-text);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-vocab-rate-hard:hover {
    border-color: var(--de-warning);
  }
}

#deTrackerApp .de-vocab-rate-easy {
  background: var(--de-vocab-easy-bg);
  border-color: var(--de-vocab-easy-border);
  color: var(--de-success);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-vocab-rate-easy:hover {
    border-color: var(--de-success);
  }
}

/* der=masc / die=fem / das=neut, sharing the same grammar-category
   palette as the vocabulary badges (--de-cat-*) for one consistent
   color language across the list, flashcard, and inline article
   highlighting. Purely a display parse of the article already written
   inline in the German text (see formatGermanArticle in the JS); words
   with no article render with no special color. */
#deTrackerApp .de-article-der {
  color: var(--de-cat-masc);
  font-weight: 700;
}

#deTrackerApp .de-article-die {
  color: var(--de-cat-fem);
  font-weight: 700;
}

#deTrackerApp .de-article-das {
  color: var(--de-cat-neut);
  font-weight: 700;
}

/* On mobile the 3D flip is a nice-to-have, not essential — and combined
   with the transform-heavy nature of the effect it's exactly the kind
   of animation prefers-reduced-motion should cancel (already handled
   globally above), plus it's cheap here since only one card animates
   at a time, unlike the blur-per-card cost this pass otherwise targets. */

/* --- Vocabulary list (spec 7): the landing view for the "لغات" tab —
   every word in the lesson at once, Persian-first, in a responsive
   card grid. auto-fill + minmax naturally gives more columns on wide
   screens and fewer on narrow ones without hand-written breakpoints
   for every size in between; the max-width 640px rule below just
   tightens the minimum card width a bit further for small phones. --- */
#deTrackerApp .de-vocab-list-wrap {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-4);
}

/* One-time instructional note, shown once above the whole grid instead
   of being repeated inside every unrevealed card (that per-card hint
   used to force a taller min-height on every single card just to fit
   a line of text 15-30 times over). Styled a bit more prominently than
   the tiny per-card hint was, since it's now the one explanation for
   the whole section rather than a repeated label. */
#deTrackerApp .de-vocab-list-note {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: var(--de-space-3) var(--de-space-4);
  border-radius: var(--de-radius-sm);
  background: rgba(var(--de-primary-rgb), 0.07);
  border: 1px solid rgba(var(--de-primary-rgb), 0.16);
  color: var(--de-text);
  font-size: 13.5px;
  line-height: 1.6;
}

#deTrackerApp .de-vocab-list-note::before {
  content: '💡';
  font-size: 15px;
  line-height: 1;
  flex-shrink: 0;
}

#deTrackerApp .de-vocab-list-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--de-space-3);
}

#deTrackerApp .de-vocab-list-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 64px;
  padding: var(--de-space-3);
  border-radius: var(--de-radius-sm);
  border: 1px solid var(--de-border);
  background: var(--de-card);
  cursor: pointer;
  text-align: center;
  font-family: var(--de-font);
  transition: var(--de-transition);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-vocab-list-card:hover {
    border-color: rgba(var(--de-primary-rgb), 0.28);
    background: var(--de-card-strong);
    transform: translateY(-2px);
    box-shadow: var(--de-shadow-sm);
  }
}

#deTrackerApp .de-vocab-list-card.revealed {
  border-color: rgba(var(--de-primary-rgb), 0.28);
  background: var(--de-card-strong);
}

#deTrackerApp .de-vocab-list-persian {
  font-size: 15px;
  font-weight: 700;
  color: var(--de-heading);
  line-height: 1.5;
  overflow-wrap: break-word;
}

#deTrackerApp .de-vocab-list-german {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--de-text);
  overflow-wrap: break-word;
}

/* Category-color card reveal (spec 6): once a card is revealed AND its
   grammar category can be resolved, the card itself takes on that
   category's solid color instead of just showing a small badge — "the
   result should feel intentional and premium," per the spec.

   Bug fix: the first version of this rule used a single class
   (.de-vocab-list-card--cat-masc, specificity 1 id + 1 class) and
   relied on source order to beat .de-vocab-list-card.revealed just
   above — but .revealed is a *compound* two-class selector (1 id + 2
   classes), which beats a single-class selector by specificity
   regardless of source order. In practice that meant: right as you
   clicked, the card was still :hover'd (a separate rule, also higher
   specificity, correctly showed the color) — but moving the mouse
   away dropped back to .revealed's plain background while the text
   stayed forced to white, i.e. white-on-white. Fixed by writing every
   rule below as .revealed + the category class together (2 classes,
   matching .revealed's own specificity), so these — declared later in
   the file — correctly win at every state: resting, and hovered. */
#deTrackerApp .de-vocab-list-card--cat-masc.revealed   { background: var(--de-cat-masc);   border-color: var(--de-cat-masc); }
#deTrackerApp .de-vocab-list-card--cat-fem.revealed    { background: var(--de-cat-fem);    border-color: var(--de-cat-fem); }
#deTrackerApp .de-vocab-list-card--cat-neut.revealed   { background: var(--de-cat-neut);   border-color: var(--de-cat-neut); }
#deTrackerApp .de-vocab-list-card--cat-plural.revealed { background: var(--de-cat-plural); border-color: var(--de-cat-plural); }
#deTrackerApp .de-vocab-list-card--cat-verb.revealed   { background: var(--de-cat-verb);   border-color: var(--de-cat-verb); }
#deTrackerApp .de-vocab-list-card--cat-adj.revealed    { background: var(--de-cat-adj);    border-color: var(--de-cat-adj); }
#deTrackerApp .de-vocab-list-card--cat-adv.revealed    { background: var(--de-cat-adv);    border-color: var(--de-cat-adv); }
#deTrackerApp .de-vocab-list-card--cat-other.revealed  { background: var(--de-cat-other);  border-color: var(--de-cat-other); }

#deTrackerApp .de-vocab-list-card[class*="de-vocab-list-card--cat-"] .de-vocab-list-persian,
#deTrackerApp .de-vocab-list-card[class*="de-vocab-list-card--cat-"] .de-vocab-list-german {
  color: var(--de-on-fill);
}

/* The inline der/die/das article coloring (formatGermanArticle(), in
   the JS) shares this same --de-cat-masc/fem/neut palette for its text
   color — exactly right on a plain card, but it would make the article
   invisible here, where the whole card background is now that same
   color. The category is already fully conveyed by the card's color
   and its badge, so the inline coloring is redundant in this context
   anyway, not just risky — safe to neutralize back to plain white. */
#deTrackerApp .de-vocab-list-card[class*="de-vocab-list-card--cat-"] .de-article-der,
#deTrackerApp .de-vocab-list-card[class*="de-vocab-list-card--cat-"] .de-article-die,
#deTrackerApp .de-vocab-list-card[class*="de-vocab-list-card--cat-"] .de-article-das {
  color: inherit;
}

/* Explicit per-category :hover rules (rather than a generic
   attribute-selector one), each still combined with .revealed for the
   same specificity reason as the resting-state rules above. */
@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-vocab-list-card--cat-masc.revealed:hover   { background: var(--de-cat-masc);   filter: brightness(1.06); }
  #deTrackerApp .de-vocab-list-card--cat-fem.revealed:hover    { background: var(--de-cat-fem);    filter: brightness(1.06); }
  #deTrackerApp .de-vocab-list-card--cat-neut.revealed:hover   { background: var(--de-cat-neut);   filter: brightness(1.06); }
  #deTrackerApp .de-vocab-list-card--cat-plural.revealed:hover { background: var(--de-cat-plural); filter: brightness(1.06); }
  #deTrackerApp .de-vocab-list-card--cat-verb.revealed:hover   { background: var(--de-cat-verb);   filter: brightness(1.06); }
  #deTrackerApp .de-vocab-list-card--cat-adj.revealed:hover    { background: var(--de-cat-adj);    filter: brightness(1.06); }
  #deTrackerApp .de-vocab-list-card--cat-adv.revealed:hover    { background: var(--de-cat-adv);    filter: brightness(1.06); }
  #deTrackerApp .de-vocab-list-card--cat-other.revealed:hover  { background: var(--de-cat-other);  filter: brightness(1.06); }
}

/* Expand button into the flashcard section (spec 8) — full width to
   match the list grid's container, not a small centered pill, so it
   reads as "expand this section" rather than a regular action button. */
#deTrackerApp .de-vocab-expand-btn {
  width: 100%;
}

/* Appearance comes from the shared .de-btn/.de-btn-ghost/.de-btn-small
   classes now (unified button design pass); align-self here was
   already inert (.de-lesson-screen-body, its actual parent, isn't a
   flex container) so only the spacing before the content below it is
   kept. */
#deTrackerApp .de-vocab-back-to-list {
  margin-bottom: var(--de-space-2);
}

/* Grammar-category badges (spec 9) — used on the vocabulary list
   (once revealed), the flashcard back, and the choice/typed answer
   feedback, so the same word shows the same badge everywhere. */
#deTrackerApp .de-vocab-cat-badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 11px;
  border-radius: var(--de-radius-pill);
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}

#deTrackerApp .de-vocab-cat-badge--masc   { background: var(--de-cat-masc-soft);   color: var(--de-cat-masc); }
#deTrackerApp .de-vocab-cat-badge--fem    { background: var(--de-cat-fem-soft);    color: var(--de-cat-fem); }
#deTrackerApp .de-vocab-cat-badge--neut   { background: var(--de-cat-neut-soft);   color: var(--de-cat-neut); }
#deTrackerApp .de-vocab-cat-badge--plural { background: var(--de-cat-plural-soft); color: var(--de-cat-plural); }
#deTrackerApp .de-vocab-cat-badge--verb   { background: var(--de-cat-verb-soft);   color: var(--de-cat-verb); }
#deTrackerApp .de-vocab-cat-badge--adj    { background: var(--de-cat-adj-soft);    color: var(--de-cat-adj); }
#deTrackerApp .de-vocab-cat-badge--adv    { background: var(--de-cat-adv-soft);    color: var(--de-cat-adv); }
#deTrackerApp .de-vocab-cat-badge--other  { background: var(--de-cat-other-soft);  color: var(--de-cat-other); }

/* Used together with one of the eight variants above (e.g.
   "de-vocab-cat-badge--masc de-vocab-cat-badge--on-color") wherever
   the badge sits on top of its own category's solid card background
   (spec 6: "category badge remains readable") — the normal soft-tint
   badge would nearly disappear against a same-hued solid background,
   so this swaps in a frosted white chip instead. One variant covers
   all eight categories, rather than needing an "on-color" version of
   each. Declared after the eight rules above so it wins the cascade
   when both classes are present (equal specificity, later wins). */
#deTrackerApp .de-vocab-cat-badge--on-color {
  background: var(--de-sheen-wash);
  color: var(--de-on-fill);
  border: 1px solid rgba(255, 255, 255, 0.4);
}

#deTrackerApp .de-vocab-cat-badge-row {
  display: flex;
  justify-content: center;
  margin-top: 2px;
}

#deTrackerApp .de-flashcard-back-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  overflow-wrap: break-word;
  max-width: 100%;
}

@media (max-width: 640px) {
  #deTrackerApp .de-vocab-list-grid {
    grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  }

  #deTrackerApp .de-vocab-list-persian {
    font-size: 14px;
  }
}

/* --- Dialogue reveal-quiz --- */

#deTrackerApp .de-dialogue-prompt {
  font-size: 18px;
  font-weight: 600;
  color: var(--de-heading);
  background: var(--de-primary-soft);
  border-radius: var(--de-radius);
  padding: var(--de-space-4);
  width: 100%;
  overflow-wrap: break-word;
}

/* --- Dialogue scene (lesson-level context, shown once above the
   dialogue list) --- */
#deTrackerApp .de-dialogue-scene {
  display: flex;
  align-items: flex-start;
  gap: var(--de-space-2);
  background: rgba(var(--de-primary-rgb), 0.06);
  border: 1px dashed var(--de-border-strong);
  border-radius: var(--de-radius);
  padding: var(--de-space-3) var(--de-space-4);
  width: 100%;
  text-align: right;
}

#deTrackerApp .de-dialogue-scene-icon {
  font-size: 16px;
  line-height: 1.6;
  flex: none;
}

#deTrackerApp .de-dialogue-scene p {
  margin: 0;
  font-size: 13.5px;
  color: var(--de-text-light);
  line-height: 1.7;
}

/* --- Dialogue list (spec 11): landing view for the "دیالوگ‌ها" tab —
   one card per separate dialogue in the lesson (grouped by blank
   lines on the PHP side). --- */
#deTrackerApp .de-dialogue-list-wrap {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-4);
}

#deTrackerApp .de-dialogue-card-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--de-space-3);
}

#deTrackerApp .de-dialogue-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 84px;
  padding: var(--de-space-3);
  border-radius: var(--de-radius-sm);
  border: 1px solid var(--de-border);
  background: var(--de-card);
  cursor: pointer;
  text-align: center;
  font-family: var(--de-font);
  transition: var(--de-transition);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-dialogue-card:hover {
    border-color: rgba(var(--de-primary-rgb), 0.28);
    background: var(--de-card-strong);
    transform: translateY(-2px);
    box-shadow: var(--de-shadow-sm);
  }
}

#deTrackerApp .de-dialogue-card--done {
  border-color: rgba(16, 185, 129, 0.35);
  background: var(--de-success-soft);
}

#deTrackerApp .de-dialogue-card-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--de-heading);
  overflow-wrap: break-word;
}

#deTrackerApp .de-dialogue-card-meta {
  font-size: 12px;
  color: var(--de-text-light);
}

/* --- Dialogue chat practice (spec 12–17) ---
   The thread wrapper is forced to physical LTR so flex-start/flex-end
   map to true left/right regardless of the page's own RTL direction —
   speaker A always renders on the left and speaker B on the right, a
   fixed convention from the spec rather than a "your messages vs
   theirs" convention that would flip with reading direction. Each
   bubble's own content then switches back to RTL, since the sentences
   themselves are Persian first. */
#deTrackerApp .de-chat-progress {
  font-size: 12.5px;
  color: var(--de-text-light);
  text-align: center;
}

#deTrackerApp .de-chat-thread {
  direction: ltr;
  display: flex;
  flex-direction: column;
  gap: var(--de-space-3);
}

#deTrackerApp .de-chat-row {
  display: flex;
  width: 100%;
}

#deTrackerApp .de-chat-row--a {
  justify-content: flex-start;
}

#deTrackerApp .de-chat-row--b {
  justify-content: flex-end;
}

#deTrackerApp .de-chat-bubble {
  direction: rtl;
  max-width: 84%;
  border-radius: var(--de-radius-sm);
  padding: var(--de-space-3) var(--de-space-4);
  font-family: var(--de-font);
  animation: de-fade-in 220ms ease both;
  overflow-wrap: break-word;
}

#deTrackerApp .de-chat-bubble--a {
  background: var(--de-card);
  border: 1.5px solid var(--de-border);
  border-bottom-left-radius: 4px;
}

/* Solid brand fill with white text — the user's own side of the
   exchange, matching the approved dialogue mockup (a bold filled
   bubble for "your" message, a plain outlined one for the other
   speaker) rather than the original pale tint. */
#deTrackerApp .de-chat-bubble--b {
  background: var(--de-primary);
  border: 1.5px solid var(--de-primary);
  border-bottom-right-radius: 4px;
}

#deTrackerApp .de-chat-speaker {
  font-size: 11px;
  font-weight: 700;
  color: var(--de-text-faint);
  margin-bottom: 4px;
}

#deTrackerApp .de-chat-bubble--b .de-chat-speaker {
  color: var(--de-on-fill-muted);
}

#deTrackerApp .de-chat-persian,
#deTrackerApp .de-chat-persian-ref {
  font-size: 14.5px;
  color: var(--de-heading);
  line-height: 1.7;
  overflow-wrap: break-word;
}

#deTrackerApp .de-chat-bubble--b .de-chat-persian,
#deTrackerApp .de-chat-bubble--b .de-chat-persian-ref {
  color: var(--de-on-fill);
}

/* Once solved, the Persian line becomes a small reference caption and
   the German line (the "delivered message") takes the visual lead —
   mirrors how the exchange would read in a real chat log once both
   sides understand each other. */
#deTrackerApp .de-chat-bubble--solved .de-chat-persian-ref {
  font-size: 12px;
  color: var(--de-text-faint);
  margin-bottom: 4px;
}

#deTrackerApp .de-chat-bubble--b.de-chat-bubble--solved .de-chat-persian-ref {
  color: var(--de-on-fill-muted);
}

#deTrackerApp .de-chat-german {
  font-size: 15px;
  font-weight: 600;
  color: var(--de-heading);
  line-height: 1.6;
  overflow-wrap: break-word;
}

#deTrackerApp .de-chat-bubble--b .de-chat-german {
  color: var(--de-on-fill);
}

#deTrackerApp .de-chat-check {
  margin-top: 4px;
  text-align: left;
  font-size: 12px;
  color: var(--de-success);
  letter-spacing: -1px;
  display: flex;
  animation: de-chat-check-pop 320ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

#deTrackerApp .de-chat-bubble--a .de-chat-check {
  justify-content: flex-start;
}

#deTrackerApp .de-chat-bubble--b .de-chat-check {
  justify-content: flex-end;
}

/* Telegram-style "delivered" tick — a small bounce-in on the double
   check keeps the moment a step gets marked solved feeling like an
   actual confirmation landing, not just static UI appearing. */
@keyframes de-chat-check-pop {
  0% { transform: scale(0.4); opacity: 0; }
  60% { transform: scale(1.15); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

/* A step solved via "نمایش پاسخ" instead of actually being answered
   correctly (spec 8) — same bubble layout, but visually distinct so
   it doesn't read as a genuine correct answer: a muted outline instead
   of the solved bubble's normal solid confidence, and the checkmark
   above swaps to an eye icon in the markup itself (see
   buildDialogueChatBubble). */
#deTrackerApp .de-chat-bubble--revealed {
  background: transparent;
  border-style: dashed;
}

#deTrackerApp .de-chat-bubble--revealed .de-chat-check {
  color: var(--de-text-light);
  letter-spacing: normal;
}

/* Speaker B's revealed bubble gets a soft green tint instead of the
   shared transparent/dashed look, so it reads as a distinct "this
   was the other side's answer, shown rather than typed" state and
   doesn't get confused with speaker A's plain outlined style.
   3.15.0: bumped ~20% more saturated/darker than the shared
   --de-success-soft token per follow-up feedback (still soft, just
   more visually distinct from the page background) — a dedicated
   value here rather than changing --de-success-soft itself, since
   that token is shared with badges/cards elsewhere that weren't
   part of this request. */
#deTrackerApp .de-chat-bubble--b.de-chat-bubble--revealed {
  background: var(--de-dialogue-b-soft);
  border-color: var(--de-success);
}

/* Bug fix (3.13.0): .de-chat-bubble--b forces white text for its
   normal solid-blue fill, but --revealed strips that fill down to a
   transparent/dashed outline. Without this override the --b + --revealed
   combination (a user-side dialogue turn solved via "نمایش پاسخ") left
   white-on-white — the German/Persian lines and speaker label were
   effectively invisible. These restore the same dark palette the --a
   (non-filled) side already uses in its default state. */
#deTrackerApp .de-chat-bubble--b.de-chat-bubble--revealed .de-chat-german {
  color: var(--de-heading);
}

#deTrackerApp .de-chat-bubble--b.de-chat-bubble--revealed .de-chat-persian,
#deTrackerApp .de-chat-bubble--b.de-chat-bubble--revealed .de-chat-persian-ref {
  color: var(--de-heading);
}

#deTrackerApp .de-chat-bubble--b.de-chat-bubble--revealed.de-chat-bubble--solved .de-chat-persian-ref {
  color: var(--de-text-faint);
}

#deTrackerApp .de-chat-bubble--b.de-chat-bubble--revealed .de-chat-speaker {
  color: var(--de-text-faint);
}

#deTrackerApp .de-chat-bubble--b.de-chat-bubble--revealed .de-chat-check {
  color: var(--de-text-light);
}

/* Telegram's own "read" ticks are a light cyan-blue rather than plain
   white — borrowing that same idea here gives the delivered check a
   bit of its own color identity instead of just fading into the
   bubble's white text. */
#deTrackerApp .de-chat-bubble--b .de-chat-check {
  text-align: right;
  color: var(--de-dialogue-b-tick);
}

/* 3.15.0: speaker B's ACTIVE (unanswered) turn now follows speaker
   A's card structure exactly (plain light card, dark text, colored
   accents on the controls) instead of the solid-blue fill used for
   B everywhere else — per direction, only the accent color changes
   here, not the layout/spacing/structure already established for A.
   Uses the same soft-green family as the revealed state above (not a
   new green), and reverts the --b white-text rules back to A's dark
   palette for just the two lines this bubble actually renders
   (speaker label, Persian prompt — .de-chat-german/.de-chat-check
   don't exist in the active markup, so no override needed there). */
#deTrackerApp .de-chat-bubble--b.de-chat-bubble--active {
  background: var(--de-dialogue-b-soft);
  border-color: var(--de-success);
}

#deTrackerApp .de-chat-bubble--b.de-chat-bubble--active .de-chat-speaker {
  color: var(--de-text-faint);
}

#deTrackerApp .de-chat-bubble--b.de-chat-bubble--active .de-chat-persian {
  color: var(--de-heading);
}

/* Send button: same shape as speaker A's (inherited from
   .de-btn-primary), just a bolder green instead of blue. Text color
   also needs its own override here — .de-btn-primary's default
   color: var(--de-on-fill) is white, correct for the usual blue fill
   but not for this green one in dark mode (see --de-on-success-fill). */
#deTrackerApp .de-chat-bubble--b.de-chat-bubble--active .de-chat-input-row .de-btn-primary {
  background: var(--de-success);
  color: var(--de-on-success-fill);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-chat-bubble--b.de-chat-bubble--active .de-chat-input-row .de-btn-primary:hover {
    background: var(--de-success-dark);
  }
}

/* Reveal button: same outlined shape as speaker A's, recolored to
   the same bolder green as the Send button above so both of B's
   controls share one accent family. */
#deTrackerApp .de-chat-bubble--b.de-chat-bubble--active .de-chat-reveal-btn {
  border-color: var(--de-success);
  color: var(--de-success-dark);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-chat-bubble--b.de-chat-bubble--active .de-chat-reveal-btn:hover {
    background: var(--de-success);
    border-color: var(--de-success);
    color: var(--de-on-success-fill);
  }
}

/* 3.14.0: previously just a flex row with the (already white) input
   sitting directly on the blue bubble background — fine for the
   input itself, but the primary Send button used the same solid-blue
   fill as the bubble behind it and all but disappeared. Wrapping the
   whole row in its own white surface gives every control inside it a
   plain white background to sit on, regardless of the bubble color
   behind it. */
#deTrackerApp .de-chat-input-row {
  display: flex;
  gap: var(--de-space-2);
  margin-top: var(--de-space-2);
  background: var(--de-card);
  border-radius: var(--de-radius-sm);
  padding: 6px;
}

#deTrackerApp .de-chat-input-row input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 10px 14px;
  border-radius: var(--de-radius-sm);
  border: none;
  font-family: var(--de-font);
  /* 16px, not smaller — iOS Safari auto-zooms the whole viewport on
     focus for any text input under 16px, and this input is focused on
     every single dialogue exchange (audit H-1). */
  font-size: 16px;
  background: transparent;
}

#deTrackerApp .de-chat-input-row input:focus {
  outline: none;
  box-shadow: 0 0 0 2px var(--de-primary-ring);
}


/* "نمایش پاسخ" (spec 8) — a quiet secondary action below the input,
   discoverable but clearly not the main path (that's typing + ارسال).
   Sits on the shared .de-btn-ghost look from the unified button system
   rather than inventing its own appearance. .de-btn is inline-flex by
   default, so it already sits at its natural width without stretching
   — no align-self needed (.de-chat-bubble isn't a flex container, so
   that would have been inert; checked before adding it, not after). */
/* Bug fix (3.13.0): the generic .de-btn-ghost look (transparent bg +
   --de-text-light) is too low-contrast for a button sitting directly
   on the light page background here — it read as barely-there. Giving
   this one its own border/text color instead of only relying on the
   shared ghost style keeps it visually "quiet" (still not the primary
   action) while actually being legible. */
#deTrackerApp .de-chat-reveal-btn {
  margin-top: 6px;
  border-color: var(--de-border-strong);
  color: var(--de-primary-dark);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-chat-reveal-btn:hover {
    background: var(--de-primary);
    border-color: var(--de-primary);
    color: var(--de-on-fill);
  }
}

/* --- Dialogue completion card (spec 17) --- */
#deTrackerApp .de-dialogue-completion {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--de-space-2);
  text-align: center;
  padding: var(--de-space-5) var(--de-space-4);
  border-radius: var(--de-radius);
  background: var(--de-success-soft);
  border: 1px solid rgba(16, 185, 129, 0.28);
  animation: de-fade-in 250ms ease both;
}

#deTrackerApp .de-dialogue-completion-icon {
  font-size: 30px;
  color: var(--de-success);
  line-height: 1;
}

#deTrackerApp .de-dialogue-completion-title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  color: var(--de-heading);
}

#deTrackerApp .de-dialogue-completion-stats {
  display: flex;
  gap: var(--de-space-5);
  margin: var(--de-space-2) 0;
}

#deTrackerApp .de-dialogue-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

#deTrackerApp .de-dialogue-stat strong {
  font-size: 16px;
  color: var(--de-heading);
}

#deTrackerApp .de-dialogue-stat span {
  font-size: 11px;
  color: var(--de-text-light);
}

#deTrackerApp .de-dialogue-completion-actions {
  display: flex;
  gap: var(--de-space-2);
  margin-top: var(--de-space-2);
  flex-wrap: wrap;
  justify-content: center;
}

@media (max-width: 640px) {
  #deTrackerApp .de-chat-bubble {
    max-width: 92%;
  }

  #deTrackerApp .de-dialogue-completion-stats {
    gap: var(--de-space-4);
  }
}

/* --- Quiz --- */

#deTrackerApp .de-quiz-setup,
#deTrackerApp .de-quiz-session {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-4);
}

#deTrackerApp .de-quiz-setup-summary {
  display: flex;
  justify-content: center;
  gap: var(--de-space-5);
  flex-wrap: wrap;
  padding: var(--de-space-4);
  background: var(--de-card-strong);
  border: 1px solid var(--de-border);
  border-radius: var(--de-radius);
}

/* Same visual language as .de-dialogue-stat (completion screen) —
   sharing the rule directly rather than duplicating it, since both
   are "a number + a small label" stat chip. */
#deTrackerApp .de-quiz-setup-summary .de-quiz-stat,
#deTrackerApp .de-quiz-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

#deTrackerApp .de-quiz-stat strong {
  font-size: 18px;
  color: var(--de-heading);
}

#deTrackerApp .de-quiz-stat span {
  font-size: 11px;
  color: var(--de-text-light);
}

#deTrackerApp .de-quiz-size-options {
  display: flex;
  gap: var(--de-space-2);
  flex-wrap: wrap;
}

#deTrackerApp .de-quiz-size-options .de-btn {
  flex: 1 1 90px;
  min-height: 44px;
}

/* Custom question-count entry, alongside the quick-pick buttons above
   (10/20/50/all) — Phase 3's "or any number the user wants" from the
   original spec. */
#deTrackerApp .de-quiz-custom-size {
  display: flex;
  gap: var(--de-space-2);
}

#deTrackerApp .de-quiz-custom-size .de-field {
  flex: 1;
  min-width: 0;
}

#deTrackerApp .de-quiz-history {
  border-top: 1px solid var(--de-border);
  padding-top: var(--de-space-4);
}

#deTrackerApp .de-quiz-history h4 {
  margin: 0 0 var(--de-space-2);
  font-size: 13.5px;
  color: var(--de-text-light);
}

#deTrackerApp .de-quiz-history-list {
  list-style: none;
  margin: 0 0 var(--de-space-3);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

#deTrackerApp .de-quiz-history-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--de-space-2);
  padding: 8px 12px;
  background: var(--de-card);
  border-radius: var(--de-radius-sm);
  font-size: 13px;
  flex-wrap: wrap;
}

#deTrackerApp .de-quiz-history-date {
  color: var(--de-text-faint);
  font-size: 11.5px;
}

#deTrackerApp .de-quiz-session-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--de-space-2);
  flex-wrap: wrap;
}

#deTrackerApp .de-quiz-type-tag {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: var(--de-radius-pill);
  background: var(--de-primary-soft);
  color: var(--de-primary-dark);
  font-size: 12px;
  font-weight: 700;
}

#deTrackerApp .de-quiz-progress {
  font-size: 12.5px;
  color: var(--de-text-light);
}

#deTrackerApp .de-quiz-situation {
  padding: var(--de-space-3) var(--de-space-4);
  background: rgba(148, 163, 184, 0.1);
  border-radius: var(--de-radius);
  font-size: 13.5px;
  color: var(--de-text-light);
  line-height: 1.7;
  margin-bottom: var(--de-space-2);
}

/* صرف فعل (conjugation) reuses the "choice" interaction entirely
   (see ensureQuizChoiceOptions() in the JS) — this is just the small
   verb/pronoun context line shown above the fill-in-the-blank
   sentence, styled like .de-quiz-situation's same "supporting context,
   not the question itself" role. */
#deTrackerApp .de-quiz-conjugation-meta {
  font-size: 13px;
  color: var(--de-text-light);
  margin-bottom: 4px;
}

/* تکمیل-دیالوگ (dialogue_completion) — Phase 4. The dialogue itself,
   blanks and all, shown as read-only context above the shared word
   bank (see getWordBankPrompt() in the JS). Styled like a compact
   .de-dialogue-message stack rather than plain paragraph text, since
   it's genuinely a short back-and-forth, not a single sentence. */
#deTrackerApp .de-quiz-dialogue-context {
  padding: var(--de-space-3) var(--de-space-4);
  background: rgba(148, 163, 184, 0.1);
  border-radius: var(--de-radius);
  font-size: 13.5px;
  color: var(--de-text);
  line-height: 1.9;
  margin-bottom: var(--de-space-2);
  white-space: normal;
}

/* Word-bank engine (Phase 1 of the multi-type quiz rework): shared
   chip-based "build something by picking pre-supplied words" UI.
   Currently used only by مرتب‌سازی (reorder), but built generic on
   purpose — the class names dropped their old "reorder"-specific
   naming so later phases (Word Bank variants of جای‌خالی/تشخیص-خطا/
   ترجمه, تبدیل-جمله, تکمیل-دیالوگ) can reuse this same CSS instead of
   duplicating it under a new name per question type. */
#deTrackerApp .de-word-bank-pool,
#deTrackerApp .de-word-bank-picked {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  min-height: 44px;
  padding: var(--de-space-3);
  border-radius: var(--de-radius);
}

#deTrackerApp .de-word-bank-picked {
  background: var(--de-primary-ring);
  border: 1px dashed rgba(var(--de-primary-rgb), 0.35);
}

#deTrackerApp .de-word-bank-pool {
  background: rgba(148, 163, 184, 0.08);
}

#deTrackerApp .de-word-bank-chip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  max-width: 100%;
  padding: 8px 16px;
  border-radius: var(--de-radius-sm);
  border: 1px solid var(--de-border-strong);
  background: var(--de-card);
  color: var(--de-heading);
  font-family: var(--de-font);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: var(--de-transition);
  overflow-wrap: break-word;
  word-break: break-word;
}

#deTrackerApp .de-word-bank-chip--picked {
  background: var(--de-primary);
  border-color: var(--de-primary);
  color: var(--de-on-fill);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-word-bank-chip:not(:disabled):hover {
    border-color: var(--de-primary);
    transform: translateY(-1px);
  }
}

#deTrackerApp .de-word-bank-chip:disabled {
  cursor: default;
  opacity: 0.75;
}

@media (max-width: 640px) {
  #deTrackerApp .de-quiz-setup-summary {
    gap: var(--de-space-4);
  }

  #deTrackerApp .de-quiz-size-options .de-btn {
    flex: 1 1 70px;
  }

  #deTrackerApp .de-word-bank-chip {
    font-size: 13px;
    padding: 7px 12px;
  }
}

/* --- Matching interaction (تطبیق) — Phase 2 of the multi-type quiz
   rework. Two independently-shuffled columns; tapping one item per
   column attempts a pair. Reuses .de-word-bank-chip's visual language
   (button chip, same border/radius/hover) via a dedicated class rather
   than sharing the class outright, since matched/selected states here
   don't map cleanly onto word-bank's picked/pool states. --- */
#deTrackerApp .de-matching-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--de-space-3);
}

#deTrackerApp .de-matching-column {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

#deTrackerApp .de-matching-item {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 8px 12px;
  border-radius: var(--de-radius-sm);
  border: 1px solid var(--de-border-strong);
  background: var(--de-card);
  color: var(--de-heading);
  font-family: var(--de-font);
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  text-align: center;
  transition: var(--de-transition);
  overflow-wrap: break-word;
  word-break: break-word;
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-matching-item:not(:disabled):hover {
    border-color: var(--de-primary);
    transform: translateY(-1px);
  }
}

#deTrackerApp .de-matching-item--selected {
  border-color: var(--de-primary);
  background: var(--de-primary-ring);
}

#deTrackerApp .de-matching-item--matched {
  background: var(--de-success-soft);
  border-color: var(--de-success);
  color: var(--de-success);
  cursor: default;
  opacity: 0.85;
}

#deTrackerApp .de-matching-item:disabled {
  cursor: default;
}

@media (max-width: 640px) {
  #deTrackerApp .de-matching-item {
    font-size: 12.5px;
    padding: 7px 8px;
    min-height: 40px;
  }
}

/* --- Self-assessment interaction (پاسخ آزاد) — Phase 2. No automatic
   grading; the sample answer is shown on request, then the user
   grades themselves. Styled close to .de-dialogue-typed-correct
   (the "here's the right answer" box other typed types show after
   answering) since it plays the same visual role here, just shown
   before grading instead of after. --- */
#deTrackerApp .de-self-assessment-sample {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: var(--de-space-3) var(--de-space-4);
  border-radius: var(--de-radius);
  background: var(--de-primary-ring);
  border: 1px dashed rgba(var(--de-primary-rgb), 0.35);
  margin-bottom: var(--de-space-2);
}

#deTrackerApp .de-self-assessment-sample span {
  font-size: 12.5px;
  color: var(--de-text-light);
}

#deTrackerApp .de-self-assessment-sample strong {
  font-size: 14.5px;
  color: var(--de-heading);
  line-height: 1.7;
}

#deTrackerApp .de-self-assessment-grade {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-2);
}

#deTrackerApp .de-self-assessment-grade-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--de-space-2);
}

/* --- Dialogue practice mode toggle --- */
#deTrackerApp .de-dialogue-mode-toggle {
  display: flex;
  gap: var(--de-space-2);
  width: 100%;
}

#deTrackerApp .de-dialogue-mode-btn {
  flex: 1;
  padding: 8px 12px;
  border-radius: var(--de-radius-tab);
  border: 1px solid var(--de-border);
  background: transparent;
  color: var(--de-text-light);
  font-family: var(--de-font);
  font-size: 13px;
  cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease;
}

#deTrackerApp .de-dialogue-mode-btn.active {
  background: var(--de-primary);
  color: var(--de-on-fill);
  border-color: var(--de-primary);
}

/* --- Theme (light/dark/system) toggle, in the schedule-settings
   modal --- reuses the exact same segmented-group pattern as the
   dialogue-mode toggle above, per "consistent design system, not a
   one-off control". */
#deTrackerApp .de-theme-toggle {
  display: flex;
  gap: var(--de-space-2);
  width: 100%;
}

#deTrackerApp .de-theme-toggle-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 10px;
  border-radius: var(--de-radius-tab);
  border: 1px solid var(--de-border);
  background: transparent;
  color: var(--de-text-light);
  font-family: var(--de-font);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease;
}

#deTrackerApp .de-theme-toggle-btn.active {
  background: var(--de-primary);
  color: var(--de-on-fill);
  border-color: var(--de-primary);
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-theme-toggle-btn:not(.active):hover {
    border-color: var(--de-border-strong);
    color: var(--de-text);
  }
}

/* --- Dialogue multiple choice --- */
#deTrackerApp .de-dialogue-choices {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-2);
  width: 100%;
}

/* 3.15.0: dropped the pressed-3D offset shadow used here since
   3.13.0 — going flat/minimal app-wide, no shadow under any button.
   :active now just a plain scale, matching the rest of the app's
   flat button feedback instead of a shadow-dependent "press". */
#deTrackerApp .de-dialogue-choice {
  width: 100%;
  padding: var(--de-space-3) var(--de-space-4);
  border-radius: var(--de-radius-sm);
  border: 2px solid var(--de-border-strong);
  background: var(--de-card);
  color: var(--de-text);
  font-family: var(--de-font);
  font-size: 15px;
  font-weight: 600;
  text-align: center;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--de-space-2);
  transition: var(--de-transition);
}

#deTrackerApp .de-dialogue-choice:active:not(:disabled) {
  transform: scale(0.98);
}

#deTrackerApp .de-dialogue-choice-text {
  flex: 1;
  text-align: center;
}

#deTrackerApp .de-dialogue-choice-icon {
  flex-shrink: 0;
  font-size: 17px;
  font-weight: 800;
}

@media (hover: hover) and (pointer: fine) {
  #deTrackerApp .de-dialogue-choice:hover:not(:disabled) {
    border-color: var(--de-primary);
    background: var(--de-primary-soft);
  }
}

#deTrackerApp .de-dialogue-choice:disabled {
  cursor: default;
}

#deTrackerApp .de-dialogue-choice--correct {
  border-color: var(--de-success);
  background: var(--de-success-soft);
  color: var(--de-success);
}

#deTrackerApp .de-dialogue-choice--correct .de-dialogue-choice-icon {
  color: var(--de-success);
}

#deTrackerApp .de-dialogue-choice--wrong {
  border-color: var(--de-danger);
  background: var(--de-danger-soft);
  color: var(--de-danger-dark);
}

#deTrackerApp .de-dialogue-choice--wrong .de-dialogue-choice-icon {
  color: var(--de-danger-dark);
}

#deTrackerApp .de-dialogue-feedback {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
}

/* 3.15.0: speaker B's active bubble is now a light green card
   (matching speaker A's structure, just recolored — see above), so
   the plain red feedback text reads fine again without any special
   casing. Removed the earlier white-text override this needed when
   B's active bubble was still solid blue. */

/* --- Dialogue typed-answer --- */
#deTrackerApp .de-dialogue-typed-input {
  display: flex;
  gap: var(--de-space-2);
  width: 100%;
  flex-wrap: wrap;
}

#deTrackerApp .de-dialogue-typed-input input {
  flex: 1 1 200px;
  min-width: 0;
  padding: 10px 14px;
  border-radius: var(--de-radius);
  border: 1px solid var(--de-border-strong);
  background: var(--de-card-strong);
  color: var(--de-text);
  font-family: var(--de-font);
  /* 16px — see the matching note on .de-chat-input-row input above
     (audit H-1). */
  font-size: 16px;
  text-align: left;
}

#deTrackerApp .de-dialogue-typed-input input:focus {
  outline: none;
  border-color: var(--de-primary);
  box-shadow: 0 0 0 3px var(--de-primary-ring);
}

#deTrackerApp .de-dialogue-typed-result {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--de-space-2);
  width: 100%;
}

#deTrackerApp .de-dialogue-typed-you,
#deTrackerApp .de-dialogue-typed-correct {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 14px;
  color: var(--de-text-light);
}

#deTrackerApp .de-dialogue-typed-you strong,
#deTrackerApp .de-dialogue-typed-correct strong {
  color: var(--de-heading);
}

@media (max-width: 480px) {
  #deTrackerApp .de-dialogue-mode-toggle {
    flex-direction: column;
  }

  #deTrackerApp .de-theme-toggle {
    flex-direction: column;
  }
}

@media (max-width: 782px) {
  #deTrackerApp .de-lesson-card-btn {
    font-size: 12.5px;
    padding: 8px 6px;
  }

  #deTrackerApp .de-flashcard {
    height: 160px;
  }

  /* Mobile lesson-summary tabs (spec 10): a 2×2 grid instead of
     cramming all four into one row, where they were shrinking down to
     truncated, ellipsized labels. Two comfortably-wide columns instead
     give every tab equal weight and a real touch target, with room
     for the label to show in full — wrapping onto a second line on an
     unusually narrow phone if it ever has to, which is still more
     readable than silently cutting text off. */
  #deTrackerApp .de-lesson-card-nav {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--de-space-2);
  }

  #deTrackerApp .de-lesson-card-btn {
    flex: none;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    white-space: normal;
    overflow: visible;
    text-overflow: unset;
    line-height: 1.3;
  }
}


/* 3.39.0 — schedule mode tabs, read-only fields, and the mode-switch
   confirmation. Mirrors the app's two-tab schedule screen. */
#deTrackerApp .de-schedule-tabs {
  display: flex;
  gap: 8px;
  margin-top: 6px;
}

#deTrackerApp .de-schedule-tab {
  flex: 1;
  padding: 10px 12px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.04);
  color: rgba(255, 255, 255, 0.72);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

#deTrackerApp .de-schedule-tab:hover {
  background: rgba(255, 255, 255, 0.08);
}

#deTrackerApp .de-schedule-tab.is-active {
  background: rgba(99, 102, 241, 0.18);
  border-color: rgba(99, 102, 241, 0.55);
  color: #fff;
}

#deTrackerApp .de-field-label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.82);
}

#deTrackerApp .de-readonly-value {
  padding: 10px 12px;
  border: 1px dashed rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.03);
  color: #fff;
  font-weight: 700;
}

#deTrackerApp .de-mode-switch-confirm {
  margin-top: 14px;
  padding: 14px;
  border: 1px solid rgba(99, 102, 241, 0.45);
  border-radius: 10px;
  background: rgba(99, 102, 241, 0.10);
}

#deTrackerApp .de-mode-switch-confirm h4 {
  margin: 0 0 8px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
}

#deTrackerApp .de-mode-switch-confirm p {
  margin: 0 0 8px;
  font-size: 13px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.78);
}

/* Green when the per-book days survive the switch, amber when they are
   cleared — the same signal the app uses. */
#deTrackerApp .de-mode-switch-keep { color: #34d399; font-size: 12px; }
#deTrackerApp .de-mode-switch-lose { color: #fbbf24; font-size: 12px; }

#deTrackerApp .de-mode-switch-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 10px;
}


/* 3.40.0 — the mode-switch confirmation sits above the settings modal
   rather than inside it, so it cannot be scrolled out of sight. */
#deTrackerApp .de-mode-switch-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(2, 6, 23, 0.62);
}

#deTrackerApp .de-mode-switch-backdrop .de-mode-switch-confirm {
  margin: 0;
  max-width: 420px;
  width: 100%;
  background: #111827;
  border-color: rgba(99, 102, 241, 0.6);
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.45);
}

/* Read-only per-book days in the target-date tab: same shape as the
   editable inputs beside them, visibly not editable. */
#deTrackerApp .de-readonly-value-small {
  padding: 8px 10px;
  font-size: 13px;
  font-weight: 600;
}
