# -*- coding: utf-8 -*- from __future__ import annotations import gradio as gr import plotly.graph_objects as go import numpy as np from datetime import datetime, timedelta import random import time import pandas as pd from typing import Callable, Any, List, Dict, Optional, Generator, Iterable, Tuple import re FOXAI_LOGO_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlwAAAEFCAYAAAA2dTkSAAAACXBIWXMAACE3AAAhNwEzWJ96AAAgAElEQVR4nO2du3IU1/q3m69Ip2T2DaDNvgDkwjlDFcSIoJUiEhx6iIYMOfNEyKFJLNLpwCI2VZZyU5YuYLOtG/hj1VyAvlribdEaZvq4zut5qlSbbeNRz+p1+K33eOPi4iIDAAAAgK+ZF/OtLMu+ybJsS/7lwU6+80/XobrJ2AIAAEDqzIt5KarKn80sy+7LsBxnWXaofvqILQUWLgAAAEgKsVqVomosf96ojMFZlmVHIrKO+oqsKli4AAAAIFrmxXy8xmq1zGnFinWiezwQXAAAABAFS+JK/dyt+V7npQVriKuwLQguAAAACI6O4qrkVATWgQkrVh0ILgAAAPCaSszVuIO4KnlXsWL97ep7IrgAAADAG+bFfLNitRrXxFytw6qrsC0ILgAAAHCGuAar7sHbPZ7lrJJReOjj20RwAQAAgBUGugaXcRaP1QcEFwAAABihYr1aVeuqD0pkHbiOx+oDggsAAAAGU4m9KgXWEOtVlWBFVhUEFwAAAHRmyT047hl7tY53Q1vp+AaCCwAAABoRgVV1EQ51Dy4TnciqguACAACAr7AgsLLYRVYVBBcAAADYElhZSiKrCoILAAAgQSwKLEUZ+H6QksiqguACAABIAMkiLMXVtmGBlcWSXagLBBcAAECEzIv5N0sCS2cW4TrKiu/7iKzrILgAAAAioVJodFtjHawmzivuQu8rvrsCwQUAABAoFTfhtoU4rCrnlcB3L3sX+gaCCwAAICDmxXzbspuwSpIZhjpAcAEAAHiMWLFKkfXYwZMS/K4BBBcAAIBnOLZiZZXgd+KyNIHgAgAAcMxSLJYLK1bJW+KyzIDgAgAAcIAUHt22nFG4CuUy3CcuyywILgAAAEuIq7B0F7pwFZZQL8syCC4AAABDSPHRbQ9chSXvJC4Ll6FlEFwAAAAaqWQV7jp2FZacicsw2T6GPoDgAgAAGIjEY+06zCpc5rySZXjE+3UPggsAAKAH0kZn2yORlREA7y8ILgAAgJZUgt63LbbRaeK8EgBPzSxPQXABAADU4KnIyiQ2aw9rVhgguAAAAJbwWGRlUpyU2KzAQHABAAD4L7LINAwcBBcAACSL5yIro25WPCC4AAAgKSolHHY9FVkqCP6AKvBxgeACAIDo8bBO1ipORWQd+PdoMBQEFwAARImHFd/X8ZaSDvGD4AIAgGio9C6ceC6yzipuQ4LgEwDBBQAAwSPB77ueNIiu41iC4HEbJgaCCwAAgkTisiYeZxhWwW2YOAguAAAIBonLKjMMfQ1+Lzmv1M4i2zBxEFwAAOA1lbgsJbLuB/C2aLkDX4HgAgAAL5kX83GllIPvLsNM4rP2KVIKq0BwAQCANwTmMiwhPgsaQXABAIBz5sW8tGT5nmVYQjV46ASCCwAAnCDWrInHLXZWQf0s6AWCCwAArBFQYdJlLgPhqZ8FfUFwAQCAcQKrmVWFQHjQAoILAACMELA1KxOhpSxaRx48C0QAggsAALQSsDUrI+MQTIHgAgCAwQRuzcpEaO2RcZg2o3y2KfNY1YD7ZkWh3dMsy5QYP1wU005u5hsXFxepjy8AAPQk0EzDKggtUEJrt8dl4TKRYlFMWyVSILgAAKAzUjcrlFY7y5Q9DintkDijfLYtc2FIkV0V77e9KKa1cwnBBQAArRC34SSwKvBVEFpwxSifKcvUU00jciaia23sH4ILAABqqfQ01HU42SY4oTXKZ9/IM4/XiNvjnh+9tcb1qz5vUicYYkHG9shArKGaZ1uLYrrSPY3gAidIYOJm5XdvSYBiHf9IsKLinxQ2BgCXiNsw1CD4LGSL1iifHTlw19YKhlgY5bMTg3NaBdWPV7kXyVIEY4zy2ZYIqc2KoNK2gYzyWSYbxEn1ByEG0J9K8+hJoEHwWeiuw1E+GzuKjduQd7/n4HdbYZTP9g1fIO7K+E2W/wUWLtCGCKyxpNSuM1vb4FzMxUeSukv2EUADldpZoboNs1hitEb57NBhE+/zRTFt8jYEiQjZPyw9+7+Xz55LwSUPMY5jSL3mpGvdDt8RkVV2+fc1iFYFM6px30d8AVxnXszL2lkhZhuWRBMML+EW/3P8GM/aljoICctu2reLYrpb/Qel4HLhK06RKG4OsiFMPBdZ61CBoQcxbiYAbZFsw9JtGGK2YUl0WYejfKbcUa8cP8bpophuOX4GrYhx4C/Lv/aalYsYLruEGg9xidQrCf0mrJ79vvjx90V8YfWCJIgkPiuLvLzDbou/Y5q7SqBEFg/rYlzLGl+XILigEanAuxf4TXiZDblFvhrls7dSLRjhBVEiQmsv8PisLPY6WrLX+rLPTjwRf7pwYbHbRXBBKyIVWqtQh9BT3cJLar180v604XEuMXSIWstI/ay9CEJGUilY6pPAUXvipKl6ekC4WAPXsiGJ4bLMopje8P0ZJYnCdOqsz2gTXjKWh6G7kzXyYlFM9639tkQJvO3OMkn0OvQkWH6ZKNar48vvg0UxVRor+3+OHgA8RC14SUf+I2GxlYnF60SCVwchC21zQFXo2HitLniyAYJmlNCaF3MlTH6NQGwpofXvnXxnN5HG0j7WvvqqllSguEwAuNrrEFxwiTIdS/FQV7VffGND4rv+FitVb5RJflFM1Wf8mPqgCkoIDB5X+IzKOJwX8715Mf9HhFboIQCpCa3SArPtwaMsc1uSpaA/V2IPwZU4YtVSVpjXuL1Wog6vP1ST06FWmUUxVTfYb6UuWOpsyLhGW9HaNKXQUuJVEkBCX7/KCvwgJaFVYdvj9xeDlcuL+YTgShgJij8hfq8VpZtxqLXrRG487+w9ute8wsXYDZVxOC/m+xEKrfFOvnPkwfO4wOeLx32JLwsWX5J1EFwJog43ZbER9wNWrfaU1q5BQaTiYlQ32meSfZU6pYsR10UNIrQOJLD6hwjWrrL0PktcaJWJNb67gWOwcrnyLFzNbQRXYshN5SiCejwu+UF1m9fgYjyQllqnwY/IcJR4+G2omI2RJaEVw7o9F6G1uZPv0PEhjFpXuxFYoV2J+qvisQiuhJDWBieJZyDq4q5YZQZlvygXo7TQ+Nmrb+eOUswG7cLQQaRCSyWOILQEmechvNsNT4P6u+Cij/FptY4ZgisRJF7rCBeiVtRYHsnYDmJRTJXJ/gkuxkvuSrxcTFWuWxOh0Mok81AJrb3IC5d2JaQ5HnSCy6KYHjrYX69Z7BFcCSAHF/FaZlBj+qsm0XVIza4rynEdnB0aCpEKrXeVEg8Ira8JSXDdjqCUi82QhTMJG7kCwRU5FbEFZtEluqjZdZ2nYkV0WbjQKJEKrTLzcDvBEg+t8KxvYltCtzrvWwye/yrRAMEVMYgt62gRXRk1u5ZRLsa/pDhvNEQqtMg8bE+I4uVpyPGVEk9lY9zfisfiGgiuSEFsOUOn6KJm13VUW6DD0F2MkQqty4B4Mg/bIaIl1PqHQVu5pN3aC4O/QgXKrxwjBFeEiJ8dseUOnaKrrNn1goD6Sx7rKEDrgkiFVlYNiPfgWUIh5LEK3tIsDblNZIafSqmfldy4uLjIpLUL1cYtsCimN0z+Fol1IRvRD666xOtA3u0BZT2u+FFcr16jhJYcsLHVvlNxWpOdfOekxd8FQSy0fwe+Rz9bDggPEbkY72t6F2/XWbZKsHBFhCzkQ8SWNxzqjHcQF+OYml1XeN0WKGKLlorTeiJxWoit7uxGsEdHUbJFROPWwMzwy/XQJLYyLFz2MWnh4j16yaWJuVr8TgfSBucAcX2JcrXurgpSdYFqKi1ul0lk70eN8z6uw2GM8tnfAWYnruJbuQRGgYQp7HZoJH4m1rGDtvv7zVgGK3VG+WwPseUld2VRar0RKnFRcTGm/t7LtkA/SwFZJ0QstDKJ05pQS2sYgfRNbMskFktX9iWY/jIERN6T2l9L63kZl3Ui7uCjPmITC5dlTFi4ZHL84ft3T5wnpiwwIrZfpT7AgrIobi+KqdXaT/NiPpE4rdiElnK17FHiQQ8qy1YSP2Lhlm7rfcwQw2UX7RXEK3Fb4DfGKqZL4PgDanZdYrUt0LyY786LuRJ3ryN0H1JPSyMSzxmT2MpisnDZwCeXotaMroQIPY7nvNJNvXz//4jZtqwu/o38eSvg77oh78pIA1i1diouxtg29a6UbYGU5Xdi4gY+L+ZjGetY3ENVfharFpYLvcQoTiaW2+UEjTcuRdPlEmIkUFfiqVjk1Jw76XoYipVoXPkJrUSCMddiiVRkj9G91YdTCajXEtwrQivWeEnKPBhklM/+iXRNGt/TYgHBFSgiPE4CuWGfijXgUHdsjZjpd+UnhLFQDU2Nt8agZtc1ziWuq7cFPeJaWpm4oveoEG+OyDt/HEv/1yCQC6mtUjInVTFKlmK4TAIQGG8lZdaYq1gEnDoI96RUwsRz64PquL9numCnsuiIBXQ/UpHQhYOK27oTknmo3tUP3n/LfvwopR5wH5rFVvbssYRj2Fzz99XF13aiSh/kgv7a4q88rsZYI7gCRCaNz+0VjsWNY3UByk3iUISGz26fySif7ZvO7ikbtUpmVIo1uy4z7PoK/nkx34u0xENWrtGdfMf7QzJ0xNpsy9J8IOEati9Zk0Ba/jgtkkyWYpj4Gp9zJskPY5e3HXXAion7maf9Bzdsbk4iRLfEtZsCx5V52FlsVTIPX0UotqrZh4gtO9ha6ypc4UD2Xu0Z8Q3shtBU3nWhVgRXYIh1y0cXkcps2vIp01TaNqjxeufB4ywzsblBqU14UUy3xIUUK0OF1nhezI8k1ibW7MNNYrXsIWvc1n5dfa+2uwFsmMrAjglciuHhW1sNr9qqLCNutW0Pg1ZLK5fV96lixyRJJqaem0NdhzEHxGdlpibZh06wWQriSnBJmZgzyxeHvSXRB0tg4QoID61bZ9In0PuUYLF2feuZi9FJzIMIE18tf11Q8+/ZAIvWNxKnFVtz6RI111/s5DtbiC1n2Frj71aEcdi+nN+W+FlYA4IrLHwKSjwVF2IwG7k869gj0bVhqyL6MsrytyimygXwwsXvH0gptDZFSHdGxWlJ5mKsLZGUmFZCi6KUjpCsaVsWplXr4NDBXhdC4LwzEFyBILEAvlQqPhXLVnCp5B6KLqfvdFFM98XyF0JbIB1CK/Y4LTVGT3bynW2C4p1jM1j+Ky+D7M+2vQ+PxRMDK0BwhcO2JzE3Z6GKrRLPRNd91xuUjMeW1E3zkXMNQku5Dw+kM0Osjfp/FqsWVb8dI2va1jyrWxMuYn7pr7gGBFc4+GCqLSt2B18ksSK6fMD5uxUX465npTTOJauyt9DKvtTTsl0M0ibK4vztTr4zoYCpN9hc02vXhqMSEbgV14DgCgC5LfnQokVbTzofkO/iQwyTN+nUImx8qNlVCq29vgJf3Iex1tPKSkFKULxfWA7/WBUsv4ztzEFnsam+g+AKAx8O5J9jbFAqMUyus/VuSzVqL6jU7PrZwfMot+a/BwqtzXkxPxT3YYxxWplYLZTQ8q1MDNgN/2jck+USRfC8ByC4wsD1beHMw/pfOtn1wI3mXdHARTFVm+YTS2NzLEJrUEsocR8qa89jvY/nDVSK9x9bYuO8g6vddrbqXUpEfA2Cy3M8cSfuxhC3tQ75bq5vZF5WaRar5qbBOJBjHe2gEnAfZmKJpVK8x4jIsLVfd/E4uJgzuBWXQHD5j+tbwrFP7XpMITdFl3FLd33tRSYB9WPNbYEGFS0tScR9eF4p9UBQvN/YFBmtrVZymbEdOvE0hP6KNkFw+Y9rwZXSLcW1lctrE7yKq9JQs+sy0HtIiYeSeTGfRO4+zCr9Dyn14DmWO4Gc9UhgcmHlIparAoLLf1wewm+HuHlCQywtLq1c3sc8VGp29bktvy0zD4c8g7gP1XO8jth9qETtA0o9BIWX1q0SCQ+wXeAYt2IFBJfHyI3JpZskxVgRl61QvMlUrGOpLVCbgHoVp/WtBMT3Fg9SvHRf3Ic+lEkxRVnANHpXfmTYFBd9LZ62LaW3KRHxBQSX37g8gM9SiN1axlEKdUlQFdClpMa4xip42WZG4rQG1YmaF/NtKV76w5DP8RysWoEiosLW5fh4gOfBxYUSwSUguPzGpeBKuemts3gZn+pxtaFSsb9as6sapzVoLCUoXgn/3yJ2H2ZYtYLHpqjo7XlwVHneefuyEtf7602XvxwacTk5Ug7SPXTYBmZLAsGDoSyrMcpn+xKjpUU0SFD8XuRCS1m1dhFa4WK5b2KmYW8+cGBNV+vYB0uX06xJLFx+42pynKYULL+M44r6wXbalwr1g4XDvJhvJRAUn2HVigabRaHfDa2J6ChsYpsSEVi4fMdVTA8HwGezu4vxD8qlqBMVFC9p5K/i+VYruWwCj9AKHxERNosW60pkOrAcD6kuTruJh6r4I7jEHRFLoOhh4E2eaYT7WXS6EFxJ3gJVqQc5BGItXlryTlyIBMXHgc2+iecare+2BZdiguDyh5iyj8ZDayo5Du5DcH3OiHNBUJmKQxGr1l7k2YeZWLV2KWAaHTbdidrmjjIIjPLZqeXyKqpExLbjkA2nEMPlL84sHYFb53RBY2DDiFXrJAGxdUy1+PiQvok2LbK65w/9FS2D4IJlbFci9hKXNchiDy6VAqax9z/MxKr1YiffGeNCjBKbbWt0uhNLXAiux76UiHABgguWwbLjnmgD5ysFTGPuf5hJMViVgZh0zEqsiGiwOYe1W0cl29F2Q+ss5f6KZCkCgHEkVusgAaGl+HEn37EZ2wP2se0aM+WOdrEmd0f5bG9oeYsQwcIFAEZJyKpVtubpJLaUC1kdQPScC4rQ3YmXyOfarsm1YbmUhjdg4QIAI4hVa99h1X6bvFWHcNdYLQm8PixLC6gsLmUBSPH2HwoijG0W5DWdbOGis8aeoxgyp2DhAgDtVDIQYxdbyjrwZCff6VxbS1m1JHGgengrK+BJaD01EyMWd2KJizjD23LZSAoEFwBoQzIQ9xPIQMwqgfGdDkRxIR7VVNRX4/aXCDLwCBHCtmvlGc2YljJALrLTk3OhI7hgGW7W0AvVAzGRulqZBMYrsdUpq1du9X+3PLRfKWFGDzqvsJ1hd2zJveyiRtzT1EpEEMPlL67KM8TcLLg1KZq7hzAv5nsJ9EDMxBKw26cPolisuo6REmZ/q7ihlCt0+4CDvomK+6N8duHniGhh13K1fqdg4fKURTF1Vg8LsXGJy0r/wTQ1VlateTE/SURsvRMXYqf308KF2IS6BP0m/WbBHbaD5VMgqZpcCC5YBW5FxqCReTGfSHyJzX5srlAV47d7BMZvdXAhNvHDKJ+dpFyp2zHJFuw0yEZK5VAQXH5z6ujpsHC5GwPvWytVWvO8TuDGr97Ht30qxo/ymTqg/9I8Rncli5GaXRaRch2xJ4G4AsEFXuCqFg+Cy34mUonXrZWk3EMKRUyziguxUzN3cSGWgtQESsD9OspnBwTUWwOBa477qZRBQXD5jatYno2U6wDJbdYV3gquSrmH2K1aqrbWswEuxCNLglTVODuiZpdZHPRNTJEk3LUILr9xefimHK+A4KpQCYxPodyDcuOPd/KdzlWwxc1nO6btrtTsIr7IHIyteZ6mYK1FcPmNy8M3yV5XjlK/q3iVoTgv5i5EhCveidjq5ELMPs8bZf371aH177VyY+JiNALuRDtEL2wRXB7juDxAUtkjFbYdu8w6H/YmqATGuxQRNumbhajitXyx/j2Wml3EYGrCQd/ElIn+vEFw+Y+rTMUspYJ0FVx+51MfmhZXKsanELcyJAuxLPngk/VPiYM/aAukDdyJ9rjtOH7WOAgu/3Fp5bqdkpVLvqvL1G/n7kSprfVXIinwvbIQsy9zRXfJB52UbYGo2dUTEdQpuNJ9ImqBi+DyH9eHcBI3ZYl9cf1dnb3rpdpaKfBjHxdi9nmuHIir1XfuS82uJOMxNYB1yz73Y74kILj8x7Xgup2Ie2LigVXHybtOzIWoSj482Ml3Os/pSrzWUzOPZgTaAvVALmAhveeYiPa8QXCZQVscjsT0vHP8fSYx3zrEdeC6F+A7F/FbibkQT/v0Qsz8jdfqQtkWiJpd7cC65Y7tWLNtb3rwDCVPHFZW18k/i2KqO9Ps0LH1YUOeIbrNWhZ255pLBji0+cuUC1G+dyoFHd/u5Du94hElXisEF2ITd6VQ6mRRTH2Y8z5DKQh3bEi2eHRz1BvBtSimVg+cwDj0YMO/q9wSi2Ia283vwBOrhbX5Ly7Ew4R6wz3rU8g0+1JfK6aCr2VbIFU6YuJDVqxv0DfRC/ZiFFy4FAPAE7diJm6JaG5+cpj6YOF5a+vgk0KmqbgQy5IPfarGl/0QY62u/1QC6nExfg3uRPfcjrGeHIIrHHxR+7/GkPUkwtGXw9T4u5UsxFCy63RwPKDkg81+iC65TVug60isqqvG9XCd6OYlgisQxOV65snTHoR8M/YsJufMdEcBcSEeJZR1peK1xj1LPowTamVU8lpqdtEWCOuWTzyOLVkLwRUWvqTLbkjwbXDuxUrPO18w+k7nxXw7MQHxbGBw/B+JtnK5n3pbIBGcBMv7RVQCGMEVEJJZdO7JE5fBt0EsCInJOfAsJufMZLbYvJgrMfdbIgLivG+8VoXUa1Wl3hbIdR9V+JqoBDCCKzx8EzjKHXHgszuiEpPjm0vNyMFWqRrvuraYLVR9rc0+8VpL7Hp0oXHJK6nZlVpbINyJ/rERU6IWgiswxCLiSyxXSZnx5J07QixwPrrUjFi3Eqsar3ibZVmveK1lJE5yUwLuU+duSm2BZO+ib6KfRCOEEVxh4qPJ/7a4I7ywdqnbuQoElt6AProJtL9DKflwlFANIdUPcVeH2CpR5TkWxVQdvi90fWbAXLUFSiCgntgtf7kbS2zhjYuLi0wOJqepsItiesPl7w8NH95ZDecSD7Nvu7CiuEH2PM/IO5ZDXRsSr5WKC1HNr8nAeK1GxBWdUoHYOpTbdtdAFw3nyJ7xPwfPcS7W6JBwdeaoWoWDRbEItz/0PFIrru31PrX2gW7sWZ44XdiQw38igepKeP1t8hfK4TgJpPSBNutWgi16zsWFaPygUuJC5tU+jYyjbgvkwrqleqcG5651WFLnqUrmMH2OmAaXYqBI7aafPX/6DckK/J/U+dnVGYgrbkMl6k6kenoIh+LPuupuVeprpSK2dAXHt0ZcjOqQeUZA/VVm8mFkLkYXgivIuCTHmfLBu32xcIXNnkzCEFKZ75fm6FE+OxWhoA7Ov9sKEDEHb0oT7RCDXM90WbfmxXws7q5U0thVayut8VpdUAeNCHtfem+65HEZUB+6i1EsNrZdxqeBW2oOHV1udz2NX24Ngitg1O1bNozfAvsWd6uH1iifZQ3xDFuRCItdHTFtEhyfSoueTCrHO7/dirjYirChdR/KtkAvFsU05PplLuZV6PXeXLnYVX/F3ZBd2rgUA0dS2d9G8FU2Klaw5Z8YxJYWV2Ji/RCzIZXjTbEopsod9AQX4yXBtgVy2Dfx0MHv1IZcPFyVJgrarYjgioOJh7W54Aunckj3RoqZptQPUYmZJ6YzEftCza5rhNoWyIV76q3tzG1DuLLS3Q+5jy+CKwJkASdRoDBAzoe+m0pwvK9lQHRTZiJ6bQmo1Oz60YPHcU3ZFigId5lY5FzsmUFbtyq4vAgFWwgVwRUJYualWKN/7A4JkJXg+JSaT1vPRBzKopgqS8kDrMyX/BBIWyAXfRPPxDIaPHLJf+foe2yHmiWL4IoICV6NIZ4rFl4M2WAlOP6PhDIRj3W16bGNxOdtOTyEfCKEtkAu3ImxWLdKXFm5NkKN5UJwxcdErATglrdDsrekcnxqmYhBiq0ScTFui6U59YD6si2Qd43tJdbMRfeA0LMTryGXSVdW3SDdigiuyBBT7xj3hlOOh7ShkEzEVNr0ZGVPRA+eQwsitMdcfC55KhXqfQp0djHXQq+9tQ5XVrvbITZWR3BFSCWIPvVbtgtO+wbjSibiSWJtZFTZh6CLGa5CYirHAXSDsMFVWyDXDyKxZS7WV2ztkEpcWu2Cs3IhuCKlsuEjuuyhxNa4T9r3vJhvJhYcr+blA1/LPuhAXIzU7PrMhtTsct0WyJUlNcp5LlY7V6VR7geQnHENBFfEILqsMkRsbUmV/ZTElorX0tJT0nck1mULF+MlZVsgVy5GV42qY6i9tQ5KRLQEwRU5iC4rDBFb22LZSiUT8UzEVtA9+LqiLAGLYrpFza5LyrZAVl3JjvomZhG7E0sOXTa0DqlEBIIrASqii0B6/bwbILbKPpipiC0lTLdSE1tVKjW7uABl2SvLbYFcWLfOY6m9tQ7Z+1x9x42Qin4juBKhbLyLW0MrqvTDdk+xNUms7MNpqDW2dCM1uzap2XWJlbZADvsmxm7dKnEZPB9M0g2CKyEqJSMojjqcZ31LP0jZh9eefR+TILaWWKrZlTo22gK5OpSTEFyOG1rfDqWPJ4IrMWSj32Wj741yBX27KKa9NlIRWymVfQi2erwNpGbXt7j7LzHSFshh38RTESKp4NLKFUQdPwRXolQ2elyM7VEuoM0+m6jU2DpKTGwFXz3eBhV3P5bnL22BdB6gLvomZgm5E0tcxqo9DaFEBIIrYSjO2Jpz6YvYN17rG8lEdBFD4oq3MVWPN03F8vyMgPpLcfSrxrZArkoHRB0sv4zU5HIZl+j9foPgSpxKccYHWLtWolxiW337IlbEVio1tjLEVn/EVU1boM8MbgsksT0u1t67SFv5NEFNrhoQXHCJypySOkE03v2Miql5siim44Eb50FiYusUsTUMZXmWtYjl+fPa+WtAWyBXczEp61aJlMBwdX5saHZFawfBBdcQS85mwgUaz+W7bw2tnyMB8o/1PZr3nIp1BjRAW6BrdG4LJH/XRczkeaqCS/DZyuXU6ojggq8QN6NKo/63BPKmsOGXQksFxe8NbcUxL+Z7iQXIU/rBAJW2QI3/4G0AAA+gSURBVK761flE2Raorah3FrsVeSufJlxmK96tc0E7cPNeexYEF6xF2pHsVixeMaauaxVa2ZcK8q/0PF4QUPrBILIOx7QFuuS21OxqU1eLRtUOcNzQOvMseP5adiyCCxopLV6LYropLo4YKmQfS/HSb3QJreyz2BonVEFeCfBnlH6wQ6UtENauz22BmoTNkWXr/OXlTToJpM7EYeKHT3vRNSPFTflf1w9IRk4giIvjUGqebMttIpSg8DOJrdg3aFpOoUGx2i+OUu6J6Ao5zMfiNtkS6zOsoG8nCBhOpbacjzywGGt67VJw4+Li4vIPjkvjnyTu8w6aSiXnscMig+s4FZF1mFjVZwAA8IgrwQWgC7l9j+WGs2XZAqbcLSfiTjhCyAMAgA8guMAKYkHdlB8lwr6R/+1rDSvjWI4k1fcECxYAAPgKggu8oK1Lm4BUAAAIEQQXAAAAgGEoCwEAAABgGAQXAAAAgGEQXAAAAACGQXABAAAAGAbBBQAAAGAYBBcAAACAYRBcAAAAAIZBcAEAAAAYBsEFAAAAYBgEFwAAAIBhEFwAAAAAhkFwAQAAABgGwQUAAABgGAQXAAAAgGEQXAAAAACGQXABAAAAGAbBBQAAAGAYBBcAAACAYRBcAAAAAIZBcAEAAAAYBsEFAAAAYBgEFwAAAIBhEFwAAAAAhkFwAQAAABgGwQUAAABgGAQXAAAAgGEQXAAAAACGQXABAAAAGAbBBQAAAGAYBBcAAACAYRBcAAAAAIZBcAEAAAAYBsEFAAAAYBgEFwAAAIBhEFwAAAAAhkFwAQAAABgGwQUAAABgmJvq40f57Jcsy57X/KrvF8X0TddHGeWzPMuyec1feb8opo86fN5U/vhQflbxKcuymfzzYlFMP3Z85jtZlv13zb9+syim33f5vCGfOcpnv9d8zz4UWZZ9UGPU533WYeBZ67gaMx/GyKc5s/QZt2Rdq/+dNv39jlx7BlfvvwnZMwZ9/0UxvbH8zzR+35nsWdrX5CoMvKcPsmY+LoppofFz1bOq+X+n5q88WhTT9wM+39ne4XK9tPzd/+lydso6+2nFv1Lz4j+dnjaMdVvS6by4KQ/2/SifPayZ3NNRPlPi5VPHh1n1AkrUZ+00fYA8lzo08pa/81bl9/40ymcfZcLNGv67FMjLcRSR/ZJx+Yrgx0iE1i8d1kx0jPLZcxkD37k6UGS+vZH96kMg7+Se/Kjn/yCX88HPLpf1OrGVybnQW3AZIKb9Vc3LzhfFoQS0bks6vfOqS7FucO80WMC+QhRq3YL5vk7AqUNjlM+Udez3gQfHHRFe/xXxBl9gXJoJaoxEbA1dM8GiLINyiw1p066i9tk/R/ms7rLqK/fk2XWslTbnTS6WYF8JeX99bnNsI1i3JbXv/EpwiWm2To3/1PYFyN+r2zDe1JmfR/nsnrhTdB4a6pl+r7gl4fq4ILrWE9IY/VJaHFJD3s+fFl01JpnKhTNEBq0VOT/a/ve+XyxC3l+tnJWRrdus7p0vB80rwVXnt21766r7e+rzX677l7LYfhfXoAl+QnStJPSbhQ28HiNZ4Klatp4b3jdckAdq6VLMxdrahy5zOJS9PMT99blpoRjpui356p3frP4f5eIb5bPvZQBWoTaAh3WBiuJ7r1swLxtiweYtBl/FOXxYDlITy9hDWYR1n6FE1/uA4iS+YlVA4DpkXJ43mOmVSTfvG/jaNvmhIUGjU6Bmi2fyaows0HT4zGTdaH9+l+9f3l3Tgfaxsm8YjftpM+/kILvX4hJbxs8a36s6rpdc3uO6A7kMeF57ua6hi4i6ZWJNmt47fNwv1zA1FScX4rot6Xte3Fz+G+pLjfLZm5oPUhvEd2se4lbDBjJrcCVOG9whtUGZ8s/Vz0zM8XXCTz1n6wzJkJFxUYkRnxo2s3uSdZEcoY+RrL2622jMCRJNoqVXpqhJ5PBQe20hl8y6fc+7vUr28WKUz/6sefbnXQWXWDy6Wjueu1yTke+vD5uMLAMIbt2W9H3n6+pwvaxxLd6TRbGK5zWB8h8bYsSyhgf/KGnArW56i2K60zC5HyYYt9Q0/j4HoNoi1DFqitvyKZtLG7KG69bxB1837ezzPvVRsrXrrP4PPQ4Or1svt8QS0IU+LnFfxifW/VW72zb0dVuh0ztfKbjE5Vd3M/lp2T/fIlC+KSsxb7jZNLkiV/7Oho0sqXgXGb86wRqjH70TAY9R02bede2EQtOlyftNW0RXkyXI172qSci3vtRWQkJW8UlcS+volEVvggj213Xv8qGczzoJft1mPd752krzpcm45kOWVW+dL3bWwiRZ90J7FdWTwahbpCkGGMd68OqEMQqHuo07mDhNiUcN7nIoe6yuOKK671g0Ca4BQfo6CXnvqBP+upM3oli3Qut33tTap85CNC3NuOJiXDeASiy18ePXvYAhfu86odfH5A3gI02LPrp5Lgds3fcKzY1aJyjueSIojFDpirCOohKju4pbKRf61cg64X+nJpSoExGu29bUCi65vdSZ9krXYp2Pt001+TsN5tbeareFZS21OC4j4xwZIY5Rk5UhxlIoTW7U0OZz0/P6GgOkQwjWhZR8rOzjdZdvHwRX0PurnPnr4pJ07SGxrdvW77yxeXWDa7HslbhuAGctTYOmXwBxS+1uFsm70kIdI1lndaJLWUj+jKwGXWxWu6Z9zrvvK+tFxx5aZz15s+bPyzx06bGIZX+VbOZVe4kuK1c067brO/+qLMQaXoolaNXCWmch+tDSlZg1CS4NtUbqJnpKmXlNh63x5rkBEPIYzRpiKe+J8Ooaj1Gu4zc9ElegJWqfG+W1SU8+Xg6brEqN7qFKTbJ1XF34pVZkUfN7nzsMuI5pf123l/zUs69yrHR6560El2wELztWy+0y6U1vJHWCLXoLV8sG4EkfpjGMkQq8lhuo7htktRn8rMNFyjRNa/f3BgHTii4FETXwseYS6NVe1SKc5FNLD0fdmnu/4sJdJ7hUce4+Ge29iXF/lb1kVT/kMtZuyMIKft32fedtLVzlC8hbxjy9DCjLIEjBNcpnFxo/rs5vHyyJjtEj6RRhymw/lc3mEbdc63izV4mwr6u7mLVZL22C5Zf/gQpzkYKTq8ZDhyBgf/3Muq4zag+I7oJu4523FlzC99JksjZILLCK1qnXnvokh6fpFhEhE8wYySb4nbgNTcVr3ZONeGXHCQgTAwdOGxdandj6VBM//KZmfg8WXBoJdn+VrjPvVxhZtIjaiFn7zhuD5pdeQJtq8Y1ZiZ6R8i1dLabvQu4paYEgx0i5/cSk/rKhc0Rf7tEE3joh7VW1ha4rNJWCWPcZddmKdzzpIhLD/ro2Y9Hj7gcuqX3nXS1cmUz0dUG3b3oq+dqFqczOA82XdRMjVcH1/XLzb4hvjCrW5ta3Ucn0yltYyHLHt9wY124Me9XLNoWqRRTVfd+1n6EOtFE++1DjOs8d13OKYn+t6a1cxu/1SVCI9cxtfOd9BJcJml7AnYGlIerchqkKrl/kYJ3hTlxLkmNUFpiUbLA/a/7qPQ2XoSE0/d5HhpruGiECi8EnOXTaFqqus259bPHuirrG2baD55eIae+YrXlXzyWJpuv3i2rdVmh8574IrjYF/3oJrhZ1MmwshrqNtNfvb8rAqKRa1wW2Ppesnp1AJ3gtgY+R9jnTFbEizFp0w3c1d5rGITQB05To4OvB/UYEUhcr6p2GDK87GmLKescZsb9+QaoUrLJyZT2tXF6uWxvv3AvBJS90XdZJJl+yb3ufJl/+NSHXUAun70SwHpgvL1v9zCTWZp0bWD3bfJTPvkvN0qVrjGKZM2t43yC4XIqa1ASX0Vggy+UvbFSFNxbYneD++nJNN4DncinrQpDrVsc77xQ0b5i6G8CQ6rZNgmvV711n8uybau80LkNunnW1k24ZaE4aFBrGKKo5U8HbQ6JFp/7QeuvVPe+nyC5ENhIu7kgpI6OksL82tPzp9N1iWLd937lPgqvOgnWrT0sBMVvX/Xfv1/j4121st3pmv9QdulYyWGraNZTkMTfHbcPAMYpuzgi+z4m6i5ov2WqNyP7WK4A8NEQE2ZpXVg7vRPbXdY2t29bnrBL8uu3zzr0RXBJoWffwP/UIKm2qjL9uE6ubDJ3UvAz4ukXfthKzLpo27dh60/Wh7xjFOmeaNj7XVpem9/WL7wddi4rtmeOMO93o6MfXltxiMkLU+6sYJ9ZZdboKpODXrdDpnftk4cpamOh+b9OcVL2oUT77vWESfKxJ4azb3FRm1u8dJkPdYWt7E22TnJA6fccoujkjB1WTEHBaY0jEZ21NJtk3fJ7b86YkiQ6Zf14j76FuX1aH+r9UPFnbn4ZzI7Pooop+f5Uzc/AlK5J1m3V9575kKV4iLRvq+mSph/9T/s5XFe1FjD2UQ6LpcFubWSG1R+qeQ/2O/5NgwQ/Lm2ElA6fJTWC7TkvT5Ei96n7Wd4ximjOVbJwmy5wvTWzrmutn8l3+W+4b8txOLXOVvarpfWeRNZVvsm7NesyputqQmZwHNurFpbK/NjXJb0tw63YFnd65V4JL+F42oDpLVi6m4r6BiC9bpOk2TYasvP33bLT5xnaqcEM2HQwfI+dzZpTP/mvxJu2FEJB3trOm71uVXH5+6jL+XbP3NLfICa1V2lpa9E3M+sSqNZQtyMoYYNOFSFPZXzv2Va77HK/WbR+6vnPfXIqln/iRQdfJyzYbmCjpR4aeQd3i+lTo1QENh5vpNUYRz5lVFD7VFpJneRTZ/P4UYKu0OpqC5ft2KslaCDVbbsVU9lctyjKSddv62b0TXJmIrkUxfdTCN9+Fy82ry21R/Mw6D9DyGXR+r67UbWi4FD/Te4winTPLfOjZ0sMosnl/5zquTBMfpCdbTKUgmkRP7zi1So2kdTy0FA+UxP7aYry7flbI67b1O/dScJWIOPrXQOH1Saxa/+oTeCqT4T8a0rJ7P4Nm6iZHECn0Fhg0RhHOmSpvpPWGlzdSJVAWxfQ7zZc1m5T7VVRiqxKzto73GiymTevERnZkSvurtktX4Ou29Tv3MYbrGpWCa7NKMG9TGvVMNq73OlLoZeO7NO1LhdlMBrJuAZWBfr5lGH2ouWmqbLo79FYcPkaRzZlyPb3xVWgtI5e1mcSalJaNNsk0LijHN5psxBU0ZbsO/t4SWzSta7uiYr0M72/J7K8tYuf6fGZI67ak9Tu/cXGhM74TAAAAAJbx2qUIAAAAEAMILgAAAADDILgAAAAADIPgAgAAADAMggsAAADAMAguAAAAAMMguAAAAAAMg+ACAAAAMAyCCwAAAMAwCC4AAAAAwyC4AAAAAEySZdn/B7L85TdpBKudAAAAAElFTkSuQmCC" custom_css = """ .gradio-container { background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 25%, #cbd5e1 50%, #94a3b8 75%, #64748b 100%); font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif !important; min-height: 100vh; } .header-container { text-align: center; margin: 20px 0; padding: 40px; background: linear-gradient(90deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%); border-radius: 24px; box-shadow: 0 20px 40px rgba(12, 83, 135, 0.15), 0 8px 16px rgba(0, 0, 0, 0.1); backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.3); position: relative; overflow: hidden; } .header-container::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); animation: shine 3s infinite; } @keyframes shine { 0% { left: -100%; } 100% { left: 100%; } } .foxai-logo { max-width: 320px; height: auto; margin: 15px auto 25px auto; display: block; filter: drop-shadow(0 4px 8px rgba(0,0,0,0.15)) brightness(1.1); transition: all 0.4s ease; animation: float 4s ease-in-out infinite; } .foxai-logo:hover { transform: scale(1.05); filter: drop-shadow(0 8px 16px rgba(12, 83, 135, 0.3)) brightness(1.2); } .header-title { background: linear-gradient(90deg, #ffffff, #f1f5f9, #e2e8f0, #ffffff); background-size: 300% 300%; animation: gradient 8s ease infinite; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; text-align: center; font-size: 2.5em; font-weight: 700; margin: 15px auto; line-height: 1.3; text-shadow: 0 0 30px rgba(255, 255, 255, 0.5); letter-spacing: -0.01em; max-width: 900px; } @keyframes gradient { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } /* Unified loading container styling */ .loading-container { text-align: center; padding: 40px; background: linear-gradient(135deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%) !important; border-radius: 20px; color: white !important; margin: 20px 0; box-shadow: 0 8px 25px rgba(12, 83, 135, 0.25) !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; animation: loadingPulse 2s ease-in-out infinite alternate; } .loading-container * { color: white !important; } @keyframes loadingPulse { 0% { box-shadow: 0 15px 35px rgba(12, 83, 135, 0.3), 0 5px 15px rgba(0, 0, 0, 0.12); } 100% { box-shadow: 0 20px 45px rgba(12, 83, 135, 0.4), 0 8px 20px rgba(0, 0, 0, 0.15); } } .loading-spinner { border: 4px solid #f3f3f3; border-top: 4px solid #1A5F91; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 0 auto 20px; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .progress-bar { width: 100%; height: 20px; background-color: rgba(255,255,255,0.3); border-radius: 10px; overflow: hidden; margin: 15px 0; } .progress-fill { height: 100%; background: linear-gradient(90deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%); border-radius: 10px; animation: progress 3s ease-in-out; box-shadow: 0 0 20px rgba(12, 83, 135, 0.4); } @keyframes progress { 0% { width: 0%; } 25% { width: 30%; } 50% { width: 60%; } 75% { width: 85%; } 100% { width: 100%; } } .prediction-box { background: linear-gradient(90deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%); border-radius: 20px; padding: 25px; color: white !important; text-align: center; font-size: 1.1em; font-weight: 600; box-shadow: 0 15px 35px rgba(12, 83, 135, 0.25), 0 5px 15px rgba(0, 0, 0, 0.12); margin: 15px 0; border: 1px solid rgba(255,255,255,0.2); position: relative; overflow: hidden; } .prediction-box * { color: white !important; } .prediction-box::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.6), transparent); } .insight-box { background: linear-gradient(90deg, #cbd5e1 0%, #94a3b8 25%, #64748b 50%, #475569 75%, #334155 100%); border-radius: 20px; padding: 25px; color: white !important; margin: 15px 0; box-shadow: 0 15px 35px rgba(51, 65, 85, 0.25), 0 5px 15px rgba(0, 0, 0, 0.12); border: 1px solid rgba(255,255,255,0.2); position: relative; overflow: hidden; } .insight-box * { color: white !important; } .executive-summary { background: linear-gradient(90deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%); border-radius: 20px; padding: 30px; color: white !important; margin: 15px 0; box-shadow: 0 20px 40px rgba(12, 83, 135, 0.25), 0 8px 16px rgba(0, 0, 0, 0.12); border-left: 5px solid rgba(255, 255, 255, 0.3); border: 1px solid rgba(255,255,255,0.2); position: relative; overflow: hidden; } .executive-summary * { color: white !important; } .executive-summary::after { content: ''; position: absolute; top: 0; right: 0; width: 100px; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1)); pointer-events: none; } .feature-box { background: linear-gradient(135deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%) !important; color: white !important; border-radius: 20px !important; padding: 25px !important; margin: 15px 0 !important; box-shadow: 0 8px 32px rgba(12, 83, 135, 0.25), 0 4px 16px rgba(0, 0, 0, 0.1) !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; backdrop-filter: blur(10px) !important; transition: all 0.3s ease !important; text-align: center !important; } .feature-box:hover { transform: translateY(-2px) !important; box-shadow: 0 12px 40px rgba(12, 83, 135, 0.35), 0 6px 20px rgba(0, 0, 0, 0.15) !important; } .feature-box h3 { margin: 0 !important; color: white !important; font-weight: 700 !important; text-shadow: 0 0 10px rgba(255, 255, 255, 0.3) !important; font-size: 1.2em !important; text-align: center !important; } .feature-box * { color: white !important; } /* ALL components get unified gradient and white text */ .system-status, .metric-card { background: linear-gradient(135deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%) !important; color: white !important; padding: 15px 25px; border-radius: 20px; margin: 15px 0; text-align: center; font-weight: 600; box-shadow: 0 8px 25px rgba(12, 83, 135, 0.25) !important; border: 1px solid rgba(255,255,255,0.3) !important; font-size: 0.95em; position: relative; overflow: hidden; } .system-status *, .metric-card * { color: white !important; } .metric-card:hover { transform: translateY(-3px); box-shadow: 0 12px 35px rgba(12, 83, 135, 0.3), 0 5px 12px rgba(0, 0, 0, 0.15); } .metric-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.8), transparent); } /* Unified button styling */ .button-predict, .btn-modern { background: linear-gradient(135deg, #1A5F91, #8B8B8B) !important; color: white !important; border: none !important; border-radius: 25px !important; padding: 15px 30px !important; font-size: 16px !important; font-weight: bold !important; box-shadow: 0 8px 25px rgba(26, 95, 145, 0.4) !important; transition: all 0.3s ease !important; position: relative; overflow: hidden; font-size: 0.95em; text-transform: uppercase; letter-spacing: 1.2px; } .button-predict:hover, .btn-modern:hover { transform: translateY(-2px) scale(1.02) !important; box-shadow: 0 12px 35px rgba(26, 95, 145, 0.6) !important; } .btn-modern::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); transition: left 0.6s; } .btn-modern:hover::before { left: 100%; } .plot-container { background: linear-gradient(135deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%) !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; border-radius: 20px !important; padding: 15px !important; margin: 15px 0 !important; box-shadow: 0 8px 32px rgba(12, 83, 135, 0.25), 0 4px 16px rgba(0, 0, 0, 0.1) !important; height: 550px !important; max-height: 650px !important; min-height: 400px !important; position: relative !important; overflow: hidden !important; backdrop-filter: blur(10px) !important; } /* Simplify chart container - remove ALL extra nesting and borders */ .plot-container .plotly-graph-div { position: relative !important; width: 100% !important; height: 500px !important; max-height: 600px !important; margin: 0 !important; padding: 0 !important; border-radius: 8px !important; background: rgba(255, 255, 255, 0.98) !important; border: none !important; box-shadow: none !important; overflow: hidden !important; } /* Remove ALL additional containers and nested divs that cause multiple frames */ .plot-container .plotly-graph-div > div, .plot-container .plotly-graph-div > div > div, .plot-container .plotly-graph-div > div > div > div { background: transparent !important; border: none !important; box-shadow: none !important; padding: 0 !important; margin: 0 !important; outline: none !important; } /* Ẩn hoàn toàn toolbar/config của biểu đồ */ .plot-container .modebar, .plot-container .modebar-container, .plotly .modebar, .js-plotly-plot .modebar, .plotly-graph-div .modebar, .plotly-graph-div .modebar-container, .svg-container .modebar, div[data-title="Plotly toolbar"], .modebar-group, .modebar-btn { display: none !important; visibility: hidden !important; opacity: 0 !important; pointer-events: none !important; height: 0 !important; width: 0 !important; overflow: hidden !important; position: absolute !important; top: -9999px !important; left: -9999px !important; } /* Cải thiện spacing cho text và labels - tránh dính chữ */ .plot-container .plotly-graph-div .svg-container { pointer-events: auto !important; overflow: hidden !important; padding: 10px !important; height: 100% !important; max-height: 480px !important; } .plot-container .plotly-graph-div .main-svg { max-width: 100% !important; height: 100% !important; max-height: 500px !important; overflow: hidden !important; padding: 5px !important; } /* Đảm bảo text không bị cắt và có spacing tốt */ .plot-container .plotly-graph-div text { font-family: "Inter", "Segoe UI", Arial, sans-serif !important; font-size: 12px !important; text-anchor: middle !important; } .plot-container .plotly-graph-div .xtick text, .plot-container .plotly-graph-div .ytick text { font-size: 11px !important; fill: #374151 !important; } /* Cải thiện title spacing - tránh dính với nội dung */ .plot-container .plotly-graph-div .gtitle { font-size: 16px !important; font-weight: 600 !important; fill: #1f2937 !important; dominant-baseline: text-before-edge !important; } /* Tối ưu responsive và tránh overflow */ .plot-container { width: 100% !important; max-width: 100% !important; overflow: visible !important; } /* Đảm bảo axis labels có đủ space */ .plot-container .plotly-graph-div .xaxislayer-above, .plot-container .plotly-graph-div .yaxislayer-above { overflow: visible !important; } /* CSS cho smooth scrolling và highlight */ html { scroll-behavior: smooth; } .plot-container.highlight { box-shadow: 0 0 15px rgba(26, 95, 145, 0.2) !important; transform: scale(1.01) !important; transition: all 0.3s ease !important; } /* Hiệu ứng highlight cho loading panel */ #loading-panel.highlight { box-shadow: 0 0 20px rgba(26, 95, 145, 0.4) !important; transform: scale(1.02) !important; transition: all 0.3s ease !important; } /* Fade-in animation cho LLM analysis */ .gradio-markdown { animation: fadeInUp 0.8s ease-out !important; opacity: 0 !important; animation-fill-mode: forwards !important; } @keyframes fadeInUp { 0% { opacity: 0; transform: translateY(30px); } 100% { opacity: 1; transform: translateY(0); } } /* Smooth reveal cho các phần tử khác */ .plot-container, .info-card { animation: slideInFade 0.6s ease-out !important; } @keyframes slideInFade { 0% { opacity: 0; transform: translateY(20px); } 100% { opacity: 1; transform: translateY(0); } } /* Cải thiện focus state cho biểu đồ */ .plot-container:focus-within { outline: 2px solid rgba(26, 95, 145, 0.3); outline-offset: 2px; } /* Unified color scheme - standardize ALL backgrounds to the main gradient */ .info-card, .feature-box, .prediction-box, .insight-box, .executive-summary, .modern-card, .glass-card { background: linear-gradient(135deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%) !important; color: white !important; border-radius: 20px !important; padding: 25px !important; margin: 15px 0 !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; box-shadow: 0 8px 25px rgba(12, 83, 135, 0.25) !important; backdrop-filter: blur(10px) !important; } /* Ensure ALL text inside these containers is white */ .info-card *, .feature-box *, .prediction-box *, .insight-box *, .executive-summary *, .modern-card *, .glass-card * { color: white !important; } /* Unified header styling */ .info-card h3, .info-card h4, .feature-box h3, .prediction-box h3, .insight-box h3, .executive-summary h3, .modern-card h3, .glass-card h3 { color: white !important; text-shadow: 0 0 10px rgba(255, 255, 255, 0.3) !important; font-weight: 700 !important; } /* Unified text styling */ .info-card p, .info-card div, .feature-box p, .prediction-box p, .insight-box p, .executive-summary p, .modern-card p, .glass-card p { color: white !important; opacity: 0.95 !important; } /* Sửa lỗi font và text rendering */ * { font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif !important; text-rendering: optimizeLegibility !important; -webkit-font-smoothing: antialiased !important; -moz-osx-font-smoothing: grayscale !important; } /* Cải thiện hiển thị tiếng Việt và căn giữa */ .gradio-markdown, .gradio-html { font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif !important; line-height: 1.6 !important; word-wrap: break-word !important; overflow-wrap: break-word !important; text-align: center !important; } /* Căn giữa tất cả text */ .gradio-container .wrap { max-width: 100% !important; overflow-x: hidden !important; text-align: center !important; } /* Căn giữa các component */ .gradio-row { flex-wrap: wrap !important; justify-content: center !important; } .gradio-column { min-width: 0 !important; flex-shrink: 1 !important; display: flex !important; flex-direction: column !important; align-items: center !important; } /* Chuẩn hóa màu sắc - text đậm trên nền sáng */ .gradio-container { color: #1e293b !important; } /* Chuẩn hóa size chữ - màu đậm cho dễ đọc */ h1, h2, h3, h4, h5, h6 { font-weight: 600 !important; color: #1A5F91 !important; text-align: center !important; margin: 0.5em auto !important; } p, div, span { color: #334155 !important; font-size: 1em !important; line-height: 1.6 !important; font-weight: 500 !important; } /* Chuẩn hóa labels và inputs - màu đậm */ label { font-weight: 600 !important; color: #1A5F91 !important; text-align: center !important; margin-bottom: 8px !important; font-size: 1em !important; } .gradio-textbox, .gradio-dropdown, .gradio-radio { text-align: center !important; color: #1e293b !important; } /* Buttons styling */ .gradio-button { font-weight: 600 !important; font-size: 1em !important; border-radius: 12px !important; margin: 8px auto !important; display: block !important; color: #1e293b !important; } /* Styling cho markdown báo cáo chiến lược */ .gradio-markdown { background: linear-gradient(135deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%) !important; color: white !important; border-radius: 20px !important; padding: 30px !important; margin: 20px 0 !important; box-shadow: 0 8px 32px rgba(12, 83, 135, 0.25), 0 4px 16px rgba(0, 0, 0, 0.1) !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; backdrop-filter: blur(10px) !important; } .gradio-markdown * { color: white !important; } .gradio-markdown h1 { color: white !important; font-size: 2.2em !important; font-weight: 800 !important; text-align: center !important; margin: 0 0 25px 0 !important; text-shadow: 0 0 15px rgba(255, 255, 255, 0.3) !important; border-bottom: 3px solid rgba(255, 255, 255, 0.3) !important; padding-bottom: 15px !important; } .gradio-markdown h2 { color: white !important; font-size: 1.6em !important; font-weight: 700 !important; margin: 25px 0 15px 0 !important; padding: 12px 20px !important; background: rgba(255, 255, 255, 0.1) !important; border-radius: 12px !important; border-left: 5px solid rgba(255, 255, 255, 0.3) !important; } .gradio-markdown h3 { color: white !important; font-size: 1.3em !important; font-weight: 650 !important; margin: 20px 0 12px 0 !important; } .gradio-markdown p { color: white !important; font-size: 1.1em !important; line-height: 1.8 !important; font-weight: 500 !important; margin: 12px 0 !important; text-align: left !important; opacity: 0.95 !important; } .gradio-markdown ul, .gradio-markdown ol { color: white !important; font-size: 1.1em !important; line-height: 1.8 !important; font-weight: 500 !important; margin: 15px 0 !important; padding-left: 25px !important; } .gradio-markdown li { color: white !important; font-size: 1.1em !important; font-weight: 500 !important; margin: 8px 0 !important; padding: 5px 0 !important; } .gradio-markdown strong { color: white !important; font-weight: 700 !important; text-shadow: 0 0 5px rgba(255, 255, 255, 0.3) !important; } .gradio-markdown em { color: white !important; font-style: italic !important; font-weight: 600 !important; } /* Highlight cho footer của báo cáo */ .gradio-markdown hr { border: 0 !important; height: 2px !important; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent) !important; margin: 25px 0 15px 0 !important; } /* Style cho phần footer AI */ .gradio-markdown p:last-child { text-align: center !important; font-style: italic !important; color: white !important; font-size: 1.1em !important; margin-top: 20px !important; padding: 15px !important; background: rgba(255, 255, 255, 0.1) !important; border-radius: 10px !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; opacity: 0.9 !important; } /* Styling cho div footer trong markdown */ .gradio-markdown div { color: white !important; font-size: 1.1em !important; } /* Enhanced styling for better readability */ .gradio-markdown h3 { color: white !important; font-size: 1.5em !important; font-weight: 700 !important; margin: 25px 0 15px 0 !important; text-shadow: 0 0 10px rgba(255,255,255,0.3) !important; } .gradio-markdown h4 { color: white !important; font-size: 1.2em !important; font-weight: 650 !important; margin: 20px 0 12px 0 !important; } /* Improve list styling */ .gradio-markdown ul li { color: white !important; font-size: 1.15em !important; font-weight: 500 !important; margin: 10px 0 !important; padding: 8px 0 !important; line-height: 1.6 !important; } /* Glass morphism effects for modern look */ .glass-card { background: linear-gradient(135deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%) !important; color: white !important; backdrop-filter: blur(15px) !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; border-radius: 20px !important; padding: 20px !important; margin: 15px 0 !important; box-shadow: 0 8px 32px rgba(12, 83, 135, 0.25), 0 4px 16px rgba(0, 0, 0, 0.1) !important; } .glass-card * { color: white !important; } /* Remove extra borders and nested containers - CLEAN UP */ .info-card .gradio-group, .info-card .gradio-column, .info-card .gradio-row, .feature-box .gradio-group, .feature-box .gradio-column, .feature-box .gradio-row, .prediction-box .gradio-group, .prediction-box .gradio-column, .prediction-box .gradio-row, .plot-container .gradio-group, .plot-container .gradio-column, .plot-container .gradio-row, .glass-card .gradio-group, .glass-card .gradio-column, .glass-card .gradio-row { background: transparent !important; border: none !important; box-shadow: none !important; padding: 0 !important; margin: 0 !important; outline: none !important; } /* Simplify neon border effect - NO OVERLAPPING with other styles */ .neon-border { box-shadow: 0 0 15px rgba(12, 83, 135, 0.4) !important; border: 1px solid rgba(12, 83, 135, 0.6) !important; animation: neonPulse 3s ease-in-out infinite alternate !important; } @keyframes neonPulse { 0% { box-shadow: 0 0 15px rgba(12, 83, 135, 0.3); } 100% { box-shadow: 0 0 25px rgba(12, 83, 135, 0.5); } } /* Remove double borders on components - SIMPLIFIED */ .plot-container { background: linear-gradient(135deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%) !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; border-radius: 20px !important; padding: 15px !important; margin: 15px 0 !important; box-shadow: 0 8px 32px rgba(12, 83, 135, 0.25) !important; height: 550px !important; max-height: 650px !important; min-height: 400px !important; position: relative !important; overflow: hidden !important; backdrop-filter: blur(10px) !important; } /* Remove conflicting styles from multiple classes */ .plot-container.glass-card, .plot-container.neon-border, .glass-card.neon-border { border: 1px solid rgba(255, 255, 255, 0.3) !important; box-shadow: 0 8px 32px rgba(12, 83, 135, 0.25) !important; } /* Remove all conflicting neon border animations */ .neon-border { box-shadow: 0 0 5px rgba(12, 83, 135, 0.3), 0 0 10px rgba(12, 83, 135, 0.3), 0 0 15px rgba(12, 83, 135, 0.3), 0 0 20px rgba(12, 83, 135, 0.2), inset 0 0 5px rgba(255, 255, 255, 0.1); border: 1px solid rgba(12, 83, 135, 0.4); animation: neonPulse 2s ease-in-out infinite alternate; } /* REMOVE - this was causing duplication */ /* Modern button styling */ .btn-modern { background: linear-gradient(90deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%); border: none; border-radius: 16px; padding: 16px 32px; color: white; font-weight: 700; text-transform: uppercase; letter-spacing: 1.2px; box-shadow: 0 8px 25px rgba(12, 83, 135, 0.3), 0 3px 8px rgba(0, 0, 0, 0.1); transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); position: relative; overflow: hidden; font-size: 0.95em; } .btn-modern::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); transition: left 0.6s; } .btn-modern:hover { transform: translateY(-3px) scale(1.02); box-shadow: 0 15px 40px rgba(12, 83, 135, 0.4), 0 8px 16px rgba(0, 0, 0, 0.15); } .btn-modern:hover::before { left: 100%; } .btn-modern:active { transform: translateY(-1px) scale(1.01); } /* Animated background gradients */ .bg-blue-grad { background: linear-gradient(90deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%); background-size: 200% 200%; animation: gradientShift 4s ease-in-out infinite alternate; } .bg-gray-grad { background: linear-gradient(90deg, #cbd5e1 0%, #94a3b8 25%, #64748b 50%, #475569 75%, #334155 100%); background-size: 200% 200%; animation: gradientShift 4s ease-in-out infinite alternate; } @keyframes gradientShift { 0% { background-position: 0% 50%; } 100% { background-position: 100% 50%; } } /* Tech-style borders and effects */ .tech-border { position: relative; border: 2px solid transparent; border-radius: 16px; background: linear-gradient(45deg, #A9AAA9, #8194A0, #5A7E98, #33688F, #0C5387) border-box; mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0); mask-composite: exclude; -webkit-mask-composite: xor; transition: all 0.3s ease; } .tech-border:hover { box-shadow: 0 0 20px rgba(12, 83, 135, 0.3); } /* Floating squares background animation */ .gradio-container::before { content: ''; position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: -1; background-image: linear-gradient(45deg, rgba(26, 95, 145, 0.03) 25%, transparent 25%), linear-gradient(-45deg, rgba(26, 95, 145, 0.03) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(26, 95, 145, 0.03) 75%), linear-gradient(-45deg, transparent 75%, rgba(26, 95, 145, 0.03) 75%); background-size: 60px 60px; background-position: 0 0, 0 30px, 30px -30px, -30px 0px; animation: floatingSquares 20s linear infinite; } @keyframes floatingSquares { 0% { transform: translateY(0px) rotate(0deg); } 100% { transform: translateY(-60px) rotate(360deg); } } /* Individual floating squares */ .floating-square { position: fixed; pointer-events: none; z-index: -1; opacity: 0.1; animation: floatUpDown 15s infinite ease-in-out; } .floating-square:nth-child(1) { width: 20px; height: 20px; background: rgba(26, 95, 145, 0.2); top: 10%; left: 10%; animation-delay: 0s; animation-duration: 12s; } .floating-square:nth-child(2) { width: 15px; height: 15px; background: rgba(139, 139, 139, 0.15); top: 20%; right: 15%; animation-delay: 2s; animation-duration: 18s; } .floating-square:nth-child(3) { width: 25px; height: 25px; background: rgba(26, 95, 145, 0.1); bottom: 30%; left: 20%; animation-delay: 4s; animation-duration: 14s; } .floating-square:nth-child(4) { width: 18px; height: 18px; background: rgba(169, 170, 169, 0.12); top: 50%; right: 25%; animation-delay: 6s; animation-duration: 16s; } .floating-square:nth-child(5) { width: 22px; height: 22px; background: rgba(26, 95, 145, 0.08); bottom: 20%; right: 10%; animation-delay: 8s; animation-duration: 20s; } .floating-square:nth-child(6) { width: 16px; height: 16px; background: rgba(90, 126, 152, 0.1); top: 70%; left: 50%; animation-delay: 10s; animation-duration: 13s; } @keyframes floatUpDown { 0%, 100% { transform: translateY(0px) rotate(0deg) scale(1); opacity: 0.1; } 25% { transform: translateY(-20px) rotate(90deg) scale(1.1); opacity: 0.2; } 50% { transform: translateY(-40px) rotate(180deg) scale(0.9); opacity: 0.15; } 75% { transform: translateY(-20px) rotate(270deg) scale(1.05); opacity: 0.18; } } /* Animated gradient background */ .gradio-container::after { content: ''; position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: -2; background: radial-gradient(circle at 20% 20%, rgba(26, 95, 145, 0.05) 0%, transparent 50%), radial-gradient(circle at 80% 80%, rgba(139, 139, 139, 0.03) 0%, transparent 50%), radial-gradient(circle at 40% 60%, rgba(26, 95, 145, 0.02) 0%, transparent 50%); animation: gradientFloat 25s ease-in-out infinite; } @keyframes gradientFloat { 0%, 100% { transform: scale(1) rotate(0deg); opacity: 0.5; } 50% { transform: scale(1.1) rotate(180deg); opacity: 0.3; } } /* Particle effect overlay */ .particle-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: -1; background-image: radial-gradient(2px 2px at 20px 30px, rgba(26, 95, 145, 0.1), transparent), radial-gradient(2px 2px at 40px 70px, rgba(139, 139, 139, 0.08), transparent), radial-gradient(1px 1px at 90px 40px, rgba(26, 95, 145, 0.06), transparent), radial-gradient(1px 1px at 130px 80px, rgba(169, 170, 169, 0.05), transparent), radial-gradient(2px 2px at 160px 30px, rgba(26, 95, 145, 0.07), transparent); background-repeat: repeat; background-size: 200px 100px, 180px 120px, 220px 90px, 190px 110px, 210px 95px; animation: particleDrift 30s linear infinite; } @keyframes particleDrift { 0% { transform: translateX(0) translateY(0); } 100% { transform: translateX(-200px) translateY(-100px); } } /* Additional geometric shapes */ .geometric-shapes { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: -1; overflow: hidden; } .shape { position: absolute; opacity: 0.08; animation: float 20s infinite ease-in-out; } .shape.triangle { width: 0; height: 0; border-left: 10px solid transparent; border-right: 10px solid transparent; border-bottom: 20px solid rgba(26, 95, 145, 0.15); top: 15%; left: 80%; animation-delay: 1s; animation-duration: 25s; } .shape.circle { width: 30px; height: 30px; border-radius: 50%; background: rgba(139, 139, 139, 0.1); top: 60%; left: 15%; animation-delay: 3s; animation-duration: 18s; } .shape.hexagon { width: 20px; height: 11.55px; background: rgba(26, 95, 145, 0.12); position: relative; top: 40%; right: 5%; animation-delay: 5s; animation-duration: 22s; } .shape.hexagon:before, .shape.hexagon:after { content: ""; position: absolute; width: 0; border-left: 10px solid transparent; border-right: 10px solid transparent; } .shape.hexagon:before { bottom: 100%; border-bottom: 5.77px solid rgba(26, 95, 145, 0.12); } .shape.hexagon:after { top: 100%; border-top: 5.77px solid rgba(26, 95, 145, 0.12); } .shape.diamond { width: 15px; height: 15px; background: rgba(169, 170, 169, 0.1); transform: rotate(45deg); top: 75%; left: 70%; animation-delay: 7s; animation-duration: 16s; } @keyframes float { 0%, 100% { transform: translateY(0px) rotate(0deg); opacity: 0.08; } 25% { transform: translateY(-30px) rotate(90deg); opacity: 0.15; } 50% { transform: translateY(-60px) rotate(180deg); opacity: 0.12; } 75% { transform: translateY(-30px) rotate(270deg); opacity: 0.18; } } /* Animated grid overlay */ .grid-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: -2; background-image: linear-gradient(rgba(26, 95, 145, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(26, 95, 145, 0.03) 1px, transparent 1px); background-size: 50px 50px; animation: gridMove 40s linear infinite; } @keyframes gridMove { 0% { transform: translate(0, 0); } 100% { transform: translate(50px, 50px); } } /* Wave animation for bottom */ .wave-container { position: fixed; bottom: 0; left: 0; width: 100%; height: 100px; pointer-events: none; z-index: -1; overflow: hidden; } .wave { position: absolute; bottom: 0; left: 0; width: 200%; height: 100px; background: linear-gradient(90deg, rgba(26, 95, 145, 0.03) 0%, rgba(139, 139, 139, 0.02) 50%, rgba(26, 95, 145, 0.03) 100%); border-radius: 100% 100% 0 0; animation: waveAnimation 15s infinite linear; } .wave:nth-child(2) { animation-delay: -5s; animation-duration: 20s; opacity: 0.5; height: 80px; } .wave:nth-child(3) { animation-delay: -10s; animation-duration: 25s; opacity: 0.3; height: 60px; } @keyframes waveAnimation { 0% { transform: translateX(-50%) rotate(0deg); } 100% { transform: translateX(-50%) rotate(360deg); } } /* Shooting stars effect */ .shooting-star { position: fixed; top: 20%; right: -10px; width: 2px; height: 2px; background: rgba(26, 95, 145, 0.8); border-radius: 50%; pointer-events: none; z-index: -1; animation: shootingStar 8s linear infinite; } .shooting-star::after { content: ''; position: absolute; top: 0; right: 0; height: 2px; width: 50px; background: linear-gradient(90deg, rgba(26, 95, 145, 0.8), transparent); } .shooting-star:nth-child(1) { animation-delay: 0s; top: 15%; } .shooting-star:nth-child(2) { animation-delay: 3s; top: 25%; animation-duration: 12s; } .shooting-star:nth-child(3) { animation-delay: 6s; top: 35%; animation-duration: 10s; } @keyframes shootingStar { 0% { transform: translateX(0) translateY(0); opacity: 1; } 70% { opacity: 1; } 100% { transform: translateX(-100vw) translateY(50px); opacity: 0; } } /* Holographic effect */ .holographic { background: linear-gradient(45deg, #A9AAA9 0%, #8194A0 20%, #5A7E98 40%, #33688F 60%, #0C5387 80%, #A9AAA9 100%); background-size: 400% 400%; animation: hologram 6s ease-in-out infinite; } @keyframes hologram { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } } /* Floating animation for cards */ @keyframes float { 0%, 100% { transform: translateY(0px); } 50% { transform: translateY(-5px); } } .floating { animation: float 3s ease-in-out infinite; } /* Modern card design - SINGLE DEFINITION */ .modern-card { background: linear-gradient(135deg, #A9AAA9 0%, #8194A0 25%, #5A7E98 50%, #33688F 75%, #0C5387 100%) !important; color: white !important; backdrop-filter: blur(20px) !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; border-radius: 20px !important; padding: 25px !important; margin: 15px 0 !important; box-shadow: 0 8px 25px rgba(12, 83, 135, 0.25) !important; transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1) !important; position: relative !important; overflow: hidden !important; } .modern-card * { color: white !important; } .modern-card::before { content: '' !important; position: absolute !important; top: 0 !important; left: 0 !important; right: 0 !important; height: 1px !important; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.8), transparent) !important; } .modern-card:hover { transform: translateY(-5px) scale(1.02) !important; box-shadow: 0 15px 35px rgba(12, 83, 135, 0.35) !important; } /* Remove duplicate info card styling */ /* Info card styling already handled in unified section above */ /* Loading spinner enhancement - cải thiện animation */ .loading-spinner { border: 4px solid rgba(255, 255, 255, 0.1); border-top: 4px solid #00ff88; border-right: 4px solid #0099ff; border-radius: 50%; width: 60px; height: 60px; animation: spin 1.2s linear infinite, pulse 2s ease-in-out infinite; margin: 0 auto 25px; box-shadow: 0 0 30px rgba(0, 255, 136, 0.4), 0 0 60px rgba(0, 153, 255, 0.2); } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes pulse { 0%, 100% { transform: scale(1); box-shadow: 0 0 30px rgba(0, 255, 136, 0.4); } 50% { transform: scale(1.05); box-shadow: 0 0 40px rgba(0, 255, 136, 0.6), 0 0 80px rgba(0, 153, 255, 0.3); } } /* Cải thiện progress bar */ .progress-bar { background: rgba(255, 255, 255, 0.1); border-radius: 15px; height: 8px; margin: 15px 0; overflow: hidden; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2); } .progress-fill { background: linear-gradient(90deg, #00ff88, #0099ff, #00ff88); height: 100%; border-radius: 15px; transition: width 0.5s ease-out; animation: shimmer 2s infinite; box-shadow: 0 0 10px rgba(0, 255, 136, 0.5); } @keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } /* Dropdown info text styling - make text white using #FFFFFF */ .gradio-dropdown .info, .gradio-dropdown .gr-form-info, .gradio-dropdown .svelte-1gfkn6j, label[data-testid="block-info"], .gr-form .info, .gradio-container .info, .gradio-container [data-testid="block-info"], .gradio-container .gr-form .info, .gradio-container .gr-form-info, .gradio-container .svelte-1gfkn6j, .gradio-container span.info, .gradio-container p.info, .gradio-container div.info, .gradio-container .block-info, .gradio-app .info, .gradio-app [data-testid="block-info"], .gradio-app .gr-form-info, .gradio-app .svelte-1gfkn6j { color: #FFFFFF !important; opacity: 1 !important; } /* More specific targeting for dropdown info */ .gr-dropdown .info, .gr-dropdown [data-testid="block-info"], .gr-textbox .info, .gr-textbox [data-testid="block-info"] { color: #FFFFFF !important; opacity: 1 !important; } /* Global override for all info elements */ * .info { color: #FFFFFF !important; } *[data-testid="block-info"] { color: #FFFFFF !important; } /* Specific class for dropdown with white info text */ .white-info-dropdown .info, .white-info-dropdown [data-testid="block-info"], .white-info-dropdown span, .white-info-dropdown p { color: #FFFFFF !important; opacity: 1 !important; } /* Force all Gradio info text to be white */ .gradio-container * { --block-info-text-color: #FFFFFF !important; } .gradio-container .block-info, .gradio-container .info-text, .gradio-container .description { color: #FFFFFF !important; } /* Specific targeting for spans with data-testid="block-info" */ span[data-testid="block-info"], div[data-testid="block-info"], label[data-testid="block-info"], .svelte-g2oxp3.has-info { color: #FFFFFF !important; background: transparent !important; text-shadow: 0 0 5px rgba(0, 0, 0, 0.5) !important; } /* Target the specific class mentioned */ .svelte-g2oxp3 { color: #FFFFFF !important; } /* Radio button and form labels improvements */ .gradio-radio label, .gradio-radio span, .gradio-dropdown label, .gradio-dropdown span { color: #FFFFFF !important; font-weight: 600 !important; text-shadow: 0 0 3px rgba(0, 0, 0, 0.3) !important; } /* Form component styling */ .gradio-radio, .gradio-dropdown { background: transparent !important; border: none !important; padding: 10px !important; } /* Remove extra backgrounds on form components */ .gradio-radio .form, .gradio-dropdown .form, .gradio-button .form { background: transparent !important; border: none !important; box-shadow: none !important; } /* Clean up nested containers */ .gradio-column > div, .gradio-row > div { background: transparent !important; border: none !important; box-shadow: none !important; } """ # ---------- MODULE STREAMING THỰC SỰ ---------- """ Hiệu ứng 'AI đang gõ' cho báo cáo CEO: - tao_bao_cao_noi_dung: tạo toàn văn báo cáo (markdown, thuần Việt) - tao_bao_cao_streaming: generator stream từng cụm có con trỏ nhấp nháy - tao_streaming_text: alias dùng cho tương thích ngược - xu_ly_du_doan_voi_loading: trả về (fig, tóm tắt, streamer hoặc full text) - bat_dau_loading / tinh_toan_ket_qua: workflow loading + stream - cap_nhat_thong_tin_case, tao_csv_gia_lap: tiện ích trình bày demo Lưu ý: - Mặc định trả về generator để front-end cập nhật liên tục. - Nếu framework của bạn không hỗ trợ generator, gọi với streaming=False để nhận về chuỗi đầy đủ rồi render một lần. """ # ---------- TIỆN ÍCH NHỎ ---------- def tao_loading_html_streaming(thong_diep: str = "🔎 Đang xử lý", do_mo: float = 0.15) -> str: """Khối HTML loading gọn, dễ nhúng.""" return f"""
Sản phẩm: {thong_tin.get('san_pham', '')}
Mô tả: {thong_tin.get('mo_ta', '')}
{chi_tiet_buoc}
🤖 FoxAI đang xử lý {pct}% • Vui lòng chờ trong giây lát...
🎯 Chọn SKU (Thủ Đô / Thăng Long) và nhấn "Tạo Dự Báo AI"
Hệ thống sẽ tính xu hướng, tồn an toàn, ROP và cảnh báo kênh GT/MT
{tong_don_vi:,} cây
{trung_binh:,.0f} cây
{dinh:,} cây
{ty_le_tang_truong:+.1f}%
📅 Chu Kỳ Dự Báo: {chu_ky} | Mức dịch vụ mục tiêu: {muc_dich_vu:.0%} | LT: {lead_time_ngay} ngày
⚡ Phân tích dữ liệu • 🧠 Machine Learning • 📊 Tạo insights • 🎯 Chiến lược AI
Phân tích thông minh với công nghệ FoxAI
Chọn sản phẩm, chu kỳ dự báo và nhận phân tích AI chi tiết
Trạng thái kết nối cơ sở dữ liệu: Ổn định
Định dạng: Time series data với cột thời gian và doanh thu
Cập nhật: Tự động sync theo mặt hàng được chọn
Những công nghệ tiên tiến đang được nghiên cứu và phát triển
Kết hợp nhiều kiến trúc AI (RNN, Transformer, Prophet, XGBoost...) để tăng độ chính xác và ổn định của dự báo.
Mô hình tự động học lại khi có thay đổi bất thường về nhu cầu (ví dụ mùa vụ, sự kiện kinh tế, khuyến mãi).
Giải thích chi tiết tại sao hệ thống đưa ra dự báo cụ thể, hiển thị các yếu tố tác động chính (giá, mùa vụ, đối thủ cạnh tranh).
Cập nhật dự báo liên tục từ dòng dữ liệu trực tiếp, kèm cảnh báo khi có tín hiệu bất thường.
Cho phép người dùng giả lập các kịch bản (tăng giá, thay đổi kênh phân phối, biến động thị trường) và xem tác động dự báo ngay.
Dự báo chi tiết tới từng SKU, từng khu vực, thậm chí từng khách hàng trọng điểm.
Tự động sinh báo cáo phân tích bằng ngôn ngữ tự nhiên (Natural Language Generation) → người dùng không cần đọc chart quá nhiều.
Tạo insights dạng văn bản từ dữ liệu
Báo cáo bằng giọng nói tự nhiên
Hỏi đáp trực tiếp với AI
Gửi tóm tắt qua email tự động
🦊 Powered by FoxAi • 🇻🇳 Made in Vietnam • 🤖 Advanced AI
YOUR TRUSTED AI PARTNER