diff --git a/luci-app-adguardhome/.official_channel b/luci-app-adguardhome/.official_channel
index 8ab67f2e7..49f33a8c6 100644
--- a/luci-app-adguardhome/.official_channel
+++ b/luci-app-adguardhome/.official_channel
@@ -1 +1 @@
-snapshot
+both
diff --git a/luci-app-adguardhome/.official_version_release b/luci-app-adguardhome/.official_version_release
new file mode 100644
index 000000000..a9b889cb7
--- /dev/null
+++ b/luci-app-adguardhome/.official_version_release
@@ -0,0 +1 @@
+26.273.20498~1fcad1e
diff --git a/luci-app-adguardhome/.official_json_version b/luci-app-adguardhome/.official_version_snapshot
similarity index 100%
rename from luci-app-adguardhome/.official_json_version
rename to luci-app-adguardhome/.official_version_snapshot
diff --git a/luci-theme-glass/.gitignore b/luci-theme-glass/.gitignore
new file mode 100644
index 000000000..d90e76d4e
--- /dev/null
+++ b/luci-theme-glass/.gitignore
@@ -0,0 +1,24 @@
+# Internal use
+docs/
+/resources/
+
+# Hidden dotfiles/folders
+.*
+!.gitignore
+!.gitkeep
+
+# Editor files
+*.swp
+*.swo
+*~
+
+# Signing keys (secret key is in GitHub Secrets, NEVER commit)
+/keys/
+
+# Build output
+dist/
+
+# Node modules (for LESS compilation)
+node_modules/
+package-lock.json
+
diff --git a/luci-theme-glass/LICENSE b/luci-theme-glass/LICENSE
new file mode 100644
index 000000000..f288702d2
--- /dev/null
+++ b/luci-theme-glass/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/luci-theme-glass/Makefile b/luci-theme-glass/Makefile
new file mode 100644
index 000000000..7cde38b34
--- /dev/null
+++ b/luci-theme-glass/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2024-2026 luci-theme-glass contributors
+#
+# This is free software, licensed under the GNU General Public License v3.0.
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Glass - Apple-inspired glassmorphism theme for LuCI
+LUCI_DEPENDS:=
+PKG_VERSION:=$(shell cat $(CURDIR)/ucode/template/themes/glass/version | tr -d '[:space:]')
+PKG_RELEASE:=1
+
+# Disable CSS minification (backdrop-filter can break with csstidy)
+CONFIG_LUCI_CSSTIDY:=
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-theme-glass/README.md b/luci-theme-glass/README.md
new file mode 100644
index 000000000..64bf7bb45
--- /dev/null
+++ b/luci-theme-glass/README.md
@@ -0,0 +1,236 @@
+# luci-theme-glass
+
+A glassmorphism theme for OpenWrt LuCI, inspired by Apple's visionOS and macOS. Frosted-glass panels, backdrop blur, translucent buttons, and inset highlights across the entire UI.
+
+**[Live Demo](https://rchen14b.github.io/luci-theme-glass/)** | **[Download](https://github.com/rchen14b/luci-theme-glass/releases/latest)**
+
+
+
+
+[](https://buymeacoffee.com/rchen14b)
+
+## Screenshots
+
+### Login
+
+
+### Overview (Light)
+
+
+### Overview (Dark)
+
+
+### Software
+
+
+### Traffic Graph
+
+
+## Features
+
+### Design
+- **Glassmorphism everywhere** — `backdrop-filter: blur() saturate()` with multi-stop gradients, translucent backgrounds, 0.5px glass borders, and inset glow highlights on all panels
+- **Glass buttons** — Neutral buttons are translucent glass, primary buttons are blue-tinted, danger buttons are red-tinted. No solid-color buttons
+- **Frosted sidebar** — Fixed sidebar with 30px blur and inset light diffusion
+- **Glass header** — Top nav bar with glass pill badges for page title and status indicators
+- **Live status indicators** — CPU load, RAM %, uptime, and WAN throughput in the header bar. Auto-detects WAN interface, resolves bridges, handles DSA hardware offloading. MikroTik-style speed formatting. Color-coded levels and detailed tooltips
+- **Glass login card** — Frosted login page with translucent inputs
+
+### Theming
+- **Auto / Light / Dark mode** — Toggle in the header bar cycles through Auto (follows OS `prefers-color-scheme`), Light, and Dark. Works on every page including login. Saved to `localStorage` so it persists across sessions
+- **Dark mode glass** — Lower-opacity gradients (`rgba(255,255,255,0.05-0.08)`) with adjusted shadows
+- **Server-side default** — Set default mode via UCI config (`option mode 'dark'`, `'light'`, or `'normal'` for auto). The client-side toggle overrides this per browser
+- **Accent color config** — Primary color, blur radius, and glass transparency are adjustable via UCI
+- **Custom wallpapers** — Drop an image or video into the background folder. It shows through all glass panels
+
+### Layout
+- **Responsive** — Sidebar collapses to a slide-out drawer on mobile with hamburger toggle
+- **Sub-navigation bar** — CBI tab menus get moved into a secondary glass bar below the header
+- **Sticky header** — Fixed header with smooth transitions
+
+### Compatibility
+- **Full LuCI coverage** — Styles all CBI components: sections, tables, forms, dropdowns, checkboxes, textareas, progress bars, tooltips, modals, tab menus
+- **Page-specific fixes** — Overrides inline CSS from LuCI view JS (package manager layout, port status grid, network status cards)
+- **OpenWrt 23.05+** — Uses ucode `.ut` templates
+- **No dependencies** — Pure CSS, no JS frameworks
+
+## Installation
+
+### From package repository (auto-update, signed)
+
+Add the repository to get updates via the package manager. Both opkg and apk feeds are signed.
+
+**OpenWrt 24.10 and earlier (opkg):**
+```sh
+# First install: fetch signing public key (one-time)
+wget -O /etc/opkg/keys/ef97c468af77cea1 \
+ https://raw.githubusercontent.com/rchen14b/luci-theme-glass/main/root/etc/opkg/keys/ef97c468af77cea1
+
+# Add feed and install
+echo "src/gz glass https://rchen14b.github.io/luci-theme-glass/packages" >> /etc/opkg/customfeeds.conf
+opkg update
+opkg install luci-theme-glass
+```
+
+**OpenWrt 25.12+ (apk):**
+```sh
+# First install: fetch signing public key (one-time)
+wget -O /etc/apk/keys/glass-apk.rsa.pub \
+ https://raw.githubusercontent.com/rchen14b/luci-theme-glass/main/root/etc/apk/keys/glass-apk.rsa.pub
+
+# Add feed and install
+echo "https://rchen14b.github.io/luci-theme-glass/apk/packages.adb" >> /etc/apk/repositories.d/customfeeds.list
+apk update
+apk add luci-theme-glass
+```
+
+After the first install, the public key persists on the router — future updates via `opkg upgrade` / `apk upgrade` just work. The LuCI web-gui "Update lists" button also works without warnings.
+
+### From release (manual)
+
+Download the latest package from [Releases](../../releases):
+
+- **OpenWrt 25.12+** (apk): `apk add --allow-untrusted luci-theme-glass-*.apk`
+- **OpenWrt 24.10 and earlier** (opkg): `opkg install luci-theme-glass_*.ipk`
+
+### From source
+
+```sh
+# Add to your OpenWrt build tree
+cd /path/to/openwrt
+git clone https://github.com/rchen14b/luci-theme-glass.git package/luci-theme-glass
+
+# Build
+make package/luci-theme-glass/compile V=s
+```
+
+### Manual install (dev)
+
+```sh
+# Copy theme files to router
+scp -r htdocs/luci-static/glass/ root@router:/www/luci-static/glass/
+scp htdocs/luci-static/resources/menu-glass.js root@router:/www/luci-static/resources/
+scp -r ucode/template/themes/glass/ root@router:/usr/share/ucode/luci/template/themes/glass/
+
+# Register and activate
+ssh root@router "uci set luci.themes.Glass=/luci-static/glass && \
+ uci set luci.main.mediaurlbase=/luci-static/glass && \
+ uci commit luci"
+```
+
+## Configuration
+
+After installation, select **Glass** in **System > System > Language and Style**.
+
+### Theme config
+
+Create `/etc/config/glass` on the router:
+
+```
+config global
+ option mode 'normal'
+ option primary '#007AFF'
+ option dark_primary '#0A84FF'
+ option blur '20'
+ option transparency '0.72'
+ option blur_dark '25'
+ option transparency_dark '0.30'
+```
+
+| Option | Default | Description |
+|--------|---------|-------------|
+| `mode` | `normal` | `normal` (auto), `dark`, or `light` |
+| `primary` | `#007AFF` | Accent color (light mode) |
+| `dark_primary` | `#0A84FF` | Accent color (dark mode) |
+| `blur` | `20` | Backdrop blur in px (light mode) |
+| `transparency` | `0.72` | Glass panel opacity 0-1 (light mode) |
+| `blur_dark` | `25` | Backdrop blur in px (dark mode) |
+| `transparency_dark` | `0.30` | Glass panel opacity 0-1 (dark mode) |
+
+### Custom wallpapers
+
+Place a file named `bg.*` in `/www/luci-static/glass/background/` on the router.
+
+Supported: `.jpg`, `.jpeg`, `.png`, `.gif`, `.webp`, `.mp4`, `.webm`
+
+The background shows through all glass panels.
+
+## Development
+
+### Prerequisites
+
+- Node.js with `lessc`: `npm install -g less`
+
+### Building CSS
+
+```sh
+lessc less/cascade.less htdocs/luci-static/glass/css/cascade.css
+lessc less/dark.less htdocs/luci-static/glass/css/dark.css
+```
+
+### Project structure
+
+```
+luci-theme-glass/
+├── Makefile # OpenWrt package build
+├── htdocs/luci-static/
+│ ├── glass/
+│ │ ├── css/ # Compiled CSS
+│ │ ├── img/ # Logo and icons
+│ │ └── background/ # User wallpapers (bg.jpg, etc.)
+│ └── resources/
+│ ├── menu-glass.js # Client-side menu renderer
+│ └── status-glass.js # Header status indicators (CPU, RAM, net, uptime)
+├── less/ # LESS source
+│ ├── cascade.less # Master import file
+│ ├── variables.less # Design tokens (CSS custom properties)
+│ ├── normalize.less # CSS reset
+│ ├── glass.less # Glassmorphism mixins
+│ ├── layout.less # Sidebar, header, footer, content
+│ ├── components.less # CBI forms, tables, buttons, alerts
+│ ├── sysauth.less # Login page
+│ ├── page-fix.less # LuCI page-specific overrides
+│ ├── dark.less # Dark mode overrides
+│ └── responsive.less # Mobile breakpoints
+├── ucode/template/themes/glass/ # Server-side ucode templates
+│ ├── header.ut # HTML head + sidebar + header
+│ ├── header_login.ut # Login page variant
+│ ├── footer.ut # Footer + scripts
+│ ├── footer_login.ut # Login footer variant
+│ └── sysauth.ut # Login page
+├── root/ # Files installed to device root
+│ ├── etc/uci-defaults/ # First-boot theme registration
+│ └── usr/share/rpcd/acl.d/ # ACL permissions
+└── screenshots/
+```
+
+### Design system
+
+Design tokens live in `variables.less`. Glass effects use three LESS mixins:
+
+| Mixin | Use | Blur | Opacity |
+|-------|-----|------|---------|
+| `.glass-effect()` | Panels, cards | `var(--glass-blur)` | `var(--glass-bg)` |
+| `.glass-frosted()` | Sidebar, login card | 30px | Higher |
+| `.glass-subtle()` | Secondary elements | Lower | Minimal |
+
+### Version bump checklist
+
+1. `Makefile` — `PKG_VERSION`
+2. `README.md` — version badge
+3. `ucode/template/themes/glass/sysauth.ut` — login version text
+4. `ucode/template/themes/glass/header.ut` — sidebar version text
+5. `ucode/template/themes/glass/footer.ut` — footer version text
+
+## Credits
+
+- Architecture based on [luci-theme-argon](https://github.com/jerrykuku/luci-theme-argon)
+- Glass effects from [glass-ui](https://github.com/crenspire/glass-ui)
+- Design language from Apple visionOS / macOS
+
+## License
+
+Dual-licensed:
+
+- **Open Source**: [GPL-3.0](LICENSE) — free for personal, educational, and open-source use. Derivatives must also be GPL-3.0.
+- **Commercial**: Proprietary/closed-source use requires a separate license. Contact the author.
diff --git a/luci-theme-glass/build-pkg.sh b/luci-theme-glass/build-pkg.sh
new file mode 100755
index 000000000..4fef455c4
--- /dev/null
+++ b/luci-theme-glass/build-pkg.sh
@@ -0,0 +1,151 @@
+#!/bin/bash
+set -e
+
+PKG_NAME="luci-theme-glass"
+PKG_VERSION="$(tr -d '[:space:]' < "$(dirname "$0")/ucode/template/themes/glass/version")"
+PKG_RELEASE="1"
+MAINTAINER="Ryan Chen "
+DESCRIPTION="Glass - Apple-inspired glassmorphism theme for LuCI"
+HOMEPAGE="https://github.com/rchen14b/luci-theme-glass"
+LICENSE="GPL-3.0"
+
+DIST_DIR="dist"
+WORK_DIR=$(mktemp -d)
+
+# Use GNU tar (gtar) on macOS, fall back to tar
+TAR=$(command -v gtar 2>/dev/null || echo tar)
+
+trap "rm -rf $WORK_DIR" EXIT
+
+echo "==> Preparing file tree..."
+
+DATA_DIR="$WORK_DIR/data"
+mkdir -p "$DATA_DIR/www/luci-static/glass"
+mkdir -p "$DATA_DIR/www/luci-static/resources"
+mkdir -p "$DATA_DIR/www/luci-static/resources/view/system"
+mkdir -p "$DATA_DIR/usr/share/ucode/luci/template/themes/glass"
+mkdir -p "$DATA_DIR/usr/share/rpcd/acl.d"
+mkdir -p "$DATA_DIR/usr/share/luci/menu.d"
+mkdir -p "$DATA_DIR/etc/uci-defaults"
+mkdir -p "$DATA_DIR/etc/config"
+mkdir -p "$DATA_DIR/etc/opkg/keys"
+mkdir -p "$DATA_DIR/etc/apk/keys"
+
+cp -r htdocs/luci-static/glass/css "$DATA_DIR/www/luci-static/glass/"
+cp -r htdocs/luci-static/glass/img "$DATA_DIR/www/luci-static/glass/"
+mkdir -p "$DATA_DIR/www/luci-static/glass/background"
+cp htdocs/luci-static/resources/menu-glass.js "$DATA_DIR/www/luci-static/resources/"
+cp htdocs/luci-static/resources/status-glass.js "$DATA_DIR/www/luci-static/resources/"
+cp htdocs/luci-static/resources/view/system/glass.js "$DATA_DIR/www/luci-static/resources/view/system/"
+cp ucode/template/themes/glass/*.ut "$DATA_DIR/usr/share/ucode/luci/template/themes/glass/"
+cp ucode/template/themes/glass/version "$DATA_DIR/usr/share/ucode/luci/template/themes/glass/"
+cp root/usr/share/rpcd/acl.d/luci-theme-glass.json "$DATA_DIR/usr/share/rpcd/acl.d/"
+cp root/usr/share/luci/menu.d/luci-theme-glass.json "$DATA_DIR/usr/share/luci/menu.d/"
+cp root/etc/uci-defaults/30_luci-theme-glass "$DATA_DIR/etc/uci-defaults/"
+cp root/etc/config/glass "$DATA_DIR/etc/config/"
+cp root/etc/opkg/keys/* "$DATA_DIR/etc/opkg/keys/" 2>/dev/null || true
+cp root/etc/apk/keys/* "$DATA_DIR/etc/apk/keys/" 2>/dev/null || true
+
+INSTALLED_SIZE=$(du -sk "$DATA_DIR" | cut -f1)
+INSTALLED_BYTES=$((INSTALLED_SIZE * 1024))
+BUILD_DATE=$(date +%s)
+
+mkdir -p "$DIST_DIR"
+
+# ============================================================
+# Build IPK (opkg — OpenWrt 24.10 and earlier)
+# ============================================================
+echo "==> Building IPK..."
+
+IPK_DIR="$WORK_DIR/ipk"
+mkdir -p "$IPK_DIR/control"
+
+cat > "$IPK_DIR/control/control" < "$IPK_DIR/control/postinst" <<'SCRIPT'
+#!/bin/sh
+if [ "$PKG_UPGRADE" != 1 ]; then
+ uci get luci.themes.Glass >/dev/null 2>&1 || \
+ uci batch <<-EOF
+ set luci.themes.Glass=/luci-static/glass
+ set luci.main.mediaurlbase=/luci-static/glass
+ commit luci
+ EOF
+fi
+exit 0
+SCRIPT
+chmod 755 "$IPK_DIR/control/postinst"
+
+echo "2.0" > "$IPK_DIR/debian-binary"
+
+(cd "$DATA_DIR" && $TAR --format=gnu --numeric-owner --owner=0 --group=0 -cf - . | gzip -n > "$IPK_DIR/data.tar.gz")
+(cd "$IPK_DIR/control" && $TAR --format=gnu --numeric-owner --owner=0 --group=0 -cf - . | gzip -n > "$IPK_DIR/control.tar.gz")
+(cd "$IPK_DIR" && $TAR --format=gnu --numeric-owner --owner=0 --group=0 -cf - ./debian-binary ./data.tar.gz ./control.tar.gz | gzip -n > "$OLDPWD/$DIST_DIR/${PKG_NAME}_${PKG_VERSION}-${PKG_RELEASE}_all.ipk")
+
+echo " -> $DIST_DIR/${PKG_NAME}_${PKG_VERSION}-${PKG_RELEASE}_all.ipk"
+
+# ============================================================
+# Build APK (apk-tools v3 ADB format — OpenWrt 25.12+)
+# Requires Docker (uses Alpine container with apk mkpkg)
+# ============================================================
+echo "==> Building APK..."
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+APK_FILENAME="${PKG_NAME}-${PKG_VERSION}-r${PKG_RELEASE}.apk"
+
+if ! command -v docker &>/dev/null; then
+ echo " [SKIP] Docker not found — APK build requires Docker with Alpine"
+ echo " IPK package was built successfully."
+else
+ # Prepare post-install script for APK
+ APK_SCRIPT="$WORK_DIR/post-install.sh"
+ cat > "$APK_SCRIPT" <<'SCRIPT'
+#!/bin/sh
+uci get luci.themes.Glass >/dev/null 2>&1 || \
+uci batch <<-EOF
+ set luci.themes.Glass=/luci-static/glass
+ set luci.main.mediaurlbase=/luci-static/glass
+ commit luci
+EOF
+exit 0
+SCRIPT
+ chmod 755 "$APK_SCRIPT"
+
+ # Build using Alpine container with apk mkpkg
+ docker run --rm \
+ -v "$DATA_DIR:/pkg/files:ro" \
+ -v "$APK_SCRIPT:/pkg/post-install.sh:ro" \
+ -v "$SCRIPT_DIR/$DIST_DIR:/pkg/out" \
+ alpine:latest sh -c "
+ apk add --no-cache apk-tools-mkpkg >/dev/null 2>&1 || true
+ apk mkpkg \
+ --info 'name:$PKG_NAME' \
+ --info 'version:${PKG_VERSION}-r${PKG_RELEASE}' \
+ --info 'description:$DESCRIPTION' \
+ --info 'arch:noarch' \
+ --info 'license:$LICENSE' \
+ --info 'origin:$PKG_NAME' \
+ --info 'url:$HOMEPAGE' \
+ --info 'maintainer:$MAINTAINER' \
+ --script 'post-install:/pkg/post-install.sh' \
+ --files /pkg/files \
+ --output /pkg/out/$APK_FILENAME
+ " 2>&1
+
+ echo " -> $DIST_DIR/$APK_FILENAME"
+fi
+
+echo ""
+echo "==> Done! Packages in $DIST_DIR/"
+ls -lh "$DIST_DIR/"
diff --git a/luci-theme-glass/htdocs/luci-static/glass/background/.gitkeep b/luci-theme-glass/htdocs/luci-static/glass/background/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/luci-theme-glass/htdocs/luci-static/glass/css/cascade.css b/luci-theme-glass/htdocs/luci-static/glass/css/cascade.css
new file mode 100644
index 000000000..ab274ba9c
--- /dev/null
+++ b/luci-theme-glass/htdocs/luci-static/glass/css/cascade.css
@@ -0,0 +1,2591 @@
+/* ==========================================================================
+ luci-theme-glass — Master Stylesheet
+ Import order matters: tokens → reset → mixins → layout → components → fixes
+ ========================================================================== */
+/* ==========================================================================
+ Design Tokens — luci-theme-glass
+ All CSS custom properties live here for easy theming.
+ ========================================================================== */
+:root {
+ /* ---- Typography ---- */
+ --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "Segoe UI", Roboto, "Noto Sans", sans-serif;
+ --font-mono: "SF Mono", SFMono-Regular, ui-monospace, Menlo, Consolas, "Liberation Mono", monospace;
+ --font-size-base: 0.875rem;
+ --font-size-sm: 0.8125rem;
+ --font-size-lg: 1rem;
+ --font-size-xl: 1.25rem;
+ --font-size-2xl: 1.75rem;
+ --line-height: 1.5;
+ --font-weight-normal: 400;
+ --font-weight-medium: 500;
+ --font-weight-semibold: 600;
+ --font-weight-bold: 700;
+ /* ---- Colors — Light mode ---- */
+ --color-primary: #007AFF;
+ --color-primary-hover: #0066d6;
+ --color-primary-active: #004fad;
+ --color-primary-rgb: 0, 122, 255;
+ --color-success: #34C759;
+ --color-warning: #FF9500;
+ --color-danger: #FF3B30;
+ --color-info: #5AC8FA;
+ --color-text: #1d1d1f;
+ --color-text-secondary: #6e6e73;
+ --color-text-tertiary: #aeaeb2;
+ --color-text-inverse: #ffffff;
+ --color-bg: #f5f5f7;
+ --color-bg-elevated: #ffffff;
+ --color-bg-secondary: #e8e8ed;
+ --color-border: rgba(0, 0, 0, 0.08);
+ --color-border-strong: rgba(0, 0, 0, 0.16);
+ --color-separator: rgba(60, 60, 67, 0.12);
+ /* ---- Glassmorphism ---- */
+ --glass-blur: 20px;
+ --glass-saturate: 180%;
+ --glass-bg: rgba(255, 255, 255, 0.72);
+ --glass-bg-hover: rgba(255, 255, 255, 0.82);
+ --glass-bg-strong: rgba(255, 255, 255, 0.85);
+ --glass-border: rgba(255, 255, 255, 0.45);
+ --glass-shadow: 0 2px 16px rgba(0, 0, 0, 0.06), 0 0 1px rgba(0, 0, 0, 0.08);
+ --glass-shadow-lg: 0 8px 40px rgba(0, 0, 0, 0.1), 0 0 1px rgba(0, 0, 0, 0.06);
+ /* ---- Layout ---- */
+ --sidebar-width: 260px;
+ --sidebar-collapsed-width: 64px;
+ --sidebar-gap: 10px;
+ --header-height: 56px;
+ --content-max-width: none;
+ --content-padding: 24px;
+ /* ---- Radius ---- */
+ --radius-sm: 6px;
+ --radius-md: 10px;
+ --radius-lg: 14px;
+ --radius-xl: 20px;
+ --radius-full: 9999px;
+ /* ---- Spacing ---- */
+ --space-xs: 4px;
+ --space-sm: 8px;
+ --space-md: 12px;
+ --space-lg: 16px;
+ --space-xl: 24px;
+ --space-2xl: 32px;
+ --space-3xl: 48px;
+ /* ---- Transitions ---- */
+ --transition-fast: 0.15s ease;
+ --transition-normal: 0.25s ease;
+ --transition-slow: 0.4s cubic-bezier(0.16, 1, 0.3, 1);
+ /* ---- Z-index ---- */
+ --z-dropdown: 100;
+ --z-sticky: 200;
+ --z-sidebar: 300;
+ --z-header: 400;
+ --z-overlay: 500;
+ --z-modal: 600;
+ --z-toast: 700;
+ /* ---- Background ---- */
+ --bg-gradient: linear-gradient(135deg, #b8c4d0 0%, #d5dce5 50%, #ddd5cc 100%);
+}
+/* ==========================================================================
+ Normalize / Reset — luci-theme-glass
+ Minimal reset for LuCI pages.
+ ========================================================================== */
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+html {
+ font-size: var(--font-size-base);
+ line-height: var(--line-height);
+ -webkit-text-size-adjust: 100%;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ /* Glass scrollbar */
+ scrollbar-width: thin;
+ scrollbar-color: rgba(128, 128, 128, 0.35) transparent;
+}
+::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+}
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+::-webkit-scrollbar-thumb {
+ background: rgba(128, 128, 128, 0.35);
+ border-radius: 3px;
+}
+::-webkit-scrollbar-thumb:hover {
+ background: rgba(128, 128, 128, 0.55);
+}
+body {
+ font-family: var(--font-sans);
+ font-size: var(--font-size-base);
+ color: var(--color-text);
+ background: var(--color-bg);
+ min-height: 100vh;
+ overflow-x: hidden;
+}
+a {
+ color: var(--color-primary);
+ text-decoration: none;
+ transition: color var(--transition-fast);
+}
+a:hover {
+ color: var(--color-primary-hover);
+}
+img {
+ max-width: 100%;
+ height: auto;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ width: 100%;
+}
+input,
+select,
+textarea,
+button {
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+code,
+pre {
+ font-family: var(--font-mono);
+ font-size: var(--font-size-sm);
+}
+hr {
+ border: none;
+ border-top: 0.5px solid var(--color-border);
+ margin: var(--space-md) 0;
+}
+p {
+ margin-bottom: var(--space-sm);
+}
+ul,
+ol {
+ padding-left: 1.5em;
+ margin: var(--space-sm) 0;
+}
+li {
+ margin-bottom: 2px;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-weight: var(--font-weight-semibold);
+ line-height: 1.25;
+ color: var(--color-text);
+}
+h1 {
+ font-size: var(--font-size-2xl);
+}
+h2 {
+ font-size: var(--font-size-xl);
+}
+h3 {
+ font-size: 1.125rem;
+}
+h4 {
+ font-size: 1rem;
+}
+::selection {
+ background: rgba(var(--color-primary-rgb), 0.25);
+}
+/* LuCI injects at the top of every
+ CBI form. Without this rule the input renders as an unstyled browser
+ button (seen in PassWall, OpenAppFilter, etc). Both bootstrap and argon
+ themes ship this utility rule. */
+.hidden {
+ display: none;
+}
+/* ==========================================================================
+ Glassmorphism Mixins — luci-theme-glass
+ ========================================================================== */
+/* Standard glass panel — default for cards, sections, dropdowns */
+/* Frosted glass — stronger blur, higher opacity (sidebar, login card) */
+/* Subtle glass — minimal effect for secondary/nested elements */
+/* Glass hover state */
+/* Inner glow — top highlight for glass panels */
+/* ==========================================================================
+ Layout — Sidebar, Header, Content
+ ========================================================================== */
+/* ---- Background layer ---- */
+#bg-wrap {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: -1;
+ background: var(--bg-gradient);
+ background-size: cover;
+ background-position: center;
+}
+#bg-wrap img,
+#bg-wrap video {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+/* ---- Sidebar ---- */
+#sidebar {
+ position: fixed;
+ top: var(--sidebar-gap);
+ left: var(--sidebar-gap);
+ width: var(--sidebar-width);
+ height: calc(100vh - var(--sidebar-gap) * 2);
+ z-index: var(--z-sidebar);
+ /* True glassmorphism — multi-stop gradient, stronger for readability */
+ background-image: linear-gradient(160deg, rgba(255, 255, 255, 0.22) 0%, rgba(255, 255, 255, 0.18) 20%, rgba(245, 250, 255, 0.2) 45%, rgba(255, 255, 255, 0.16) 70%, rgba(240, 245, 255, 0.19) 100%);
+ background-color: rgba(255, 255, 255, 0.62);
+ backdrop-filter: blur(30px) saturate(var(--glass-saturate));
+ -webkit-backdrop-filter: blur(30px) saturate(var(--glass-saturate));
+ border: 0.5px solid rgba(255, 255, 255, 0.4);
+ box-shadow: 0 8px 40px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.5), inset 0 0 12px rgba(255, 255, 255, 0.15);
+ border-radius: var(--radius-xl);
+ display: flex;
+ flex-direction: column;
+ transition: transform var(--transition-slow);
+ overflow: hidden;
+}
+.sidebar-header {
+ display: flex;
+ align-items: center;
+ padding: var(--space-lg) var(--space-xl);
+ height: var(--header-height);
+ border-bottom: 0.5px solid var(--color-separator);
+}
+.sidebar-header .logo {
+ width: 32px;
+ height: 32px;
+ flex-shrink: 0;
+}
+.sidebar-header .brand {
+ margin-left: var(--space-md);
+ font-size: var(--font-size-lg);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text);
+ white-space: nowrap;
+ overflow: hidden;
+}
+.sidebar-footer {
+ padding: var(--space-md) var(--space-xl);
+ border-top: 0.5px solid var(--color-separator);
+ text-align: center;
+}
+.sidebar-footer .sidebar-version {
+ font-size: 0.75rem;
+ color: var(--color-text-tertiary);
+}
+/* ---- Sidebar navigation ---- */
+.sidebar-nav {
+ flex: 1;
+ overflow-y: auto;
+ overflow-x: hidden;
+ padding: var(--space-sm) 0;
+}
+.sidebar-nav::-webkit-scrollbar {
+ width: 4px;
+}
+.sidebar-nav::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.15);
+ border-radius: var(--radius-full);
+}
+.nav-group {
+ margin-bottom: var(--space-xs);
+}
+.nav-group .nav-group-title {
+ padding: var(--space-sm) var(--space-xl);
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-tertiary);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+}
+.nav-item {
+ display: flex;
+ align-items: center;
+ padding: 0.55rem var(--space-xl);
+ margin: 1px var(--space-sm);
+ border-radius: var(--radius-md);
+ font-size: var(--font-size-lg);
+ color: var(--color-text);
+ cursor: pointer;
+ transition: background var(--transition-fast), color var(--transition-fast);
+ text-decoration: none;
+}
+.nav-item:hover {
+ background: rgba(0, 0, 0, 0.04);
+ color: var(--color-text);
+}
+.nav-item.active {
+ background: rgba(255, 255, 255, 0.45);
+ color: var(--color-text);
+ font-weight: var(--font-weight-medium);
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), inset 0 1px 1px rgba(255, 255, 255, 0.6), inset 0 0 6px rgba(255, 255, 255, 0.15);
+}
+.nav-item .nav-icon {
+ width: 20px;
+ height: 20px;
+ margin-right: var(--space-md);
+ flex-shrink: 0;
+ opacity: 0.65;
+}
+.nav-item.active .nav-icon {
+ opacity: 1;
+}
+.nav-item .nav-label {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.nav-item .nav-arrow {
+ margin-left: auto;
+ width: 16px;
+ height: 16px;
+ opacity: 0.4;
+ transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity var(--transition-fast);
+}
+.nav-item.open .nav-arrow {
+ transform: rotate(90deg);
+ opacity: 0.6;
+}
+.nav-sub {
+ position: relative;
+ overflow: hidden;
+ max-height: 0;
+ opacity: 0;
+ transition: max-height 0.35s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.25s ease;
+}
+.nav-sub.open {
+ /* Upper bound for the expand animation. Must exceed the tallest submenu
+ encountered in the wild (System commonly reaches ~14 items once third-
+ party apps like theme-config, scheduled-tasks, etc. are installed). */
+ max-height: 2000px;
+ opacity: 1;
+}
+.nav-sub .nav-item {
+ padding-left: calc(var(--space-xl) + 32px);
+ font-size: var(--font-size-base);
+ transform: translateX(-8px);
+ opacity: 0;
+ transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease, color var(--transition-fast);
+ /* Sub-item active state — no background, slider handles it */
+}
+.nav-sub .nav-item.active {
+ background: transparent;
+ box-shadow: none;
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
+ font-weight: var(--font-weight-medium);
+}
+.nav-sub.open .nav-item {
+ transform: translateX(0);
+ opacity: 1;
+}
+.nav-sub.open .nav-item:nth-of-type(1) {
+ transition-delay: 0.03s;
+}
+.nav-sub.open .nav-item:nth-of-type(2) {
+ transition-delay: 0.06s;
+}
+.nav-sub.open .nav-item:nth-of-type(3) {
+ transition-delay: 0.09s;
+}
+.nav-sub.open .nav-item:nth-of-type(4) {
+ transition-delay: 0.12s;
+}
+.nav-sub.open .nav-item:nth-of-type(5) {
+ transition-delay: 0.15s;
+}
+.nav-sub.open .nav-item:nth-of-type(6) {
+ transition-delay: 0.18s;
+}
+.nav-sub.open .nav-item:nth-of-type(7) {
+ transition-delay: 0.21s;
+}
+.nav-sub.open .nav-item:nth-of-type(8) {
+ transition-delay: 0.24s;
+}
+.nav-sub.open .nav-item:nth-of-type(n+9) {
+ transition-delay: 0.27s;
+}
+.nav-sub .nav-slider {
+ position: absolute;
+ left: var(--space-sm);
+ right: var(--space-sm);
+ top: 0;
+ height: 0;
+ opacity: 0;
+ border-radius: var(--radius-md);
+ background: rgba(255, 255, 255, 0.35);
+ backdrop-filter: blur(12px) saturate(150%);
+ -webkit-backdrop-filter: blur(12px) saturate(150%);
+ border: 0.5px solid rgba(255, 255, 255, 0.45);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04), inset 0 1px 1px rgba(255, 255, 255, 0.35), inset 0 0 4px rgba(255, 255, 255, 0.1);
+ transition: top 0.3s cubic-bezier(0.16, 1, 0.3, 1), height 0.2s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
+ z-index: 0;
+ pointer-events: none;
+}
+.nav-sub .nav-item {
+ position: relative;
+ z-index: 1;
+}
+/* ---- Sidebar user area ---- */
+.sidebar-user {
+ padding: var(--space-lg) var(--space-xl);
+ border-top: 0.5px solid var(--color-separator);
+ display: flex;
+ align-items: center;
+}
+.sidebar-user .user-avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: var(--radius-full);
+ background: var(--color-bg-secondary);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: var(--font-weight-semibold);
+ font-size: var(--font-size-sm);
+ color: var(--color-text-secondary);
+ flex-shrink: 0;
+}
+.sidebar-user .user-info {
+ margin-left: var(--space-md);
+ overflow: hidden;
+}
+.sidebar-user .user-info .user-name {
+ font-weight: var(--font-weight-medium);
+ font-size: var(--font-size-sm);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.sidebar-user .user-info .user-host {
+ font-size: var(--font-size-sm);
+ color: var(--color-text-tertiary);
+}
+/* ---- Top header nav bar ---- */
+#header {
+ position: fixed;
+ top: var(--sidebar-gap);
+ left: calc(var(--sidebar-width) + var(--sidebar-gap) * 2);
+ right: var(--sidebar-gap);
+ height: 44px;
+ z-index: var(--z-header);
+ border-radius: var(--radius-lg);
+ display: flex;
+ align-items: center;
+ padding: 4px 4px 4px 6px;
+ gap: var(--space-xs);
+ transition: left var(--transition-slow);
+ /* True glassmorphism — multi-stop gradient like sub-nav */
+ background-image: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.12) 25%, rgba(240, 248, 255, 0.13) 50%, rgba(255, 255, 255, 0.11) 75%, rgba(230, 240, 255, 0.12) 100%);
+ background-color: transparent;
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ border: 0.5px solid rgba(255, 255, 255, 0.35);
+ box-shadow: 0 2px 16px rgba(0, 0, 0, 0.06), 0 1px 4px rgba(0, 0, 0, 0.03), inset 0 1px 0 rgba(255, 255, 255, 0.4), inset 0 0 8px rgba(255, 255, 255, 0.12);
+}
+/* ---- Header top-level tabs ---- */
+#header-nav,
+#header-tabs {
+ display: flex;
+ align-items: center;
+ position: relative;
+}
+.header-sep {
+ width: 1px;
+ height: 18px;
+ background: var(--color-separator);
+ margin: 0 var(--space-xs);
+ flex-shrink: 0;
+}
+.header-tab {
+ padding: 6px var(--space-lg);
+ border-radius: var(--radius-md);
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-text-secondary);
+ text-decoration: none;
+ transition: background var(--transition-fast), color var(--transition-fast), box-shadow var(--transition-fast);
+ white-space: nowrap;
+}
+.header-tab:hover {
+ background: rgba(0, 0, 0, 0.04);
+ color: var(--color-text);
+}
+.header-tab.active {
+ color: var(--color-text);
+ background: transparent;
+ box-shadow: none;
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
+}
+/* Horizontal sliding selector for header tabs */
+.tab-slider {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 0;
+ opacity: 0;
+ border-radius: var(--radius-md);
+ background: rgba(255, 255, 255, 0.45);
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), inset 0 1px 1px rgba(255, 255, 255, 0.6), inset 0 0 6px rgba(255, 255, 255, 0.15);
+ transition: left 0.3s cubic-bezier(0.16, 1, 0.3, 1), width 0.2s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
+ z-index: 0;
+ pointer-events: none;
+}
+.header-tab,
+.sub-tab {
+ position: relative;
+ z-index: 1;
+}
+/* ---- Status indicators (center of header) ---- */
+#status-indicators {
+ margin: 0 auto;
+ gap: var(--space-sm);
+ padding: 4px var(--space-md);
+ height: 2.25rem;
+ display: flex;
+ align-items: center;
+ border-radius: var(--radius-md);
+ background-image: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.1) 25%, rgba(240, 248, 255, 0.12) 50%, rgba(255, 255, 255, 0.08) 75%, rgba(230, 240, 255, 0.1) 100%);
+ background-color: transparent;
+ border: 0.5px solid rgba(255, 255, 255, 0.18);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.03), 0 0 0 0.5px rgba(255, 255, 255, 0.08) inset, 0 1px 6px rgba(255, 255, 255, 0.1) inset;
+ backdrop-filter: blur(12px) saturate(180%);
+ -webkit-backdrop-filter: blur(12px) saturate(180%);
+}
+#status-indicators:empty {
+ display: none;
+}
+#status-indicators [data-indicator] {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-text-secondary);
+ white-space: nowrap;
+ cursor: default;
+ transition: color 0.3s ease;
+ /* Level colors — neutral by default, color only when noteworthy */
+}
+#status-indicators [data-indicator] .indicator-icon {
+ width: 14px;
+ height: 14px;
+ flex-shrink: 0;
+ opacity: 0.6;
+ transition: color 0.3s ease, opacity 0.3s ease;
+}
+#status-indicators [data-indicator][data-level="warn"] {
+ color: #FF9500;
+}
+#status-indicators [data-indicator][data-level="warn"] .indicator-icon {
+ opacity: 0.85;
+}
+#status-indicators [data-indicator][data-level="crit"] {
+ color: #FF3B30;
+}
+#status-indicators [data-indicator][data-level="crit"] .indicator-icon {
+ opacity: 0.9;
+}
+#status-indicators [data-indicator][data-level="active"] {
+ color: #007AFF;
+}
+#status-indicators [data-indicator][data-level="active"] .indicator-icon {
+ opacity: 0.8;
+}
+#status-indicators [data-indicator][data-level="busy"] {
+ color: #AF52DE;
+}
+#status-indicators [data-indicator][data-level="busy"] .indicator-icon {
+ opacity: 0.85;
+}
+#status-indicators [data-indicator] + [data-indicator] {
+ border-left: 0.5px solid rgba(128, 128, 128, 0.25);
+ padding-left: var(--space-sm);
+}
+/* ---- Glass pill badges (title + indicators) ---- */
+.header-title {
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text);
+ white-space: nowrap;
+ cursor: default;
+ padding: 4px var(--space-md);
+ height: 2.25rem;
+ display: flex;
+ align-items: center;
+ border-radius: var(--radius-md);
+ background-image: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.1) 25%, rgba(240, 248, 255, 0.12) 50%, rgba(255, 255, 255, 0.08) 75%, rgba(230, 240, 255, 0.1) 100%);
+ background-color: transparent;
+ border: 0.5px solid rgba(255, 255, 255, 0.18);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.03), 0 0 0 0.5px rgba(255, 255, 255, 0.08) inset, 0 1px 6px rgba(255, 255, 255, 0.1) inset;
+ backdrop-filter: blur(12px) saturate(180%);
+ -webkit-backdrop-filter: blur(12px) saturate(180%);
+}
+.header-actions {
+ margin-left: auto;
+ flex-shrink: 0;
+ gap: var(--space-sm);
+ padding: 4px var(--space-md);
+ height: 2.25rem;
+ display: flex;
+ align-items: center;
+ border-radius: var(--radius-md);
+ background-image: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.1) 25%, rgba(240, 248, 255, 0.12) 50%, rgba(255, 255, 255, 0.08) 75%, rgba(230, 240, 255, 0.1) 100%);
+ background-color: transparent;
+ border: 0.5px solid rgba(255, 255, 255, 0.18);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.03), 0 0 0 0.5px rgba(255, 255, 255, 0.08) inset, 0 1px 6px rgba(255, 255, 255, 0.1) inset;
+ backdrop-filter: blur(12px) saturate(180%);
+ -webkit-backdrop-filter: blur(12px) saturate(180%);
+}
+.header-actions:empty {
+ display: none;
+}
+.header-actions [data-indicator] {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-text-secondary);
+ white-space: nowrap;
+ cursor: default;
+}
+.header-actions [data-indicator][data-style="active"] {
+ color: var(--color-primary);
+}
+/* ---- Theme toggle button ---- */
+.theme-toggle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 24px;
+ height: 24px;
+ padding: 0;
+ border: none;
+ border-radius: var(--radius-sm);
+ background: none;
+ color: var(--color-text-secondary);
+ cursor: pointer;
+ transition: color var(--transition-fast);
+ flex-shrink: 0;
+}
+.theme-toggle:hover {
+ color: var(--color-text);
+}
+.theme-toggle:active {
+ transform: scale(0.92);
+}
+.theme-toggle .theme-icon {
+ width: 15px;
+ height: 15px;
+ display: none;
+}
+/* Show only the icon matching current theme */
+html[data-theme="auto"] .theme-icon-auto,
+html:not([data-theme]) .theme-icon-auto {
+ display: block;
+}
+html[data-theme="light"] .theme-icon-light {
+ display: block;
+}
+html[data-theme="dark"] .theme-icon-dark {
+ display: block;
+}
+/* Floating variant for login page */
+.theme-toggle-float {
+ position: fixed;
+ top: var(--space-lg);
+ right: var(--space-lg);
+ z-index: var(--z-dropdown);
+ width: 36px;
+ height: 36px;
+ height: 2.25rem;
+ display: flex;
+ align-items: center;
+ border-radius: var(--radius-md);
+ background-image: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.1) 25%, rgba(240, 248, 255, 0.12) 50%, rgba(255, 255, 255, 0.08) 75%, rgba(230, 240, 255, 0.1) 100%);
+ background-color: transparent;
+ border: 0.5px solid rgba(255, 255, 255, 0.18);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.03), 0 0 0 0.5px rgba(255, 255, 255, 0.08) inset, 0 1px 6px rgba(255, 255, 255, 0.1) inset;
+ backdrop-filter: blur(12px) saturate(180%);
+ -webkit-backdrop-filter: blur(12px) saturate(180%);
+}
+.theme-toggle-float .theme-icon {
+ width: 18px;
+ height: 18px;
+}
+/* Mobile hamburger */
+.menu-toggle {
+ display: none;
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: var(--space-sm);
+ margin-right: var(--space-md);
+ color: var(--color-text);
+}
+.menu-toggle svg {
+ width: 22px;
+ height: 22px;
+}
+/* ---- Sub-navigation bar (CBI tabs) ---- */
+#header-sub {
+ position: fixed;
+ top: calc(var(--sidebar-gap) + 44px + 4px);
+ left: calc(var(--sidebar-width) + var(--sidebar-gap) * 2);
+ width: fit-content;
+ max-width: calc(100vw - var(--sidebar-width) - var(--sidebar-gap) * 3);
+ height: 2.25rem;
+ z-index: var(--z-header);
+ border-radius: var(--radius-md);
+ display: none;
+ align-items: center;
+ padding: 3px 4px;
+ gap: 2px;
+ overflow-x: auto;
+ transition: left var(--transition-slow);
+ /* Glass tab-list background — from glass-ui */
+ background-image: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.12) 25%, rgba(240, 248, 255, 0.13) 50%, rgba(255, 255, 255, 0.11) 75%, rgba(230, 240, 255, 0.12) 100%);
+ background-color: transparent;
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ border: 0.5px solid rgba(255, 255, 255, 0.12);
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08), 0 1px 4px rgba(0, 0, 0, 0.04), 0 0 0 0.5px rgba(255, 255, 255, 0.08) inset, 0 0 12px rgba(255, 255, 255, 0.1) inset;
+ /* Hide scrollbar */
+ scrollbar-width: none;
+}
+#header-sub::-webkit-scrollbar {
+ display: none;
+}
+#header-sub.active {
+ display: inline-flex;
+}
+/* Sub-nav tab items — glass-ui TabsTrigger pattern */
+.sub-tab {
+ padding: 4px var(--space-md);
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-text-secondary);
+ text-decoration: none;
+ white-space: nowrap;
+ cursor: pointer;
+ transition: background var(--transition-fast), color var(--transition-fast), box-shadow var(--transition-fast), backdrop-filter var(--transition-fast);
+}
+.sub-tab:hover {
+ background: rgba(0, 0, 0, 0.04);
+ color: var(--color-text);
+}
+.sub-tab.active {
+ color: var(--color-text);
+ background: transparent;
+ box-shadow: none;
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
+}
+/* Sub-tab slider — inset to match sub-tab content area */
+#header-sub > .tab-slider {
+ border-radius: var(--radius-sm);
+ top: 3px;
+ bottom: 3px;
+ height: auto;
+}
+/* ---- Page transition ---- */
+@keyframes page-enter {
+ from {
+ opacity: 0;
+ transform: translateY(12px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+/* ---- Main content area ---- */
+#content-wrap {
+ margin-left: calc(var(--sidebar-width) + var(--sidebar-gap) * 2);
+ margin-top: calc(44px + var(--sidebar-gap) * 2);
+ min-height: calc(100vh - 44px - var(--sidebar-gap) * 2);
+ padding: var(--content-padding);
+ transition: margin-left var(--transition-slow);
+ animation: page-enter 0.3s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+/* JS view content — fade in when LuCI replaces the spinner */
+#view > :not(.spinning) {
+ animation: page-enter 0.25s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+/* Style LuCI's "Loading view..." spinner as a centered glass card */
+#view > .spinning {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translateX(-50%) translateY(-50%);
+ padding: var(--space-lg) var(--space-xl);
+ border-radius: var(--radius-lg);
+ background: var(--glass-bg);
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ border: 0.5px solid var(--glass-border);
+ box-shadow: var(--glass-shadow);
+ z-index: 100;
+}
+/* Fade-out before navigation */
+body.page-leaving #content-wrap {
+ opacity: 0;
+ transform: translateY(-8px);
+ transition: opacity 0.15s ease, transform 0.15s ease;
+}
+/* Push content down when sub-nav is visible */
+body.has-sub-nav #content-wrap {
+ margin-top: calc(44px + 36px + var(--sidebar-gap) * 2 + 4px);
+}
+#content {
+ max-width: var(--content-max-width);
+ margin: 0 auto;
+}
+/* Mobile overlay */
+.sidebar-overlay {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.3);
+ z-index: calc(var(--z-sidebar) - 1);
+ opacity: 0;
+ transition: opacity var(--transition-normal);
+}
+.sidebar-overlay.active {
+ display: block;
+ opacity: 1;
+}
+/* ---- Page title ---- */
+.page-title {
+ margin-bottom: var(--space-xl);
+}
+.page-title h2 {
+ font-size: var(--font-size-xl);
+ font-weight: var(--font-weight-bold);
+}
+.page-title .page-description {
+ margin-top: var(--space-xs);
+ color: var(--color-text-secondary);
+ font-size: var(--font-size-sm);
+}
+/* ---- Footer ---- */
+.glass-footer {
+ margin-left: calc(var(--sidebar-width) + var(--sidebar-gap) * 2);
+ padding: var(--space-lg) var(--content-padding);
+ text-align: right;
+ font-size: var(--font-size-sm);
+ color: var(--color-text-tertiary);
+ transition: margin-left var(--transition-slow);
+}
+.glass-footer a {
+ color: var(--color-text-tertiary);
+ text-decoration: none;
+}
+.glass-footer a:hover {
+ color: var(--color-text-secondary);
+}
+.glass-footer .footer-sep {
+ margin: 0 var(--space-sm);
+ opacity: 0.4;
+}
+/* ==========================================================================
+ Components — CBI forms, tables, buttons, inputs, dropdowns
+ ========================================================================== */
+/* ---- CBI Sections ---- */
+.cbi-section {
+ background: var(--glass-bg);
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ border: 0.5px solid var(--glass-border);
+ box-shadow: var(--glass-shadow);
+ border-radius: var(--radius-lg);
+ padding: var(--space-xl);
+ margin-bottom: var(--space-xl);
+ position: relative;
+ overflow-x: auto;
+ /* Lift section above siblings when a dropdown is open inside it */
+ /* Nested sections — flatten (no extra box/padding) */
+}
+.cbi-section:has(.cbi-dropdown[open]) {
+ z-index: var(--z-dropdown);
+}
+.cbi-section .cbi-section {
+ padding: 0;
+ margin-bottom: 0;
+ background: none;
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
+ border: none;
+ box-shadow: none;
+ border-radius: 0;
+}
+/* Allow dropdowns to escape stacking contexts created by backdrop-filter.
+ Temporarily remove glass effects on the section and force overflow visible
+ on all ancestors up to the section. */
+.cbi-section:has(.cbi-dropdown[open]) {
+ z-index: var(--z-modal);
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
+ overflow: visible;
+}
+.cbi-section:has(.cbi-dropdown[open]) .table,
+.cbi-section:has(.cbi-dropdown[open]) .tr,
+.cbi-section:has(.cbi-dropdown[open]) .td,
+.cbi-section:has(.cbi-dropdown[open]) .cbi-value,
+.cbi-section:has(.cbi-dropdown[open]) .cbi-value-field {
+ overflow: visible !important;
+}
+.cbi-section-descr {
+ color: var(--color-text-secondary);
+ font-size: var(--font-size-sm);
+ margin-bottom: var(--space-lg);
+ padding-bottom: var(--space-lg);
+ border-bottom: 0.5px solid var(--color-separator);
+}
+.cbi-section > h3,
+.cbi-section-node > h3 {
+ font-size: var(--font-size-lg);
+ font-weight: var(--font-weight-semibold);
+ margin-bottom: var(--space-lg);
+}
+/* ---- Section fold/collapse ---- */
+.cbi-section.fade-in .cbi-title {
+ display: flex;
+ align-items: center;
+ /* Move toggle button to the right of the title */
+}
+.cbi-section.fade-in .cbi-title > h3 {
+ flex: 1;
+ margin-bottom: 0;
+}
+.cbi-section.fade-in .cbi-title > div:last-child {
+ flex-shrink: 0;
+ margin-left: var(--space-md);
+}
+.cbi-section.fade-in .cbi-title > div:last-child span {
+ font-size: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 28px;
+ height: 28px;
+ cursor: pointer;
+ border-radius: var(--radius-sm);
+ transition: background var(--transition-fast);
+}
+.cbi-section.fade-in .cbi-title > div:last-child span:hover {
+ background: rgba(0, 0, 0, 0.06);
+}
+.cbi-section.fade-in .cbi-title > div:last-child span::after {
+ content: '';
+ display: block;
+ width: 7px;
+ height: 7px;
+ border-right: 1.5px solid var(--color-text-tertiary);
+ border-bottom: 1.5px solid var(--color-text-tertiary);
+ transform: rotate(-135deg);
+ transition: transform 0.3s ease;
+ margin-top: 3px;
+}
+.cbi-section.fade-in .cbi-title > div:last-child span[data-style="inactive"]::after {
+ transform: rotate(45deg);
+ margin-top: -2px;
+}
+/* ---- CBI Map (page-level container) ---- */
+.cbi-map {
+ margin-bottom: var(--space-xl);
+}
+/* Title shown in header bar; description becomes tooltip */
+.cbi-map-descr,
+.cbi-map > h2,
+#view > h2,
+#maincontent > h2 {
+ display: none;
+}
+/* Hide descriptions only when moved to header tooltip (data-moved set by footer.ut JS) */
+.cbi-map-descr[data-moved],
+#view > h2 + [data-moved] {
+ display: none;
+}
+/* ---- CBI Value rows ---- */
+.cbi-value {
+ display: flex;
+ align-items: flex-start;
+ padding: var(--space-md) 0;
+ border-bottom: 0.5px solid var(--color-separator);
+ /* Plugins that use form.DummyValue.extend({ renderWidget }) to emit a
+ full-width section or table (usteer Hearing map, DHCP leases, etc.)
+ render the section as the *only* child of .cbi-value — no title, no
+ value-field wrapper. As a flex item with default `flex: 0 1 auto` it
+ shrinks to content width, leaving a big empty right zone
+ ([#8](https://github.com/rchen14b/luci-theme-glass/issues/8)). Switch
+ the row to block so the section spans full width. */
+}
+.cbi-value:last-child {
+ border-bottom: none;
+}
+.cbi-value:has(> .cbi-section),
+.cbi-value:has(> table) {
+ display: block;
+}
+.cbi-value-title {
+ flex: 0 0 200px;
+ padding-right: var(--space-xl);
+ font-weight: var(--font-weight-medium);
+ padding-top: var(--space-sm);
+ color: var(--color-text);
+}
+.cbi-value-description {
+ color: var(--color-text-tertiary);
+ font-size: var(--font-size-sm);
+ margin-top: var(--space-xs);
+ font-weight: var(--font-weight-normal);
+}
+.cbi-value-field {
+ flex: 1;
+ min-width: 0;
+}
+/* ---- Inputs ---- */
+.cbi-input-text,
+.cbi-input-password,
+input[type="text"],
+input[type="password"],
+input[type="number"],
+textarea {
+ width: 100%;
+ max-width: 400px;
+ padding: var(--space-sm) var(--space-md);
+ background: rgba(255, 255, 255, 0.55);
+ border: 1px solid var(--color-border-strong);
+ border-radius: var(--radius-md);
+ font-size: var(--font-size-base);
+ color: var(--color-text);
+ transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
+ outline: none;
+}
+.cbi-input-text:focus,
+.cbi-input-password:focus,
+input[type="text"]:focus,
+input[type="password"]:focus,
+input[type="number"]:focus,
+textarea:focus {
+ border-color: var(--color-primary);
+ box-shadow: 0 0 0 3px rgba(var(--color-primary-rgb), 0.15);
+}
+.cbi-input-text:disabled,
+.cbi-input-password:disabled,
+input[type="text"]:disabled,
+input[type="password"]:disabled,
+input[type="number"]:disabled,
+textarea:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+textarea {
+ max-width: 100%;
+ min-height: 80px;
+ resize: vertical;
+}
+select {
+ padding: var(--space-sm) var(--space-md);
+ background: rgba(255, 255, 255, 0.55);
+ border: 1px solid var(--color-border-strong);
+ border-radius: var(--radius-md);
+ font-size: var(--font-size-base);
+ color: var(--color-text);
+ outline: none;
+ cursor: pointer;
+}
+select:focus {
+ border-color: var(--color-primary);
+ box-shadow: 0 0 0 3px rgba(var(--color-primary-rgb), 0.15);
+}
+/* ---- Buttons — glass-ui pattern ---- */
+.btn,
+.cbi-button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: var(--space-sm) var(--space-lg);
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-medium);
+ border-radius: var(--radius-md);
+ /* Glass button surface */
+ background-image: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.12) 25%, rgba(240, 248, 255, 0.13) 50%, rgba(255, 255, 255, 0.11) 75%, rgba(230, 240, 255, 0.12) 100%);
+ background-color: rgba(255, 255, 255, 0.55);
+ border: 0.5px solid rgba(255, 255, 255, 0.3);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.4), inset 0 0 6px rgba(255, 255, 255, 0.1);
+ backdrop-filter: blur(10px) saturate(180%);
+ -webkit-backdrop-filter: blur(10px) saturate(180%);
+ color: var(--color-text);
+ cursor: pointer;
+ transition: all var(--transition-fast);
+ text-decoration: none;
+ white-space: nowrap;
+ gap: var(--space-xs);
+}
+.btn:hover,
+.cbi-button:hover {
+ opacity: 0.9;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.05), inset 0 1px 0 rgba(255, 255, 255, 0.45), inset 0 0 8px rgba(255, 255, 255, 0.12);
+}
+.btn:active,
+.cbi-button:active {
+ transform: scale(0.98);
+ opacity: 0.85;
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+.btn:disabled,
+.cbi-button:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ transform: none;
+}
+.cbi-button-positive,
+.cbi-button-apply,
+.cbi-button-action,
+.cbi-button-reload,
+.cbi-button[value="Submit"],
+.cbi-button[value="Upload"],
+.cbi-button[value$="Apply"] {
+ /* Primary glass — blue-tinted (Save & Apply, actions) */
+ background-image: linear-gradient(135deg, rgba(0, 122, 255, 0.1) 0%, rgba(0, 122, 255, 0.07) 25%, rgba(10, 132, 255, 0.09) 50%, rgba(0, 122, 255, 0.06) 75%, rgba(0, 100, 220, 0.08) 100%);
+ background-color: rgba(255, 255, 255, 0.45);
+ color: var(--color-primary);
+ border: 0.5px solid rgba(0, 122, 255, 0.18);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.35), inset 0 0 6px rgba(255, 255, 255, 0.08);
+}
+.cbi-button-positive:hover,
+.cbi-button-apply:hover,
+.cbi-button-action:hover,
+.cbi-button-reload:hover,
+.cbi-button[value="Submit"]:hover,
+.cbi-button[value="Upload"]:hover,
+.cbi-button[value$="Apply"]:hover {
+ opacity: 0.9;
+ color: var(--color-primary);
+ box-shadow: 0 4px 12px rgba(0, 122, 255, 0.12), 0 1px 3px rgba(0, 0, 0, 0.05), inset 0 1px 0 rgba(255, 255, 255, 0.4), inset 0 0 8px rgba(255, 255, 255, 0.1);
+}
+.cbi-button-positive:active,
+.cbi-button-apply:active,
+.cbi-button-action:active,
+.cbi-button-reload:active,
+.cbi-button[value="Submit"]:active,
+.cbi-button[value="Upload"]:active,
+.cbi-button[value$="Apply"]:active {
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+.cbi-button-save {
+ /* Secondary glass — green-tinted (Save) */
+ background-image: linear-gradient(135deg, rgba(52, 199, 89, 0.1) 0%, rgba(52, 199, 89, 0.07) 25%, rgba(48, 209, 88, 0.09) 50%, rgba(52, 199, 89, 0.06) 75%, rgba(40, 180, 75, 0.08) 100%);
+ background-color: rgba(255, 255, 255, 0.45);
+ color: #248a3d;
+ border: 0.5px solid rgba(52, 199, 89, 0.2);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.35), inset 0 0 6px rgba(255, 255, 255, 0.08);
+}
+.cbi-button-save:hover {
+ opacity: 0.9;
+ color: #248a3d;
+ box-shadow: 0 4px 12px rgba(52, 199, 89, 0.12), 0 1px 3px rgba(0, 0, 0, 0.05), inset 0 1px 0 rgba(255, 255, 255, 0.4), inset 0 0 8px rgba(255, 255, 255, 0.1);
+}
+.cbi-button-save:active {
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+.cbi-button-negative,
+.cbi-button-remove,
+.cbi-button-reset {
+ /* Tinted danger glass */
+ background-image: linear-gradient(135deg, rgba(255, 59, 48, 0.1) 0%, rgba(255, 59, 48, 0.07) 50%, rgba(255, 59, 48, 0.09) 100%);
+ background-color: rgba(255, 255, 255, 0.45);
+ color: var(--color-danger);
+ border: 0.5px solid rgba(255, 59, 48, 0.18);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.03), inset 0 1px 0 rgba(255, 255, 255, 0.35), inset 0 0 6px rgba(255, 255, 255, 0.08);
+}
+.cbi-button-negative:hover,
+.cbi-button-remove:hover,
+.cbi-button-reset:hover {
+ background-color: rgba(255, 255, 255, 0.5);
+ box-shadow: 0 4px 12px rgba(255, 59, 48, 0.12), 0 1px 3px rgba(0, 0, 0, 0.05), inset 0 1px 0 rgba(255, 255, 255, 0.4), inset 0 0 8px rgba(255, 255, 255, 0.1);
+}
+.cbi-button-negative:active,
+.cbi-button-remove:active,
+.cbi-button-reset:active {
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+.cbi-button-neutral,
+.cbi-button-link,
+.cbi-button-edit,
+.cbi-button-find,
+.cbi-button-add,
+.cbi-button-download,
+.cbi-button-up,
+.cbi-button-down {
+ background-color: rgba(255, 255, 255, 0.4);
+ color: var(--color-text);
+}
+.cbi-page-actions {
+ border-radius: var(--radius-lg);
+ padding: var(--space-md) var(--space-xl);
+ margin-top: var(--space-xl);
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: var(--space-sm);
+ /* Match header nav bar glass style */
+ background-image: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.12) 25%, rgba(240, 248, 255, 0.13) 50%, rgba(255, 255, 255, 0.11) 75%, rgba(230, 240, 255, 0.12) 100%);
+ background-color: transparent;
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ border: 0.5px solid rgba(255, 255, 255, 0.35);
+ box-shadow: 0 2px 16px rgba(0, 0, 0, 0.06), 0 1px 4px rgba(0, 0, 0, 0.03), inset 0 1px 0 rgba(255, 255, 255, 0.4), inset 0 0 8px rgba(255, 255, 255, 0.12);
+}
+/* ---- Tables ---- */
+.table,
+table.cbi-section-table {
+ width: 100%;
+ border-collapse: separate;
+ border-spacing: 0;
+}
+.table thead tr th,
+table.cbi-section-table thead tr th,
+.table .tr.table-titles th,
+table.cbi-section-table .tr.table-titles th,
+.table .tr.cbi-section-table-titles th,
+table.cbi-section-table .tr.cbi-section-table-titles th,
+.table thead tr .th,
+table.cbi-section-table thead tr .th,
+.table .tr.table-titles .th,
+table.cbi-section-table .tr.table-titles .th,
+.table .tr.cbi-section-table-titles .th,
+table.cbi-section-table .tr.cbi-section-table-titles .th {
+ padding: var(--space-sm) var(--space-lg);
+ text-align: left;
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-secondary);
+ border-bottom: 1px solid var(--color-separator);
+ white-space: nowrap;
+}
+.table tbody tr,
+table.cbi-section-table tbody tr,
+.table .tr,
+table.cbi-section-table .tr {
+ /* Action button cells — right-aligned, equal-width buttons */
+}
+.table tbody tr td,
+table.cbi-section-table tbody tr td,
+.table .tr td,
+table.cbi-section-table .tr td,
+.table tbody tr .td,
+table.cbi-section-table tbody tr .td,
+.table .tr .td,
+table.cbi-section-table .tr .td {
+ padding: var(--space-md) var(--space-lg);
+ border-bottom: 0.5px solid var(--color-separator);
+ font-size: var(--font-size-sm);
+ vertical-align: middle;
+}
+.table tbody tr:last-child td,
+table.cbi-section-table tbody tr:last-child td,
+.table .tr:last-child td,
+table.cbi-section-table .tr:last-child td,
+.table tbody tr:last-child .td,
+table.cbi-section-table tbody tr:last-child .td,
+.table .tr:last-child .td,
+table.cbi-section-table .tr:last-child .td {
+ border-bottom: none;
+}
+.table tbody tr td:last-child:has(.cbi-button),
+table.cbi-section-table tbody tr td:last-child:has(.cbi-button),
+.table .tr td:last-child:has(.cbi-button),
+table.cbi-section-table .tr td:last-child:has(.cbi-button),
+.table tbody tr .td:last-child:has(.cbi-button),
+table.cbi-section-table tbody tr .td:last-child:has(.cbi-button),
+.table .tr .td:last-child:has(.cbi-button),
+table.cbi-section-table .tr .td:last-child:has(.cbi-button) {
+ text-align: right;
+ white-space: nowrap;
+}
+.table tbody tr td:last-child:has(.cbi-button) .cbi-button,
+table.cbi-section-table tbody tr td:last-child:has(.cbi-button) .cbi-button,
+.table .tr td:last-child:has(.cbi-button) .cbi-button,
+table.cbi-section-table .tr td:last-child:has(.cbi-button) .cbi-button,
+.table tbody tr .td:last-child:has(.cbi-button) .cbi-button,
+table.cbi-section-table tbody tr .td:last-child:has(.cbi-button) .cbi-button,
+.table .tr .td:last-child:has(.cbi-button) .cbi-button,
+table.cbi-section-table .tr .td:last-child:has(.cbi-button) .cbi-button {
+ min-width: 6rem;
+ width: 6rem;
+ text-align: center;
+}
+.table tbody tr:hover td,
+table.cbi-section-table tbody tr:hover td,
+.table .tr:hover td,
+table.cbi-section-table .tr:hover td,
+.table tbody tr:hover .td,
+table.cbi-section-table tbody tr:hover .td,
+.table .tr:hover .td,
+table.cbi-section-table .tr:hover .td {
+ background: rgba(0, 0, 0, 0.02);
+}
+/* ---- CBI Dynamic List (IP addresses, DNS servers, etc.) ---- */
+.cbi-dynlist {
+ line-height: 1.3;
+ flex-direction: column;
+ min-height: 30px;
+ cursor: text;
+ gap: 10px;
+}
+.cbi-dynlist > .item {
+ display: inline-flex;
+ flex-wrap: nowrap;
+ position: relative;
+ max-width: 25rem;
+ pointer-events: none;
+ color: var(--color-text-secondary);
+ outline: 0;
+ /* Remove button (×) */
+}
+.cbi-dynlist > .item::after {
+ content: "\00D7";
+ pointer-events: auto;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 2.5rem !important;
+ height: 2.5rem;
+ margin: 0;
+ font-weight: normal;
+ font-size: 1rem;
+ line-height: 1.5rem;
+ color: var(--color-danger);
+ border: 0.5px solid rgba(255, 59, 48, 0.25);
+ border-left: none;
+ border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
+ outline: 0;
+ background: rgba(255, 59, 48, 0.08);
+ background-image: none;
+ box-shadow: none;
+ box-sizing: border-box;
+ transition: background 0.2s;
+}
+.cbi-dynlist > .item::after:hover {
+ background: rgba(255, 59, 48, 0.18);
+}
+.cbi-dynlist > .item > span {
+ display: block;
+ padding: 0.5rem 0.75rem;
+ box-sizing: border-box;
+ min-width: 15.5rem;
+ width: 15.5rem;
+ height: 2.5rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ user-select: text;
+ white-space: nowrap;
+ word-break: break-word;
+ line-height: 1.5em;
+ color: var(--color-text-primary);
+ border: 0.5px solid var(--color-border);
+ border-radius: var(--radius-sm) 0 0 var(--radius-sm);
+ border-right: none;
+ outline: 0;
+ background: var(--color-bg-elevated);
+ box-shadow: none;
+}
+.cbi-dynlist > .add-item {
+ display: inline-flex;
+ align-items: center;
+ width: 100%;
+ min-width: 15.5rem;
+ flex-wrap: nowrap;
+}
+.cbi-dynlist > .add-item input {
+ display: block;
+ padding: 0.5rem 0.75rem;
+ box-sizing: border-box;
+ min-width: 15.5rem;
+ width: 15.5rem;
+ white-space: nowrap;
+ font-size: var(--font-size-sm);
+ line-height: 1.5rem;
+ color: var(--color-text-primary);
+ border: 0.5px solid var(--color-border);
+ border-radius: var(--radius-sm) 0 0 var(--radius-sm);
+ border-right-width: 0;
+ outline: 0;
+ background: var(--color-bg-elevated);
+ box-shadow: none;
+}
+.cbi-dynlist > .add-item .cbi-button {
+ display: flex;
+ width: auto !important;
+ padding-left: 0.8rem;
+ padding-right: 0.8rem;
+ margin-left: 0;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.875rem;
+ line-height: 1.5rem;
+ outline: 0;
+ box-shadow: none;
+ border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
+}
+.cbi-dynlist > .add-item .cbi-button-add {
+ width: 2.5rem !important;
+ padding: 0.5rem 0 !important;
+ font-weight: normal;
+ font-size: 1.2rem;
+ color: #fff;
+ background: var(--color-primary);
+ border: 1px solid var(--color-primary);
+}
+/* ---- CBI Dropdown ---- */
+.cbi-dropdown {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ min-width: 150px;
+ cursor: pointer;
+ /* Opened dropdown list */
+ /* Hidden input container */
+ /* When dropdown is used as a button (.btn / .cbi-button) */
+ /* Arrow color inherits from button text */
+}
+.cbi-dropdown > ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+}
+.cbi-dropdown > ul > li {
+ display: none;
+ padding: var(--space-sm) var(--space-md);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ min-height: auto;
+ line-height: 1.4;
+ /* Hide placeholder
only when LuCI hasn't marked it as the
+ active display item. LuCI sets [display] on whichever
+
+
+
+
+
diff --git a/luci-theme-glass/ucode/template/themes/glass/version b/luci-theme-glass/ucode/template/themes/glass/version
new file mode 100644
index 000000000..ab679818c
--- /dev/null
+++ b/luci-theme-glass/ucode/template/themes/glass/version
@@ -0,0 +1 @@
+1.1.6
\ No newline at end of file
diff --git a/luci-theme-liquid/.gitignore b/luci-theme-liquid/.gitignore
new file mode 100644
index 000000000..4baff0ddc
--- /dev/null
+++ b/luci-theme-liquid/.gitignore
@@ -0,0 +1,12 @@
+.research/
+.reasonix/
+*.ipk
+*.apk
+
+# macOS
+.DS_Store
+.AppleDouble
+.LSOverride
+reasonix.toml
+# CDP/headless 调试临时目录
+.liquid-tmp/
diff --git a/luci-theme-liquid/ChangeLogs.md b/luci-theme-liquid/ChangeLogs.md
new file mode 100644
index 000000000..c71a7f074
--- /dev/null
+++ b/luci-theme-liquid/ChangeLogs.md
@@ -0,0 +1,61 @@
+# 更新日志
+
+> 本文件归档 **[README.md](README.md)** 的全部历史更新条目;README 正文只保留最近一次更新。
+> English version: **[ChangeLogs_EN.md](ChangeLogs_EN.md)**
+
+- **2026-09-28 · v1.0(主版本升级)**:透明度调节 + 在线更新 + 登录体验 + 宽表格自适应
+ - **玻璃透明度滑杆**:顶栏颜色区拖动 0–100 即时生效、点刻度回默认;菜单、按钮、主题色背景同步变化,亮 / 暗各有默认;登录页同样可调,设置存在路由器上、换设备保留
+ - **在线检查更新**:点页脚版本号即可检查新版本、一键更新;检查中 / 已是最新 / 有新版本 / 失败 四种状态一目了然,失败会明确提示;手机页脚也显示版本、按钮加大
+ - **登录页**:登录前调好的明暗 / 主题色 / 透明度,登录后弹提示一键应用(30 秒可忽略);logo 换上毛玻璃光晕;修复明暗切换闪动与误触提交
+ - **下拉菜单**:玻璃外观重做,宽度随最长选项伸缩、贴边自动上弹、点内容不闪烁;暗色下选项更清楚;无线设置等页面异常的下拉恢复正常
+ - **提示弹窗**:成功提示(如“密码已更改”)改为屏幕居中玻璃弹窗、6 秒自动关闭;手机上按钮自动换行不溢出
+ - **宽表格自适应**:表格超出屏幕时先折叠操作按钮(一行一个)把宽度让给文字,文字不再被挤窄或裁掉;手机上表格收回屏内、无需左右拖动
+ - **其他修复**:菜单滑块切分类不再停在原处;OpenClash 打开不再先闪黑;选中标签改高对比配色,暗色下更清楚
+
+- **2026-08-18 · v0.7**:移动端体验优化 + 多处体验优化
+ - **移动端体验优化**
+ - 滚动页面时浏览器工具栏 / 地址栏自动收缩,移动端获得更大视野
+ - 底部横幅避让全面屏手势条
+ - EasyTier 重启、刷新版本确认框在手机上居中显示
+ - **多处体验优化**
+ - 修复按钮描边不贴合圆角、边缘出现竖缝的问题(普通按钮 / 保存并应用 / 分割按钮)
+- **2026-08-15 · v0.6**:菜单搜索 + 多处体验优化
+ - **菜单搜索**:颜色区新增搜索入口,点击推出搜索框,实时过滤菜单项,支持中英文匹配
+ - **多处体验优化**:多实例配置(如 Dropbear)独立卡片显示;刷新按钮玻璃质感;iStoreOS 页面标签适配主题样式
+- **2026-08-13 · v0.5**:弹窗居中 + 状态条样式优化
+ - **弹窗居中**:应用配置提示(正在等待 / 已应用 / 无待应用)与待应用变更窗口在移动端正确居中显示;超高表单弹窗(如添加 DHCP)保持正常滚动
+ - **状态条样式**:磁盘 / 内存使用条加深主题色、玻璃渐变质感,条内显示已用 / 总量
+- **2026-08-08 · v0.3**:自定义主题色 + 颜色开关优化 + 移动端 / 暗色模式改进
+ - **自定义主题色**:5 套预置色之外新增自定义色——点击彩虹圆点下拉输入 hex 颜色编码(`#RRGGBB`),离开输入框即保存并全局生效;非法输入自动回退默认蓝(仍保持自定义模式启用);经 uci 持久化,换设备保留
+ - **颜色开关**:外框统一 35px 高;自定义色选中时圆点内部显示自定义色、外圈彩虹渐变描边(一眼识别);Bing 壁纸按钮方形描边贴合图标、启用时单层白色描边
+ - **移动端**:首屏不再闪现"菜单先展开再收缩"(禁用桌面入场动画干扰抽屉收起态,加载完成即可操作);底部 foot 高度缩减
+ - **暗色模式**:模式开关图标(外链 svg)反色为浅色,深色背景下清晰可见
+ - **修复**:自定义色保存(controller 白名单支持 `accent=custom` / `accent_custom`)、登录页颜色开关定位、颜色区外框高度
+
+- **2026-08-08 · v0.2-r52**:移动端体验优化 + 下拉菜单优化
+ - **移动端接口页**:接口小框(ifacebox)与其所在行卡片左缘间隔优化为 4px,显示更整齐
+ - **移动端层叠卡片缩进**:弹出窗口宽 95%、嵌套卡片每层向内缩 2px,展示更大操作区
+ - **移动端菜单**:点击空白处退出菜单
+ - **桌面端弹出式层叠卡片**:显示效果优化(modal 80% 宽、嵌套卡片逐层限宽防溢出)
+ - **下拉菜单**:单选 / 多选交互与显示效果优化
+
+- **2026-08-07 · v0.2**:配置持久化(uci)+ Bing 每日壁纸 + 多项修复
+ - **配置持久化(uci)**:亮/暗/自动模式、5 套主题色、Bing 开关统一存入 `uci /etc/config/liquid`,换客户端(浏览器/设备)仍保留;改动经 LuCI RPC 直接提交,立即生效、无"待应用"提示
+ - **Bing 每日壁纸(锁屏)**:主题色前新增 Bing 开关按钮(字母 b logo);启用后锁屏显示 Bing 当日壁纸(缓存于各设备 `/tmp/liquid/`);无互联网或按钮关闭时自动回退默认壁纸(亮/暗各按模式)
+ - **锁屏只读**:登录页仅本地预览,不写入配置
+ - **uci 优先**:修复本地 localStorage 旧值覆盖 uci 新配置的问题,页面加载即应用路由器端配置
+ - **接口页修复**:未加入防火墙区域的接口 head 色条由白色改为灰色(亮色浅灰 / 暗色近黑),暗色下接口名不再不可见
+ - **CI**:GitHub Actions 双平台(x86 + MT798x)× 双格式(ipk / apk)自动构建发布
+ - 版本号 0.2-r1
+
+- **2026-08-07 · v0.1 首次发布**:全新 macOS 风格 Liquid Glass(液态玻璃)LuCI 主题,带来以下功能:
+ - **液态玻璃设计语言**:侧栏、内容卡片、登录页、页脚统一 frosted-glass(模糊 + 高光 + 主题色光晕),暗色下玻璃更实、文字更清晰
+ - **三态模式切换**:亮色 / 暗色 / 跟随系统,顶栏一键切换,`localStorage` 持久化、加载无闪烁
+ - **主题色**:5 套主题色(蓝 / 玫红 / 橙黄 / 郁金香紫 / 黄绿),菜单、按钮、Tab、logo 全链路联动
+ - **菜单**:默认全部折叠(点击一级菜单才展开二级)、hover 追踪滑块、选中玻璃胶囊、移动端滑出菜单
+ - **下拉控件**:一体化渐变胶囊(主体 + 分隔箭头,同"保存并应用"),视口边缘自动向上弹出、不被页脚遮挡
+ - **悬浮内容框(tooltip)**:磨砂玻璃底、顶层显示不被相邻卡片遮挡、边缘自动避让、互斥防残留
+ - **表格**:行内单元格等高(分割线对齐)、移动端按内容宽度 + 横向滚动(长列不重叠)
+ - **接口 / 设备页**:SVG 图标统一、连接状态由图标文件区分(不再半透误导)
+ - **锁屏(登录页)**:macOS 风格玻璃登录卡片 + Monterey 壁纸 + 主题色水滴 logo
+ - **兼容**:LuCI ≥ 23(OpenWrt 23.05 / 24.10 / master)
diff --git a/luci-theme-liquid/ChangeLogs_EN.md b/luci-theme-liquid/ChangeLogs_EN.md
new file mode 100644
index 000000000..ed157ef6b
--- /dev/null
+++ b/luci-theme-liquid/ChangeLogs_EN.md
@@ -0,0 +1,61 @@
+# Changelog
+
+> Full history of the entries from **[README_EN.md](README_EN.md)**; the README body only keeps the latest one.
+> 中文版本:**[ChangeLogs.md](ChangeLogs.md)**
+
+- **2026-09-28 · v1.0 (major release)**: opacity slider + online update + lock screen polish + wide-table adaptivity
+ - **Glass opacity slider**: drag 0–100 in the top-bar color area for an instant preview, click the tick to reset; menus, buttons and accent backgrounds follow along, with separate light / dark defaults; also on the lock screen, and the setting stays on your router across devices
+ - **Online update check**: click the footer version to check and install a new release; checking / up to date / update available / failed are all clearly shown, and failures report an error instead of hanging; mobile footers show the version too, with bigger buttons
+ - **Lock screen**: mode, accent and opacity picked before signing in are offered with one click after login (30-second countdown, dismissible); the logo gets a frosted glass glow; fixed mode-switch flicker and accidental submits
+ - **Dropdowns**: refreshed glass look — width follows the longest option, menus flip up near the edge, no flash on click; clearer highlights in dark mode; previously broken dropdowns (e.g. wireless settings) work again
+ - **Notifications**: success messages (e.g. "password changed") now show as a centered glass dialog that closes after 6 seconds; buttons wrap on phones instead of overflowing
+ - **Wide tables**: when a table no longer fits the screen, action buttons stack one per line so text keeps its width — no more squeezed or clipped columns; on phones the table stays on screen with no sideways scrolling
+ - **Fixes**: the sidebar slider no longer sticks to the previous category; OpenClash no longer flashes dark on open; the selected tab uses a high-contrast color for dark mode
+
+- **2026-08-18 · v0.7**: mobile UX polish + general UX polish
+ - **Mobile**
+ - Toolbar / address bar auto-collapses on scroll, giving a larger view on phones
+ - Bottom banner clears the fullscreen gesture bar
+ - EasyTier restart / refresh-version dialogs center on mobile
+ - **General**
+ - Fixed button borders not hugging rounded corners / edge seams (plain buttons, Save & Apply, split-select)
+- **2026-08-15 · v0.6**: menu search + UX polish
+ - **Menu search**: new search entry in the color area — opens a search box that filters menu items in real time, matching Chinese and English titles
+ - **UX polish**: multi-instance configs (e.g. Dropbear) show as separate cards; refresh button gets a frosted glass look; iStoreOS page tabs adopt the theme tab style
+- **2026-08-13 · v0.5**: centered modals + status-bar style polish
+ - **Centered modals**: apply-progress toasts (applying / applied / nothing to apply) and the pending-changes dialog are now properly centered on mobile; tall form modals (e.g. add DHCP) keep normal scrolling
+ - **Status bars**: disk / memory usage bars get a deeper accent color with a glass gradient, showing used / total inline
+- **2026-08-08 · v0.3**: custom accent color + color-switch polish + mobile / dark-mode improvements
+ - **Custom accent color**: beyond the 5 presets — click the rainbow dot to drop a hex input (`#RRGGBB`); leaving the field saves and applies instantly; invalid values fall back to default blue (custom mode stays enabled); persisted via uci across clients
+ - **Color switch**: pill fixed at 35px; a selected custom dot shows its color inside with a rainbow ring for instant recognition; the Bing wallpaper button gets a square outline hugging its icon and a single white ring when active
+ - **Mobile**: no first-load menu flash (desktop entry animation disabled so the drawer starts closed and usable immediately); footer height trimmed
+ - **Dark mode**: mode-switch icons (external svg) inverted to light so they stay visible on dark backgrounds
+ - **Fixes**: custom-color persistence (controller whitelist accepts `accent=custom` / `accent_custom`), login-page color-switch position, pill height
+
+- **2026-08-08 · v0.2-r52**: mobile UX & dropdown improvements
+ - **Mobile interfaces page**: ifacebox aligned 4px from its row card's left edge for a cleaner look
+ - **Mobile nested-card insets**: modal at 95% width, each nested layer steps in 2px, giving a larger usable area
+ - **Mobile menu**: tapping outside closes the menu
+ - **Desktop modal / nested cards**: improved display (modal at 80% width, nested layers width-constrained to prevent overflow)
+ - **Dropdowns**: single-select & multi-select interaction and display polish
+
+- **2026-08-07 · v0.2**: persistent config (uci) + Bing daily wallpaper + fixes
+ - **Persistent config (uci)**: light/dark/auto mode, the 5 accent colors and the Bing switch are stored in `uci /etc/config/liquid`, so the settings follow the router across clients (browsers / devices); changes are committed through the theme's own ucode endpoint and take effect immediately with no "pending apply" entries or save prompts.
+ - **Bing daily wallpaper (lock screen)**: a Bing toggle button (letter-b logo) sits before the five accent colors; when enabled, the lock screen shows Bing's picture of the day (cached per device in `/tmp/liquid/`); when offline or disabled it falls back to the default wallpaper (per mode).
+ - **Lock screen read-only**: the login page only previews locally and never writes configuration.
+ - **uci wins**: fixed stale localStorage overriding the uci config — every reload applies the router-wide config.
+ - **Interfaces page fix**: the head bar of interfaces not assigned to any firewall zone is now gray (light: light gray / dark: near-black) so the interface name stays readable in dark mode.
+ - **CI**: GitHub Actions build & release on two platforms (x86 + MT798x) × two formats (ipk / apk).
+ - Version bumped to 0.2-r1.
+
+- **2026-08-07 · v0.1 (initial release)**: a brand-new macOS-style Liquid Glass LuCI theme
+ - **Liquid glass design language**: frosted glass (blur + highlight + theme glow) across sidebar, content cards, login page and footer
+ - **Three-way mode**: light / dark / auto from the top bar, persisted, no flash on load
+ - **Accent colors**: 5 themes driving menus, buttons, tabs and logo
+ - **Menu**: collapsed by default, hover-tracking slider, selected glass capsule, mobile slide-out menu
+ - **Dropdowns**: gradient capsule (body + divider + arrow, same as "Save & Apply"), auto-flip at viewport edges
+ - **Tooltips**: frosted glass, top-level display, edge avoidance
+ - **Tables**: equal-height rows, horizontal scroll on mobile (no long-column overlap)
+ - **Interfaces / Devices**: uniform SVG icons, link state told by the icon file
+ - **Lock screen**: macOS-style frosted login card + Monterey wallpaper + accent-colored waterdrop logo
+ - **Compatibility**: LuCI ≥ 23 (OpenWrt 23.05 / 24.10 / master)
diff --git a/luci-theme-liquid/LICENSE b/luci-theme-liquid/LICENSE
new file mode 100644
index 000000000..f288702d2
--- /dev/null
+++ b/luci-theme-liquid/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/luci-theme-liquid/Makefile b/luci-theme-liquid/Makefile
new file mode 100644
index 000000000..262c3c673
--- /dev/null
+++ b/luci-theme-liquid/Makefile
@@ -0,0 +1,67 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-theme-liquid
+PKG_VERSION:=1.0
+PKG_RELEASE:=1
+
+PKG_MAINTAINER:=然后七年
+PKG_LICENSE:=Apache-2.0
+
+# OpenWrt 23.05 的 luci.mk 版本规则只取 PKG_VERSION(忽略 PKG_RELEASE),
+# 导致 GitHub Action 编译出的 ipk 没有 r 小版本(0.4 vs 0.4-r1)。
+# 用 override VERSION 强制统一为 PKG_VERSION-rPKG_RELEASE。注意:必须
+# 写在 include luci.mk 之前——luci.mk 末尾会立即 eval BuildPackage,
+# 其 ipk 命名/control 里的 $(VERSION) 在那一刻固化,写后面就晚了。
+# luci.mk 的 VERSION:= 是普通赋值(被 override 压住),且本值在新版
+# luci.mk 与 i18n 子包(PKG_PO_VERSION)下与默认一致,不影响 apk/新版。
+override VERSION:=$(if $(PKG_RELEASE),$(PKG_VERSION)-r$(PKG_RELEASE),$(PKG_VERSION))
+
+LUCI_TITLE:=Liquid glass theme for LuCI (>= 23)
+LUCI_PKGARCH:=all
+LUCI_DEPENDS:=+luci-base
+
+# 汉化/子包版本与主包保持同步
+PKG_PO_VERSION:=$(PKG_VERSION)-r$(PKG_RELEASE)
+
+# csstidy 会破坏 @media 块(只保留首条规则,其余泄漏到块外无条件生效),
+# 导致移动端断点样式全部失效;本主题关闭 CSS 压缩,样式原样打包。
+CONFIG_LUCI_CSSTIDY:=
+
+define Package/$(PKG_NAME)/postinst
+#!/bin/sh
+[ -n "$${IPKG_INSTROOT}" ] || {
+ # 公钥信任不在此处写(去冗余):apk 装机时生成的 post-install 会先
+ # 调 default_postinst —— 它会执行包内 etc/uci-defaults/99-zed-apk-key-liquid
+ # 完成公钥条件写入并消费该脚本;固件场景由首次开机执行同一脚本覆盖
+ # (构建期 --no-scripts 不跑脚本,文件随镜像保留);装 r9 之前的设备
+ # 由 OTA 安装前自举兜底。23.05 为 opkg 系统,不使用 apk 钥匙。
+
+ # 23.05 opkg 不执行 uci-defaults,必须在 postinst 中设置主题配置。
+ # 确保 mediaurlbase 指向 liquid,否则 fallback 到 null。
+ if [ "$$(uci -q get luci.main.mediaurlbase)" != "/luci-static/liquid" ]; then
+ uci set luci.main.mediaurlbase=/luci-static/liquid
+ uci commit luci
+ fi
+
+ rm -f /tmp/luci-indexcache.*
+ rm -rf /tmp/luci-modulecache/
+ /etc/init.d/rpcd reload 2>/dev/null
+ exit 0
+}
+exit 0
+endef
+
+define Package/$(PKG_NAME)/postrm
+#!/bin/sh
+[ -n "$${IPKG_INSTROOT}" ] || {
+ uci -q delete luci.themes.Liquid
+ [ "$$(uci -q get luci.main.mediaurlbase)" = "/luci-static/liquid" ] && \
+ uci -q delete luci.main.mediaurlbase
+ uci commit luci
+}
+exit 0
+endef
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-theme-liquid/README.md b/luci-theme-liquid/README.md
new file mode 100644
index 000000000..ad2095c70
--- /dev/null
+++ b/luci-theme-liquid/README.md
@@ -0,0 +1,177 @@
+
+
+[license]: /LICENSE
+[license-badge]: https://img.shields.io/github/license/zzsj0928/luci-theme-liquid?style=flat-square&a=1
+[prs]: https://github.com/zzsj0928/luci-theme-liquid/pulls
+[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
+[issues]: https://github.com/zzsj0928/luci-theme-liquid/issues/new
+[issues-badge]: https://img.shields.io/badge/Issues-welcome-brightgreen.svg?style=flat-square
+[release]: https://github.com/zzsj0928/luci-theme-liquid/releases
+[release-badge]: https://img.shields.io/github/v/release/zzsj0928/luci-theme-liquid?style=flat-square
+[download]: https://github.com/zzsj0928/luci-theme-liquid/releases
+[download-badge]: https://img.shields.io/github/downloads/zzsj0928/luci-theme-liquid/total?style=flat-square
+[en-us-link]: /README_EN.md
+[zh-cn-link]: /README.md
+[official]: https://github.com/openwrt/openwrt
+[immortalwrt]: https://github.com/immortalwrt/immortalwrt
+[luci-mod]: https://github.com/xylz0928/luci-mod
+
+