/* ===========================================================================
   MOBILE PARITY — μία οθόνη, δύο υλοποιήσεις

   Αυτό το αρχείο κρατά ΟΛΕΣ τις οπτικές αποκλίσεις του web κάτω από τα 768px,
   ώστε το κινητό web να διαβάζεται σαν την εφαρμογή iOS/Android και όχι σαν
   στριμωγμένο desktop.

   ── ΓΙΑΤΙ ΕΝΑ ΑΡΧΕΙΟ ΚΑΙ ΟΧΙ ΣΚΟΡΠΙΑ ΣΤΑ COMPONENTS ─────────────────────────

   Τα components εδώ ενίοτε εγχέουν τα δικά τους styles (KPICard.js). Για την
   ΠΑΡΙΤΗΤΑ όμως ισχύει το αντίστροφο: όταν αλλάξει κάτι στην εφαρμογή, θέλεις
   ΕΝΑ σημείο να ανοίξεις και ΕΝΑ μοτίβο να ακολουθήσεις. Σκορπισμένοι κανόνες
   σε έξι αρχεία σημαίνουν έξι ευκαιρίες να γραφτεί η επόμενη αλλαγή με άλλη
   μορφολογία — που είναι ακριβώς το πρόβλημα που λύνει αυτό το αρχείο.

   ── Ο ΚΑΝΟΝΑΣ, ΤΕΣΣΕΡΙΣ ΓΡΑΜΜΕΣ ────────────────────────────────────────────

   1. Κάθε μπλοκ ονομάζει ΤΟ ΑΡΧΕΙΟ ΤΗΣ ΕΦΑΡΜΟΓΗΣ που καθρεφτίζει. Αν δεν
      υπάρχει τέτοιο αρχείο, ο κανόνας δεν ανήκει εδώ.
   2. Κάθε τιμή που αντιγράφεται από το `mobile/src/theme.ts` γράφεται ως
      custom property στο :root παρακάτω, ΜΕ το όνομα του token. Ποτέ literal
      μέσα στον κανόνα — αλλιώς το επόμενο sync είναι αναζήτηση για hex.
   3. Ένα breakpoint: 767.98px. Είναι το `md` του Tailwind μείον ένα subpixel,
      δηλαδή ακριβώς εκεί που κρύβεται το sidebar και ο μήνας κατεβαίνει στο
      περιεχόμενο. Δεύτερο breakpoint σημαίνει τρίτη σύνθεση να συντηρείς.
   4. Τα hooks στο markup είναι ΠΡΟΣΘΕΤΙΚΕΣ κλάσεις (`js-*` δεν χρησιμοποιείται·
      χρησιμοποιούμε σημασιολογικά ονόματα). Καμία υπάρχουσα κλάση δεν
      αφαιρείται — έτσι το desktop αποδεικνύεται αμετάβλητο με `git diff`.

   🔴 5. ΚΑΘΕ ΚΑΝΟΝΑΣ ΠΟΥ ΠΑΤΑΕΙ ΠΑΝΩ ΣΕ UTILITY ΘΕΛΕΙ ΕΞΕΙΔΙΚΕΥΣΗ > (0,1,0).

      Ο κανόνας 4 έχει τίμημα που κόστισε ήδη: αφού οι utilities του Tailwind
      ΜΕΝΟΥΝ στο στοιχείο, ένας δικός μας κανόνας με μία κλάση έχει ΙΣΗ
      εξειδίκευση με το `.gap-3` ή το `.ml-auto` — και σε ισοπαλία αποφασίζει η
      σειρά. Το Tailwind CDN γράφει το <style> του στον χρόνο εκτέλεσης, δηλαδή
      ΜΕΤΑ από αυτό το αρχείο. Κερδίζει πάντα εκείνο.

      Η αποτυχία είναι αθόρυβη: ο κανόνας φαίνεται σωστός στο αρχείο και απλώς
      δεν ισχύει. Έξι κανόνες εδώ έπεσαν ακριβώς σε αυτό (ο δίσκος κρατούσε
      `gap-3`, το κλάσμα της ομάδας κρατούσε `ml-auto` και έμενε δεξιά, τα
      pills κρατούσαν `rounded-sm` και `px-3`).

      Ο τρόπος: πρόθεμα προγόνου (`main .x`, `nav[…] .x`) όπου υπάρχει σταθερός
      πρόγονος, αλλιώς διπλή κλάση (`.x.x`). ΟΧΙ `!important` — αυτό το κρατάμε
      για τα λίγα σημεία που πρέπει να νικήσουν και μελλοντικά utilities.

      Έλεγχος πριν γράψεις κανόνα: κοίτα το `class=` του στοιχείου στο markup.
      Αν κάποιο utility ορίζει την ίδια ιδιότητα, χρειάζεσαι εξειδίκευση.

   Ο πλήρης οδηγός, με τον πίνακα αντιστοίχισης αρχείο→αρχείο και τη λίστα
   ελέγχου για κάθε νέα αλλαγή: `./README.md`, δίπλα σε αυτό το αρχείο.
   =========================================================================== */

:root {
    /* ── Καθρέφτης του mobile/src/theme.ts ──────────────────────────────────
       Τα ονόματα είναι ΤΑ ΙΔΙΑ με τα tokens εκεί.

       ⚠️ ΟΣΑ ΕΙΝΑΙ ΧΡΩΜΑ ΔΕΙΧΝΟΥΝ ΠΛΕΟΝ ΣΤΟ styles/<version>/theme.css και
       δεν κρατούν δική τους τιμή. Ο λόγος είναι ότι ένα θέμα που αλλάζει δεν
       μπορεί να έχει δύο αρχεία να διαφωνούν για το τι είναι «το μελάνι».
       Τα ονόματα μένουν, γιατί τα χρησιμοποιεί όλο το αρχείο από κάτω.

       🔴 ΤΑ ΤΕΣΣΕΡΑ ΟΥΔΕΤΕΡΑ ΗΤΑΝ Η ΤΡΥΠΑ. Είχαν μείνει literal επειδή δεν
       αντιστοιχούσαν σε κανένα σημασιολογικό token, με ένα σχόλιο που έλεγε
       «θα τα βρει κάποιος στη φάση 5». Τα βρήκε ο χρήστης, στο κινητό, σε
       σκούρο: η κάψουλα του υποτμήματος έμενε ανοιχτόχρωμη — λευκό πλακίδιο
       μέσα σε σκούρη σελίδα — επειδή το --t-gray100 ήταν #f3f4f6 και τίποτα
       δεν το άλλαζε ποτέ. Ένα σχόλιο δεν είναι μηχανισμός.

       Το mobile app είναι παγωμένο, άρα αυτό ΔΕΝ είναι πια ζωντανός
       καθρέφτης — είναι το ιστορικό του, και ο συγχρονισμός δεν οφείλεται
       σε κανέναν μέχρι να ξεπαγώσει. */
    --t-white: var(--surface-card);
    --t-gray100: var(--surface-subtle-2);
    --t-gray300: var(--rule-soft);
    /* --t-bg και --t-gray400 βγήκαν: κανένας κανόνας σε αυτό το αρχείο δεν τα
       διάβαζε. Ένα token που δεν ζητά κανείς δεν είναι token, είναι χρέος —
       και στο σκούρο θα ήταν δύο ακόμα τιμές να συντηρούνται στα τυφλά. */
    --t-card-border: var(--border-neutral);
    --t-ink: var(--ink);
    --t-ink-soft: var(--ink-soft);
    --t-muted: var(--ink-muted);
    --t-blue: var(--brand-navy);
    --t-teal: var(--brand-teal);

    /* Ύψη που στο RN είναι ΔΗΛΩΜΕΝΑ, όχι μετρημένα. Ο λόγος είναι ο ίδιος και
       εδώ: δύο τιμές που πρέπει να συμφωνούν δεν γίνεται να προκύπτουν από δύο
       μετρήσεις. */
    /* Η φυσαλίδα καλύπτει ΕΙΚΟΝΙΔΙΟ ΚΑΙ ΕΤΙΚΕΤΑ, όχι μόνο το εικονίδιο.
       Μετρημένο στη μπάρα, σε pixel διάταξης: η πλατύτερη ετικέτα είναι 70px
       («ΠΕΡΙΣΣΟΤΕΡΑ», όχι το «DASHBOARD» που φαίνεται μεγαλύτερο), το κουμπί
       101px, και το περιεχόμενο από την κορυφή του εικονιδίου ως τη βάση της
       ετικέτας 31,5px. Άρα 92×44 αφήνει 11px αέρα δεξιά-αριστερά από τη
       μακρύτερη λέξη και 6px πάνω-κάτω, και μένει 4,5px μέσα από την άκρη του
       κουμπιού ώστε δύο γειτονικά tabs να μην ακουμπάνε.

       Ήταν 52×28 — ακριβώς γύρω από το εικονίδιο. Αποκλίνει πλέον από τα
       tabBubbleW/H του theme.ts της εφαρμογής· η εφαρμογή είναι στον πάγο και
       όταν ξεπαγώσει παίρνει αυτά τα νούμερα. */
    --t-bubble-w: 92px;    /* theme.ts             tabBubbleW */
    --t-bubble-h: 44px;    /* theme.ts             tabBubbleH */
    --t-px: 16px;          /* theme.ts             px (το περιθώριο σελίδας) */
    --t-capsule-h: 36px;   /* SubDeptSelector.tsx  CAPSULE_H */
    --t-tray-h: 50px;      /* OverviewScreen.tsx   s.controlTray */
    --t-header-h: 64px;    /* index.html           header h-16 */
    --t-tabbar-h: 56px;    /* BottomTabBar.tsx */

    /* Το ύψος της μπάρας impersonation, ΜΕΤΡΗΜΕΝΟ και γραμμένο εδώ από το
       logic/impersonation.js — δεν είναι σταθερά και δεν μπορεί να είναι:
       στα 390px το κείμενο αναδιπλώνεται και η μπάρα ψηλώνει.
       0 όταν δεν υπάρχει μπάρα, που είναι η περίπτωση για όλους εκτός από
       δύο ανθρώπους. Δες το `main` παρακάτω. */
    --t-impbar-h: 0px;

    /* Οι σκιές, αυτούσιες από τα αντίστοιχα components. */
    /* Σκιές: σε σκούρο μια σκιά από μελάνι δεν φαίνεται, οπότε τις παίρνουν
       τα ίδια tokens που τις ξαναγράφουν εκεί. */
    --t-shadow-raised: var(--shadow-raised-mobile);
    --t-shadow-tray: var(--shadow-tray-mobile);
}

/* Το tabbar υπάρχει στο DOM πάντα· φαίνεται μόνο κάτω από τα 768px. Χωρίς
   αυτόν τον κανόνα ένα <nav> είναι `display: block` και θα εμφανιζόταν στο
   desktop κάτω από το περιεχόμενο. */
#mobile-tabbar { display: none; }

/* ===========================================================================
   Ο ΔΙΣΚΟΣ ΤΩΝ ΕΛΕΓΧΩΝ — ΚΙΝΗΤΟ **ΚΑΙ** TABLET

   Ζούσε μέσα στο `@media (max-width: 767.98px)` παρακάτω, δηλαδή ο δίσκος
   υπήρχε ως σχήμα μόνο σε κινητό. Σε tablet το app έβγαζε το κουμπί του
   μπόνους χωρίς θήκη και χωρίς τον μήνα δίπλα του: ένα chip να αιωρείται
   πάνω στο ground, ακριβώς το «δύο αντικείμενα που τυχαίνει να είναι σε μια
   σειρά» που αυτή η ενότητα είχε διορθώσει στο κινητό.

   Δεν μπήκε δεύτερο breakpoint. Η ΠΥΛΗ ΕΙΝΑΙ ΤΟ ΠΕΡΙΕΧΟΜΕΝΟ: `:has(.month-native)`
   σημαίνει «αυτός ο δίσκος κρατάει όντως τον επιλογέα μήνα». Το ποιος τον
   κρατάει το αποφασίζει ΕΝΑ σημείο, το App.Chrome.isCompact() στο
   logic/ui-utils.js, και το στυλ ακολουθεί το αποτέλεσμα αντί να το μαντεύει
   δεύτερη φορά με δικό του πλάτος. Έτσι δεν γίνεται ποτέ να έχουμε δίσκο με
   μήνα και χωρίς θήκη, ή θήκη γύρω από σκέτο chip σε desktop.

   Τα `.month-native` δεν χρειάζονται πύλη: το στοιχείο δεν υπάρχει καθόλου
   όταν ο δίσκος δεν τον κρατάει.
   ========================================================================= */

/* =======================================================================
   #2  Ο ΔΙΣΚΟΣ ΤΩΝ ΕΛΕΓΧΩΝ
   καθρέφτης: mobile/src/screens/OverviewScreen.tsx  s.controlTray

   Ήταν δύο αντικείμενα που απλώς τύχαινε να είναι σε μια σειρά, το καθένα
   με δικό του σχήμα, να αιωρούνται πάνω στο ground. Τώρα είναι ΕΝΑ πράγμα
   — μια επιφάνεια που λέει «από εδώ ρυθμίζεις τι βλέπεις», με την ίδια ύλη
   που έχουν οι κάρτες από κάτω.

   Η ΘΕΣΗ: το φίλτρο αριστερά, η ενέργεια δεξιά πέρα από το διαχωριστικό,
   κατά το HIG. Όταν η θέση λέει τη διαφορά, το χρώμα τη λέει δεύτερη φορά.
   ===================================================================== */
/* 🔴 ΤΟ ΕΞΩΤΕΡΙΚΟ ROW ΔΕΝ ΕΙΝΑΙ Ο ΔΙΣΚΟΣ.
   Πρώτη εκδοχή: όλη η σειρά γινόταν δίσκος, με το badge του agent μέσα.
   Δύο λάθη μαζί. Σημασιολογικά, ο δίσκος λέει «από εδώ ρυθμίζεις τι
   βλέπεις» — το badge λέει «αυτό βλέπεις», που είναι ανάγνωσμα και όχι
   έλεγχος, και δεν έχει θέση μέσα του. Και πρακτικά, τα δύο μοιράζονταν
   390px και κανένα δεν είχε αρκετό.

   Τώρα ο δίσκος είναι ΤΟ SLOT: πλήρες πλάτος, μήνας αριστερά, bonus δεξιά.
   Το badge κάθεται σε δική του γραμμή από πάνω. */
main .context-tray:has(.month-native) {
    display: block;
    margin-bottom: 0;
}

/* Το badge του agent — μόνο αυτό μπορεί να είναι το πρώτο παιδί. */
main .context-tray:has(.month-native) > .inline-flex {
    margin-bottom: 10px;
}

/* Ο ΔΙΣΚΟΣ. Η εξειδίκευση (0,2,1) είναι που νικά το `ml-auto` του
   markup — βλ. κανόνα 5 στην κορυφή. */
main .context-tray:has(.month-native) > [data-bonus-trigger-slot] {
    margin-left: 0;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-height: var(--t-tray-h);
    padding: 0 6px;
    margin-bottom: 20px;
    background: var(--t-white);
    border: 1px solid var(--t-card-border);
    border-radius: 12px;
    box-shadow: var(--t-shadow-tray);
}

/* Χωρίζει το ΦΙΛΤΡΟ από την ΕΝΕΡΓΕΙΑ — σαν περίγραμμα στο δεύτερο παιδί
   και όχι ως ::before στο slot: το slot γεμίζει από το bonus-mount.js
   ΜΕΤΑ το render, οπότε ένα ψευδοστοιχείο στην αρχή του θα κατέληγε
   αριστερά του μήνα αντί ανάμεσα στα δύο. */
main .context-tray:has(.month-native) > [data-bonus-trigger-slot] > *:not(:first-child) {
    border-left: 1px solid var(--t-card-border);
    padding-left: 10px;
}

/* Ο επιλογέας μήνα μέσα στον δίσκο δεν είναι πια κάρτα — ο δίσκος είναι η
   επιφάνεια. Και το βελάκι πάει ΑΡΙΣΤΕΡΑ: όταν ο έλεγχος κάθεται στο
   leading άκρο, το σήμα «αυτό ανοίγει» πρέπει να συναντά πρώτο το μάτι.

   Η λίστα είναι ΤΟΥ ΣΥΣΤΗΜΑΤΟΣ (ένα `<select>` από πάνω, διάφανο)· το
   κουμπί είναι δικό μας. Βλ. MonthSelectorNative. */
main .month-native {
    gap: 7px;
    padding: 0 8px;
    height: 34px;
}

.month-native > i {
    color: var(--t-blue);
    font-size: 12px;
    pointer-events: none;
}

.month-native > span {
    font-size: 13px;
    font-weight: 900;
    letter-spacing: .04em;
    color: var(--t-ink);
    /* Χωρίς `text-transform`: τα κεφαλαία τα φτιάχνει η MonthSelector.js,
       γιατί το <option> δεν μπορεί να στυλαριστεί. Δύο μηχανισμοί για την
       ίδια δουλειά είναι δύο ευκαιρίες να διαφωνήσουν. */
    font-variant-numeric: tabular-nums;
    pointer-events: none;
}

/* Καλύπτει ΟΛΟΚΛΗΡΟ το control, όχι μόνο το κείμενο: το χτύπημα οπουδήποτε
   πάνω στο κουμπί πρέπει να ανοίγει τον επιλογέα.

   `font-size: 16px` αν και αόρατο — το iOS μεγεθύνει τη σελίδα όταν
   εστιάζει σε control με μικρότερα γράμματα, και το μετράει στο ΠΡΑΓΜΑΤΙΚΟ
   στοιχείο, όχι σε ό,τι βλέπει ο χρήστης. */
.month-native > select {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    font-size: 16px;
    -webkit-appearance: none;
    appearance: none;
    border: 0;
    background: transparent;
}

/* Το outline πρέπει να φανεί στο ΟΡΑΤΟ κουμπί, όχι στο αόρατο select. */
.month-native:focus-within {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
    border-radius: 8px;
}

/* ── Ο ΔΕΙΚΤΗΣ ΤΟΥ BONUS ────────────────────────────────────────────
   καθρέφτης: mobile/src/components/BonusCard.tsx  BonusCardTrigger

   Ήταν γεμάτο πλακίδιο με radial gradient, περίγραμμα και inset highlight
   — το πιο έντονα χρωματισμένο αντικείμενο της οθόνης. Ο τότε λόγος ήταν
   σωστός: «είναι το μόνο control της σειράς που ΑΝΟΙΓΕΙ κάτι αντί να
   ΦΙΛΤΡΑΡΕΙ». Αυτό όμως το λέει πλέον η ΘΕΣΗ — τελευταίο στον δίσκο, πέρα
   από το διαχωριστικό, εκεί που το HIG βάζει τις ενέργειες. Όταν η θέση το
   λέει ήδη, το χρώμα το λέει δεύτερη φορά.

   Το `font-weight: 400` ΔΕΝ είναι τυπογραφία: στο Font Awesome 6 το βάρος
   ΕΙΝΑΙ η παραλλαγή. 900 = solid, 400 = regular (περίγραμμα). Έτσι το ίδιο
   `fa-credit-card` του markup γίνεται outline εδώ και μένει solid στο
   desktop, χωρίς δεύτερο εικονίδιο και χωρίς αλλαγή στο κοινό αρχείο. */
main .context-tray:has(.month-native) [data-bonus-trigger] {
    width: 34px;
    height: 34px;
    min-width: 0;
    aspect-ratio: auto;
    align-self: center;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
}

main .context-tray:has(.month-native) [data-bonus-trigger] i {
    color: var(--t-blue);
    font-size: 19px;
    font-weight: 400;
}

/* 🔴 ΤΟ ΣΗΜΑ LIVE ΣΤΗ ΘΕΣΗ ΤΟΥ. Η κουκκίδα ήταν μονίμως ΛΕΥΚΗ με τιρκουάζ
   δαχτυλίδι, πάνω σε μονίμως τιρκουάζ πλακίδιο — δηλαδή το `teal` ήταν
   εκεί ό,τι κι αν έτρεχε. Ο κανόνας του v2 λέει ότι το teal είναι το ΣΗΜΑ
   LIVE και τίποτε άλλο. Τώρα το γλυφό είναι μπλε και τιρκουάζ εμφανίζεται
   ΜΟΝΟ όταν ο μήνας τρέχει — που είναι και η μόνη στιγμή που η bonus.js
   ξεκρύβει αυτή την κουκκίδα.

   Το λευκό δαχτυλίδι δεν είναι στολίδι: χωρίς αυτό η κουκκίδα ακουμπά τη
   γωνία του γλυφού και διαβάζεται ως μέρος του σχεδίου της κάρτας. */
main .context-tray:has(.month-native) [data-bonus-trigger] .bonus-trigger-dot {
    top: 4px;
    right: 4px;
    background: var(--t-teal);
    box-shadow: 0 0 0 1.5px var(--t-white);
}

@media (max-width: 767.98px) {

    /* =======================================================================
       #1  ΤΑ BLOCKS ΩΣ ΜΙΑ ΚΑΨΟΥΛΑ
       καθρέφτης: mobile/src/components/SubDeptSelector.tsx

       Ήταν N ανεξάρτητα πλακίδια με κουκκίδες και «│» ανάμεσα. N πλακίδια σε
       σειρά δηλώνουν N ανεξάρτητες επιλογές που τυχαίνει να είναι δίπλα-δίπλα.
       Μία κάψουλα δηλώνει ΕΝΑ control με N καταστάσεις — δηλαδή ότι διαλέγεις
       ΑΚΡΙΒΩΣ ένα, που είναι η αλήθεια εδώ.

       🔴 Και διορθώνει σφάλμα ακτίνας: τα pills ήταν `rounded-sm` (2px) χωρίς
       `data-v2-control`, δηλαδή 2px ανάμεσα σε κάρτες 12px και controls 7px.
       Το ίδιο το v2 (KPICard.js) το ονομάζει «a rendering fault rather than a
       decision».
       ===================================================================== */
    #subdept-selector-bar {
        display: flex;
        align-items: center;
        gap: 0;
        height: var(--t-capsule-h);
        padding: 3px;
        border-radius: 9999px;
        background: var(--t-gray100);
        border: 1px solid var(--t-card-border);
        /* Το κόψιμο στο χείλος ΕΙΝΑΙ το σήμα ότι υπάρχει κι άλλο δεξιά. */
        overflow-x: auto;
        overflow-y: hidden;
        flex-wrap: nowrap;
        scrollbar-width: none;
    }

    #subdept-selector-bar::-webkit-scrollbar { display: none; }

    /* Ως 4 επιλογές απλώνονται· από 5 και πάνω κυλούν. Το κατώφλι ΔΗΛΩΝΕΤΑΙ
       από τη JS ως `data-count`, δεν μετριέται με :has() — το ίδιο νούμερο
       (SPREAD_MAX) ζει στο SubDeptSelector.tsx. */
    #subdept-selector-bar[data-count="1"] .subdept-pill,
    #subdept-selector-bar[data-count="2"] .subdept-pill,
    #subdept-selector-bar[data-count="3"] .subdept-pill,
    #subdept-selector-bar[data-count="4"] .subdept-pill {
        flex: 1 1 0;
        min-width: 0;
    }

    /* Διπλή κλάση: `px-3 py-1.5` και `rounded-sm` κάθονται στο ίδιο στοιχείο
       και θα κέρδιζαν σε ισοπαλία. Βλ. κανόνα 5 στην κορυφή. */
    .subdept-pill.subdept-pill {
        height: calc(var(--t-capsule-h) - 8px);
        padding: 0 12px;
        border-radius: 9999px;
        background: transparent !important;
        justify-content: center;
        white-space: nowrap;
        flex: 0 0 auto;
    }

    /* Ξεχωρίζει από την ΥΛΗ, όχι από το χρώμα: λευκό φύλλο υψωμένο μέσα σε
       γκρι αυλάκι. Τα ανενεργά δεν έχουν καθόλου φόντο — σκέτο κείμενο, όπως
       πρέπει να είναι κάτι που δεν έχει επιλεγεί. */
    .subdept-pill.is-active {
        background: var(--t-white) !important;
        box-shadow: var(--t-shadow-raised);
    }

    /* Η κουκκίδα ήταν ο μόνος δείκτης επιλογής· τώρα το λέει η ύλη. Μια
       κουκκίδα που δεν διακρίνει τίποτα είναι θόρυβος. */
    .subdept-pill > span:first-child,
    #subdept-selector-bar > .subdept-divider {
        display: none;
    }

    .subdept-pill > span:last-child {
        font-size: 10px;
        letter-spacing: .08em;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* ── N = 1: ετικέτα, όχι control ─────────────────────────────────────
       καθρέφτης: SubDeptSelector.tsx  s.label / s.labelText

       Η κάψουλα παραπάνω πιάνει ΚΑΙ αυτή την περίπτωση, και σωστά: στην
       εφαρμογή η ετικέτα έχει ΕΠΙΤΗΔΕΣ ίδια γεωμετρία με την κάψουλα, ώστε η
       μπάρα να μην αλλάζει σχήμα ανάλογα με το πόσα blocks έχει ο καθένας.

       Αυτό που δεν έπιανε είναι η κουκκίδα. Στο N=1 δεν είναι παιδί κάποιου
       `.subdept-pill`, άρα γλίτωσε από τον κανόνα από πάνω — και μόλις το
       `gap: 0` της κάψουλας ακύρωσε το `gap-2` του markup (κανόνας 5: το ID
       κερδίζει το utility), κόλλησε πάνω στο κείμενο.

       Δεν της δίνουμε πίσω το κενό: στην εφαρμογή ΔΕΝ υπάρχει κουκκίδα εδώ
       (`s.label` είναι σκέτο κείμενο σε κάψουλα). Ο λόγος είναι ο ίδιος με τα
       pills, μόνο πιο καθαρός — μια κουκκίδα που δείχνει τη μία και μοναδική
       επιλογή δεν διακρίνει τίποτα. */
    #subdept-selector-bar[data-count="1"] {
        justify-content: center;
        padding: 0 14px;       /* s.label      paddingHorizontal */
    }

    #subdept-selector-bar[data-count="1"] > span:first-child {
        display: none;
    }

    #subdept-selector-bar[data-count="1"] > span:last-child {
        font-size: 10px;       /* s.labelText  fontSize */
        letter-spacing: .1em;  /* s.labelText  letterSpacing: 1 στα 10px */
    }

    /* =======================================================================
       #3  Η ΕΠΙΚΕΦΑΛΙΔΑ ΤΑΥΤΟΤΗΤΑΣ
       καθρέφτης: mobile/src/components/IdentityHeader.tsx

       Το web ήταν ήδη οριζόντιο breadcrumb — δεν είχε το κατακόρυφο δέντρο με
       εσοχές που έφυγε από την εφαρμογή. Έμεναν τρία:

         • `fa-sitemap`: κυριολεκτικά το γλυφό «χάρτης ιστότοπου». Αυτό που
           δείχνει είναι ΑΝΘΡΩΠΟΙ.
         • Το όνομα στα 12px ΚΕΦΑΛΑΙΑ — μικρότερο από τις ετικέτες των καρτών
           που ακολουθούν, και σε κεφαλαία, δηλαδή δυσανάγνωστο και φωναχτό.
         • Ο ρόλος σε παρένθεση με opacity .6, σαν υποσημείωση. Μια παρένθεση
           λέει «παρεμπιπτόντως»· ο ρόλος ορίζει ΤΙ μετρούν τα νούμερα.

       Η άβαταρ ζωγραφίζεται με `content: attr(data-initials)` — δηλαδή ΔΕΝ
       υπάρχει καθόλου στο DOM του desktop. Ένα data attribute δεν ζωγραφίζει
       τίποτα από μόνο του, οπότε το desktop δεν το βλέπει ποτέ.
       ===================================================================== */
    /* Η εξειδίκευση εδώ είναι για το `.fas` του Font Awesome, που ορίζει κι
       αυτό `display` σε (0,1,0). Σήμερα κερδίζουμε λόγω σειράς φόρτωσης· με
       πρόθεμα δεν εξαρτιόμαστε από αυτήν. */
    .hb-bar .hb-icon { display: none; }

    .hb-bar {
        border: 0 !important;
        background: transparent !important;
        box-shadow: none !important;
        padding: 0 !important;
        margin-bottom: 18px !important;
        display: block !important;
    }

    .hb-bar .hb-trail {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 5px;
        overflow: visible !important;
        white-space: normal !important;
    }

    /* Οι πρόγονοι: μία γραμμή, μικρά, μπλε γιατί πατιούνται. */
    .hb-step:not(:last-child) .hb-name { font-size: 11px; }
    .hb-step:not(:last-child) .hb-role { display: none; }
    .hb-step:not(:last-child) button {
        text-transform: none !important;
        letter-spacing: 0 !important;
        font-size: 11px !important;
        color: var(--t-blue) !important;
    }
    .hb-sep { color: var(--t-gray300) !important; }

    /* Το ΤΡΕΧΟΝ πρόσωπο πέφτει σε δική του γραμμή, με την άβαταρ. */
    .hb-step.hb-step:last-child {
        flex: 0 0 100%;
        align-items: center;
        gap: 12px;
        margin-top: 8px;
    }

    .hb-step.hb-step:last-child::before {
        content: attr(data-initials);
        flex: 0 0 auto;
        width: 42px;
        height: 42px;
        border-radius: 9999px;
        display: flex;
        align-items: center;
        justify-content: center;
        /* Wash, όχι συμπαγές γέμισμα: με πλήρες μπλε ο κύκλος θα ήταν το πιο
           σκούρο αντικείμενο της οθόνης, βαρύτερος από κάθε νούμερο KPI. */
        background: var(--brand-wash);
        border: 1px solid var(--brand-wash-border);
        color: var(--t-blue);
        font-size: 14px;
        font-weight: 900;
        letter-spacing: .5px;
    }

    .hb-current {
        display: flex;
        flex-direction: column;
        min-width: 0;
        text-transform: none !important;
        letter-spacing: normal !important;
    }

    /* 17px: το μόνο κείμενο αυτού του μεγέθους πάνω από τις κάρτες. */
    .hb-current .hb-name {
        font-size: 17px;
        font-weight: 900;
        color: var(--t-ink);
        letter-spacing: -.3px;
        line-height: 1.2;
    }

    /* Ο ρόλος βγαίνει από την παρένθεση και γίνεται υπότιτλος. Οι παρενθέσεις
       κρύβονται με CSS ώστε το markup να μείνει ένα για τις δύο διατάξεις. */
    .hb-current .hb-role {
        font-size: 11px;
        font-weight: 700;
        color: var(--t-muted);
        opacity: 1 !important;
        letter-spacing: .3px;
        margin-top: 2px;
    }
    .hb-role .hb-paren { display: none; }

    /* Το κλάσμα της ομάδας: δική του γραμμή, ΑΡΙΣΤΕΡΑ.
       Το `ml-auto` του markup είναι για το desktop, όπου το κλάσμα κάθεται στο
       δεξί άκρο μιας μονής γραμμής. Εδώ η γραμμή είναι δική του, οπότε «δεξιά»
       σημαίνει απλώς «μακριά από όλα τα άλλα» — το μάτι που μόλις διάβασε το
       όνομα πρέπει να γυρίσει στην αρχή της γραμμής για το επόμενο πράγμα.

       Η εξειδίκευση εδώ ΔΕΝ είναι διακοσμητική: `.hb-team` σκέτο έχει ίση
       εξειδίκευση με `.ml-auto`, άρα έχανε — γι' αυτό το κλάσμα έμενε δεξιά
       παρά τον κανόνα. Βλ. κανόνα 5 στην κορυφή. */
    .hb-bar .hb-team {
        margin-left: 0;
        padding-left: 0;
        margin-top: 8px;
        flex-basis: 100%;
    }

    /* =======================================================================
       #4  ΤΟ CHROME ΠΟΥ ΥΠΟΧΩΡΕΙ
       καθρέφτης: mobile/src/components/ChromeBackdrop.tsx

       Στην κορυφή η κεφαλίδα είναι συμπαγής και διαβάζεται ως πλαίσιο πάνω από
       τη σελίδα. Μόλις αρχίσεις να κυλάς, το ΦΟΝΤΟ σβήνει και μένουν μόνο τα
       ίδια τα στοιχεία — ο τίτλος, τα λογότυπα — να επιπλέουν πάνω στο
       περιεχόμενο που ανεβαίνει από πίσω.

       🔴 Για να έχει τι να περάσει από πίσω, η κεφαλίδα ΠΡΕΠΕΙ να επικαλύπτει
       τον scroller. Στο desktop είναι αδερφός του <main> σε flex στήλη, άρα
       τίποτα δεν περνά ποτέ από κάτω της και το σβήσιμο δεν θα έδειχνε παρά
       το κενό. Εδώ γίνεται absolute και το <main> αποκτά padding ίσο με το
       ύψος της — η ίδια σχέση που έχει το `headerTransparent` στο RN.

       ΔΕΝ είναι γυαλί, και στην εφαρμογή επίσης δεν είναι: animated opacity
       πάνω σε blur δείχνει θολό-πάνω-σε-καθαρό στα ενδιάμεσα καρέ, που
       διαβάζεται ως σφάλμα. */
    header {
        position: absolute !important;
        top: 0;
        left: 0;
        right: 0;

        /* 🔴 Η ΚΕΦΑΛΙΔΑ ΦΕΥΓΕΙ — ΤΟ <main> ΘΟΛΩΝΕΙ ΣΤΗ ΖΩΝΗ ΠΟΥ ΑΔΕΙΑΣΕ.
           Πρώτη εκδοχή: alpha 1 → 0 και τίποτε άλλο. Η κεφαλίδα έφευγε σωστά,
           αλλά ένα «45.8» καρτας περνούσε ΩΜΟ πίσω από το λογότυπο. Αυτό που
           έλειπε δεν ήταν πέπλο, ήταν θόλωμα: στην εφαρμογή πίσω από την
           κεφαλίδα υπάρχει ΚΑΙ το γυαλί του iOS 26 στις κάψουλες των
           λογοτύπων· εδώ δεν υπήρχε τίποτα να πιάσει το περιεχόμενο.

           Το `backdrop-filter` θολώνει ό,τι περνά από πίσω, ακριβώς στο ύψος
           της κεφαλίδας. ΕΙΝΑΙ διαθέσιμο στο κινητό web — το χρησιμοποιεί ήδη
           το login — και είναι η μία θέση όπου έχει νόημα: από πίσω περνούν
           ΚΑΡΤΕΣ, δηλαδή υπάρχει πραγματική αντίθεση να θολώσει. (Στο
           ChromeBackdrop του RN δεν μπαίνει γυαλί για αντίστροφο λόγο: εκεί το
           φόντο είναι ομοιόμορφο ground, και θόλωμα ομοιόμορφου χρώματος δίνει
           το ίδιο ακριβώς χρώμα.)

           Το πέπλο πέφτει στο .18 — ψίθυρος, όχι επιφάνεια. Σκέτο blur πάνω σε
           χρωματιστή κάρτα αφήνει το κείμενο της κεφαλίδας χωρίς δάπεδο· ένα
           υλικό τύπου iOS είναι πάντα θόλωμα ΣΥΝ ελαφριά απόχρωση.

           Το θόλωμα είναι ΣΤΑΘΕΡΟ, δεν αυξάνεται με το scroll: στην κορυφή το
           πέπλο είναι αδιαφανές, άρα δεν φαίνεται έτσι κι αλλιώς, και ένα
           `backdrop-filter` που αλλάζει τιμή ανά καρέ κοστίζει ένα pass χωρίς
           να το δει κανείς. */
        /* 🔴 ΤΡΙΠΛΟ RGB, ΟΧΙ ΧΡΩΜΑ. Το alpha το οδηγεί το scroll (--chrome-alpha
           από τη menu.js), άρα το υλικό ΔΕΝ μπορεί να είναι ένα token χρώματος
           — θα έσβηνε τη μεταβλητή που κινείται. Ήταν καρφωτό λευκό, και αυτό
           ήταν ο λόγος που η κεφαλίδα του κινητού έμενε ΑΝΟΙΧΤΗ σε σκούρο θέμα
           ενώ το περιεχάμενο από κάτω ήταν σκούρο: μόνο το alpha ήταν
           μεταβλητό, το χρώμα ποτέ. Ίδια λύση με το γυαλί του login. */
        background: rgba(var(--chrome-rgb), var(--chrome-alpha, 1)) !important;

        /* 🔴 ΚΑΜΙΑ ΤΡΙΧΑ ΣΤΟ ΚΙΝΗΤΟ — ΟΥΤΕ ΣΤΗΝ ΚΟΡΥΦΗ.
           Πρώτη εκδοχή: το χρώμα έσβηνε με δικό του κανάλι (`--chrome-edge`)
           ενώ το πλάτος έμενε 1px. Σωστό στα χαρτιά — `rgba(...,0)` δεν
           ζωγραφίζει — αλλά κρατούσε 1px ΔΙΑΤΑΞΗΣ στο κάτω χείλος, και μαζί
           του ένα σημείο όπου η σύνθεση αλλάζει απότομα.

           Και δεν χρειάζεται καν: στην κορυφή η κεφαλίδα ξεχωρίζει από τη
           σελίδα επειδή είναι συμπαγές ΥΛΙΚΟ πάνω στο φόντο της· στο scroll
           ξεχωρίζει από το θόλωμα. Μια τρίχα που δεν χωρίζει τίποτα είναι
           ακριβώς η γραμμή που ενοχλεί.

           Το `--chrome-edge` μένει στη menu.js — δεν κοστίζει τίποτα και
           είναι εκεί αν χρειαστεί ξανά χείλος. */
        border-bottom: 0 !important;
    }

    /* ── ΤΟ ΘΟΛΩΜΑ ΕΙΝΑΙ ΒΑΘΜΙΑΙΟ ────────────────────────────────────────
       Ένα σκέτο `backdrop-filter` πάνω στην κεφαλίδα θολώνει ΟΜΟΙΟΜΟΡΦΑ και
       κόβει με μαχαίρι στο χείλος της: φαίνεται ευθεία γραμμή εκεί που
       τελειώνει το θόλωμα, που είναι χειρότερη από το να μην υπήρχε καθόλου.

       Η λύση είναι ΔΥΟ ΣΤΡΩΜΑΤΑ με `mask-image`. Το mask δεν κρύβει
       περιεχόμενο — σβήνει το ΙΔΙΟ ΤΟ ΣΤΡΩΜΑ, μαζί με το θόλωμά του, σε
       ντεγκραντέ. Δύο στρώματα και όχι ένα, γιατί ένα μόνο σβήνει την ΕΝΤΑΣΗ
       ομοιόμορφα ενώ εμείς θέλουμε η ΑΚΤΙΝΑ να μικραίνει προς τα κάτω:

         ::after   βαρύ θόλωμα (22px), ζει μόνο στο πάνω ~70%
         ::before  ελαφρύ (6px), φτάνει ως το κάτω χείλος και σβήνει εκεί

       Το ::after κάθεται πάνω από το ::before, άρα στην κορυφή τα δύο
       θολώματα ΑΘΡΟΙΖΟΝΤΑΙ και προς τα κάτω μένει μόνο το ελαφρύ. Καμία
       ευθεία πουθενά.

       🔴 `z-index: -1` μέσα στο stacking context της κεφαλίδας (το φτιάχνει
       το `z-40` με το `position`): τα ψευδοστοιχεία ζωγραφίζονται ΜΕΤΑ το
       φόντο και ΠΡΙΝ τα παιδιά. Χωρίς αυτό ένα absolute ψευδοστοιχείο
       ζωγραφίζεται πάνω από τα μη-τοποθετημένα παιδιά και θα θόλωνε το ίδιο
       το λογότυπο. Και δεν μπορούμε να δώσουμε `position` στα παιδιά για να
       το λύσουμε: ο τίτλος κεντράρεται με `left: 50%` ως προς την ΚΕΦΑΛΙΔΑ,
       και ένας τοποθετημένος γονιός θα του άλλαζε το containing block.

       🔴 ΚΑΙ ΤΑ ΔΥΟ ΣΤΡΩΜΑΤΑ ΞΕΠΕΡΝΟΥΝ ΤΟ ΚΑΤΩ ΧΕΙΛΟΣ ΚΑΤΑ 28px.
       Αυτό είναι που έσβησε τη γραμμή που έμενε. Το `backdrop-filter`
       δειγματοληπτεί ΜΟΝΟ μέσα στα όρια του στοιχείου και σφίγγει στα άκρα
       (edge clamping): στο κάτω χείλος το θόλωμα σταματά απότομα, και όσο
       ομαλό κι αν είναι το mask, η ασυνέχεια πέφτει ακριβώς εκεί. Σε WebKit
       το φαινόμενο είναι εντονότερο, γιατί το mask δεν περιορίζει πάντα το
       αποτέλεσμα του backdrop-filter.

       Με τα στρώματα να φτάνουν 28px πιο κάτω, το σκληρό χείλος βρίσκεται
       εκεί όπου το mask έχει ΗΔΗ μηδενιστεί — δηλαδή δεν ζωγραφίζεται
       τίποτα να κοπεί. Τα ποσοστά των gradients είναι ξαναυπολογισμένα πάνω
       στο νέο ύψος (64 + 28 = 92px):
           βαρύ:  22% → 20px,  σβήνει στο 49% → 45px
           ελαφρύ: 62% → 57px, σβήνει στο 88% → 81px, πριν το χείλος
       `pointer-events: none` ήταν ήδη εκεί, άρα τα 28px δεν κλέβουν αγγίγματα
       από το περιεχόμενο. */
    header::before,
    header::after {
        content: "";
        position: absolute;
        inset: 0 0 -28px 0;
        z-index: -1;
        pointer-events: none;
    }

    header::before {
        /* Η απόχρωση του ΥΛΙΚΟΥ — σταθερή, και σβήνει με το ίδιο mask που
           σβήνει το θόλωμα. Χωριστή από το πέπλο της κεφαλίδας (που είναι
           scroll-linked και πάει στο 0): ένα υλικό τύπου iOS είναι θόλωμα ΣΥΝ
           ελαφριά απόχρωση, και αν η απόχρωση ζούσε στην κεφαλίδα θα έκοβε
           ευθεία στο χείλος της όσο αχνή κι αν ήταν. */
        background: var(--chrome-tint);
        -webkit-backdrop-filter: blur(6px) saturate(140%);
        backdrop-filter: blur(6px) saturate(140%);
        -webkit-mask-image: linear-gradient(to bottom, #000 0%, rgba(0, 0, 0, .55) 62%, transparent 88%);
        mask-image: linear-gradient(to bottom, #000 0%, rgba(0, 0, 0, .55) 62%, transparent 88%);
    }

    header::after {
        -webkit-backdrop-filter: blur(22px);
        backdrop-filter: blur(22px);
        -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 22%, transparent 49%);
        mask-image: linear-gradient(to bottom, #000 0%, #000 22%, transparent 49%);
    }

    /* Χωρίς backdrop-filter (παλιός Firefox, κάποια embedded webviews) δεν
       υπάρχει τίποτα να κρατήσει το κείμενο πάνω από τις κάρτες. Συμπαγές
       λευκό: χάνεται το εφέ, όχι η αναγνωσιμότητα. */
    @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
        header { background: var(--t-white) !important; }
        header::before,
        header::after { display: none; }
    }

    /* ── Η ΜΠΑΡΑ IMPERSONATION ΒΓΑΙΝΕΙ ΚΙ ΑΥΤΗ ΕΚΤΟΣ ΡΟΗΣ ───────────────
       Κάτω από τα 768px το `header` γίνεται `position: absolute` ώστε το
       περιεχόμενο να κυλάει από κάτω του (δες το veil παραπάνω). Το
       `#impersonation-bar` είναι αδελφός ΜΕΣΑ στη ροή, ανάμεσα στο header
       και στο `main` — οπότε ξεκινούσε στο `top: 0` και το header, με
       z-index 40, το σκέπαζε ΟΛΟΚΛΗΡΟ. 73 pixels μπάρας, αόρατα.

       Στο desktop δεν φαινόταν ποτέ το πρόβλημα: εκεί το header είναι μέσα
       στη ροή και η μπάρα κάθεται από κάτω του κανονικά.

       Μπαίνει στο ίδιο σχήμα με το header — απόλυτη, ακριβώς από κάτω του.
       Κοινό containing block: ο wrapper `flex-1 flex flex-col ... relative`
       του index.html. Άρα δεν κυλάει, που είναι ακριβώς ο λόγος που η μπάρα
       μπήκε έξω από το `main` εξαρχής.

       z-index 39, ένα κάτω από το header: η παγωμένη άκρη του header
       ζωγραφίζεται ΠΑΝΩ στη μπάρα, αντί η μπάρα να την κόβει. */
    #impersonation-bar {
        position: absolute;
        top: var(--t-header-h);
        left: 0;
        right: 0;
        z-index: 39;
    }

    main {
        /* +8 για αέρα: στο RN το `paddingTop` είναι σκέτο το ύψος της κεφαλίδας
           και το πρώτο στοιχείο φέρνει το δικό του περιθώριο· εδώ το `p-6` του
           Tailwind χάνεται από το override, οπότε ο αέρας δηλώνεται.

           Το --t-impbar-h είναι 0 για σχεδόν όλους. Όταν δεν είναι, το
           περιεχόμενο πρέπει να ξεκινά κάτω ΚΑΙ από τη μπάρα — αλλιώς η
           πρώτη κάρτα γεννιέται από κάτω της. */
        padding-top: calc(var(--t-header-h) + var(--t-impbar-h) + 8px) !important;

        /* ── ΤΟ ΠΕΡΙΘΩΡΙΟ ΣΕΛΙΔΑΣ: 24 → 16 ───────────────────────────────
           Το `p-6` του Tailwind είναι 24px ανά πλευρά· η εφαρμογή δίνει
           `paddingHorizontal: T.px`, δηλαδή 16. Οι κάρτες στο κινητό web ήταν
           32px στενότερες από τις ίδιες κάρτες στην εφαρμογή — αρκετά ώστε να
           φαίνεται, και χωρίς κανέναν λόγο: σε μονή στήλη (`grid-cols-1`) κάθε
           pixel του περιθωρίου βγαίνει κατευθείαν από το πλάτος της κάρτας.

           🔴 `!important` εδώ και όχι εξειδίκευση (κανόνας 5): το `main` είναι
           ΣΤΟΙΧΕΙΟ, και μια κλάση utility νικά πάντα οποιονδήποτε αριθμό
           στοιχείων. Χωρίς κλάση στο markup — που θα ήταν αλλαγή σε κοινό
           αρχείο για μηδέν κέρδος — το `!important` είναι ο μόνος δρόμος. */
        padding-left: var(--t-px) !important;
        padding-right: var(--t-px) !important;
    }

    /* =======================================================================
       #6  Η ΣΥΝΘΕΣΗ ΤΗΣ ΚΕΦΑΛΙΔΑΣ
       καθρέφτης: mobile/src/navigation/MainNavigator.tsx (headerLeft/Title/Right)

       CQS αριστερά, τίτλος ΚΕΝΤΡΟ, λογότυπο project δεξιά. Χωρίς το «│»: ένα
       διαχωριστικό ανάμεσα σε δύο πράγματα που ήδη απέχουν είναι τρίτο
       αντικείμενο που δεν λέει τίποτα.
       ===================================================================== */
    header #page-title {
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
        white-space: nowrap;
        pointer-events: none;
    }

    .header-divider { display: none; }

    /* Ο badge του τμήματος φεύγει από την κεφαλίδα: το ίδιο πράγμα το λέει
       ήδη η κάψουλα των blocks από κάτω, και δύο φορές σημαίνει ότι ο
       αναγνώστης πρέπει να αποφασίσει ποιο από τα δύο φιλτράρει. */
    header .tech-badge { display: none; }

    /* =======================================================================
       #7  Η ΚΑΤΩ ΜΠΑΡΑ
       καθρέφτης: mobile/src/components/BottomTabBar.tsx

       ΔΕΝ είναι `position: fixed`. Δεν χρειάζεται: το <body> έχει
       `overflow:hidden` και ύψος `100dvh`, άρα η σελίδα δεν κυλάει ποτέ και η
       μπάρα διεύθυνσης του browser δεν μαζεύει. Η μπάρα είναι απλώς τρίτο
       παιδί της flex στήλης με `flex: none`, και το <main> παίρνει ό,τι
       περισσεύει. Δεν υπάρχει επικάλυψη να συμβεί.
       ===================================================================== */
    /* ΔΟΚΙΜΑΣΤΗΚΕ ΚΑΙ ΑΠΟΡΡΙΦΘΗΚΕ (18/09/2026): η ύλη της εφαρμογής εδώ —
       πέπλο `tabVeil`→`tabVeilBottom`, τρίχα `tabHairline`, ανταύγεια
       `tabRim`, και λευκό φύλλο για φυσαλίδα.

       Στην εφαρμογή εκείνο το πέπλο είναι ΓΥΑΛΙ: η μπάρα είναι absolute και οι
       οθόνες περνούν από κάτω της, οπότε η κυανή απόχρωση κάθεται πάνω σε ό,τι
       κυλάει. Εδώ η μπάρα είναι στατική, στη ροή — πίσω της δεν περνά ποτέ
       τίποτα. Η ίδια απόχρωση χωρίς τίποτα να διαθλάσει γίνεται απλώς μια
       γαλάζια λωρίδα, και το λευκό φύλλο πάνω της ένα δεύτερο επίπεδο χωρίς
       λόγο. Το λευκό είναι εδώ η σωστή μετάφραση, όχι η φτωχή. */
    #mobile-tabbar {
        display: flex;
        flex: none;
        align-items: stretch;
        height: calc(var(--t-tabbar-h) + env(safe-area-inset-bottom));
        padding-bottom: env(safe-area-inset-bottom);
        background: var(--t-white);
        border-top: 1px solid var(--t-card-border);
        position: relative;
        z-index: 40;
    }

    .mobile-tab {
        flex: 1 1 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 3px;
        position: relative;
        z-index: 1;
        background: transparent;
        border: 0;
        color: var(--t-muted);
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
    }

    .mobile-tab i { font-size: 15px; }

    .mobile-tab span {
        font-size: 9px;
        font-weight: 900;
        letter-spacing: .06em;
        text-transform: uppercase;
    }

    .mobile-tab.is-active { color: var(--t-blue); }

    /* Η φυσαλίδα που γλιστράει.
       ΣΤΑΘΕΡΗ ΤΑΧΥΤΗΤΑ, όχι σταθερός χρόνος: τη διάρκεια την υπολογίζει η
       menu.js από την απόσταση, γιατί μια μετάβαση που κρατά το ίδιο είτε πας
       δίπλα είτε στο τέρμα δεν διαβάζεται ως κίνηση αντικειμένου.

       🔴 Το μέγεθος είναι ΔΗΛΩΜΕΝΟ (52×28, τα ίδια `tabBubbleW`/`tabBubbleH`
       του theme.ts) και η θέση υπολογίζεται από το ΕΙΚΟΝΙΔΙΟ, όχι από το
       κουμπί. Πρώτη εκδοχή: `top: 8px` σταθερό και πλάτος από το κουμπί — αλλά
       το κουμπί είναι flex στήλη (εικονίδιο, κενό, ετικέτα), άρα το κέντρο του
       πέφτει ΑΝΑΜΕΣΑ στα δύο. Η φυσαλίδα κατέληγε να κόβει το εικονίδιο στη
       μέση και να προεξέχει από πάνω του. Το κέντρο του εικονιδίου είναι το
       μόνο σημείο που δεν χρειάζεται να μαντευτεί. */
    #tabbar-bubble {
        position: absolute;
        top: 0;
        left: 0;
        width: var(--t-bubble-w);
        height: var(--t-bubble-h);
        border-radius: 9999px;
        background: var(--brand-wash-bubble);
        transition-property: transform;
        transition-timing-function: cubic-bezier(.33, 1, .68, 1);
        pointer-events: none;
        z-index: 0;
    }

    /* 🔴 ΤΟ SIDEBAR ΔΕΝ ΚΡΥΒΕΤΑΙ — ΑΛΛΑΖΕΙ ΔΟΥΛΕΙΑ.
       Η πλοήγηση περνά στην κάτω μπάρα, οπότε η λίστα των tabs μέσα στο
       συρτάρι φεύγει: δύο πλοηγήσεις για το ίδιο πράγμα σημαίνει ότι ο χρήστης
       πρέπει να μάθει ποια από τις δύο είναι η αληθινή.

       Το συρτάρι όμως ΜΕΝΕΙ, γιατί κρατά το προφίλ και το logout — και αυτά
       δεν έχουν άλλη θέση στο κινητό. Το ανοίγει πλέον το «Περισσότερα» της
       κάτω μπάρας, όχι ο hamburger. Αυτό ΕΙΝΑΙ το More sheet της εφαρμογής. */
    #sidebar-nav-links,
    #mobile-menu-toggle,
    #sidebar-edge-toggle {
        display: none !important;
    }

    /* Ο χώρος του hamburger παίρνει το σήμα CQS — μία φορά, εκεί που το
       περιμένει το μάτι. */
    .header-brand { display: flex !important; }

    /* =======================================================================
       #8  ΤΑ CHARTS ΑΠΛΩΝΟΥΝ ΚΑΙ ΚΥΛΟΥΝ
       καθρέφτης: — (δεν υπάρχει στην εφαρμογή· εκεί τα charts είναι δικά της
       components με δικό τους πλάτος)

       Σε πλάτος τηλεφώνου ο Chart.js έχει ~300px για έξι ή δώδεκα περιόδους.
       Ο προεπιλεγμένος `autoSkip` αρχίζει να πετάει ετικέτες και ο
       `maxRotation` να τις γέρνει — καταλήγεις με λοξούς μισούς μήνες. Καμία
       από τις δύο συμπεριφορές δεν ρυθμίζεται σωστά «προς τα κάτω»: το
       πρόβλημα δεν είναι οι ετικέτες, είναι ότι ΔΕΝ ΥΠΑΡΧΕΙ ΠΛΑΤΟΣ.

       Άρα δίνουμε πλάτος και το κάνουμε διάδρομο. 64px ανά περίοδο είναι το
       σημείο όπου «ΣΕΠ 2026» στέκεται οριζόντιο με αέρα δεξιά κι αριστερά.
       Το `max()` με 100% κρατά τα λίγα σημεία (δύο-τρεις περίοδοι) να γεμίζουν
       την κάρτα αντί να στριμώχνονται αριστερά. */
    /* Πρόθεμα `main`: το κουτί κουβαλά `overflow-hidden` του Tailwind, που
       είναι shorthand και ορίζει ΚΑΙ τους δύο άξονες. Σε ισοπαλία θα κέρδιζε
       εκείνο — βλ. κανόνα 5 στην κορυφή. */
    main .chart-scroll {
        overflow-x: auto;
        overflow-y: hidden;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
    }

    main .chart-scroll::-webkit-scrollbar { display: none; }

    main .chart-canvas-box {
        min-width: max(100%, calc(var(--chart-cols, 6) * 64px));
    }

    /* =======================================================================
       #9  Ο ΠΙΝΑΚΑΣ ΞΕΠΑΓΩΝΕΙ ΤΗ ΣΤΗΛΗ ΤΟΥ ΟΝΟΜΑΤΟΣ
       καθρέφτης: mobile/src/components/EntityTable.tsx

       Στο desktop η κολλημένη στήλη είναι σωστή: ο πίνακας είναι πλατύς, το
       όνομα πρέπει να μένει ορατό όσο κυλάς δεκαπέντε στήλες δεξιά, και
       χώρος περισσεύει.

       Σε πλάτος τηλεφώνου ισχύει το αντίστροφο. Η στήλη του ονόματος τρώει
       γύρω στο 40% του πλάτους ΜΟΝΙΜΩΣ, άρα τα νούμερα — που είναι ο λόγος
       που ανοίγεις τον πίνακα — στριμώχνονται σε ό,τι απομένει. Και η
       κολλημένη στήλη φέρνει μαζί της σκιά και αδιαφανές φόντο, δηλαδή ένα
       δεύτερο κινούμενο χείλος μέσα σε μια οθόνη που έχει ήδη αρκετά.

       `position: static` και όχι `unset`: το `.sticky` του Tailwind ορίζει
       `position: sticky`, και χρειάζεται ρητή τιμή να το αντικαταστήσει. Το
       `left: 0` μένει αβλαβές — δεν ισχύει σε static στοιχείο.

       Δύο επιλογείς: ο πρώτος (0,3,1) χρειάζεται για να νικήσει τον κανόνα
       της σκιάς `[data-entity-scroll].is-scrolled [data-sticky-col]` που είναι
       (0,3,0)· ο δεύτερος (0,2,1) για το `.sticky`. Βλ. κανόνα 5. */
    main [data-entity-scroll].is-scrolled [data-sticky-col],
    main [data-entity-scroll] [data-sticky-col] {
        position: static;
        box-shadow: none;
    }

    /* =======================================================================
       #5  Η ΚΑΡΤΑ BONUS ΜΠΑΙΝΕΙ ΑΠΟ ΤΑ ΔΕΞΙΑ
       καθρέφτης: mobile/src/components/BonusCard.tsx  BonusCardModal

       Το πρότυπο δεν είναι το συρτάρι ενός πορτοφολιού· είναι η παρουσίαση
       πληρωμής του Apple Pay, όπου η κάρτα μπαίνει πλάγια και μαζεύεται πίσω
       από εκεί που ήρθε. Και αυτό ΕΙΝΑΙ κάρτα πληρωμής: ποσό, σχέδιο
       πιστωτικής, flip.

       Είσοδος και έξοδος ΔΕΝ είναι συμμετρικές, επίτηδες: η άφιξη έχει βάρος
       και κάθεται με μικρή υπέρβαση· η αναχώρηση είναι απόσυρση, και μια
       αναχώρηση που αναπηδά μοιάζει με σφάλμα.

       Καθόλου fade στην κάρτα: έρχεται από εκτός οθόνης, δεν χρειάζεται να
       ξεθωριάσει για να εμφανιστεί — και ημιδιαφανές πλαστικό είναι ακριβώς
       αυτό που δεν είναι μια κάρτα.
       ===================================================================== */
    /* 🔴 Ο πρόθεμα `body` ΔΕΝ είναι καλλωπισμός — είναι εξειδίκευση.
       Το BonusCard.js εγχέει τους δικούς του κανόνες σε <style> ΣΤΟΝ ΧΡΟΝΟ
       ΕΚΤΕΛΕΣΗΣ, δηλαδή μετά από αυτό το <link>. Με ίδια εξειδίκευση
       (`[data-bonus-modal] .bonus-modal-card`) θα κέρδιζε εκείνο, γιατί
       αποφασίζει η σειρά. Το `body` ανεβάζει σε (0,2,1) και κλείνει το θέμα
       χωρίς !important. */
    body [data-bonus-modal] .bonus-modal-card {
        opacity: 1;
        transform: translateX(100vw) scale(.96);
        /* Απόσυρση: γρήγορη, χωρίς αναπήδηση. */
        transition: transform 240ms cubic-bezier(.4, 0, 1, 1);
    }

    body [data-bonus-modal].bonus-modal-open .bonus-modal-card {
        transform: translateX(0) scale(1);
        /* Άφιξη: βάρος και μικρή υπέρβαση (το 1.06 στην καμπύλη). */
        transition: transform 620ms cubic-bezier(.16, 1.06, .32, 1);
    }
}

/* Πάνω από 362px η κάρτα ζωγραφίζεται στο 1.1 — η υπέρβαση πρέπει να ξεκινά
   και να καταλήγει στην ίδια κλίμακα, αλλιώς η κάρτα «ζουμάρει» μπαίνοντας. */
@media (max-width: 767.98px) and (min-width: 362px) {
    body [data-bonus-modal] .bonus-modal-card {
        transform: translateX(100vw) scale(1.056);
    }

    body [data-bonus-modal].bonus-modal-open .bonus-modal-card {
        transform: translateX(0) scale(1.1);
    }
}

@media (prefers-reduced-motion: reduce) {
    #tabbar-bubble,
    body [data-bonus-modal] .bonus-modal-card {
        transition-duration: .01ms !important;
    }
}
