diff --git a/CHANGELOG.md b/CHANGELOG.md index 4565505..478a7a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Release history focused on **features, settings, default behavior, compatibility ## 0.34.9 — experimental TLS post-quantum key exchange (source candidate) +- **Dashboard source maintenance:** panel styles are organized into eight modules with consolidated responsive conditions and a deterministic build step. Login and the dashboard continue to load one fingerprinted stylesheet; rebuilding the binary applies changes. Source checks and browser regression tooling cover the module order and UI cascade. Native Rust validation remains required for this source candidate. +- **CSS build preflight fix:** ship local `scripts`/`build` package markers so unrelated installed Python packages cannot hide `build.panel_css` or the application scripts. This fixes `dev.sh` startup/repair, the CSS audit and packaging imports on affected environments; generated styles remain unchanged. +- **Generated CSS recovery:** detect a root `build.rs` without the panel generator, track the generated CSS as a Cargo dependency and reject missing/empty helper output before compiling the application. Document refreshing `build.rs` after overlaying a reproducibly timestamped archive onto an existing build cache. - **Settings → TLS policy:** new disabled-by-default experimental `post_quantum` toggle. When enabled with TLS 1.3, inbound TCP TLS and HTTP/3 QUIC use an explicit aws-lc-rs provider, prioritizing the hybrid **X25519MLKEM768** key-exchange group while retaining classical alternatives. When disabled, the original ring provider is retained. This changes KEX, not TLS cipher suites or certificate signing; no change to upstream/client-side TLS providers. - **Per-vhost TLS override:** may opt out when global PQC is enabled; cannot enable PQC if the global policy disables it. TLS 1.3 is required. Existing per-vhost policies lacking the field continue to default to non-PQC. HTTP/3 continues to require exactly matching global policy. - **UI/docs/API:** English/Polish switch and key-exchange group readout, authenticated Reader guide linked from the TLS policy page, OpenAPI and regression unit tests. diff --git a/README.md b/README.md index 2bbb343..47e712f 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,13 @@ checks. See [Linux filesystem access](docs/INSTALL.md#linux-debrpm-read-only-acc The authenticated **`/docs`** page includes the guides and a separate interactive **OpenAPI/Swagger** tab. +## Dashboard source maintenance + +The panel stylesheet is maintained in eight ordered modules under +[`src/admin/web/css`](src/admin/web/css/README.md). The build embeds one compact, +fingerprinted stylesheet for login and the dashboard. See the module guide for +ownership, cascade rules, duplicate checks and browser regression commands. + ## Status of this source archive Version **0.34.9** is provided as a **source-only experimental candidate**. PQC dependencies were provisionally locked using published crate metadata, not checked by Cargo in the packaging environment. Run `cargo metadata --locked`, `cargo check --locked --all-targets`, `cargo test --locked --all-targets` and protocol compatibility checks on your target system before production deployment. See [Installation](docs/INSTALL.md) and [Roadmap](ROADMAP.md). diff --git a/SHA256SUMS b/SHA256SUMS index 483955e..67e644b 100644 --- a/SHA256SUMS +++ b/SHA256SUMS @@ -1,15 +1,15 @@ 61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore 4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example -9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore +18ea9db93171d23888334488151554d52d9a3af701581b89ccbe4150a5b81e7f .gitignore 237ed6e2c0d4fa1f125180c349856749c13820b72593b5fc58e1b58cce47c840 AGENTS.md -4f695dd4ded1ef0c6fbe421ae9bc01993140ddbb4f77cb7a46dd8191a2584ddb CHANGELOG.md +608cc471556fa6e29fefae1d5ea18194d6ef94b5d32410965b0140a4fcf7018c CHANGELOG.md 310d60da6c7f501ccdde86b93ee1d5b0102033ba71ac9bb42a4e3ce184692ef9 Cargo.lock c18c0ba1a22a8534a4cf3b6db907f41f04e0b6cb47cd305a21af139a06e303fb Cargo.toml 25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile -fb75f6ff1fee8e5af4c89943c67b7e75cbac048254cecc3dc9fd0ea33d53ac25 README.md +a288a9c873bfe2e866a41b116af8cf3dac7661db68164bbe6b57706e2fca7bc6 README.md ae6d14ada6fde7d0b8514840b821678418cf5b1816c18434e3b7fb2ae27a0b21 ROADMAP.md da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml -7309cfaa188c7a1fcf30be1b8313ee99c00b7fe5d6d626ddefcc7345fa066112 build.rs +ac07b3bdd46c787989e93f55ebd6ea3d7f5b150ef36f2422e2b58df3f6b7543a build.rs 2c6a0a5cb088399e2cc912073069eb334ec6615eee31734d2f0c161f1415fb6a deploy/README.md 13d514de5042a230cacf8c10abe1ef183c94ac799a89b8e2c111152ac7c69b48 deploy/rustproxy.service c111f5b20484707b80efb7dd9176acbaa27b63ac78db497261ac234821e1fade dev.sh @@ -36,7 +36,7 @@ e52b31d0abf65ae3034f6cb367cca113633014ce8d1d799ad4dd5624d2c7b8bb docker-agent/n f24767c2f9d4db4db5005b700d03323dd20f0a1d180ae939be4fe54a126aaf32 docs/CONFIGURATION.md 80bcc666736b10f6202386d0342e8d6feeba858d77aea310bf3ec6de01c2bfb3 docs/CONTAINER.md dc670d912a6a946160c2f8528c9bb710ec38ebe406dcb7dcaf18fb2653f667b6 docs/CUSTOM_LOCATIONS.md -9fb2d0bcac70d1883dd16557622851a1f0527b3d3569a4b5177f5e6dedd3c74d docs/DASHBOARD.md +302a9db0a5a4e5f6c49684ef0ea9ad9f1796fdbcb5e28bb5a98c75fffd4374f4 docs/DASHBOARD.md 3e2f7670d136911c1b7bfe8571c60590932f74a60bcac308add4ccb0446bf970 docs/DOCKER_DISCOVERY.md f1baabe34e8a1b204d7c78e2f558c019c156b03adfa6ddecb931b4b8a5501f78 docs/INSTALL.md 37001cbf8ed248ca05ec40f12dd9876d33f6e0c97dcb21eb2e6f53c3d97a60bb docs/INTEGRATIONS.md @@ -44,7 +44,7 @@ f1baabe34e8a1b204d7c78e2f558c019c156b03adfa6ddecb931b4b8a5501f78 docs/INSTALL.m 888cdc8c56e10b8a7615012e192e085bf2899d0ec9b3ddfb5731e7de6ab5bcde docs/MIGRATION.md 140b95ad778261455f269f2144a1c61c2315bd3b85300fb613822eacddf3cfc2 docs/MONITORING.md d5894c07c68ec4230987767198434abee13b0e525842fea21bcbe752fa82742b docs/PHP_FPM.md -8b22a8f738f62cb27faf97683cc467de0fba02ab5999217b502f24a38f10ab4b docs/README.md +bcd8c4eff9d57342d75441f5941a5828275d6b320317668e727467bc0d876f66 docs/README.md 490a6dbe870f2b1fd4ec7816768dde6c86fa7957fc9de2d2f075e123c9222222 docs/RESPONSE_DELIVERY.md e2b36152c13d65621a367c69dd400118dae1e4426a4d2b2139d2f63fa30442d6 docs/SECURITY.md bbef60ff1771da9f48af812987766e1e479408873ae2f079b13f1d5c62ecc6ff docs/STREAMS.md @@ -97,8 +97,11 @@ ee50f38c037765fab742e888d4b9fc4d874c31c785c6064d4eca3d2c4d24d944 nagios/check_r 1465396cf1358067e9fe48b3d61f28fd4ac65c577b67c502950ce6b42ecbe3b3 nagios/check_rustproxy_latency 6e036f736a2b40feb78eec17bf34871771524190f283591d1c89e48e6d2271e6 nagios/check_rustproxy_queues 7458b2d74495e6fb1c1edeea4d5a793705476431dadb60627187111f91441e13 rust-toolchain.toml -bd313cacfc1b450a90368b30ae39ab48b877c8bed3c2db8bbd4e4a1f40865c9e scripts/README.md -f550ffab868c5241ff5d43d9ae569e19f6842b324b80ecefdc7034642125ed9b scripts/audit_css_usage.py +fd1c34599f725ad5f7c3c0cc89e08b11917baef02c83af054ac8528b2e01dd8e scripts/README.md +1d63e9cea61a744f1bf2085cbb7c3d8755b3eda1569c8532574dd4fb6a840678 scripts/__init__.py +1843422d648101bb675d8bfcdb109ef350bfda273999ad7395d5f6ceb923eaa1 scripts/audit_css_usage.py +5fc5c7f274aa5a6c07970f7b2e548acabae3dc2a24d598308fd768a8d9de2510 scripts/build/__init__.py +c3066f40f152a2655f88314fd9cf784eddbfa6c3238a494da13d972ef9ad930c scripts/build/panel_css.py 216abccbb8b0b77b28de96382bb5790d1ce831699746d3fb06ef13a6dd1f5d21 scripts/build/swagger_assets.py e4d15aa6aa864d04b9b30aca33826b833632af02ca643f8b23e219a5ea1298dd scripts/deploy/deploy.py 618603239b000bbe4af36a6a6ee268758af632c761fc53fc6b7f44a4a818bdf1 scripts/deploy/deploy_core.py @@ -107,7 +110,7 @@ a641bd35400c56f3b68b0a92e97b602d06090f03972ca8f01fa744e90dd97403 scripts/deploy 2cc658faaa610b21b48942c59b955525539ad4503fd60f2a49c7e626a22faaff scripts/migration/migrate_npm.py a459d95bbb0076f33ade8b273b813308167f9d8d45242b309a2d18320ca0093c scripts/migration/migrate_npm_api.py 31ccad3ec3c880119f3b7002f0c6652f8b43634edad8ab3763cb0e4497d0bb1f scripts/migration/migrate_npm_core.py -3077405aae2dd0f32c0e168ef710745bd44e42283a4c566ab411e68d3bb745af scripts/preflight.py +8f9f2dc7e6d6688b915a7dfbfc05643a80e5814819926cf43458a54965cfc83c scripts/preflight.py 3d6a0503afc25beab79ddc86864bcbb15248ded7368d33aa64321b8cdff59933 scripts/resolve_dependencies_0312.sh 20124173452b4514bad8850c1637569a9a191882478451e549bd16b432605fc1 scripts/runtime/acme_provider.py e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/runtime/acme_rfc2136.py @@ -137,7 +140,7 @@ a402209141667fdce69698a60dc0b867248effa191494d0a0d93d468d7df9476 src/admin/cert 159c3970d25fa29acf2483fc9422b003e5d63884713f62baa8375e4a4020730e src/admin/dashboard_presets.rs aae1ca0e70d987830bd63948d431f54302083148177c1b551565be12df7fe771 src/admin/documentation.rs da28dd57087df138913b784b71dca11d36c68100f2f100f452fbf4e4d7982433 src/admin/documentation/documents.rs -67fa0e842f6789ed38e101ed6717072736e85c34af873d1a27d7efee5dc0c83d src/admin/documentation/tests.rs +6ca14ae276c0e683903cfe326f6733cf33ea05f3abbefd5a22671ea160a58ea6 src/admin/documentation/tests.rs 1ce779b84aa8f47bdb3c34f83593c1f2f06d2235f0319847096cfcd035434168 src/admin/file_log_search.rs ab18acf6fb4ac86fb22635b9219a12dac76d24952c8f15538486267a9b4b7855 src/admin/file_logs.rs d58a2ed83319176de4e66790ed292447c9497ae031464ec55cfe27174acb639a src/admin/file_logs_io.rs @@ -146,7 +149,7 @@ f66ff718f82a326f80808b0a5088bd05a48625be802e5ba305fff3c344b59948 src/admin/logg 1ba0520c6348246cbb35b60e596e99dc600137c0696de86508cf06587bd86563 src/admin/mod.rs 8e1012a8ec776c3525310b03a70f274955dce9de3528109070ea11de0ff5c16c src/admin/openapi.json 7cfd6593ac9bcea3d1b9e61bbcb7ed98d65ed3190f54f94c1e104541f4fbb4f5 src/admin/operations.rs -f14afb6ae141053d83d0dc59df835b20e38a37a9e83f86f0f2053f2bd5d9ce57 src/admin/panel_assets.rs +257d93a29d0bda96462bcf695aba8d0ec0f9412c379e066ec46ae8ee034e3a53 src/admin/panel_assets.rs f5127855c2994d052678865df36d703b74f4a0afc633daf7718b10da18d26e37 src/admin/panel_content.rs 730c559afaa8a50affc3eb6dc6fbde7005c0356237352fcf8c5423b65725a8fe src/admin/preferences.rs 35a7f99c7448c90b8637f4b3a7becca138f1f378fe5dc3f87122151525bb20f9 src/admin/preferences_columns.rs @@ -160,7 +163,7 @@ e135431cb078ddb483165581123c64af8f00ea67acb3a395ec3e53c85d0d0d45 src/admin/sess 2d5b4c28fd0bd48b06890fa8ec44a2296de948c1f88eb2909f4b495710d4e13e src/admin/status_live.rs 8315c856cd50f1072593ecb30c274d42d82f078b5fb93e7c68a8857cd81b0841 src/admin/status_system.rs cadd7dca29b9aad8c77c7bc23dd249ea37a15adfa4d636a8ef00273a91ce35dc src/admin/tail.rs -666ef444c81e59a2fd47ace3be26e51a8630194d8ffd4db131eb228947fee8ca src/admin/tests.rs +dacdf1b948991698f16640a375e3af90c3a18bb2247b0ad51aa97f763ee3f7bd src/admin/tests.rs c18c369124368f58524652565102817ed69fd25b222b0c5e737ef7632b45dcfb src/admin/web/acl-picker.js 48fcd818c522fe80f2c7b2e218f87de6a7772ffd0a24e93502780b03ec78e335 src/admin/web/acme.js 86abf50f39862d821cd2e43ca425671aa85289272915132c5a9a5dd1fc4182e2 src/admin/web/api-tokens.js @@ -169,7 +172,6 @@ a085ee336e6669229f3ad2f74536f19bd088412ca79bcaff30dff293f1e1baf9 src/admin/web/ d6c5448db12de34a7bb4924411b44d09c7144bc3e659bd3c8a7c0ebf1938da1c src/admin/web/app/config-history.js c8e1d31a7bf77d9e4f4be979bf6a7474369aae3e1e46e6b30d1a76677fa3d762 src/admin/web/app/core.js 622d34e1616fee9a480d208192fc5a84d62d84ef4233d6a7d95e2bfb8cef415b src/admin/web/app/dashboard-inline-layout.js -851a4337127f332db91f6954f689363ee227fcddde2dda82e55f645940d0ce3b src/admin/web/app/dashboard-layout-editor.js a184c50e1ca6bb5a14954e31b15f09ff9978fb939064ca724851bbb4e661dc56 src/admin/web/app/dashboard-presets.js bfb3fbac62f9ff762a298d9c06808ae81ae55de7a41e0bb35ce5c167f333ffe2 src/admin/web/app/overview-extra-metrics.js 5e62cccf6a9507fe208e9f41bbd08453c3d8c6a6b83c56d93a2bb22550ea6e9a src/admin/web/app/overview-masonry.js @@ -185,6 +187,16 @@ fa00bce2651308a4e0b1303cc0146be17ed4794b67b2f0cebfaf861558d8c78c src/admin/web/ 7f89bfae66e2a08a05a112733864e908f036bb1ee3d8b5b9ff23783b870acd96 src/admin/web/app/web-overview.js 849b2ca9d183bdf6cb1e04651cda52da55b8a631e11ab1d827a052c8436bbd76 src/admin/web/cache-panel.js e9788a9d1e0bce8775f612a5c842afea8a0dcf01301acdef4b69ff6a52d2ef45 src/admin/web/certificate-api.js +9d000e98497b3387641500d5273d23eda45d532c714f22e187572928da152a97 src/admin/web/css/00-tokens.css +11093f12033d0c549c2d2b0dfdcbc660ed2318a4ab17a9dc562b5902d7ddfb84 src/admin/web/css/10-base.css +4aae1657e8f5faa755cab0399769e6179364148f80243e742fc7a5e6e237b3e6 src/admin/web/css/20-shell.css +31834acc1fc234ed314b0ca069658bfab175d782bcfaa5e6ee07d46fef689457 src/admin/web/css/30-overview.css +c89c877479c6fc8fc810766ea4787448953665764d18566fc550a119796121d5 src/admin/web/css/40-editors.css +86f7e08a9973ebddd67aaeca92d2b5e2c0f7f53dee45d65a93bb88b35b207cdc src/admin/web/css/50-settings.css +d019d9741951c9d652fab9e520693e0dd3f00b395e32169e5c7ca3ccaf04132c src/admin/web/css/60-monitoring.css +6afa10207938787013f5a4731910630b19bec4a22682a07c3e67707d17cfd97e src/admin/web/css/70-responsive.css +f2a2b0476cc55d8dadb4fd52c2c589d64785ad400ccc6cac5a2d10cfca729928 src/admin/web/css/README.md +8241bd9489dfed082863948829a44a49bf0a8e9cd63c55809655109e393e0918 src/admin/web/css/manifest.json 560a8e047182fbcee08aa4f8d999400a8a5ddca0b6a4640efd46f6b5c34fde91 src/admin/web/dashboard-defaults.json 14213011cfb6ac334c8711b76bbdde97588a7b7ba4eb2592a1e387f877d6cbe0 src/admin/web/dashboard-widgets.json 50d1ebcbaf4d0b2968da23bb943fc3f67131c5d32df3f61d23ff37aad9da5b81 src/admin/web/demo.html @@ -208,7 +220,7 @@ e6c4d91d1542d0a5b47bb8736f9539d43a6713df5a473b635519d5b7eac2eb9c src/admin/web/ 0a5fff35555153f59d4346933030a67b6e1a8768cee7a47cfa2fcf1a2c564f63 src/admin/web/favicon.svg 7cbdef9192a9dee1f8eac42b85f52b3190f7f6efae50004aa7ad116a910e4036 src/admin/web/file-logs.js b14e7b3578322f309627f6fe965cc16abcf85da3437c9070be6caecae1908743 src/admin/web/host404.js -ede1f17067ac1622c66dfaa54bf7b7bbb0ce160ee883235be743ef5b2aa83243 src/admin/web/index.html +66af48d25f91f6dfe64887e20bf75709b9ca2efeb0f6dab3b139f11d8a7e9ee5 src/admin/web/index.html 7915a06cebb3dfa2c51833c326586920691ba4431b1f984429b039333f9c2a93 src/admin/web/integrations.js 7a12f020fc587205694978ab41b660e9a80487d413fbf6e34fc525eabc58308f src/admin/web/login-keys.json cbd41678eadd994dc5252628ee8ef9551fa9f7d8694ca5abd206240d9a96ad30 src/admin/web/login.html @@ -221,7 +233,6 @@ a8afc6e70950bc70b98725eb049d73cff35dc3a3ee659229a15a8e08d3eb4f12 src/admin/web/ 3ee4508cdda0b0b0e0f880ecac4b085368098b46240d24f2e6efed21c96bd5e9 src/admin/web/settings.js 8eadbada619b15087783dcfa0e889a81f88e78034d084c5ab616f7912b835940 src/admin/web/status_200.html 6feb1996b1caa9d19e0dc56eae7ca5b48b7e7cdf0a93bb2e4b527fb8f13a2fe5 src/admin/web/streams.js -38716fae968aeb8f6a8510f28bb9054a73a438a969c3b69f9c9d921acfed6e06 src/admin/web/style.css d290c048450655b3eeae7c6ec54a82d645d1965513694ab29fdf7eef796a1abd src/admin/web/traffic-panel.js dfb4ded2fefb90fc1e2d33370cdef10b040a85cb43235ea9dbc1d73bef823a79 src/admin/web/transform-panel.js a55f45dad5b540def419f8487161fb506b7e52744c76a5e661c7a5fa0a98a426 src/admin/web/vhosts/core.js @@ -330,7 +341,7 @@ c1b49bef28d87712e4c46138e70b52c4bcfb1a00196579031fac043d0b4aaa7b src/geoip/upda 9a912dad0b8d1e10410fd95d0da3684656092ef85cf8b23eda4ffe7fb92921d8 src/geoip/update_tests.rs 61a2b01db9010f9debfb38b43a26bf588438a5e470dff938e4e54f9d7ba46732 src/headers/mod.rs e4f07bfb7eb75b6897ef812870caff158679a3046d64582428abe8c6df490dbe src/headers/tests.rs -b458510eb89ae8bf8fda84989073c6e64c69f2b12fde96c454b33f9df6a57df2 src/http3.rs +1b291ef7b06647ce820111cc802874394e3600b4b2c86ee9a1fbf7eee6cb864a src/http3.rs 0cc484bf0cc59a55a13e0e70435833a9a7a22ad7dfb5b9f86e059cf70a80cc2d src/http3/body.rs 741972222414c280104acbcd8f8071bed9cbfcfa2f503c487c11c56f553f53cd src/http3/diagnostics.rs 49ce4452ef7973cafa2e4071e2e14be0e16000e931a6501e4dc613652e49e230 src/http3/identity.rs @@ -451,8 +462,8 @@ df98becda799b6bf4ff5e2fec7d0e0a486e6b7eca6f9f5ad49ffdafd49a4f498 src/tls.rs 2ff3c219f87bd82677045a4809d3d94921c5b230bbc2f3e7131b07548f73ea7e src/tls/external_paths.rs f88a6c8c2e35c3aa58b7763c71a5a2cec82a34f24478a98b0960b81659a1ec4c src/tls/external_paths_tests.rs 7b4c0f58b3a1e1791bb3c682a001b78bfdbbdafdbc143db8063b3f22dc168616 src/tls/server_config.rs -68a29d90f1675c7d97953e50ebccd4fa21c47fd0a4d8a0838b436ee84399afe0 src/tls/tests.rs -bf0ccec8909e678aaed84d358058b1fa865b38e09d10471b02fca76eea38633d src/tls/types.rs +7c0c06c3009c726043168ae47254a84642dbabbbce1b2e718544414312ca3e9b src/tls/tests.rs +d625e2e3f669484432ffc0bf7c1aba94aa5bb709dc4d080daa3bfa8004dee9f2 src/tls/types.rs a535c8526b38de2990da7781e158b9ea70ef8c1f6fae0aac5ce6e9da21d3a309 src/traffic/mod.rs dc8a83fae47da0d86e6ce0a329cb5274233264df56eebdb56b9bf37cb7be2bb4 src/traffic/tests.rs 65fcac840f8b5f1508213e3f374aa318b450b0f9f90ba827c5ea935b9c60bee7 src/transform/apply.rs @@ -468,7 +479,7 @@ d13fa462d6df516a4cfaee9f4e184044c5b6345e311aeb3d9058efafe54e4c5e src/transform/ 954b86300c3ea47281acb2f43b0771e838f83ac09eb55e882e42de26aee3128f src/transform/model.rs 4b52257e1a3643910b5e7043852d7218230c0021c31215d90d0f8e384f2ae233 src/transform/svg/mod.rs 0f53c64f442390ee20a59087a10f8f9ffb58053263e9aa848ddabfae1a6f3f51 src/transform/svg/tests.rs -d1f3f53fedae0d8ef69eeee457f7d30ed5a670a1ed4ced60683400ee46680260 src/transform/tests.rs +3c99de898e4c516a805fec1bc77007a211550b7d7a563933a81ae635d235a2e6 src/transform/tests.rs 8d38f2f7d4ca4dd453cd0e2337614ec704fbd0a36d3e2374cda43f05dfcad663 src/update_check/mod.rs c42d000d1d576721af3ffe930589388693001b8113554a7658f6dcbc665b5d1b src/update_check/tests.rs b7ee3629a8fde0972bc1822b764f5fa2c813a760dd5dfb7f1ce643a7db8f9f75 src/upstream.rs @@ -511,11 +522,18 @@ b791762b4ab7a43c3be19df84e523faf3dfb4df055f66d02178f93edb60d2204 src/web_server df888adf893d0b6b99348d19fa07bd9c2b8fc0ce7f411d06be7411afb4311fe0 src/web_server/tests.rs f00743af2cf4a5952caef7a1dcf68e7817b5599a252a9973e1c22045517c0b0e tests/fixtures/argon2id.json eca68a07c2b1e45fdb8f512294f5a432db3ae5a6aec8fb200b47f155cd9a31d6 tools/README.md +577302f167c9d76655f12852ce57a272712044b67a76823cf1ec625116d461e3 tools/audit/CSS_REFACTOR_REPORT.md 358c1e94dd2b4a756430e5335747f0b437345b1713670a9972c5bcf78b837ccb tools/audit/admin_audit.py +3f5c3654e9667a60d2e93558ee3f6872a4fe6839b8efcc64671c4a36fb3d85a2 tools/audit/css-refactor-results.json +915de79d40e79bb34fd3dc45eb39028442e8023ab0dedbf0028ec4e36bc65591 tools/dev/README-dashboard-css.md 7c30c280d0b0a8bf855eeffab583ca34dc161255f7ef6124148aa979c17c26d3 tools/dev/memory_load.py d1418f8e574a7390fd45a2aa879bdf5720b773162d65e295ab0350506aa5220a tools/dev/mock_auth.py 2d3ff92300b09ce9cecbbaaa9309b4d33160fa4b2291c9952b784e8cfb599ab5 tools/dev/mock_server.py +18f9d60dd78b6d7f6cbdc21d4cb579d4b48fbda2557c9c1f84370ac02af80a57 tools/dev/package.json 538b5f6cfa738355a729940ee3d92e7ab3dc25ca69ae330ddaa08f831775b8ba tools/dev/test_compression.py +8147bbc6f6e87768f47cf7279471ad283786bdf01eeddcc2fd3582d62886bdbd tools/dev/test_dashboard_css.cjs +91937cf81f6667590e119be04c11bfcf438a77b278f97b8ac1785a17062db3b8 tools/dev/test_dashboard_css_motion.cjs +68aed10c460bd7df52aa142ffb9f97eff37b23d979158a1c281297c74ec57957 tools/dev/test_panel_css.py c711154a9df425a56607bd88f03815ffb408213a31a2b66ed1c1c9a6301a2fc6 tools/legacy/backup.py 89d2b4e60a2f4cb93e8943b74b1b5ca84374b01fa74ea07bec07bdff315ac0e9 update.sh 56c702111022e1ca2c8f6b72c635c749b05936ffe6387bdb0a202691614cbc96 upgrade.sh diff --git a/build.rs b/build.rs index 2d2fef1..2c66062 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,53 @@ use serde_json::{json, Value}; use std::{collections::HashSet, env, fs, path::PathBuf, process::Command}; +fn panel_styles( + root: &std::path::Path, + out: &std::path::Path, +) -> Result<(), Box> { + println!("cargo:rerun-if-env-changed=RUST_PROXY_PYTHON"); + let helper = root.join("scripts/build/panel_css.py"); + println!("cargo:rerun-if-changed={}", helper.display()); + println!("cargo:rerun-if-changed={}", root.join("src/admin/web/css").display()); + let interpreters = match env::var_os("RUST_PROXY_PYTHON") { + Some(value) if !value.is_empty() => vec![value], + _ => vec!["python3".into(), "python".into()], + }; + for interpreter in interpreters { + match Command::new(&interpreter) + .arg(&helper) + .arg("--root") + .arg(root) + .arg("--out-dir") + .arg(out) + .arg("--cargo") + .status() + { + Ok(status) if status.success() => { + let generated = out.join("panel-style.css"); + let metadata = fs::metadata(&generated).map_err(|error| { + format!( + "Panel CSS helper succeeded but {} is unavailable: {error}", + generated.display() + ) + })?; + if !metadata.is_file() || metadata.len() == 0 { + return Err(format!( + "Panel CSS helper did not create a non-empty regular file: {}", + generated.display() + ) + .into()); + } + return Ok(()); + } + Ok(_) => return Err("Panel CSS preparation failed; see the diagnostic above and src/admin/web/css/README.md".into()), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => continue, + Err(error) => return Err(error.into()), + } + } + Err("Python 3.10+ is required for build assets; install python3 or set RUST_PROXY_PYTHON to its executable".into()) +} + fn swagger_assets( root: &std::path::Path, out: &std::path::Path, @@ -35,6 +82,7 @@ fn main() -> Result<(), Box> { env::var_os("CARGO_MANIFEST_DIR").ok_or("Cargo did not supply CARGO_MANIFEST_DIR")?, ); let out = PathBuf::from(env::var_os("OUT_DIR").ok_or("Cargo did not supply OUT_DIR")?); + panel_styles(&root, &out)?; swagger_assets(&root, &out)?; let langs = root.join("langs"); println!("cargo:rerun-if-changed={}", langs.display()); diff --git a/docs/DASHBOARD.md b/docs/DASHBOARD.md index f95eb35..f798182 100644 --- a/docs/DASHBOARD.md +++ b/docs/DASHBOARD.md @@ -2,6 +2,14 @@ The authenticated panel is the primary way to manage RustProxy. Pages cover **Overview**, **Vhosts**, **Web Server**, **Certificates**, **Docker vhosts**, **Streams**, **Traffic**, **Cache**, **Logs**, **Settings** and **Documentation**. Availability of individual cards depends on enabled features and the account's display preferences. +## Stylesheet updates + +The panel and login page load one versioned stylesheet embedded in the Rust +binary. Source styles are split into eight modules and assembled during the +build. Rebuild the binary to apply CSS changes; copying individual CSS files to +an installed server does not update the panel. The authenticated documentation +page has its own stylesheet. + ## Overview cards and layouts The Overview contains live counters, charts and optional backend, cache, PHP, Docker and Stream cards. Under **Settings → Appearance → Dashboard layout** or the Overview editor, configure visibility, order, card width, density and the number of history samples. A hidden card stops consuming its optional browser-side visualization work but does not turn off its underlying proxy feature. diff --git a/docs/README.md b/docs/README.md index f1054a1..78a6a5b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -27,7 +27,7 @@ Practical guides for **RustProxy 0.34.9**. This index and the listed pages are a - [Certificates and automatic renewal](ACME_PROVIDERS.md) — PEM, HTTP-01, DNS-01, Certbot and external certificates. - [Docker discovery](DOCKER_DISCOVERY.md) — optional Linux agent DEB/RPM or manual installation, TLS/token CLI, networking, generated Vhosts and their per-service live HTTP logs. - [Other integrations](INTEGRATIONS.md) — Tinyauth, NPM migration, OPNsense and monitoring tools. -- [Dashboard](DASHBOARD.md) — metrics panels, HIT/MISS/BYPASS/OTHER request mix, presets and configuration history. +- [Dashboard](DASHBOARD.md) — metrics panels, HIT/MISS/BYPASS/OTHER request mix, presets, configuration history and applying stylesheet updates. - [Monitoring](MONITORING.md) — Prometheus, full JSON and compact `summary` API, traffic, backend and cache observations. - [Logs](LOGS.md) — live logs, stored logs, error logs and searches. - [Backups and recovery](BACKUP.md) — encrypted backups, restore and Secret Store. diff --git a/scripts/README.md b/scripts/README.md index 51ba690..9e00e46 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -10,6 +10,74 @@ External development and QA helpers live under `tools/`; external platform integ Keep these paths stable because Rust source, `build.rs` and shell entry points reference them directly. +## Panel stylesheet sources + +The dashboard stylesheet lives in `src/admin/web/css/`, with its complete cascade +order declared in `manifest.json`. Edit those modules, not a generated bundle. +`build.rs` invokes `build/panel_css.py` using Python 3.10+ (or +`RUST_PROXY_PYTHON`) and embeds the compact `OUT_DIR/panel-style.css`. Login and +the dashboard continue to load one fingerprinted stylesheet, using the existing +panel compression and access rules; no npm install or runtime CSS imports are +needed. The documentation page retains its separate `src/admin/web/docs.css`. + +```bash +python3 scripts/build/panel_css.py --check +python3 tools/dev/test_panel_css.py +python3 scripts/audit_css_usage.py --strict +``` + +The first two commands use only the Python standard library. The optional +selector/structure audit needs `tinycss2` and `cssselect2`; it checks all manifest +modules together and reviews the documentation stylesheet separately. Use +`--json PATH` for selector references and `--structure-json PATH` for repeated +selectors, conditional blocks, properties and identical declaration blocks. +Missing class tokens are review candidates: classes assembled by JavaScript +must be checked before deleting their styles. Strict mode also rejects identical +declaration bodies within a compatible selector family. It permits separate +standard/WebKit/Gecko rules only when joining them would create an incompatible +vendor selector list. Choose shared-rule placement by checking the cascade, +not by mechanically moving every matching block to the beginning of a module. + +Preflight rejects missing, repeated or unlisted CSS modules and stale monolithic +`style.css` files. `./dev.sh repair` backs up only the exact original 0.34.9 +monolith; custom or unknown content requires manual review and is never moved +or discarded automatically. Rebuild the Rust binary after changing CSS. + +Keep `scripts/__init__.py` and `scripts/build/__init__.py` in the source package. +They keep preflight, audit, tests and packaging imports local even when unrelated +Python packages named `scripts` or `build` are installed. An older CSS-refactor +archive without the build marker can fail +with `ModuleNotFoundError: No module named 'build.panel_css'`; restore the marker +from the complete source archive and run `bash dev.sh repair` again. Installing +another Python package is not required. + +### Missing generated CSS during Rust compilation + +`panel-style.css` is generated inside Cargo's `OUT_DIR`. `bash dev.sh repair` +validates the source layout and preserves known obsolete files; it does not +generate Cargo outputs. Preflight also checks that the current `build.rs` calls +the panel generator from `main` and reports a mixed source tree before Cargo. + +After overlaying a source ZIP onto a previously built checkout, a compiler error +for a missing `OUT_DIR/panel-style.css` can indicate an old cached build script +or a `build.rs` from another source version. Restore the complete matching +archive, including the root `build.rs`, then run from the project root: + +```bash +bash dev.sh repair +touch build.rs +bash dev.sh build +``` + +The source ZIP uses reproducible timestamps. Cargo's +[build-script change detection](https://doc.rust-lang.org/cargo/reference/build-scripts.html#change-detection) +uses file modification times, so refreshing `build.rs` forces regeneration after +an archive overlay. This works with the target directory selected by `dev.sh`. +The generator now also registers its output as a watched Cargo dependency and +recreates a removed bundle. An identical bundle keeps its modification time. +The Rust build script rejects a missing, empty or non-file output immediately +after the helper returns. + ## Stream TCP/UDP test helpers - `start_stream_backends.sh` — launches two TCP echo mocks (9101, 9102) and two UDP echo/probe mocks (9201, 9202), defaulting to loopback, and terminates all on Ctrl+C. diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100755 index 0000000..8be4611 --- /dev/null +++ b/scripts/__init__.py @@ -0,0 +1 @@ +"""Application-owned RustProxy scripts.""" diff --git a/scripts/audit_css_usage.py b/scripts/audit_css_usage.py index 95cb60a..0e670ea 100755 --- a/scripts/audit_css_usage.py +++ b/scripts/audit_css_usage.py @@ -3,14 +3,14 @@ Requires: pip install tinycss2 cssselect2 (audit-only tooling, not runtime deps). Output is an inventory, NOT permission to remove dynamically generated styles. -Selectors are considered dead only when a required class/ID token is absent -from all application templates and scripts; :not() is not treated as a requirement. +Absent class/ID tokens are candidates for review, because templates can assemble +class names at runtime; :not() is not treated as a positive requirement. The Swagger UI bundle emits its own markup, so docs.css is reviewed separately. """ from __future__ import annotations import argparse -from collections import Counter +from collections import defaultdict import json from pathlib import Path import re @@ -23,14 +23,106 @@ except ImportError as exc: ROOT = Path(__file__).resolve().parents[1] WEB = ROOT / 'src/admin/web' -STYLES = ('style.css', 'docs.css') SOURCE_SUFFIXES = {'.js', '.html', '.json', '.rs'} UNSUPPORTED_PSEUDO_ELEMENTS = re.compile(r'::(?:-webkit-[\w-]+|-moz-[\w-]+|backdrop)\b') WORD_PATTERN = re.compile(r'(? list[Path]: + if __package__: + from .build.panel_css import source_files + else: + from build.panel_css import source_files + return source_files(ROOT) + + +def embedded_javascript_sources() -> list[Path]: + paths = set() + for parent in (ROOT / 'src').rglob('*.rs'): + for relative in re.findall(r'\binclude_str!\s*\(\s*"([^"\n]+\.js)"\s*\)', parent.read_text(encoding='utf-8')): + path = (parent.parent / relative).resolve() + if path.is_relative_to(WEB) and path.is_file(): + paths.add(path) + return sorted(paths) + + +def structural_inventory(paths: list[Path]) -> dict: + """Report repetitions in their actual conditional scope; never reorder CSS.""" + selectors = defaultdict(list) + conditions = defaultdict(list) + identical_rules = defaultdict(list) + identical_blocks = defaultdict(list) + properties = [] + parse_errors = [] + + def walk(items, path, context=()): + for item in items: + location = f'{path.relative_to(ROOT)}:{item.source_line}' + if item.type == 'error': + parse_errors.append({'location': location, 'message': item.message}) + elif item.type == 'qualified-rule': + parts = selector_parts(item.prelude) + declarations = [] + property_locations = defaultdict(list) + for declaration in tinycss2.parse_declaration_list(item.content, skip_whitespace=True, skip_comments=True): + if declaration.type == 'error': + parse_errors.append({'location': location, 'message': declaration.message}) + elif declaration.type == 'declaration': + name = declaration.name if declaration.name.startswith('--') else declaration.lower_name + value = tinycss2.serialize(declaration.value).strip() + declarations.append((name, value, declaration.important)) + property_locations[name].append(declaration.source_line) + for name, lines in property_locations.items(): + if len(lines) > 1: + properties.append({'location': location, 'selector': ', '.join(parts), 'property': name, 'lines': lines}) + for selector in parts: + selectors[(context, selector)].append(location) + identical_rules[(context, tuple(parts), tuple(declarations))].append(location) + if declarations: + identical_blocks[(context, tuple(declarations))].append({'location': location, 'selectors': parts}) + elif item.type == 'at-rule' and item.content is not None: + prelude = re.sub(r'\s+', ' ', tinycss2.serialize(item.prelude).strip()) + prelude = re.sub(r'\s*([():,])\s*', r'\1', prelude) + label = f'@{item.lower_at_keyword} {prelude}' + if item.lower_at_keyword in ('media', 'container', 'supports'): + conditions[(context, label)].append(location) + if item.lower_at_keyword in ('media', 'container', 'supports', 'layer', 'keyframes', '-webkit-keyframes'): + walk(tinycss2.parse_rule_list(item.content, skip_whitespace=True, skip_comments=True), path, context + (label,)) + + for path in paths: + walk(tinycss2.parse_stylesheet(path.read_text(encoding='utf-8'), skip_whitespace=True, skip_comments=True), path) + repeated_bodies = [] + for (scope, _), entries in identical_blocks.items(): + if len(entries) < 2: + continue + families = [tuple(sorted({family for selector in entry['selectors'] + for family in VENDOR_SELECTOR.findall(selector)})) + for entry in entries] + # A browser can reject an entire comma-separated selector list when it + # contains another engine's pseudo-element. Keep one body per compatible + # family, while still rejecting copies within the same family. + vendor_only = (any(families) and all(len(family) <= 1 for family in families) + and len(families) == len(set(families))) + repeated_bodies.append({'scope': list(scope), 'rules': entries, + 'vendor_compatibility_only': vendor_only}) + return { + 'repeated_selectors': [{'scope': list(scope), 'selector': selector, 'locations': locations} + for (scope, selector), locations in selectors.items() if len(locations) > 1], + 'repeated_conditions': [{'scope': list(scope), 'condition': condition, 'locations': locations} + for (scope, condition), locations in conditions.items() if len(locations) > 1], + 'identical_rules': [{'scope': list(scope), 'selectors': list(parts), 'locations': locations} + for (scope, parts, _), locations in identical_rules.items() if len(locations) > 1], + 'identical_declaration_blocks': repeated_bodies, + 'repeated_properties': properties, + 'parse_errors': parse_errors, + } def source_words(scope: str) -> set[str]: - paths = list(WEB.rglob('*.js')) + list(WEB.rglob('*.html')) + # Rust's include_str! lists are the asset entry points; an abandoned JS file + # beside a bundle must not make its obsolete selectors appear live. + paths = embedded_javascript_sources() + list(WEB.rglob('*.html')) if scope == 'panel': paths += list(WEB.rglob('*.json')) + list((ROOT / 'src').rglob('*.rs')) else: @@ -80,8 +172,8 @@ def token_refs(node) -> list[tuple[str, str]]: def possibly_matches(node, words: set[str]) -> bool: - # Deliberately conservative: class/id absence proves a positive selector - # impossible but does NOT make a selector in :not() impossible. + # Only positive class/id requirements identify review candidates. Runtime + # interpolation still requires manual inspection before removing a rule. if isinstance(node, css_parser.ClassSelector): return node.class_name in words if isinstance(node, css_parser.IDSelector): @@ -125,7 +217,9 @@ def audit_file(path: Path, words: set[str], scope: str) -> dict: if external: dead = False missing = sorted({f'{typ}:{word}' for typ, word in references if word not in words}) - parts.append({'selector': selector, 'dead': dead, 'missing_tokens': missing, + # Retain the historical JSON key for older audit consumers; + # "dead" is a candidate flag, never proof of runtime absence. + parts.append({'selector': selector, 'dead': dead, 'review_required': dead, 'missing_tokens': missing, 'external': external}) except css_parser.SelectorError as exc: parts.append({'selector': selector, 'dead': False, 'missing_tokens': [], 'unparsed': str(exc)}) @@ -146,13 +240,21 @@ def audit_file(path: Path, words: set[str], scope: str) -> dict: def main(): cli = argparse.ArgumentParser(description=__doc__) cli.add_argument('--json', type=Path, help='Write complete selector audit JSON to this file') + cli.add_argument('--structure-json', type=Path, help='Write cross-module duplicate inventory JSON to this file') + cli.add_argument('--strict', action='store_true', help='Fail on repeated panel selectors/conditions/rules/properties, compatible declaration blocks or parse errors') args = cli.parse_args() - results = [audit_file(WEB / filename, source_words('docs' if filename == 'docs.css' else 'panel'), - 'docs' if filename == 'docs.css' else 'panel') for filename in STYLES] + panel = panel_sources() + orphan_scripts = sorted(set(WEB.rglob('*.js')) - set(embedded_javascript_sources())) + if orphan_scripts: + print('JavaScript outside embedded asset entry points (review):', ', '.join(str(path.relative_to(ROOT)) for path in orphan_scripts)) + paths = [*panel, WEB / 'docs.css'] + words = {'panel': source_words('panel'), 'docs': source_words('docs')} + results = [audit_file(path, words['docs' if path.name == 'docs.css' else 'panel'], + 'docs' if path.name == 'docs.css' else 'panel') for path in paths] for r in results: print(f"{r['file']}: {r['total_rules']} rules, {r['total_selectors']} selector branches, " f"{len(r['referenced_classes'])} referenced classes, {len(r['unreferenced_classes'])} unreferenced classes, " - f"{r['unused_selector_count']} provably dead selector branches, {len(r['unparsed'])} unparsed") + f"{r['unused_selector_count']} selector branches requiring review, {len(r['unparsed'])} unparsed") print(' Unreferenced:', ', '.join(r['unreferenced_classes']) or '(none)') for e in r['entries']: for p in e['parts']: @@ -160,6 +262,15 @@ def main(): print(f" {e['line']}: {p['selector'][:170]} ({', '.join(p['missing_tokens'])})") if args.json: args.json.write_text(json.dumps(results, indent=2, ensure_ascii=False) + '\n', encoding='utf-8') + structure = structural_inventory(panel) + print('Panel structure:', ', '.join(f'{len(entries)} {name.replace("_", " ")}' for name, entries in structure.items())) + if args.structure_json: + args.structure_json.write_text(json.dumps(structure, indent=2, ensure_ascii=False) + '\n', encoding='utf-8') + failures = [name for name in ('repeated_selectors', 'repeated_conditions', 'identical_rules', 'repeated_properties', 'parse_errors') if structure[name]] + if any(not group['vendor_compatibility_only'] for group in structure['identical_declaration_blocks']): + failures.append('identical_declaration_blocks') + if args.strict and failures: + raise SystemExit('Panel CSS structure check failed: ' + ', '.join(failures)) if __name__ == '__main__': diff --git a/scripts/build/__init__.py b/scripts/build/__init__.py new file mode 100755 index 0000000..b4d8187 --- /dev/null +++ b/scripts/build/__init__.py @@ -0,0 +1 @@ +"""RustProxy build helpers; keep local imports separate from external build packages.""" diff --git a/scripts/build/panel_css.py b/scripts/build/panel_css.py new file mode 100755 index 0000000..3b6f701 --- /dev/null +++ b/scripts/build/panel_css.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python3 +"""Build the embedded panel stylesheet in manifest order, without npm or imports. + +Only comments and formatting whitespace beside rule/declaration delimiters are +minified. Selector whitespace, escapes, strings, functions (including calc and +url), declaration values and rule order are otherwise retained verbatim. +""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import re +import sys + +ROOT = Path(__file__).resolve().parents[2] +CSS_DIRECTORY = Path("src/admin/web/css") +FILENAME = re.compile(r"[A-Za-z0-9][A-Za-z0-9_-]*\.css\Z") +WHITESPACE = " \t\n\r\f" +HEX = "0123456789abcdefABCDEF" +DELIMITERS = frozenset("{};") + + +def _escape_end(text: str, start: int) -> int: + end = start + 1 + if end == len(text): + raise ValueError("Unterminated CSS escape") + if text[end] in HEX: + limit = min(end + 6, len(text)) + while end < limit and text[end] in HEX: + end += 1 + if end < len(text) and text[end] in WHITESPACE: + if text[end:end + 2] == "\r\n": + return end + 2 + end += 1 + return end + return end + (2 if text[end:end + 2] == "\r\n" else 1) + + +def _string_end(text: str, start: int) -> int: + end = start + 1 + while end < len(text): + if text[end] == text[start]: + return end + 1 + if text[end] == "\\": + end = _escape_end(text, end) + elif text[end] in "\n\r\f": + raise ValueError("Unescaped newline in CSS string") + else: + end += 1 + raise ValueError("Unterminated CSS string") + + +def _comment_end(text: str, start: int) -> int: + end = text.find("*/", start + 2) + if end < 0: + raise ValueError("Unterminated CSS comment") + return end + 2 + + +def _group_end(text: str, start: int) -> int: + # Do not rewrite anything in functions or attribute selectors. This also + # protects unquoted data URLs and whitespace required by calc()/CSS escapes. + closing = ")" if text[start] == "(" else "]" + is_url = (text[start] == "(" and text[max(0, start - 3):start].lower() == "url" + and (start < 4 or not (text[start - 4].isalnum() or text[start - 4] in "_-"))) + end = start + 1 + while end < len(text): + if text[end] == closing: + return end + 1 + if text[end] in "\"'": + end = _string_end(text, end) + elif text[end] == "\\": + end = _escape_end(text, end) + elif text.startswith("/*", end) and not is_url: + end = _comment_end(text, end) + elif text[end] in "([" and not is_url: + end = _group_end(text, end) + elif text[end] in ")]" and not is_url: + raise ValueError("Mismatched CSS delimiter") + else: + # In an unquoted url(), /* and square brackets are literal data. + end += 1 + raise ValueError(f"Unterminated CSS {text[start]} group") + + +def minify(text: str) -> str: + """Conservatively compact a stylesheet without reordering its CSS tokens.""" + tokens: list[tuple[str, str]] = [] + position = 0 + depth = 0 + while position < len(text): + char = text[position] + if char in WHITESPACE: + end = position + 1 + while end < len(text) and text[end] in WHITESPACE: + end += 1 + kind = "space" + elif text.startswith("/*", position): + end, kind = _comment_end(text, position), "comment" + elif char in "\"'": + end, kind = _string_end(text, position), "protected" + elif char == "\\": + end, kind = _escape_end(text, position), "protected" + elif char in "([": + end, kind = _group_end(text, position), "protected" + else: + end, kind = position + 1, "plain" + if char == "{": + depth += 1 + elif char == "}": + depth -= 1 + if depth < 0: + raise ValueError("Unexpected closing CSS brace") + elif char in ")]": + raise ValueError("Unexpected closing CSS delimiter") + tokens.append((kind, text[position:end])) + position = end + if depth: + raise ValueError("Unterminated CSS rule") + + # Source modules must stay independent of runtime imports and encoding + # declarations; all are UTF-8 and are delivered as one embedded response. + for index, token in enumerate(tokens): + if token != ("plain", "@"): + continue + name = "" + for offset in range(index + 1, len(tokens)): + kind, value = tokens[offset] + if kind == "plain" and (value.isalnum() or value in "_-"): + name += value + elif kind == "protected" and value.startswith("\\"): + escaped = value[1:] + if escaped and escaped[0] in HEX: + codepoint = int(escaped.rstrip(WHITESPACE), 16) + name += chr(codepoint) if 0 < codepoint <= 0x10FFFF else "\ufffd" + else: + name += escaped + else: + break + if name.lower() in ("import", "charset"): + raise ValueError("CSS modules must not contain @import or @charset") + + compact: list[tuple[str, str]] = [] + for index, (kind, value) in enumerate(tokens): + if kind == "comment": + if value.startswith("/*!"): + compact.append(("protected", value)) + continue + before = tokens[index - 1] if index else None + after = tokens[index + 1] if index + 1 < len(tokens) else None + if (before and after and before[0] != "space" and after[0] != "space" + and not (before[0] == "plain" and before[1] in DELIMITERS) + and not (after[0] == "plain" and after[1] in DELIMITERS)): + # Retain a token boundary: e.g. --value:foo/**/bar must never + # become the single identifier foobar. No guessed whitespace. + compact.append(("protected", "/**/")) + continue + if kind == "space" and compact and compact[-1][0] == "space": + compact[-1] = ("space", compact[-1][1] + value) + else: + compact.append((kind, value)) + + output: list[str] = [] + for index, (kind, value) in enumerate(compact): + if kind == "space": + before = compact[index - 1] if index else None + after = compact[index + 1] if index + 1 < len(compact) else None + if (not before or not after + or (before[0] == "plain" and before[1] in DELIMITERS) + or (after[0] == "plain" and after[1] in DELIMITERS)): + continue + output.append(value) + return "".join(output) + + +def source_files(root: Path = ROOT) -> list[Path]: + directory = root / CSS_DIRECTORY + manifest = directory / "manifest.json" + if directory.is_symlink() or manifest.is_symlink(): + raise ValueError("Panel CSS directory and manifest must not be symlinks") + config = json.loads(manifest.read_text(encoding="utf-8")) + if not isinstance(config, dict) or set(config) != {"files"}: + raise ValueError('CSS manifest must be an object containing only a "files" array') + names = config["files"] + if (not isinstance(names, list) or not names + or not all(isinstance(name, str) and FILENAME.fullmatch(name) for name in names)): + raise ValueError("CSS manifest must list CSS basenames without directory components") + if len(names) != len(set(names)): + raise ValueError("CSS manifest lists a source more than once") + actual = {path.name for path in directory.iterdir() if path.suffix.lower() == ".css"} + if set(names) != actual: + missing = sorted(set(names) - actual) + unlisted = sorted(actual - set(names)) + raise ValueError(f"CSS manifest mismatch: missing={missing}; unlisted={unlisted}") + paths = [directory / name for name in names] + for path in paths: + if path.is_symlink() or not path.is_file(): + raise ValueError(f"CSS source must be a regular, non-symlink file: {path.name}") + return paths + + +def bundle(root: Path = ROOT) -> tuple[str, list[Path], int]: + paths = source_files(root) + parts: list[str] = [] + source_bytes = 0 + for path in paths: + raw = path.read_bytes() + source_bytes += len(raw) + try: + parts.append(minify(raw.decode("utf-8"))) + except ValueError as error: + raise ValueError(f"{path.name}: {error}") from error + return "\n".join(parts) + "\n", paths, source_bytes + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, default=ROOT) + mode = parser.add_mutually_exclusive_group(required=True) + mode.add_argument("--out-dir", type=Path, help="Write panel-style.css here") + mode.add_argument("--check", action="store_true", help="Validate sources without writing files") + parser.add_argument("--cargo", action="store_true", help="Emit Cargo input/output dependencies") + args = parser.parse_args() + try: + root = args.root.resolve() + result, paths, source_bytes = bundle(root) + encoded = result.encode("utf-8") + if args.out_dir is not None: + args.out_dir.mkdir(parents=True, exist_ok=True) + destination = args.out_dir / "panel-style.css" + if not destination.exists() or destination.read_bytes() != encoded: + destination.write_bytes(encoded) + if args.cargo: + watched = [root / CSS_DIRECTORY, root / CSS_DIRECTORY / "manifest.json", *paths] + if args.out_dir is not None: + # Cargo must rerun the generator when its cached output is + # removed even if every source module is unchanged. + watched.append(args.out_dir.resolve() / "panel-style.css") + for path in watched: + if any(char in str(path) for char in "\r\n\0"): + raise ValueError("CSS build dependency path contains a control character") + print(f"cargo:rerun-if-changed={path}") + else: + print(f"Panel CSS: {len(paths)} modules, {source_bytes} source bytes, " + f"{len(encoded)} bundled bytes; cascade order preserved.") + except (OSError, ValueError) as error: + print(f"Panel CSS preparation failed: {error}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/preflight.py b/scripts/preflight.py index e062b0d..d97b1e4 100755 --- a/scripts/preflight.py +++ b/scripts/preflight.py @@ -8,10 +8,74 @@ from pathlib import Path import sys LEGACY_AUTH = "4df9d473c9214365d91c83072f97aafab6ede4edcf4e1369b5c3679ded5cff85" +LEGACY_PANEL_CSS = "38716fae968aeb8f6a8510f28bb9054a73a438a969c3b69f9c9d921acfed6e06" + + +def validate_panel_css_build(root: Path) -> None: + """Check this project's build.rs startup contract, not arbitrary Rust code.""" + source = (root / "build.rs").read_text(encoding="utf-8") + # Ignore ordinary strings and comments, including nested block comments. + # The required call precedes the first nested block in the shipped main(). + code: list[str] = [] + position = 0 + token = re.compile(r'"(?:\\.|[^"\\])*"|//[^\n]*|/\*', re.DOTALL) + while match := token.search(source, position): + code.extend((source[position:match.start()], " ")) + position = match.end() + if match.group() == "/*": + depth = 1 + while depth: + boundary = re.search(r'/\*|\*/', source[position:]) + if boundary is None: + position = len(source) + break + depth += 1 if boundary.group() == "/*" else -1 + position += boundary.end() + code.append(source[position:]) + main = re.search(r'\bfn\s+main\s*\(\s*\)[^{]*\{([^{}]*)', "".join(code)) + if main is None or re.search( + r'\bpanel_styles\s*\(\s*&root\s*,\s*&out\s*,?\s*\)\s*\?\s*;', + main.group(1), + ) is None: + raise ValueError( + "Mixed modular panel CSS source tree: build.rs is missing the expected " + "panel_styles(&root, &out)? call in main's startup block. Restore build.rs " + "from the complete matching source archive, preserving your local edits, " + "then run 'touch build.rs' and retry ./dev.sh. " + "Automatic repair does not rewrite build.rs." + ) + + +def preserve_legacy_panel_css(root: Path, repair_known: bool = False) -> list[str]: + old = root / "src/admin/web/style.css" + manifest = root / "src/admin/web/css/manifest.json" + if not manifest.is_file() or not (old.exists() or old.is_symlink()): + return [] + if old.is_symlink() or hashlib.sha256(old.read_bytes()).hexdigest() != LEGACY_PANEL_CSS: + raise ValueError( + "Obsolete src/admin/web/style.css remains beside the modular CSS sources and " + "contains unknown/custom content. Preserve it outside src/admin/web, review " + "your changes and port them into the CSS modules before rebuilding. " + "Automatic repair does not move or discard custom styles." + ) + if not repair_known: + raise ValueError( + "Known obsolete 0.34.9 src/admin/web/style.css remains beside the modular CSS sources. " + "Run ./dev.sh repair to preserve it under .upgrade-backup/panel-css/." + ) + target = root / ".upgrade-backup/panel-css/style.css.0.34.9" + if target.exists() or target.is_symlink(): + if target.is_symlink() or target.read_bytes() != old.read_bytes(): + raise ValueError("Legacy CSS backup already contains different data; refusing to overwrite it") + old.unlink() # The exact original is already backed up. + else: + target.parent.mkdir(parents=True, exist_ok=True) + old.rename(target) + return [f"Preserved obsolete panel CSS as {target.relative_to(root)}"] def check(root: Path, repair_known: bool = False) -> list[str]: - messages: list[str] = [] + messages: list[str] = preserve_legacy_panel_css(root, repair_known) obsolete_split_files = ( "src/admin/web/app.js", "src/admin/web/vhosts.js", @@ -71,6 +135,15 @@ def check(root: Path, repair_known: bool = False) -> list[str]: for path in ["src/auth/mod.rs", "src/auth/store.rs", "src/backup/snapshot.rs", "src/upstream.rs", "src/tls.rs", "src/config/internal_routing.rs", "src/proxy/websocket.rs", "src/proxy/error_fallback.rs", "src/cache/mod.rs", "src/cache/model.rs", "src/cache/lifecycle.rs", "src/cache/cold.rs", "src/cache/dashboard.rs", "src/cache/tickets.rs", "src/cache/lookup.rs", "src/cache/policy.rs", "src/cache/fill.rs", "src/cache/tests.rs", "src/settings.rs", "src/compression/mod.rs", "src/diagnostics.rs", "src/traffic/mod.rs", "src/system_stats/mod.rs", "src/system_stats/linux/mod.rs", "src/vhost.rs", "src/vhost/error_fallback.rs", "src/web_server/mod.rs", "src/web_server/config.rs", "src/web_server/filesystem.rs", "src/web_server/security.rs", "src/web_server/response.rs", "src/web_server/request.rs", "src/web_server/routing.rs", "src/web_server/fastcgi.rs", "src/web_server/htaccess.rs", "src/web_server/serve.rs", "src/web_server/tests.rs", "src/balance.rs", "src/security/mod.rs", "src/admin/web/vhosts/core.js", "src/admin/web/vhosts/rates.js", "src/admin/web/vhosts/health.js", "src/admin/web/vhosts/editor.js", "src/admin/web/vhosts/render.js", "src/admin/web/web-server.js", "src/admin/web/streams.js", "src/admin/web/host404.js", "src/admin/web/app/core.js", "src/admin/web/app/session-resume.js", "src/admin/web/app/preferences.js", "src/admin/web/app/config-history.js", "src/admin/web/app/runtime.js", "src/admin/web/app/settings.js", "src/admin/web/management.js", "src/admin/web/drafts.js", "src/pages/mod.rs", "tools/dev/mock_server.py", "tools/dev/mock_auth.py", "src/forwarding.rs", "src/tinyauth/mod.rs", "src/http3.rs", "src/stream.rs", "src/admin/web/integrations.js", "src/admin/web/traffic-panel.js", "src/prometheus.rs", "src/admin/web/settings.js", "src/admin/web/docker.js", "src/docker.rs", "docker-agent/main.go", "docker-agent/model.go", "docker-agent/docker.go", "docker-agent/network.go", "docker-agent/certificate.go", "docker-agent/server.go", "docker-agent/main_test.go", "docker-agent/rustproxy-docker-agent.service", "docker-agent/install.sh", "docs/DOCKER_DISCOVERY.md", "docs/WEB_SERVER.md", "docs/PHP_FPM.md", "docs/README.md", "CHANGELOG.md", "ROADMAP.md", "scripts/runtime/acme_provider.py", "src/basic_auth/mod.rs", "scripts/deploy/deploy.py", "scripts/deploy/deploy_core.py", "scripts/deploy/deploy_runtime.py", "scripts/migration/migrate_npm.py", "scripts/migration/migrate_npm_core.py", "scripts/migration/migrate_npm_api.py", "docs/MIGRATION.md", "docs/STREAMS.md", "install.sh", "install-debian13.sh", "update.sh", "build.rs", "scripts/build/swagger_assets.py", "src/admin/documentation.rs", "src/admin/openapi.json", "langs/en_US.json", "langs/pl_pl.json", "Dockerfile", ".dockerignore", "docs/CONTAINER.md", "docs/MONITORING.md", "docs/CUSTOM_LOCATIONS.md", "grafana/rustproxy-overview.json"]: if not (root / path).is_file(): raise ValueError(f"Incomplete RustProxy 0.34.2 source tree: missing {path}") + for path in ("scripts/__init__.py", "scripts/build/__init__.py", "scripts/build/panel_css.py", "src/admin/web/css/manifest.json"): + if not (root / path).is_file(): + raise ValueError(f"Incomplete modular panel CSS source tree: missing {path}") + validate_panel_css_build(root) + if __package__: + from .build.panel_css import bundle + else: + from build.panel_css import bundle + bundle(root) # Validate the manifest and every module without generating files. registry = root / "src/admin/documentation/documents.rs" if not registry.is_file(): raise ValueError("Incomplete RustProxy 0.34.2 source tree: missing src/admin/documentation/documents.rs") diff --git a/src/admin/panel_assets.rs b/src/admin/panel_assets.rs index 00c320a..213f9c0 100644 --- a/src/admin/panel_assets.rs +++ b/src/admin/panel_assets.rs @@ -79,7 +79,11 @@ static ASSETS: LazyLock> = LazyLock::new(|| { ("/assets/streams.js", JS, include_str!("web/streams.js")), ("/assets/redirects.js", JS, include_str!("web/redirects.js")), ("/assets/host404.js", JS, include_str!("web/host404.js")), - ("/assets/style.css", CSS, include_str!("web/style.css")), + ( + "/assets/style.css", + CSS, + include_str!(concat!(env!("OUT_DIR"), "/panel-style.css")), + ), ("/favicon.svg", SVG, include_str!("web/favicon.svg")), ]; entries diff --git a/src/admin/tests.rs b/src/admin/tests.rs index 265f07a..56e848a 100644 --- a/src/admin/tests.rs +++ b/src/admin/tests.rs @@ -2966,6 +2966,69 @@ mod tests { } store.close().await; } + #[tokio::test] + async fn modular_panel_css_is_one_embedded_public_asset_with_existing_compression() { + use std::io::Read; + + let (router, store, _dir, secret, _) = fixture().await; + let cookie = format!("{}={secret}", auth::COOKIE_NAME); + let css = include_str!(concat!(env!("OUT_DIR"), "/panel-style.css")); + let path = format!("/assets/style_{}.css", super::asset_build_id()); + for authenticated in [false, true] { + let mut req = request(&path); + if authenticated { + req = req.header("cookie", &cookie); + } + let response = router + .clone() + .oneshot(req.body(Body::empty()).unwrap()) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.headers()["content-type"], "text/css; charset=utf-8"); + assert_eq!(response.headers()["vary"], "Accept-Encoding"); + let bytes = axum::body::to_bytes(response.into_body(), css.len() + 1) + .await + .unwrap(); + assert_eq!(bytes.as_ref(), css.as_bytes()); + } + let response = router + .clone() + .oneshot( + request(&path) + .header("accept-encoding", "gzip") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.headers()["content-encoding"], "gzip"); + let compressed = axum::body::to_bytes(response.into_body(), css.len() + 1024) + .await + .unwrap(); + let mut decoded = Vec::new(); + flate2::read::GzDecoder::new(compressed.as_ref()) + .read_to_end(&mut decoded) + .unwrap(); + assert_eq!(decoded.as_slice(), css.as_bytes()); + + for path in ["/assets/css/manifest.json", "/assets/css/base.css"] { + let response = router + .clone() + .oneshot( + request(path) + .header("cookie", &cookie) + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::NOT_FOUND, "{path}"); + } + store.close().await; + } + #[tokio::test] async fn public_document_and_discovery_contain_only_login_information() { let (router, store, _dir, _, _) = fixture().await; diff --git a/src/admin/web/css/00-tokens.css b/src/admin/web/css/00-tokens.css new file mode 100644 index 0000000..a267c77 --- /dev/null +++ b/src/admin/web/css/00-tokens.css @@ -0,0 +1,518 @@ +/* Theme palettes and appearance tokens. Runtime preference variables remain writable by JavaScript. + * Order is defined in manifest.json; responsive rules are centralized in 70-responsive.css. + */ + +:root { + color-scheme: dark; + --bg: #131615; + --panel: #1b1f1d; + --panel2: #202622; + --line: #323a34; + --muted: #a0aaa0; + --text: #edf0e9; + --accent: #f69b74; + --green: #bdd293; + --red: #ffaaa2; + --sidebar-bg: #101310; + --brand-secondary: #cfe0ac; + --nav-active-bg: #242b23; + --nav-hover-bg: #20251f; + --text-subtle: #879082; + --separator: #596454; + --border-hover: #89917c; + --field-bg: #131813; + --field-line: #47523f; + --placeholder: #778070; + --row-line: #2b332c; + --outline-line: #717b67; + --chart-grid: #333c33; + --code-bg: #101810; + --code-alt-bg: #101922; + --code-alt-text: #d3e4ec; + --nav-selected-bg: #34412a; + --accent-ink: #221810; + --accent-hover: #ffb090; + --selected-ink: #10251e; + --notice-text: #c9b99c; + --notice-line: #554833; + --notice-bg: #29251c; + --pill-line: #6d5a45; + --pill-live-line: #576847; + --toast-bg: #d0dfb4; + --toast-text: #1c2818; + --toast-line: #ebf2de; + --toast-error-bg: #ffd2bd; + --toast-error-line: #ff9f7a; + --toast-error-text: #42241b; + --danger-line: #644a47; + --danger-line-strong: #79514e; + --warn-line: #786647; + --blue: #8ebcff; + --transform-chart: #d7adff; + --backdrop: rgba(0, 0, 0, .68); + --shadow-mid: #0006; + --shadow-strong: #0009; + --neutral-soft: hsla(0, 0%, 49.8%, .05); + --scrollbar-track: #151a16; + --scrollbar-thumb: #4f5d50; + --scrollbar-thumb-hover: #6b7b69; + --chart-accent: var(--accent); + --chart-secondary: var(--green); + --chart-tertiary: var(--blue); + --chart-quaternary: var(--transform-chart); + --chart-quinary: var(--red); + --chart-senary: var(--notice-text); + --chart-septenary: var(--border-hover); + --chart-octonary: var(--separator); + --chart-surface: var(--panel2); + --chart-surface-alt: var(--panel); + --chart-line-width: 2; + --chart-linecap: round; + --chart-linejoin: round; + --chart-grid-opacity: .28; + --chart-grid-dash: none; + --chart-svg-filter: none; + --rp-progress-fill: var(--chart-accent); + --rp-progress-track: var(--line); + --rp-progress-radius: 3px; + --rp-nav-active-color: var(--brand-secondary); + --rp-nav-active-bg: var(--nav-active-bg); + --rp-nav-hover-bg: var(--nav-hover-bg); + --traffic-h1: var(--chart-accent); + --traffic-h2: var(--chart-secondary); + --traffic-h3: var(--chart-tertiary); + --ui-scale: 1; + --ui-field-font-size: 12px; + --ui-field-pad-y: 6px; + --ui-field-pad-x: 8px; + --ui-field-min-height: 27px; + --ui-check-size: 15px; + --ui-range-height: 18px; + --mono: ui-monospace, SFMono-Regular, Consolas, monospace; + --sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; +} + +html[data-theme=light] { + color-scheme: light; + --bg: #f4f6f2; + --panel: #fff; + --panel2: #edf1eb; + --line: #cbd3c7; + --muted: #5e685f; + --text: #1d241f; + --accent: #b85b38; + --green: #5f783f; + --red: #b64a43; + --sidebar-bg: #eef2ec; + --brand-secondary: #6b8249; + --nav-active-bg: #e1e8dc; + --nav-hover-bg: #e8ede6; + --text-subtle: #687369; + --separator: #9ba69b; + --border-hover: #839083; + --field-bg: #fff; + --field-line: #b7c1b5; + --placeholder: #7a867b; + --row-line: #e0e5dd; + --outline-line: #98a497; + --chart-grid: #d4dbd1; + --code-bg: #edf3e9; + --code-alt-bg: #eef3f6; + --code-alt-text: #2d4049; + --nav-selected-bg: #e1e9db; + --accent-ink: #fff; + --accent-hover: #a84f30; + --selected-ink: #fff; + --notice-text: #685b45; + --notice-line: #d4c2a5; + --notice-bg: #fff9ef; + --pill-line: #cbb28f; + --pill-live-line: #a8ba8d; + --danger-line: #d7a29d; + --danger-line-strong: #c98f89; + --warn-line: #c6b27e; + --blue: #4f77a9; + --transform-chart: #7d4fa3; + --shadow-mid: rgba(31, 39, 33, .16); + --shadow-strong: rgba(31, 39, 33, .26); + --neutral-soft: rgba(70, 78, 72, .05); + --scrollbar-track: #e7ece4; + --scrollbar-thumb: #a9b5a6; + --scrollbar-thumb-hover: #879684; +} + +html:is([data-theme=light], [data-theme=cream]) { + --rp-nav-active-color: color-mix(in srgb, var(--brand-secondary) 45%, var(--text) 55%); +} + +html[data-theme=ultra_dark] { + color-scheme: dark; + --bg: #030404; + --panel: #0b0d0e; + --panel2: #121516; + --line: #292f30; + --muted: #a4afb0; + --text: #f1f4f2; + --accent: #93dfc8; + --logo-accent: #f69b74; + --logo-secondary: #cfe0ac; + --green: #b1dcb0; + --red: #ff9899; + --sidebar-bg: #000; + --brand-secondary: #a9cfb5; + --nav-active-bg: #172422; + --nav-hover-bg: #121819; + --text-subtle: #879695; + --separator: #526261; + --border-hover: #7c9f96; + --field-bg: #090b0c; + --field-line: #445352; + --placeholder: #879593; + --row-line: #202627; + --outline-line: #79968f; + --chart-grid: #303839; + --code-bg: #0e1313; + --code-alt-bg: #101a1d; + --code-alt-text: #d5eae9; + --nav-selected-bg: #213730; + --accent-ink: #071814; + --accent-hover: #b6f2dd; + --selected-ink: #071814; + --notice-text: #e6c99c; + --notice-line: #66533a; + --notice-bg: #201a11; + --pill-line: #625544; + --pill-live-line: #506c59; + --toast-bg: #bcf3da; + --toast-text: #10221a; + --toast-line: #d8ffe8; + --toast-error-bg: #ffd5d0; + --toast-error-line: #ff9c96; + --toast-error-text: #472625; + --danger-line: #754245; + --danger-line-strong: #965257; + --warn-line: #89754c; + --blue: #8bbdfb; + --transform-chart: #cfaeff; + --backdrop: rgba(0, 0, 0, .86); + --shadow-mid: rgba(0, 0, 0, .64); + --shadow-strong: rgba(0, 0, 0, .84); + --neutral-soft: rgba(132, 171, 158, .045); + --scrollbar-track: #060808; + --scrollbar-thumb: #364243; + --scrollbar-thumb-hover: #617373; +} + +html[data-theme=cream] { + color-scheme: light; + --bg: #f2ede3; + --panel: #fcf9f2; + --panel2: #eae3d6; + --line: #d5cabb; + --muted: #6b6257; + --text: #302c27; + --accent: #8c5942; + --green: #5e7655; + --red: #a54645; + --sidebar-bg: #eae2d5; + --brand-secondary: #668367; + --nav-active-bg: #e0d3c0; + --nav-hover-bg: #e6dccc; + --text-subtle: #786e61; + --separator: #afa494; + --border-hover: #968776; + --field-bg: #fffcf7; + --field-line: #baad9a; + --placeholder: #807466; + --row-line: #e2d9cb; + --outline-line: #9c8d7c; + --chart-grid: #ddd3c4; + --code-bg: #f0e8da; + --code-alt-bg: #e7ebe9; + --code-alt-text: #364f4e; + --nav-selected-bg: #d9cbb7; + --accent-ink: #fff; + --accent-hover: #774632; + --selected-ink: #fff; + --notice-text: #765e40; + --notice-line: #d9c3a1; + --notice-bg: #f8efdf; + --pill-line: #c2a78d; + --pill-live-line: #b0bd9d; + --toast-bg: #d6ead2; + --toast-text: #263d29; + --toast-line: #b8d5b6; + --toast-error-bg: #f8ded7; + --toast-error-line: #dca99c; + --toast-error-text: #60352f; + --danger-line: #d2a29a; + --danger-line-strong: #b97870; + --warn-line: #b49c6b; + --blue: #4d7292; + --transform-chart: #83659a; + --backdrop: rgba(37, 27, 16, .44); + --shadow-mid: rgba(64, 49, 34, .13); + --shadow-strong: rgba(64, 49, 34, .24); + --neutral-soft: rgba(104, 91, 69, .045); + --scrollbar-track: #ece4d7; + --scrollbar-thumb: #b5a997; + --scrollbar-thumb-hover: #978a76; +} + +body[data-density=dense] { + --ui-gap: 4px; + --ui-card: 7px; + --ui-font: 11.5px; + --ui-control: 25px; +} + +body[data-density=comfortable] { + --ui-gap: 13px; + --ui-card: 17px; + --ui-font: 14px; + --ui-control: 36px; +} + +html[data-button-style=compact] body { + --button-padding: 3px 7px; + --button-font-size: 10px; + --button-min-height: 22px; +} + +.chart-style-choice[data-chart-preset=default] { + --sw1: var(--accent); + --sw2: var(--green); + --sw3: var(--blue); + --sw4: var(--transform-chart); +} + +.chart-style-choice[data-chart-preset=soft] { + --sw1: #e8a998; + --sw2: #95c293; + --sw3: #8daef6; + --sw4: #b49de6; +} + +.chart-style-choice[data-chart-preset=technical] { + --sw1: #79c9ff; + --sw2: #9adf90; + --sw3: #ffd56e; + --sw4: #c39cff; +} + +.chart-style-choice[data-chart-preset=high_contrast] { + --sw1: #ffd54f; + --sw2: #8dff80; + --sw3: #6fd9ff; + --sw4: #ff78d7; +} + +.chart-style-choice[data-chart-preset=neon] { + --sw1: #2cf6ff; + --sw2: #7eff85; + --sw3: #ff5cd0; + --sw4: #8b7cff; +} + +.chart-style-choice[data-chart-preset=monochrome] { + --sw1: #f2f2f2; + --sw2: #b8b8b8; + --sw3: #858585; + --sw4: #595959; +} + +.chart-style-choice[data-chart-preset=warm] { + --sw1: #ff9c5c; + --sw2: #f4c46b; + --sw3: #ff7b72; + --sw4: #f4a261; +} + +.chart-style-choice[data-chart-preset=cool] { + --sw1: #5fb9ff; + --sw2: #45d3c3; + --sw3: #7d8fff; + --sw4: #70d7ff; +} + +html[data-chart-style=default] { + --chart-accent: var(--accent); + --chart-secondary: var(--green); + --chart-tertiary: var(--blue); + --chart-quaternary: var(--transform-chart); + --chart-quinary: var(--red); + --chart-senary: var(--notice-text); + --chart-septenary: var(--border-hover); + --chart-octonary: var(--separator); + --chart-surface: var(--panel2); + --chart-surface-alt: var(--panel); + --chart-line-width: 2; + --chart-linecap: round; + --chart-linejoin: round; + --chart-grid-opacity: .28; + --chart-grid-dash: 2 5; + --chart-svg-filter: none; +} + +html[data-chart-style=soft] { + --chart-accent: #e8a998; + --chart-secondary: #95c293; + --chart-tertiary: #8daef6; + --chart-quaternary: #b49de6; + --chart-quinary: #d99490; + --chart-senary: #d0b78e; + --chart-septenary: #99a790; + --chart-octonary: #728070; + --chart-surface: color-mix(in srgb, var(--panel2) 88%, #fff 12%); + --chart-surface-alt: color-mix(in srgb, var(--panel) 92%, #fff 8%); + --chart-line-width: 2.8; + --chart-linecap: round; + --chart-linejoin: round; + --chart-grid-opacity: .42; + --chart-grid-dash: 1 0; + --chart-svg-filter: saturate(.88) brightness(1.04); +} + +html[data-chart-style=technical] { + --chart-accent: #79c9ff; + --chart-secondary: #9adf90; + --chart-tertiary: #ffd56e; + --chart-quaternary: #c39cff; + --chart-quinary: #ff8c7b; + --chart-senary: #b8d0d7; + --chart-septenary: #7e9aac; + --chart-octonary: #6a7884; + --chart-surface: color-mix(in srgb, var(--panel2) 90%, #0b1a27 10%); + --chart-surface-alt: color-mix(in srgb, var(--panel) 90%, #09151f 10%); + --chart-line-width: 1.35; + --chart-linecap: square; + --chart-linejoin: miter; + --chart-grid-opacity: .82; + --chart-grid-dash: 3 4; + --chart-svg-filter: saturate(.76) contrast(1.08); +} + +html[data-chart-style=high_contrast] { + --chart-accent: #ffd54f; + --chart-secondary: #8dff80; + --chart-tertiary: #6fd9ff; + --chart-quaternary: #ff78d7; + --chart-quinary: #ff8b6f; + --chart-senary: #f2e48f; + --chart-septenary: #d7f0d9; + --chart-octonary: #cfcfcf; + --chart-surface: color-mix(in srgb, var(--panel2) 94%, #000 6%); + --chart-surface-alt: color-mix(in srgb, var(--panel) 94%, #000 6%); + --chart-line-width: 3; + --chart-grid-opacity: .95; + --chart-grid-dash: 1 0; + --chart-svg-filter: saturate(1.6) contrast(1.22) brightness(1.08); + --chart-linecap: round; + --chart-linejoin: round; +} + +html[data-chart-style=cool] { + --chart-linecap: round; + --chart-linejoin: round; + --chart-accent: #5fb9ff; + --chart-secondary: #45d3c3; + --chart-tertiary: #7d8fff; + --chart-quaternary: #70d7ff; + --chart-quinary: #54a0ff; + --chart-senary: #8bcbe0; + --chart-septenary: #6ea6bf; + --chart-octonary: #7d91a9; + --chart-surface: color-mix(in srgb, var(--panel2) 88%, #0d2740 12%); + --chart-surface-alt: color-mix(in srgb, var(--panel) 90%, #0d2033 10%); + --chart-line-width: 2.2; + --chart-grid-opacity: .46; + --chart-grid-dash: 2 4; + --chart-svg-filter: saturate(1.08) brightness(1.03); +} + +html[data-chart-style=monochrome] { + --chart-linecap: round; + --chart-linejoin: round; + --chart-accent: color-mix(in srgb, var(--text) 92%, #fff 8%); + --chart-secondary: color-mix(in srgb, var(--muted) 80%, var(--text) 20%); + --chart-tertiary: color-mix(in srgb, var(--separator) 72%, var(--text) 28%); + --chart-quaternary: color-mix(in srgb, var(--line) 45%, var(--text) 55%); + --chart-quinary: color-mix(in srgb, var(--muted) 55%, #fff 45%); + --chart-senary: color-mix(in srgb, var(--outline-line) 58%, var(--text) 42%); + --chart-septenary: color-mix(in srgb, var(--field-line) 70%, var(--text) 30%); + --chart-octonary: color-mix(in srgb, var(--scrollbar-thumb) 75%, var(--text) 25%); + --chart-surface: color-mix(in srgb, var(--panel2) 96%, var(--text) 4%); + --chart-surface-alt: color-mix(in srgb, var(--panel) 96%, var(--text) 4%); + --chart-line-width: 2.1; + --chart-grid-opacity: .4; + --chart-grid-dash: 2 3; + --chart-svg-filter: saturate(0) contrast(1.05); +} + +html[data-chart-style=neon] { + --chart-linecap: round; + --chart-linejoin: round; + --chart-accent: #2cf6ff; + --chart-secondary: #7eff85; + --chart-tertiary: #ff5cd0; + --chart-quaternary: #8b7cff; + --chart-quinary: #ffb454; + --chart-senary: #ffe66b; + --chart-septenary: #7edcff; + --chart-octonary: #d8b3ff; + --chart-surface: #0b1117; + --chart-surface-alt: #101720; + --chart-line-width: 2.45; + --chart-grid-opacity: .62; + --chart-grid-dash: 2 4; + --chart-svg-filter: saturate(1.25) brightness(1.12); +} + +html[data-chart-style=warm] { + --chart-linecap: round; + --chart-linejoin: round; + --chart-accent: #ff9c5c; + --chart-secondary: #f4c46b; + --chart-tertiary: #ff7b72; + --chart-quaternary: #f4a261; + --chart-quinary: #e76f51; + --chart-senary: #d9a15e; + --chart-septenary: #c9895c; + --chart-octonary: #b37258; + --chart-surface: color-mix(in srgb, var(--panel2) 88%, #3d1f10 12%); + --chart-surface-alt: color-mix(in srgb, var(--panel) 90%, #32180d 10%); + --chart-line-width: 2.4; + --chart-grid-opacity: .36; + --chart-grid-dash: 1 0; + --chart-svg-filter: saturate(1.1) brightness(1.04); +} + +html[data-chart-style] { + --traffic-h1: var(--chart-accent); + --traffic-h2: var(--chart-secondary); + --traffic-h3: var(--chart-tertiary); +} + +html[data-corner-style=square] { + --rp-radius-surface: 0px; + --rp-radius-control: 0px; + --rp-progress-radius: 0px; +} + +html[data-corner-style=subtle] { + --rp-radius-surface: 5px; + --rp-radius-control: 4px; + --rp-progress-radius: 2px; +} + +html[data-corner-style=rounded] { + --rp-radius-surface: 12px; + --rp-radius-control: 8px; + --rp-progress-radius: 4px; +} + +html[data-corner-style=extra] { + --rp-radius-surface: 18px; + --rp-radius-control: 12px; + --rp-progress-radius: 999px; +} diff --git a/src/admin/web/css/10-base.css b/src/admin/web/css/10-base.css new file mode 100644 index 0000000..a92f480 --- /dev/null +++ b/src/admin/web/css/10-base.css @@ -0,0 +1,1480 @@ +/* Shared reset, typography, controls, forms, tables, cards and overlay primitives. + * Order is defined in manifest.json; responsive rules are centralized in 70-responsive.css. + */ + +* { + box-sizing: border-box; + scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track); + scrollbar-width: thin; +} + +::-webkit-scrollbar { + width: 10px; + height: 10px; +} + +::-webkit-scrollbar-track, +::-webkit-scrollbar-corner { + background: var(--scrollbar-track); +} + +::-webkit-scrollbar-thumb { + background: var(--scrollbar-thumb); + border: 2px solid var(--scrollbar-track); + border-radius: var(--rp-radius-control, 999px); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--scrollbar-thumb-hover); +} + +body { + margin: 0; + background: var(--bg); + color: var(--text); + font-family: var(--sans); + line-height: 1.5; + zoom: var(--ui-scale, 1); + --ui-gap: 5px; + --ui-card: 8px; + --ui-font: 12px; + --ui-control: 27px; + --button-padding: 6px 10px; + --button-font-size: 11px; + --button-min-height: var(--ui-control); + font-size: var(--ui-font); +} + +button { + font: inherit; + -webkit-tap-highlight-color: transparent; + touch-action: manipulation; + cursor: pointer; + border: 0; + padding: var(--button-padding); + font-size: var(--button-font-size); + min-height: var(--button-min-height); +} + +input { + font: inherit; + -webkit-tap-highlight-color: transparent; +} + +select, +textarea { + font: inherit; + -webkit-tap-highlight-color: transparent; + display: block; + width: 100%; + background: var(--field-bg); + border: 1px solid var(--field-line); + border-radius: var(--rp-radius-control, 3px); + color: var(--text); + min-width: 0; + padding: var(--ui-field-pad-y) var(--ui-field-pad-x); + font-size: var(--ui-field-font-size); + min-height: var(--ui-field-min-height); +} + +a { + -webkit-tap-highlight-color: transparent; + touch-action: manipulation; + color: inherit; + text-decoration: none; +} + +button:disabled { + opacity: .5; + cursor: wait; +} + +[hidden], +#file-log-view-counts[hidden], +#file-log-view-entries[hidden], +[data-dashboard-widget^=stream_][hidden], +#panel-overview .dashboard-overview-grid>[hidden], +.overview-inline-actions[hidden], +.overview-inline-hint[hidden], +.acl-picker-empty[hidden], +.acl-picker-limit[hidden], +.acl-picker-option[hidden], +.stream-health-probe-fields[hidden], +.web-service-page[hidden], +.settings-page[hidden], +#panel-tls .certificate-page[hidden], +#panel-tls .certificate-editor-card[hidden] { + display: none !important; +} + +h1 { + margin: 0; + font-size: calc(clamp(32px, 3.6vw, 52px)); + line-height: 1.1; + font-weight: 520; + letter-spacing: -2px; +} + +h2 { + margin: 0; + letter-spacing: -.5px; + font-size: 17px; +} + +h3 { + margin: 0; + font-size: 14px; + margin-bottom: 10px; +} + +a:focus-visible, +button:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 4px; +} + +.small-label { + font: 10px var(--mono); + color: var(--muted); + font-size: 9px; + letter-spacing: 1.2px; +} + +.signal, +.local-tag i { + display: inline-block; + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--green); +} + +.quiet { + color: var(--muted); + background: transparent; + border: 1px solid var(--line); + border-radius: var(--rp-radius-control, 3px); + padding: var(--button-padding); + font-size: var(--button-font-size); + min-height: var(--button-min-height); +} + +.quiet:hover, +.top-log-menu[open]>summary, +.file-log-search-help[open]>summary { + color: var(--text); + border-color: var(--border-hover); +} + +.primary { + background: var(--accent); + color: var(--accent-ink); + border: 1px solid var(--accent); + font-weight: 650; + border-radius: var(--rp-radius-control, 3px); + white-space: nowrap; + padding: var(--button-padding); + font-size: var(--button-font-size); + min-height: var(--button-min-height); +} + +.primary:hover { + background: var(--accent-hover); +} + +.outline { + display: inline-flex; + justify-content: center; + align-items: center; + border: 1px solid var(--outline-line); + background: transparent; + color: var(--text); + border-radius: var(--rp-radius-control, 3px); + white-space: nowrap; + padding: var(--button-padding); + font-size: var(--button-font-size); + min-height: var(--button-min-height); +} + +.outline:hover, +.overview-inline-hidden-item>button:hover { + border-color: var(--accent); +} + +.notice { + display: flex; + gap: 13px; + line-height: 1.65; + color: var(--notice-text); + border: 1px solid var(--notice-line); + background: var(--notice-bg); + border-radius: var(--rp-radius-surface, 3px); + font-size: 10px; + margin-bottom: var(--ui-gap); + padding: 5px 9px; +} + +.notice b { + font-family: var(--mono); + color: var(--accent); +} + +.card-heading { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + margin-bottom: 8px; +} + +.card-heading .small-label { + display: block; + font-size: 8px; + margin-bottom: 3px; +} + +.pill { + padding: 4px 8px; + border: 1px solid var(--pill-line); + color: var(--accent); + font: 9px var(--mono); + letter-spacing: 1px; +} + +.pill.live, +.vhost-quick-toggle.is-enable, +.acl-picker-policy.is-allow { + border-color: var(--pill-live-line); + color: var(--green); +} + +table { + width: 100%; + border-collapse: collapse; + text-align: left; + font-size: 11px; + white-space: nowrap; +} + +#sparkline { + stroke: var(--chart-accent); + stroke-width: 2; + fill: none; + vector-effect: non-scaling-stroke; +} + +.table-scroll { + overflow: auto; +} + +th { + font: 10px var(--mono); + color: var(--muted); + font-weight: 400; + border-bottom: 1px solid var(--line); + padding: 7px 8px; +} + +td { + border-bottom: 1px solid var(--row-line); + font-family: var(--mono); + max-width: 260px; + overflow: hidden; + text-overflow: ellipsis; + padding: 8px; +} + +td:first-child, +.pool-vhost-disabled td, +.connection-legend span span, +.dashboard-widget-toggle.is-hidden, +#panel-overview .dashboard-overview-grid .mini-live-chart-head>span:first-child, +#panel-overview .dashboard-overview-grid>.card:not(.system-card):not(.connection-card) .card-heading h2, +.vh-origin-protocol-info, +.vh-origin-protocol-warning, +.docker-health-editor>summary::marker, +.cache-bypass-key, +#panel-cache>.card .card-heading h2, +#panel-transform>.card .card-heading h2 { + color: var(--muted); +} + +.status-error, +.host-chip button:hover, +.acl-policy-preview-head .is-deny, +.acl-profile-rule-action-badge.is-deny, +.vhost-backend-popup-field.has-error>span:last-child, +#panel-tls .certificate-operation-card[data-state=failed] .certificate-operation-heading strong, +#panel-tls .certificate-operation-card[data-state=failed] .pill, +#panel-tls .certificate-operation-card[data-state=unavailable] .certificate-operation-heading strong, +#panel-tls .certificate-operation-card[data-state=unavailable] .pill { + color: var(--red); +} + +.fine, +.meta { + font-size: 11px; + line-height: 1.7; + color: var(--muted); + overflow-wrap: anywhere; +} + +.empty { + color: var(--muted); + font-size: 12px; + padding: 17px 3px; +} + +.section-heading { + display: flex; + justify-content: space-between; + align-items: center; + gap: 10px; + margin: 16px 0 12px; +} + +.entity { + border-bottom: 1px solid var(--line); + padding: 12px 0; +} + +.entity:first-child { + padding-top: 0; +} + +.entity:last-child { + border-bottom: 0; + padding-bottom: 0; +} + +.entity-top { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; +} + +.entity-title { + font-weight: 650; + font-size: 13px; +} + +.entity-details { + font: 11px/1.8 var(--mono); + color: var(--muted); + overflow-wrap: anywhere; + white-space: pre-wrap; + margin: 6px 0; + line-height: 1.6; +} + +.entity-actions { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.entity-actions button { + padding: 5px 9px; + font-size: 10px; +} + +.entity small { + font: 9px var(--mono); + color: var(--green); +} + +.edit-form label { + display: block; + color: var(--muted); + font-size: 11px; + margin: 10px 0 5px; + margin-top: 9px; +} + +input:not([type=checkbox]):not([type=radio]) { + display: block; + width: 100%; + background: var(--field-bg); + border: 1px solid var(--field-line); + border-radius: var(--rp-radius-control, 3px); + color: var(--text); + min-width: 0; + padding: var(--ui-field-pad-y) var(--ui-field-pad-x); + font-size: var(--ui-field-font-size); + min-height: var(--ui-field-min-height); +} + +input:not([type=checkbox]):not([type=radio]):focus, +select:focus, +textarea:focus, +.host-chip-editor:focus-within, +.acl-policy-mode-card:has(input:checked) { + outline: 1px solid var(--green); + border-color: var(--green); +} + +input::placeholder { + color: var(--placeholder); +} + +input[type=checkbox], +input[type=radio] { + display: inline-block; + width: var(--ui-check-size); + height: var(--ui-check-size); + min-width: var(--ui-check-size); + min-height: var(--ui-check-size); + padding: 0; + margin: 0; + accent-color: var(--green); + vertical-align: middle; +} + +input.field-invalid, +select.field-invalid, +textarea.field-invalid { + border-color: var(--red) !important; + outline: 1px solid rgba(255, 170, 162, .72); +} + +button.is-saving { + cursor: progress; +} + +.check { + display: flex !important; + align-items: center; + gap: 9px; + margin-top: 13px !important; + font-size: 11px; +} + +.form-actions { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 9px; + margin-top: 13px; +} + +#config-editor { + font: 12px/1.7 var(--mono); + resize: vertical; + min-height: 330px; + margin-top: 16px; + tab-size: 2; + white-space: pre; +} + +#settings-page-manual-config>p { + margin-top: 20px; +} + +.manual-config-source { + padding: 12px 16px; + margin-top: 14px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 8px); + overflow-wrap: anywhere; +} + +.manual-config-source p { + margin: 6px 0 0; +} + +.manual-config-path { + font: 12px/1.6 var(--mono); + user-select: text; +} + +.page-footer { + display: flex; + justify-content: space-between; + gap: 15px; + border-top: 1px solid var(--line); + font: 9px var(--mono); + color: var(--muted); + letter-spacing: .5px; + padding: 12px 0; + margin-top: 8px; +} + +.page-footer b { + color: var(--accent); + font-weight: 400; + margin-left: 9px; +} + +.command { + display: block; + padding: 12px 15px; + background: var(--code-bg); + border-left: 2px solid var(--green); + font-size: 13px; + color: var(--green); + margin: 15px 0 20px; + overflow-wrap: anywhere; + white-space: normal; +} + +.toast { + position: fixed; + bottom: 26px; + right: 26px; + max-width: calc(min(580px, 100vw - 40px)); + background: var(--toast-bg); + color: var(--toast-text); + border: 1px solid var(--toast-line); + padding: 14px 20px; + font-size: 13px; + z-index: 50; + box-shadow: 0 6px 35px var(--shadow-mid); + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +.toast.error { + background: var(--toast-error-bg); + border-color: var(--toast-error-line); + color: var(--toast-error-text); +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +#policy-rules { + font: 11px/1.6 var(--mono); + resize: vertical; +} + +.signal.status-error { + background: var(--red); +} + +#panel-tls .section-heading { + flex-wrap: wrap; + gap: 12px; +} + +.language-select { + width: auto; + max-width: 160px; + padding: 6px 9px; + font-size: 11px; +} + +#system-sparkline { + fill: none; + stroke: var(--chart-secondary); + stroke-width: 2; + vector-effect: non-scaling-stroke; +} + +progress { + display: block; + width: 100%; + height: 5px; + margin: 6px 0 9px; + border: 0; + -webkit-appearance: none; + background: var(--rp-progress-track); + accent-color: var(--rp-progress-fill); + border-radius: var(--rp-progress-radius); + overflow: hidden; +} + +progress::-webkit-progress-bar { + background: var(--rp-progress-track); + border-radius: var(--rp-progress-radius); +} + +progress::-webkit-progress-value { + background: var(--rp-progress-fill); + border-radius: var(--rp-progress-radius); +} + +progress::-moz-progress-bar { + background: var(--rp-progress-fill); + border-radius: var(--rp-progress-radius); +} + +.diagnostics-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--ui-gap); + margin-bottom: var(--ui-gap); +} + +.mini-heading { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.mini-heading h3 { + font-size: 12px; + margin: 0; +} + +.diagnostic-line, +.vhost-chart-line { + fill: none; + stroke: var(--chart-accent); + stroke-width: var(--chart-line-width); + stroke-linecap: var(--chart-linecap); + stroke-linejoin: var(--chart-linejoin); + vector-effect: non-scaling-stroke; +} + +.network-tx-line, +.process-rss-line, +.appearance-chart-secondary, +#panel-overview .overview-wide-series-1, +#panel-overview .overview-donut-miss, +#panel-overview .www-ring-1, +.www-dot-1, +.cache-hit-line, +#sparkline-h2.traffic-line-h2, +.cache-fill-line { + stroke: var(--chart-secondary); +} + +.edit-form .check { + margin-top: 9px !important; +} + +.section-heading h2, +.h3-editor>label, +.vh-backend-health-fields { + margin-top: 4px; +} + +.subheading { + margin-top: 17px; + margin-bottom: 7px !important; +} + +.field-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0 12px; +} + +.switch-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0 12px; + row-gap: 8px; +} + +.pending-note { + font-size: 11px; + color: var(--accent); + padding: 9px 0; +} + +.header-rule { + display: grid; + grid-template-columns: 86px minmax(90px, 1fr) minmax(80px, 1.2fr) auto; + gap: 6px; + margin: 8px 0; +} + +.header-rule input, +.header-rule select { + padding: 7px !important; + font-size: 11px; +} + +.header-rule button { + font-size: 10px; + padding: 6px; +} + +.header-rules { + margin-bottom: 9px; +} + +[dir=rtl] #config-editor, +[dir=rtl] .entity-details, +[dir=rtl] input[type=url] { + direction: ltr; + text-align: left; +} + +#pool-search { + width: calc(min(250px, 30vw)); + min-width: 170px; +} + +#toast[popover] { + overflow-wrap: anywhere; + position: fixed; + inset: auto 18px 18px auto; + margin: 0; + width: max-content; + max-width: calc(min(520px, 100vw - 28px)); + padding: 12px 16px; + color: var(--text); + background: var(--panel); + border: 1px solid var(--green); + box-shadow: 0 10px 40px var(--shadow-strong); + pointer-events: none; + border-radius: 0; +} + +.card { + border: 1px solid var(--line); + background: var(--panel); + min-width: 0; + padding: var(--ui-card); +} + +.stats { + gap: var(--ui-gap); + margin-bottom: var(--ui-gap); +} + +.audit-dialog { + color: var(--text); + background: var(--bg); + border: 1px solid var(--line); + padding: 0; + max-width: calc(100vw - 24px); + box-shadow: 0 18px 70px var(--shadow-strong); + border-radius: var(--rp-radius-surface, 7px); + width: calc(min(1040px, 100vw - 24px)); + max-height: calc(100dvh - 28px); + overflow: hidden; +} + +.logs-dialog { + color: var(--text); + background: var(--bg); + border: 1px solid var(--line); + padding: 0; + max-width: calc(100vw - 24px); + max-height: calc(100dvh - 28px); + box-shadow: 0 18px 70px var(--shadow-strong); + border-radius: var(--rp-radius-surface, 7px); + width: 1180px; + overflow: visible; +} + +.vhost-dialog { + color: var(--text); + background: var(--bg); + border: 1px solid var(--line); + padding: 0; + max-width: calc(100vw - 24px); + max-height: calc(100dvh - 28px); + box-shadow: 0 18px 70px var(--shadow-strong); + border-radius: var(--rp-radius-surface, 7px); + width: 960px; + overflow: auto; +} + +.page-editor { + display: grid; + grid-template-columns: minmax(105px, .9fr) minmax(115px, 1fr) minmax(105px, 1fr); + gap: 6px; + align-items: center; + padding: 7px 0; + border-bottom: 1px solid var(--line); +} + +.page-label { + font-size: 11px; + font-weight: 500; +} + +.page-source input { + font-size: 11px; + margin: 6px 0; +} + +.page-source textarea { + font: 11px var(--mono); + min-height: 100px; +} + +.pages-errors { + margin: 12px 0; +} + +.pages-errors>summary { + font-size: 12px; + cursor: pointer; +} + +.window-heading { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 12px; + border-bottom: 1px solid var(--line); +} + +.window-tools { + margin-inline-start: auto; + display: flex; + gap: 5px; +} + +#toast { + z-index: 1000; +} + +#toast[popover].error { + border-color: #d57867; +} + +.named-listener-row { + border: 1px solid var(--line); + padding: 10px; + margin: 10px 0; + min-width: 0; +} + +.h3-editor, +.vh-wide { + grid-column: 1/-1; + min-width: 0; +} + +.h3-editor details { + padding: 7px 0; +} + +.h3-editor summary { + font-size: 11px; + cursor: pointer; +} + +.h3-editor .fine, +.h3-editor .vh-grid { + margin-top: 7px; +} + +#named-listener-rows { + overflow: visible; +} + +#cache-domain-scroll { + max-height: 260px; + overflow: auto; + scrollbar-gutter: stable; +} + +#cache-object-scroll { + max-height: calc(min(49vh, 520px)); + overflow: auto; + scrollbar-gutter: stable; +} + +.metrics-example { + font: 11px/1.55 ui-monospace, SFMono-Regular, monospace; + max-width: 100%; + overflow: auto; + white-space: pre; + padding: 12px; + background: var(--code-alt-bg); + border-radius: var(--rp-radius-surface, 8px); + color: var(--code-alt-text); +} + +.metrics-preview { + font: 11px/1.55 ui-monospace, SFMono-Regular, monospace; + max-width: 100%; + overflow: auto; + white-space: pre; + padding: 12px; + background: var(--code-alt-bg); + border-radius: var(--rp-radius-surface, 8px); + color: var(--code-alt-text); + max-height: 360px; +} + +dialog::backdrop { + background: var(--shadow-strong) !important; + backdrop-filter: blur(2px) !important; +} + +dialog .card, +dialog .window-heading, +dialog .vh-form, +.acl-profile-layout>.card, +.logging-grid>.card { + border-radius: 0; +} + +#location-help-dialog { + width: calc(min(760px, 100vw - 28px)); + max-height: calc(min(82vh, 760px)); +} + +#location-help-body { + display: grid; + gap: 8px; + padding: 14px 16px 18px; + overflow: auto; +} + +#location-help-body p { + margin: 0; + line-height: 1.55; +} + +.basic-user { + display: grid; + align-items: end; + gap: 7px; + margin-block: 5px; + grid-template-columns: 1fr 1fr auto auto; +} + +.basic-user .fine { + align-self: center; + font-size: 10px; +} + +#certificate-list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); + gap: 10px; +} + +#certificate-list>.entity { + border: 1px solid var(--line); + padding: 12px; + min-width: 0; +} + +#settings-page-logging>.edit-form, +.auth-main .login-card, +.settings-page, +.settings-page>.edit-form { + width: 100%; + max-width: none; +} + +#vhost-list, +#web-service-list, +#stream-list, +.vh-www-choices, +.docker-service-list { + display: grid; + gap: 10px; +} + +#panel-notfound .card-heading, +#panel-redirects .card-heading, +#panel-vhosts .card-heading, +#panel-web-server .card-heading, +#panel-streams .card-heading, +.activity-card .card-heading, +.overview-presets-dialog .overview-layout-footer, +.cache-memory-sizing .form-actions { + flex-wrap: wrap; +} + +#notfound-search, +#redirect-search, +#vhost-search, +#web-service-search, +#stream-search { + width: calc(min(260px, 32vw)); + min-width: 180px; +} + +.toolbar-actions { + display: flex; + gap: 8px; + flex-wrap: wrap; + justify-content: flex-end; +} + +.pill.warn { + border-color: var(--warn-line); + color: var(--accent); +} + +.pill.danger { + border-color: var(--danger-line-strong); + color: var(--red); +} + +#panel-settings:not([hidden]) { + display: grid; + grid-template-columns: minmax(180px, 220px) minmax(0, 1fr); + gap: 12px; + align-items: start; +} + +body[data-density=compact] { + --ui-gap: 2px; + --ui-card: 5px; + --ui-font: 10.75px; + --ui-control: 22px; + line-height: 1.32; +} + +body[data-density=compact] main { + padding: 0 10px; +} + +body[data-density=compact] .card-heading { + margin-bottom: 5px; +} + +body[data-density=compact] .card-heading h2, +html[data-dashboard-density=compact] #panel-overview .dashboard-overview-grid>.card .card-heading h2 { + font-size: 14px; +} + +body[data-density=compact] .outline, +body[data-density=compact] .primary, +body[data-density=compact] .quiet, +body[data-density=compact] button, +body[data-density=compact] input:not([type=checkbox]):not([type=radio]), +body[data-density=compact] select, +body[data-density=compact] textarea { + padding: 3px 7px; + font-size: 10px; +} + +body[data-density=compact] .page-footer { + padding: 7px 0; + margin-top: 4px; +} + +.network-interface-row-head { + display: flex; + justify-content: space-between; + gap: 10px; + margin-bottom: 5px; + align-items: center; +} + +.network-interface-row-head b { + min-width: 0; + overflow-wrap: anywhere; + color: var(--text); + font: 600 10px var(--mono); +} + +.network-interface-empty { + margin: 0; + padding: 8px 0; +} + +#docker-agent-add { + display: inline-flex; + margin-top: 14px; +} + +#docker-agents-form>.form-actions { + justify-content: flex-start; + margin-top: 16px; + padding-top: 14px; + border-top: 1px solid var(--line); +} + +.network-interface-row { + min-width: 0; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; +} + +.network-interface-row-head .fine { + flex: 0 0 auto; + text-align: right; + white-space: nowrap; + font: 10px var(--mono); +} + +.network-interface-row svg { + display: block; + width: 100%; + height: 60px; + overflow: visible; +} + +.check>span { + display: inline-flex; + align-items: center; + min-height: 15px; + line-height: 1.25; +} + +.check input[type=checkbox] { + flex: 0 0 auto; + align-self: center; + transform: translateY(-1px); +} + +.operation-message { + margin: 14px 0 0; + padding: 11px 12px; + border: 1px solid var(--line); + background: var(--panel2); + color: var(--text); + font-size: 12px; + line-height: 1.5; + overflow-wrap: anywhere; +} + +.operation-message.error, +.login-section #auth-message.error { + border-color: rgba(255, 170, 162, .58); + background: rgba(255, 170, 162, .07); + color: var(--red); +} + +.proto-h1-line { + stroke: var(--traffic-h1); + fill: none; + stroke-width: var(--chart-line-width); + stroke-linecap: var(--chart-linecap); +} + +.proto-h2-line { + fill: none; + stroke-width: var(--chart-line-width); + stroke-linecap: var(--chart-linecap); + stroke: var(--traffic-h2); +} + +.proto-h3-line { + fill: none; + stroke-width: var(--chart-line-width); + stroke-linecap: var(--chart-linecap); + stroke: var(--traffic-h3); +} + +.function-live-line { + fill: none; + stroke-width: 2.2; + vector-effect: non-scaling-stroke; +} + +.payload-split { + display: flex !important; + align-items: center; + gap: 10px; + font: 9px var(--mono) !important; +} + +.payload-split span { + display: inline-flex; + align-items: center; + gap: 3px; + white-space: nowrap; +} + +.payload-split b, +.function-live-chart-legend b, +.connection-card .chart-footer b, +.connection-legend b { + color: var(--text); + font-weight: 650; +} + +.network-interface-head-actions { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 7px; + min-width: 0; +} + +.network-interface-hide, +.network-interface-hidden .quiet { + min-height: 24px; + padding: 3px 7px; + font: 9px var(--mono); +} + +.network-interface-row+.network-interface-row, +.docker-service-editor { + border-top: 1px solid var(--line); + padding-top: 8px; +} + +.network-interface-hidden { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 6px; + padding-top: 8px; + border-top: 1px solid var(--line); + color: var(--muted); + font: 9px var(--mono); +} + +#cache-memory-warning { + color: var(--notice-text); + font-weight: 600; +} + +.mini-heading-actions, +.mini-live-chart-actions { + display: inline-flex; + align-items: center; + justify-content: flex-end; + gap: 7px; + min-width: 0; +} + +.metric-info, +.top-log-menu, +.backend-column-picker, +.file-log-search-help { + position: relative; +} + +.metric-info>summary { + width: 18px; + height: 18px; + display: inline-grid; + place-items: center; + list-style: none; + cursor: pointer; + border: 1px solid var(--line); + color: var(--muted); + background: var(--panel2); + font: 700 10px var(--mono); + user-select: none; + border-radius: var(--rp-radius-control, 50%); +} + +.metric-info>summary::-webkit-details-marker, +.top-log-menu>summary::-webkit-details-marker, +.function-live-chart-summary::-webkit-details-marker, +.backend-column-picker>summary::-webkit-details-marker, +.log-column-picker>summary::-webkit-details-marker, +.file-log-search-help>summary::-webkit-details-marker { + display: none; +} + +.metric-info>summary:focus-visible, +.metric-info>summary:hover, +.metric-info[open]>summary { + color: var(--text); + border-color: var(--border-hover); + outline: none; +} + +.metric-info-popover { + position: absolute; + z-index: 40; + top: calc(100% + 7px); + right: 0; + width: calc(min(360px, 100vw - 44px)); + padding: 10px 11px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 6px); + background: var(--panel2); + box-shadow: 0 12px 30px var(--shadow-mid); +} + +.metric-info-popover .fine { + margin: 0; + line-height: 1.5; +} + +.manual-config-tools { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; +} + +.about-dialog { + width: calc(min(560px, 100vw - 24px)); + overflow: hidden; +} + +.about-dialog .window-heading button { + margin-inline-start: auto; +} + +.about-card { + display: grid; + gap: 16px; + margin: 0; + padding: 18px; + border: 0; + box-shadow: none; +} + +.about-product { + display: flex; + align-items: center; + gap: 12px; +} + +.about-product>div { + display: grid; + gap: 4px; +} + +.about-product strong { + font: 600 16px var(--mono); +} + +.about-meta { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1px; + margin: 0; + border: 1px solid var(--line); + background: var(--line); +} + +.about-meta>div { + min-width: 0; + padding: 10px 12px; + background: var(--panel); +} + +.about-meta dt { + margin-bottom: 5px; + color: var(--muted); + font: 9px var(--mono); + letter-spacing: .8px; + text-transform: uppercase; +} + +.about-meta dd { + margin: 0; + overflow-wrap: anywhere; +} + +.about-card>p { + margin: 0; + color: var(--muted); + line-height: 1.65; +} + +.about-repository { + justify-content: space-between; + gap: 12px; + width: 100%; + white-space: normal; + text-align: left; +} + +.about-repository span:last-child { + color: var(--muted); + font: 10px var(--mono); + overflow-wrap: anywhere; +} + +html[data-table-density=compact] :is(table th, table td) { + padding-top: 6px; + padding-bottom: 6px; + font-size: 11px; + line-height: 1.28; +} + +html[data-table-density=ultra] :is(table th, table td) { + padding-top: 3px; + padding-bottom: 3px; + font-size: 10px; + line-height: 1.15; +} + +.php-switch-row { + justify-content: flex-start; + display: flex; + align-items: center; + gap: 10px; + min-width: 0; + min-height: 30px; + margin: 4px 0; +} + +.php-switch-row .php-switch-label { + display: inline-flex !important; + flex: 0 1 auto; + align-items: center; + gap: 9px; + min-width: 0; + margin: 0 !important; + line-height: 1.5; +} + +.php-switch-row .php-switch-label input[type=checkbox] { + position: static; + flex: 0 0 var(--ui-check-size); + margin: 0; +} + +.php-switch-row .php-field-info, +.vh-check input, +.docker-service-head-actions .docker-view-toggle, +.appearance-section-reset { + flex: 0 0 auto; +} + +.php-switch-row .php-field-info .metric-info-popover { + right: auto; + left: 0; +} + +.php-reference pre { + overflow-x: auto; + white-space: pre; + padding: 10px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 4px); + background: var(--panel2); + font: 12px/1.6 var(--mono); +} + +.php-profile-help-divider { + width: 100%; + height: 0; + margin: 16px 0 12px; + border: 0; + border-top: 1px solid var(--line); +} + +.php-origin-errors { + grid-column: 1/-1; + display: grid; + gap: 8px; + padding: 10px 0; + border-top: 1px solid var(--line); +} + +.php-origin-errors>label { + max-width: 430px; +} + +#file-log-view-counts, +#file-log-view-entries, +#panel-filelogs { + min-width: 0; + max-width: 100%; +} + +.form-actions .check { + margin-top: 0 !important; + min-height: var(--button-min-height); + align-items: center; +} diff --git a/src/admin/web/css/20-shell.css b/src/admin/web/css/20-shell.css new file mode 100644 index 0000000..44ea84e --- /dev/null +++ b/src/admin/web/css/20-shell.css @@ -0,0 +1,773 @@ +/* Application startup, authentication, navigation, page chrome and draft dock. + * Order is defined in manifest.json; responsive rules are centralized in 70-responsive.css. + */ + +html.app-loading .shell { + visibility: hidden; + pointer-events: none; +} + +.app-startup { + position: fixed; + inset: 0; + z-index: 10000; + display: grid; + place-items: center; + padding: 24px; + box-sizing: border-box; + background: var(--bg, #10151b); + color: var(--text, #edf2f7); + text-align: center; +} + +.page-source[hidden], +#toast[popover]::backdrop, +.operation-message:empty, +#certificate-api-cancel-edit[hidden], +#certificate-api-edit-note[hidden], +#certificate-api-token-field[hidden], +html:not(.app-loading) .app-startup, +html.app-startup-failed .app-startup-message, +html.app-startup-failed .app-startup-spinner, +.login-section #auth-message:empty, +.mobile-menu-toggle, +.draft-dock-list[hidden], +#settings-page-appearance .dashboard-widget-setting .overview-widget-type, +.dashboard-appearance-grid .dashboard-widget-list.is-expanded~.dashboard-widget-scroll-hint, +.vh-group[hidden], +.stream-socket-editor input[hidden], +.vh-cache-options[hidden], +.acl-policy-custom-controls[hidden], +.listener-protocol-settings-actions button[hidden], +.logs-dialog .logs-card>.card-heading>div:first-child { + display: none; +} + +.app-startup-card { + width: min(100%, 360px); +} + +.app-startup-brand { + font: 700 29px var(--mono, monospace); + letter-spacing: -2px; + margin-bottom: 24px; +} + +.app-startup-brand span { + color: var(--logo-secondary, var(--accent, #65cfa2)); +} + +.app-startup-spinner { + box-sizing: border-box; + width: 40px; + height: 40px; + margin: 0 auto 22px; + border: 3px solid var(--line, #374352); + border-top-color: var(--accent, #65cfa2); + border-radius: 50%; + animation: app-startup-spin .8s linear infinite; +} + +.app-startup h2 { + margin: 0; + font-size: 19px; + font-weight: 600; + line-height: 1.4; +} + +.app-startup-message, +.app-startup-recovery p { + font-size: 13px; + line-height: 1.6; + color: var(--muted, #b5c0cc); +} + +.app-startup-recovery { + visibility: hidden; + animation: app-startup-reveal 0s linear 12s forwards; +} + +.app-startup-recovery .outline:not([hidden]) { + margin-top: 10px; + display: inline-flex; +} + +html.app-startup-failed .app-startup-recovery { + visibility: visible; + animation: none; +} + +.shell { + display: grid; + min-height: calc(100vh / var(--ui-scale, 1)); + grid-template-columns: 182px minmax(0, 1fr); +} + +.sidebar { + position: sticky; + top: 0; + height: calc(100vh / var(--ui-scale, 1)); + background: var(--sidebar-bg); + border-right: 1px solid var(--line); + display: flex; + flex-direction: column; + padding: 20px 13px; +} + +.brand { + display: flex; + align-items: center; + font-weight: 780; + line-height: 1.1; + letter-spacing: -.6px; + font-size: 17px; + gap: 8px; +} + +.brand-mark { + font: 700 33px var(--mono); + border: 1px solid var(--logo-accent, var(--accent)); + color: var(--logo-accent, var(--accent)); + place-content: center; + position: relative; + box-sizing: border-box; + padding-right: 2px; + display: flex; + align-items: center; + justify-content: center; + letter-spacing: -3px; + flex-shrink: 0; + width: 47px; + height: 47px; + font-size: 29px; +} + +.brand-mark span { + font-size: 22px; + left: 29px; + top: 19px; + color: var(--logo-secondary, var(--brand-secondary)); + position: static; +} + +.edition { + font: 9px/1.6 var(--mono); + color: var(--muted); + letter-spacing: 1px; + margin: 17px 0 28px; +} + +.edition span { + display: block; + color: var(--accent); + margin-top: 5px; +} + +nav { + flex-direction: column; + display: flex; + gap: 5px; +} + +.local-tag { + display: flex; + font: 9px var(--mono); + color: var(--green); + gap: 8px; + align-items: center; +} + +.nav { + display: flex; + align-items: center; + gap: 13px; + text-align: left; + background: transparent; + color: var(--muted); + border-left: 2px solid transparent; + border-radius: var(--rp-radius-control, 0); + padding: 9px 10px; +} + +.nav span { + font: 11px var(--mono); + opacity: .65; +} + +.nav b { + font-weight: 530; +} + +.nav.active { + background: var(--rp-nav-active-bg); + color: var(--rp-nav-active-color); + border-left-color: var(--rp-nav-active-color); +} + +.nav:not(.active):hover { + background: var(--rp-nav-hover-bg); +} + +.sidebar .main-navigation .nav:focus-visible { + outline: 2px solid var(--rp-nav-active-color); + outline-offset: 2px; +} + +.sidebar-bottom { + margin-top: auto; + padding-top: 32px; +} + +.sidebar-bottom p { + font-size: 11px; + line-height: 1.9; + color: var(--text-subtle); + margin: 13px 0 26px; +} + +main { + min-width: 0; + width: 100%; + max-width: none; + padding: 0 16px; + margin: 0 auto; +} + +.topbar { + border-bottom: 1px solid var(--line); + display: flex; + justify-content: space-between; + align-items: center; + gap: 14px; + height: 48px; +} + +.breadcrumb { + font: 10px var(--mono); + color: var(--muted); + letter-spacing: 1px; +} + +.breadcrumb span { + margin: 0 13px; + color: var(--separator); +} + +.breadcrumb b { + font-weight: 400; + color: var(--text); +} + +.top-actions { + display: flex; + align-items: center; + gap: 8px; +} + +.system-card h2 small { + font: 10px var(--mono); + color: var(--muted); +} + +.eyebrow { + font: 10px var(--mono); + letter-spacing: 2px; + color: var(--green); + display: flex; + gap: 8px; + align-items: center; + margin-bottom: 16px; +} + +.lead { + font-size: 13px; + line-height: 1.7; + color: var(--muted); + margin-top: 15px; + max-width: 690px; +} + +.page-intro { + display: flex; + justify-content: space-between; + align-items: center; + padding: 13px 0 11px; + gap: 12px; +} + +.login-card label { + display: block; + color: var(--muted); + font-size: 11px; + margin: 16px 0 7px; +} + +.page-footer .footer-update { + color: var(--green); + text-decoration: underline; + text-underline-offset: 2px; +} + +.login-section { + padding: 62px 0 40px; + max-width: 720px; + padding-top: 40px; +} + +.login-section h1 { + font-size: calc(clamp(42px, 5vw, 68px)); + letter-spacing: -3px; + max-width: 640px; +} + +.login-section .lead { + font-size: 15px; + max-width: 570px; +} + +.login-card { + background: var(--panel); + border: 1px solid var(--line); + padding: 27px; + max-width: 510px; + margin-top: 23px; +} + +.login-card>p { + font-size: 12px; + color: var(--muted); + line-height: 1.7; +} + +.login-card .primary { + width: 100%; + margin-top: 18px; +} + +.login-card .fine { + font-size: 10px; + margin-top: 16px; +} + +.login-footer { + font: 9px var(--mono); + letter-spacing: 1.7px; + color: var(--muted); + margin-top: 30px; + line-height: 2; +} + +.login-footer span { + margin: 0 11px; + color: var(--accent); +} + +.page-intro .eyebrow { + font-size: 9px; + margin-bottom: 4px; +} + +[dir=rtl] .nav { + text-align: right; + border-left: 0; + border-right: 2px solid transparent; +} + +[dir=rtl] .nav.active { + border-right-color: var(--rp-nav-active-color); +} + +.app-confirm-message { + overflow-wrap: anywhere; + margin-top: 14px; + color: var(--text); + white-space: pre-line; +} + +.sidebar nav { + margin-top: 22px; +} + +.page-intro h1 { + font-size: 25px; + letter-spacing: -.7px; +} + +.page-intro .lead { + font-size: 11px; + margin-top: 4px; +} + +.app-confirm-dialog { + width: calc(min(520px, 100vw - 28px)); + max-width: none; + padding: 0; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 4px); + background: var(--panel); + color: var(--text); +} + +.app-confirm-dialog::backdrop { + background: var(--backdrop); + backdrop-filter: blur(2px); +} + +.app-confirm-card { + margin: 0; + border: 0; + padding: 18px; +} + +.app-confirm-actions { + justify-content: flex-end; + margin-top: 18px; +} + +.app-confirm-danger { + border-color: var(--red); + color: var(--red); +} + +.app-confirm-danger:hover { + border-color: var(--red); + background: rgba(255, 170, 162, .08); +} + +.auth-page.auth-loading .auth-main, +.auth-page.auth-loading .auth-top { + visibility: hidden; +} + +.auth-page { + min-height: 100vh; +} + +.auth-top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 18px 24px; + border-bottom: 1px solid var(--line); +} + +.auth-main { + max-width: 560px; + margin: auto; + padding: 28px 20px; +} + +.auth-main .login-section { + padding: 0; + max-width: none; +} + +.auth-main h1 { + font-size: 1.65rem; + margin-bottom: 18px; +} + +.login-section #auth-message { + margin-top: 14px; + padding: 11px 12px; + border: 1px solid var(--line); + background: var(--panel2); + color: var(--text); + font-size: 12px; + line-height: 1.5; + overflow-wrap: anywhere; +} + +.auth-advanced>summary, +.vh-backend-advanced>summary { + cursor: pointer; + color: var(--muted); + font-size: 11px; + font-weight: 600; + padding: 4px 0 8px; +} + +.auth-subcard { + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 6px); + padding: 10px; + display: grid; + gap: 8px; +} + +.auth-subcard>h3 { + margin: 0; + font-size: 12px; +} + +.auth-advanced { + border-top: 1px solid var(--line); + padding-top: 4px; +} + +body[data-density=compact] .shell { + grid-template-columns: 164px minmax(0, 1fr); +} + +body[data-density=compact] .sidebar { + padding: 14px 9px; +} + +body[data-density=compact] .nav, +body[data-density=compact] .stat { + padding: 6px 7px; +} + +body[data-density=compact] .topbar { + height: 42px; +} + +body[data-density=compact] .page-intro { + padding: 7px 0 6px; + gap: 7px; +} + +body[data-density=compact] .page-intro h1 { + font-size: 20px; +} + +body[data-density=compact] .page-intro .lead { + font-size: 10px; + margin-top: 2px; +} + +.top-log-menu>summary, +.backend-column-picker>summary, +.file-log-search-help>summary { + list-style: none; + cursor: pointer; + user-select: none; +} + +.top-log-menu-items { + position: absolute; + z-index: 120; + top: calc(100% + 6px); + inset-inline-end: 0; + display: grid; + gap: 4px; + min-width: 170px; + padding: 6px; + border: 1px solid var(--line); + background: var(--panel); + box-shadow: 0 12px 28px var(--shadow-strong); +} + +.top-log-menu-items .quiet { + width: 100%; + text-align: start; + white-space: nowrap; +} + +.draft-dock { + position: fixed; + right: 14px; + bottom: 14px; + z-index: 900; + width: calc(min(330px, 100vw - 28px)); + padding: 0; + border: 1px solid var(--border-hover); + border-radius: var(--rp-radius-surface, 0); + background: var(--panel); + box-shadow: 0 12px 32px var(--shadow-strong); +} + +.draft-dock-heading { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + min-height: 34px; + padding: 7px 9px; + border-bottom: 1px solid var(--line); + background: var(--panel2); +} + +.draft-dock.is-collapsed .draft-dock-heading, +.draft-dock-item:last-child { + border-bottom: 0; +} + +.draft-dock-heading>strong { + font: 700 10px var(--mono); + letter-spacing: .12em; + text-transform: uppercase; +} + +.draft-dock-heading .pill { + min-width: 24px; + padding: 2px 6px; + text-align: center; + border-radius: var(--rp-radius-control, 0); +} + +.draft-dock-heading-actions, +.traffic-protocol-legend span, +.stat-chart .stat-chart-legend span, +.function-live-chart-legend span, +.connection-legend span { + display: inline-flex; + align-items: center; + gap: 5px; +} + +.draft-dock-toggle { + display: inline-grid; + place-items: center; + width: 26px; + min-width: 26px; + height: 24px; + padding: 0; + border: 1px solid var(--line); + border-radius: var(--rp-radius-control, 0); + background: var(--panel); + color: var(--text); + font: 700 14px var(--mono); +} + +.draft-dock-toggle:hover, +.chart-style-choice:hover { + border-color: var(--border-hover); + background: var(--nav-hover-bg); +} + +.draft-dock-list { + display: grid; + gap: 0; + max-height: calc(min(42vh, 360px)); + overflow: auto; +} + +.draft-dock-item { + display: grid; + grid-template-columns: minmax(0, 1fr) 32px; + gap: 0; + align-items: stretch; + border-bottom: 1px solid var(--line); +} + +.draft-dock-open { + min-height: 42px; + border: 0; + border-radius: 0; + background: transparent; + min-width: 0; + padding: 6px 9px; + text-align: left; + border-right: 1px solid var(--line); +} + +.draft-dock-remove { + min-height: 42px; + border: 0; + border-radius: 0; + background: transparent; + padding: 0; + font-size: 18px; + color: var(--muted); +} + +.draft-dock-open:hover { + background: var(--nav-hover-bg); + color: var(--text); + box-shadow: inset 3px 0 0 var(--accent); +} + +.draft-dock-remove:hover { + background: var(--nav-hover-bg); + color: var(--text); +} + +.draft-dock-identity { + display: grid; + min-width: 0; + gap: 1px; +} + +.draft-dock-meta { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + min-width: 0; +} + +.draft-dock-identity small { + color: var(--muted); + font: 9px var(--mono); + text-transform: uppercase; + letter-spacing: .08em; +} + +.draft-dock-identity time { + flex: 0 0 auto; + color: var(--text-subtle); + font: 9px var(--mono); +} + +.draft-dock-identity b { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 12px; + font-weight: 650; +} + +.page-intro-copy, +.acl-picker, +.vh-acl-profile-picker, +.docker-health-grid input:not([type=checkbox]), +.docker-health-grid select, +.docker-route-grid input:not([type=checkbox]), +.docker-route-grid select { + min-width: 0; + width: 100%; +} + +.page-intro-title-row { + min-width: 0; + width: 100%; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + gap: 10px; +} + +.page-intro-title-row h1 { + min-width: 0; + flex: 1 1 auto; + margin-right: auto; +} + +.page-intro .page-intro-meta-row { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 6px 16px; + min-width: 0; + margin-bottom: 4px; +} + +.page-intro .page-intro-meta-row .eyebrow { + margin-bottom: 0; + flex: 0 0 auto; +} + +@keyframes app-startup-spin { + to { + transform: rotate(1turn); + } +} + +@keyframes app-startup-reveal { + to { + visibility: visible; + } +} diff --git a/src/admin/web/css/30-overview.css b/src/admin/web/css/30-overview.css new file mode 100644 index 0000000..f730047 --- /dev/null +++ b/src/admin/web/css/30-overview.css @@ -0,0 +1,2055 @@ +/* Overview metrics, charts, dashboard density, inline layout controls and presets. + * Order is defined in manifest.json; responsive rules are centralized in 70-responsive.css. + */ + +html[data-theme=ultra_dark] :is(.card, .stat, dialog) { + box-shadow: 0 6px 24px rgba(0, 0, 0, .18); +} + +html[data-theme=cream] :is(.card, .stat, dialog) { + box-shadow: 0 6px 20px rgba(78, 58, 38, .055); +} + +.stats-grid { + display: grid; + grid-template-columns: repeat(6, minmax(0, 1fr)); + gap: var(--ui-gap); + margin-bottom: var(--ui-gap); +} + +.stat { + border: 1px solid var(--line); + background: var(--panel); + position: relative; + padding: 11px; +} + +.stat:first-child { + border-top: 2px solid var(--accent); +} + +.stat small { + color: var(--muted); + font-size: 10px; +} + +.stat strong { + display: block; + font-family: var(--mono); + font-weight: 450; + letter-spacing: -1px; + line-height: 1.3; + font-size: calc(clamp(20px, 1.8vw, 26px)); + margin: 4px 0; +} + +.status-good, +.stat:first-child small, +.acl-policy-preview-head .is-allow, +.acl-profile-rule-action-badge.is-allow, +.vhost-backend-popup-field.has-success>span:last-child, +#panel-tls .certificate-operation-card[data-state=complete] .pill, +#panel-tls .certificate-operation-card[data-state=ready] .pill, +.cache-hit-key, +.cache-fill-key { + color: var(--green); +} + +.activity-card #chart-peak { + margin-left: auto; + color: var(--muted); + font: 9px var(--mono); + white-space: nowrap; +} + +#traffic-chart { + width: 100%; + height: 107px; + margin: 3px 0; +} + +.chart-grid { + stroke: var(--chart-grid); + stroke-width: 1; + fill: none; + opacity: var(--chart-grid-opacity, .55); + stroke-dasharray: var(--chart-grid-dash, 2 5); +} + +.chart-footer { + display: flex; + justify-content: space-between; + color: var(--muted); + font: 9px var(--mono); + gap: 10px; + line-height: 1.5; +} + +.mini-metrics dd { + overflow-wrap: anywhere; + font: 10px var(--mono); + margin: 0; + text-align: end; + min-width: 0; +} + +.system-card h2 { + font: 23px var(--mono); +} + +#system-chart { + display: block; + width: 100%; + height: 65px; +} + +.memory-line { + display: flex; + justify-content: space-between; + gap: 8px; + font-size: 9px; + color: var(--muted); + margin-top: 4px; +} + +.memory-line b { + font-weight: 400; + font-family: var(--mono); +} + +.mini-metrics { + margin: 8px 0 0; +} + +.mini-metrics div { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 10px; + margin-top: 4px; +} + +.mini-metrics dt, +.acl-policy-condition b { + font-size: 10px; + color: var(--muted); +} + +.mini-live-chart-stack { + display: grid; + gap: 8px; + margin-top: 10px; +} + +.mini-live-chart { + min-width: 0; + margin-top: 10px; + border-top: 1px solid var(--line); + padding-top: 8px; +} + +.mini-live-chart-stack .mini-live-chart, +.runtime-information-card { + margin-top: 0; +} + +.mini-live-chart-head { + display: flex; + justify-content: space-between; + align-items: baseline; + gap: 8px; + margin-bottom: 5px; + color: var(--muted); + font: 10px var(--mono); +} + +.mini-live-chart-head b { + color: var(--text); + font-weight: 600; + text-align: right; +} + +.mini-live-chart svg { + display: block; + width: 100%; + height: 60px; + overflow: visible; + filter: var(--chart-svg-filter); +} + +.mini-chart-legend { + display: flex; + justify-content: flex-end; + gap: 10px; + margin-top: 3px; + font: 9px var(--mono); + color: var(--muted); +} + +.mini-chart-legend span:before { + content: ""; + display: inline-block; + width: 12px; + height: 2px; + margin-right: 4px; + vertical-align: middle; + background: currentColor; +} + +.vh-group legend { + font-weight: 600; + padding: 0 5px; + color: var(--text); + font-size: 11px; +} + +.pool-card .fine { + margin: 0 0 8px; +} + +.pool-table { + font-size: 11px; + width: 100%; + min-width: 640px; +} + +.pool-table td:nth-child(n/**/+4) { + font-variant-numeric: tabular-nums; + text-align: right; +} + +[dir=rtl] table, +.pool-table th:nth-child(n/**/+4), +.logging-builder-action .small-label { + text-align: right; +} + +.pool-table td:first-child, +.pool-table td:nth-child(2) { + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; +} + +.window-heading h2, +.card-heading h2 { + font-size: 16px; +} + +.notice.fine, +.stat p, +.pool-card>.fine, +.vh-header-row label span, +.docker-protocol-fixed small { + font-size: 10px; +} + +.activity-card svg { + height: 105px; +} + +.system-card svg { + height: 80px; +} + +.pool-card { + margin-bottom: var(--ui-gap); +} + +.pool-table td, +.pool-table th { + white-space: nowrap; + padding: 5px 7px; +} + +.pool-card .table-scroll { + max-height: 205px; +} + +body[data-density=dense] .stat strong { + font-size: 22px; + margin: 2px 0; +} + +.traffic-chart-grid, +.logging-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + margin: 14px 0; +} + +.traffic-chart-legend { + display: grid; + gap: 6px; + padding: 0; + margin: 0; + list-style: none; + min-width: 0; +} + +.traffic-chart-legend li { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + min-width: 0; + font-size: 11px; +} + +.traffic-chart-legend li>span { + display: flex; + align-items: center; + gap: 7px; + min-width: 0; +} + +.traffic-chart-swatch { + width: 9px; + height: 9px; + border-radius: 50%; + flex: 0 0 auto; +} + +.traffic-chart-label { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.traffic-chart-legend b { + font: 700 10px var(--mono); + flex: 0 0 auto; +} + +.traffic-wide-chart, +#panel-overview .dashboard-overview-grid>.card:not(.pool-card), +.docker-alias-field, +.docker-acl-grid .docker-profile-field, +.docker-agent-grid .docker-profile-field { + grid-column: span 2; +} + +.traffic-bar-chart { + display: grid; + gap: 8px; + min-width: 0; +} + +.vhost-chart { + border: 1px solid var(--line); + margin-top: 9px; + background: var(--chart-surface-alt); +} + +.vhost-chart-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: 7px 10px 0; +} + +.vhost-chart svg { + display: block; + width: 100%; + height: 54px; + filter: var(--chart-svg-filter); +} + +.pool-vhost-disabled { + opacity: .55; + background: var(--neutral-soft); +} + +.stream-chart-rx { + fill: none; + stroke-width: var(--chart-line-width); + stroke-linecap: var(--chart-linecap); + stroke-linejoin: var(--chart-linejoin); + vector-effect: non-scaling-stroke; + stroke: var(--chart-accent); +} + +.stream-chart-tx { + fill: none; + stroke-width: var(--chart-line-width); + stroke-linecap: var(--chart-linecap); + stroke-linejoin: var(--chart-linejoin); + vector-effect: non-scaling-stroke; + stroke: var(--chart-secondary); +} + +.stream-chart-legend { + display: flex; + justify-content: flex-end; + flex-wrap: wrap; + gap: 12px; + padding: 0 10px 7px; + color: var(--muted); + font: 10px var(--mono); +} + +.redirect-rule legend { + padding: 0 .4rem; + font-weight: 700; +} + +.traffic-protocol-legend { + display: flex; + flex-wrap: wrap; + gap: 7px 16px; + margin: 2px 0 7px; + color: var(--muted); + font: 9px var(--mono); +} + +.traffic-protocol-legend i { + width: 12px; + height: 2px; + display: inline-block; + background: var(--muted); +} + +.traffic-protocol-legend .legend-h1 i { + background: var(--accent); +} + +.traffic-protocol-legend .legend-h2 i { + background: var(--green); +} + +.traffic-protocol-legend .legend-h3 i { + background: var(--blue); +} + +.traffic-protocol-legend .legend-transform i { + background: var(--transform-chart); +} + +.traffic-protocol-legend b { + color: var(--text); + font-weight: 600; +} + +body[data-density=compact] .stat strong { + font-size: 18px; + margin: 1px 0; +} + +body[data-density=compact] .stat p, +body[data-density=compact] .stat small, +html[data-dashboard-density=normal] #panel-overview .stat small, +html[data-dashboard-density=ultra] #panel-overview .stat-cache-outcomes .cache-mix-header>span, +html[data-dashboard-density=ultra] #panel-overview .stat-cache-outcomes .cache-mix-caption { + font-size: 9px; +} + +body[data-density=compact] .activity-card svg { + height: 84px; +} + +body[data-density=compact] .system-card svg { + height: 66px; +} + +body[data-density=compact] .pool-table td, +body[data-density=compact] .pool-table th { + padding: 3px 5px; + font-size: 10px; +} + +body[data-density=compact] .pool-card .table-scroll { + max-height: 170px; +} + +.network-interface-charts { + display: grid; + max-height: 260px; + overflow: auto; + gap: 8px; + padding-right: 0; +} + +.stat-with-action { + padding-right: 116px; +} + +.stat-corner-action { + position: absolute; + top: 10px; + right: 10px; + margin: 0; +} + +.stat-chart strong { + margin-bottom: 3px; +} + +.stat-chart svg { + display: block; + width: 100%; + height: 26px; + margin-top: 2px; +} + +.stat-chart .stat-chart-legend { + display: flex; + flex-wrap: nowrap; + gap: 6px 10px; + margin-top: 2px; + white-space: nowrap; +} + +.stat-chart .stat-chart-legend i { + width: 8px; + height: 8px; + border-radius: 999px; + display: inline-block; + background: currentColor; +} + +.stat-chart .stat-chart-legend .legend-h1 i, +#panel-overview .backend-mode-h1 { + background: var(--traffic-h1); +} + +.stat-chart .stat-chart-legend .legend-h2 i, +#panel-overview .backend-mode-auto { + background: var(--traffic-h2); +} + +.stat-chart .stat-chart-legend .legend-h3 i, +#panel-overview .backend-mode-h2 { + background: var(--traffic-h3); +} + +.stat-chart .stat-chart-legend b { + font-family: var(--mono); + font-weight: 500; +} + +.stat-chart .chart-grid { + opacity: var(--chart-grid-opacity, .28); +} + +.function-live-chart { + width: 100%; + padding: 0; + overflow: hidden; +} + +.function-live-chart-summary { + display: flex; + align-items: center; + gap: 12px; + min-height: 52px; + padding: 13px 15px; + cursor: pointer; + list-style: none; + user-select: none; +} + +.function-live-chart-summary:before { + content: "▸"; + flex: 0 0 auto; + color: var(--muted); + font: 12px var(--mono); +} + +.function-live-chart[open]>.function-live-chart-summary:before { + content: "▾"; +} + +.function-live-chart-heading { + display: grid; + gap: 3px; + min-width: 0; + flex: 1 1 auto; +} + +.function-live-chart-heading strong { + font-size: 13px; + font-weight: 650; +} + +.function-live-chart-heading small { + color: var(--muted); + font: 9px var(--mono); +} + +.function-live-chart-summary>b { + flex: 0 0 auto; + color: var(--text); + font: 11px var(--mono); + font-weight: 650; + white-space: nowrap; +} + +.function-live-chart-body { + border-top: 1px solid var(--line); + padding: 14px 15px 12px; +} + +.function-live-chart svg { + display: block; + width: 100%; + height: 180px; + overflow: visible; +} + +.function-live-chart-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px 18px; + margin-top: 8px; +} + +.function-live-chart-samples { + display: inline-flex; + align-items: center; + gap: 7px; + color: var(--muted); + font: 9px var(--mono); + white-space: nowrap; +} + +.function-live-chart-samples select { + min-width: 116px; + padding: 5px 25px 5px 8px; + font: 10px var(--mono); +} + +.function-live-chart-legend { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: 7px 16px; + color: var(--muted); + font: 9px var(--mono); +} + +.function-live-chart-legend i, +.connection-legend i { + display: inline-block; + width: 14px; + height: 2px; + background: currentColor; +} + +.runtime-information-card h3 { + margin: 16px 0 8px; + font-size: 12px; + font-weight: 600; +} + +.runtime-information-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 12px; +} + +.runtime-information-grid>div { + min-width: 0; + display: grid; + gap: 5px; +} + +.runtime-information-grid code { + overflow-wrap: anywhere; + white-space: normal; +} + +.connection-card h2 { + display: flex; + align-items: baseline; + gap: 6px; +} + +.connection-card h2 small { + color: var(--muted); + font: 9px var(--mono); + font-weight: 400; +} + +.connection-card svg { + display: block; + width: 100%; + height: 100px; + overflow: visible; +} + +.connection-exchange-line { + fill: none; + vector-effect: non-scaling-stroke; + stroke-width: 2; + stroke: var(--chart-secondary); +} + +.connection-open-line { + fill: none; + vector-effect: non-scaling-stroke; + stroke-width: 2; + stroke: var(--chart-accent); +} + +.connection-legend { + display: flex; + flex-wrap: wrap; + gap: 7px 14px; + margin: 4px 0 8px; + color: var(--muted); + font: 9px var(--mono); +} + +.connection-open-key { + color: var(--accent) !important; +} + +.connection-exchange-key { + color: var(--green) !important; +} + +.dashboard-widget-setting-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 11px; +} + +.appearance-chart-preview { + min-height: 118px; + display: grid; + grid-template-rows: 1fr auto; + gap: 6px; + padding: 10px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 0); + background: var(--chart-surface); +} + +.appearance-chart-preview svg { + width: 100%; + height: 78px; + filter: var(--chart-svg-filter); +} + +.appearance-chart-preview polyline { + fill: none; + stroke-width: var(--chart-line-width); + stroke-linecap: var(--chart-linecap); + stroke-linejoin: var(--chart-linejoin); + vector-effect: non-scaling-stroke; +} + +.appearance-chart-primary, +#panel-overview .overview-wide-series-0, +#panel-overview .overview-donut-hit, +#panel-overview .www-ring-0, +.www-dot-0, +.cache-miss-line, +#sparkline-h1.traffic-line-h1, +.cache-hot-line, +.cache-transient-total-line { + stroke: var(--chart-accent); +} + +.chart-style-palette { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 6px; + margin-top: 10px; +} + +.chart-style-choice { + display: grid; + grid-template-columns: 42px minmax(0, 1fr); + align-items: center; + gap: 8px; + min-height: 34px; + padding: 5px 7px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-control, 0); + background: var(--panel); + color: var(--text); + text-align: left; + font-size: 10px; +} + +.chart-style-choice.active, +.docker-service-row.is-dirty { + border-color: var(--accent); + box-shadow: inset 3px 0 0 var(--accent); +} + +.chart-style-swatches { + display: grid; + grid-template-columns: repeat(4, 1fr); + height: 16px; + border: 1px solid var(--line); +} + +.chart-style-swatches i:first-child { + background: var(--sw1); +} + +.chart-style-swatches i:nth-child(2) { + background: var(--sw2); +} + +.chart-style-swatches i:nth-child(3) { + background: var(--sw3); +} + +.chart-style-swatches i:nth-child(4) { + background: var(--sw4); +} + +.dashboard-widget-settings { + display: grid; + min-width: 0; + gap: 5px; +} + +.dashboard-widget-list { + min-width: 0; + grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); + margin-top: 4px; + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.dashboard-widget-setting { + grid-template-columns: minmax(0, 1fr) auto; + min-height: 34px; + border: 1px solid var(--line); + background: var(--panel); + display: inline-flex; + align-items: center; + gap: 8px; + flex: 1 1 280px; + min-width: min(280px, 100%); + max-width: 100%; + padding: 4px 6px; +} + +.dashboard-widget-toggle { + display: flex; + align-items: center; + gap: 7px; + min-width: 0; + min-height: 26px; + padding: 3px 6px; + border: 0; + background: transparent; + color: var(--text); + text-align: left; +} + +.dashboard-widget-visibility { + flex: 0 0 8px; + width: 8px; + height: 8px; + border: 1px solid var(--green); + border-radius: 50%; + background: var(--green); +} + +.dashboard-widget-toggle.is-hidden .dashboard-widget-visibility { + border-color: var(--muted); + background: transparent; +} + +.dashboard-widget-setting-actions { + display: inline-flex; + gap: 2px; +} + +.dashboard-widget-setting-actions button { + min-width: 24px; + min-height: 24px; + padding: 1px 5px; + border-radius: 0; +} + +:is(.stat-chart, .activity-card, .system-card, .function-live-chart) svg, +:is(.traffic-donut, .traffic-bar-chart span) { + filter: var(--chart-svg-filter); +} + +html[data-dashboard-density=compact] #panel-overview .stats-grid, +html[data-dashboard-density=normal] #panel-overview .stats-grid, +html[data-dashboard-density=ultra] #panel-overview .stats-grid { + gap: var(--ui-gap); +} + +html[data-dashboard-density=normal] #panel-overview .stat, +#panel-tls .certificate-inventory-card, +#panel-tls .certificate-generator-card { + padding: 14px; +} + +html[data-dashboard-density=normal] #panel-overview .stat strong { + font-size: 26px; + margin: 6px 0 4px; + line-height: 1.3; +} + +html[data-dashboard-density=compact] #panel-overview .stat { + padding: 9px 10px; +} + +html[data-dashboard-density=compact] #panel-overview .stat>span { + font-size: 11px; + line-height: 1.35; +} + +html[data-dashboard-density=compact] #panel-overview .stat strong { + font-size: 22px; + margin: 3px 0 2px; + line-height: 1.2; +} + +html[data-dashboard-density=compact] #panel-overview .stat small { + font-size: 10px; + line-height: 1.35; +} + +html[data-dashboard-density=compact] #panel-overview .payload-split { + font-size: 10px !important; +} + +html[data-dashboard-density=ultra] #panel-overview .stat { + padding: 7px 8px; +} + +html[data-dashboard-density=ultra] #panel-overview .stat>span { + font-size: 9px; + line-height: 1.08; +} + +html[data-dashboard-density=ultra] #panel-overview .stat strong { + font-size: 18px; + margin: 2px 0 1px; + line-height: 1.08; +} + +html[data-dashboard-density=ultra] #panel-overview .stat small { + font-size: 7.5px; + line-height: 1.12; +} + +html[data-dashboard-density=ultra] #panel-overview .stat-chart svg { + height: 24px; +} + +html[data-dashboard-density=normal] #panel-overview .stat-chart svg { + height: 34px; +} + +html[data-dashboard-density=compact] #panel-overview .stat-chart svg { + height: 28px; +} + +html[data-chart-style] :is(#workspace, dialog) svg polyline { + stroke-width: var(--chart-line-width); + stroke-linecap: var(--chart-linecap); + stroke-linejoin: var(--chart-linejoin); +} + +.system-info-heading-actions { + display: flex; + align-items: center; + gap: 8px; + margin-left: auto; + flex-shrink: 0; +} + +.system-info-heading-actions .pill, +.docker-open-logs, +.docker-open-app, +.config-history-detail-actions button { + white-space: nowrap; +} + +.stream-backend-row>legend { + font-weight: 650; + padding: 0 .35rem; +} + +.dashboard-widget-toggle:focus-visible, +.file-log-tab:focus-visible { + outline: 2px solid var(--accent); + outline-offset: -2px; +} + +#panel-overview .stream-traffic-legend .legend-rx i, +#panel-overview .www-ring-legend i, +#panel-overview .www-segment-0, +#panel-overview .cache-mix-hit, +#panel-overview .cache-mix-hit-key i { + background: var(--chart-accent); +} + +#panel-overview .stats-grid .stat-chart>.stat-chart-legend, +#panel-overview .dashboard-overview-grid.is-aligned[data-overview-layout-mode=custom] .overview-wide-legend, +#panel-overview .dashboard-overview-grid.is-aligned[data-overview-layout-mode=custom] .stat-chart-legend { + flex-wrap: wrap; + white-space: normal; +} + +#panel-overview .backend-mode-bar { + display: flex; + height: 7px; + width: 100%; + overflow: hidden; + gap: 2px; + border-radius: var(--rp-radius-surface, 5px); + background: var(--line); + margin: 7px 0 5px; +} + +#panel-overview .backend-mode-compat { + background: var(--traffic-h2); + opacity: .5; +} + +#panel-overview .backend-mode-bar[aria-label] { + background-clip: padding-box; +} + +.overview-layout-footer { + position: sticky; + bottom: 0; + display: flex; + align-items: center; + justify-content: flex-end; + gap: 8px; + padding: 12px; + border-top: 1px solid var(--line); + background: var(--bg); + z-index: 2; +} + +.overview-layout-footer p { + margin: 0 auto 0 0; + font-size: 11px; + overflow-wrap: anywhere; +} + +#panel-overview .dashboard-overview-grid { + display: grid; + grid-template-columns: repeat(6, minmax(0, 1fr)); + gap: var(--ui-gap); + align-items: stretch; +} + +#panel-overview .dashboard-overview-grid>.pool-card { + grid-column: 1/-1; + margin-bottom: 0; +} + +.overview-widget-type { + display: inline-block; + width: fit-content; + max-width: 100%; + padding: 2px 6px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-control, 4px); + background: var(--panel2); + color: var(--muted); + font-size: 10px; + line-height: 1.25; + overflow-wrap: anywhere; +} + +.dashboard-widget-setting .overview-widget-type { + margin-left: 15px; +} + +.overview-inline-actions { + display: inline-flex; + gap: 7px; + align-items: center; + justify-content: flex-end; + flex-wrap: wrap; + max-width: 100%; + margin-left: auto; +} + +.overview-inline-hint { + margin: 0 0 10px; +} + +#panel-overview .dashboard-overview-grid.is-unlocked>[data-dashboard-widget] { + position: relative; + outline: 1px dashed var(--accent); + outline-offset: -2px; + padding-top: 101px; +} + +#panel-overview .dashboard-overview-grid.is-unlocked>.is-overview-dragging { + opacity: .45; +} + +#panel-overview .dashboard-overview-grid.is-unlocked>.is-overview-drop-before { + box-shadow: inset 4px 0 var(--accent); +} + +#panel-overview .dashboard-overview-grid.is-unlocked>.is-overview-drop-after { + box-shadow: inset -4px 0 var(--accent); +} + +.overview-inline-toolbar { + position: absolute; + top: 3px; + left: 4px; + right: 4px; + z-index: 4; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 3px; + pointer-events: auto; +} + +#panel-overview .overview-inline-toolbar button { + min-width: 28px; + min-height: 27px; + padding: 2px 7px; + font-size: 12px; +} + +#panel-overview .overview-inline-toolbar .overview-inline-grip { + cursor: grab; + user-select: none; +} + +#panel-overview .overview-inline-toolbar .overview-inline-grip:active { + cursor: grabbing; +} + +#panel-overview .overview-inline-toolbar .overview-widget-type { + margin: 0 auto 0 0; + margin-right: auto; +} + +#panel-overview .overview-inline-toolbar .overview-inline-hide { + color: var(--red); + border: 1px solid var(--danger-line); + background: var(--panel); + font-size: 18px; + line-height: 1; + margin-left: 0; +} + +#panel-overview .overview-inline-toolbar .overview-inline-hide:focus-visible, +#panel-overview .overview-inline-toolbar .overview-inline-hide:hover { + border-color: var(--danger-line-strong); + background: var(--notice-bg); +} + +.overview-inline-hidden { + margin: 0 0 10px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 9px); + background: var(--panel); + color: var(--text); +} + +.overview-inline-hidden>summary { + padding: 9px 12px; + cursor: pointer; + color: var(--muted); + font-size: 12px; +} + +.overview-inline-hidden>summary:focus-visible, +.vhost-backend-protocol-badge:focus-visible, +.vhost-backend-trigger:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +.overview-inline-hidden-list { + display: flex; + flex-wrap: wrap; + gap: 7px; + max-height: calc(min(30vh, 240px)); + overflow-y: auto; + padding: 10px 12px; + border-top: 1px solid var(--line); +} + +.overview-inline-hidden-item { + display: inline-flex; + align-items: center; + gap: 7px; + max-width: 100%; + padding: 4px 7px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 7px); + color: var(--text); + background: var(--panel2); + font-size: 12px; +} + +.overview-inline-hidden-item>button { + flex: 0 0 auto; + min-width: 27px; + min-height: 27px; + padding: 1px 7px; + border-radius: var(--rp-radius-control, 5px); + border: 1px solid var(--field-line); + color: var(--green); + background: var(--panel); +} + +#panel-overview { + container: overview-canvas / inline-size; +} + +#panel-overview .dashboard-overview-grid.is-aligned { + display: grid; + --overview-gap: var(--ui-gap); + margin: 0; + grid-template-columns: repeat(12, minmax(0, 1fr)); + grid-auto-flow: row; + grid-auto-rows: auto; + align-items: stretch; + align-content: start; + gap: var(--overview-gap); + overflow-anchor: none; +} + +#panel-overview .dashboard-overview-grid.is-aligned.stats-grid { + gap: var(--overview-gap); +} + +#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget] { + grid-column: auto/span 2; + grid-row: auto; + align-self: stretch; + width: 100%; + max-width: 100%; + min-width: 0; + box-sizing: border-box; + margin-block: 0; + position: relative; +} + +#panel-overview .dashboard-overview-grid.is-aligned>.card:not(.pool-card) { + grid-column: auto/span 4; +} + +#panel-overview .dashboard-overview-grid.is-aligned>.card.overview-wide-chart { + grid-column: auto/span 6; +} + +.page-source, +#panel-overview .dashboard-overview-grid.is-aligned>.pool-card, +.acl-policy-mode-custom, +.stream-backend-section, +.web-service-setting-section, +.renewal-entry .fine { + grid-column: 1/-1; +} + +#panel-overview .dashboard-overview-grid.is-aligned>.card.overview-row-start, +#panel-overview .dashboard-overview-grid.is-aligned>.overview-row-start, +#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget].overview-row-start { + grid-column-start: 1; +} + +#panel-overview .overview-wide-chart { + padding: 15px; + overflow: hidden; +} + +html[data-dashboard-density=normal] #panel-overview .dashboard-overview-grid>.card:not(.pool-card) { + padding: 13px; +} + +html[data-dashboard-density=compact] #panel-overview .dashboard-overview-grid>.card:not(.pool-card) { + padding: 10px; +} + +html[data-dashboard-density=ultra] #panel-overview .dashboard-overview-grid>.card:not(.pool-card) { + padding: 7px; +} + +#panel-overview .overview-wide-chart .card-heading { + min-width: 0; + align-items: baseline; + gap: 10px; +} + +#panel-overview .overview-wide-chart h2, +.file-log-field span { + min-width: 0; + overflow-wrap: anywhere; +} + +#panel-overview .overview-wide-summary { + color: var(--accent); + font: 500 12px var(--mono); + white-space: nowrap; +} + +#panel-overview .overview-wide-svg { + display: block; + width: 100%; + height: 125px; + margin-block: 13px 9px; +} + +#panel-overview .overview-wide-svg .chart-grid { + vector-effect: non-scaling-stroke; + stroke: var(--line); + fill: none; + stroke-width: 1; + opacity: .6; +} + +#panel-overview .overview-wide-series { + fill: none; + vector-effect: non-scaling-stroke; + stroke-width: 2; + stroke-linejoin: round; + stroke-linecap: round; +} + +#panel-overview .overview-wide-series-2, +#panel-overview .www-ring-2, +.www-dot-2, +.cache-bypass-line, +#sparkline-h3.traffic-line-h3, +.cache-cold-line, +.cache-queue-line { + stroke: var(--chart-tertiary); +} + +#panel-overview .overview-wide-legend { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px 16px; + color: var(--muted); + font-size: 11px; +} + +#panel-overview .overview-wide-legend>span { + display: inline-flex; + align-items: center; + gap: 6px; + min-width: 0; +} + +#panel-overview .overview-wide-legend i { + width: 8px; + height: 8px; + border-radius: 100%; + background: var(--chart-accent); +} + +#panel-overview .overview-wide-legend .overview-wide-key-2, +#panel-overview .www-ring-legend .www-dot-2, +#panel-overview .www-segment-2, +#panel-overview .cache-mix-bypass, +#panel-overview .cache-mix-bypass-key i { + background: var(--chart-tertiary); +} + +#panel-overview .overview-wide-legend b { + font: 500 11px var(--mono); + color: var(--text); +} + +.dashboard-widget-setting .dashboard-widget-toggle .overview-widget-type { + margin: 0 0 0 auto; + flex: 0 0 auto; +} + +.dashboard-widget-setting .dashboard-widget-setting-actions { + justify-self: end; + flex-shrink: 0; +} + +.overview-inline-hidden-item>.overview-widget-type { + margin-right: 0; +} + +.overview-inline-hidden-item>.overview-inline-restore { + order: 3; + margin-left: auto; +} + +.dashboard-widget-setting .dashboard-widget-toggle { + width: auto; + flex: 1 1 auto; + overflow: hidden; + min-width: 0; + flex-wrap: nowrap; +} + +.dashboard-widget-setting .dashboard-widget-setting-label { + flex: 1 1 auto; + min-width: 0; +} + +.dashboard-widget-setting>.overview-widget-type { + flex: none; + margin: 0; + white-space: nowrap; + overflow-wrap: normal; +} + +.dashboard-widget-setting>.dashboard-widget-setting-actions { + justify-self: end; + white-space: nowrap; +} + +.appearance-settings-grid.dashboard-appearance-grid { + grid-template-columns: minmax(0, 1fr); + gap: 12px; +} + +.appearance-settings-grid.dashboard-appearance-grid>.dashboard-density-setting { + display: grid; + grid-template-columns: minmax(145px, 200px) minmax(180px, 290px) minmax(0, 1fr); + align-items: center; + gap: 12px; +} + +.dashboard-appearance-grid>.dashboard-widget-settings { + grid-column: 1/-1; + gap: 6px; +} + +.dashboard-widget-settings-head { + display: flex; + align-items: flex-start; + flex-wrap: wrap; + gap: 8px 14px; +} + +.dashboard-widget-settings-intro { + display: grid; + gap: 3px; + min-width: 0; + flex: 1 1 290px; +} + +.dashboard-widget-settings-controls { + display: flex; + flex: 0 0 auto; + align-items: center; + flex-wrap: wrap; + justify-content: flex-end; + gap: 6px; + margin-left: auto; +} + +.dashboard-widget-settings-controls>.dashboard-widget-scroll-button { + min-width: 30px; +} + +.dashboard-appearance-grid .dashboard-widget-list { + display: flex; + flex-wrap: nowrap; + align-items: stretch; + gap: 6px; + max-width: 100%; + min-width: 0; + margin: 0; + padding: 2px 1px 9px; + overflow-x: auto; + overflow-y: hidden; + scrollbar-width: thin; + overscroll-behavior-x: contain; + scroll-snap-type: x proximity; + -webkit-overflow-scrolling: touch; +} + +.dashboard-appearance-grid .dashboard-widget-list>.dashboard-widget-setting { + flex: 0 0 226px; + width: 226px; + min-width: 226px; + max-width: 226px; + min-height: 33px; + box-sizing: border-box; + scroll-snap-align: start; +} + +.dashboard-appearance-grid .dashboard-widget-list.is-expanded { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(min(225px, 100%), 1fr)); + gap: 7px; + overflow: visible; + padding-bottom: 0; + scroll-snap-type: none; +} + +.dashboard-appearance-grid .dashboard-widget-list.is-expanded>.dashboard-widget-setting { + width: 100%; + max-width: none; + min-width: 0; +} + +html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-style=rounded], [data-corner-style=extra]) :is(.card, .stat, dialog, .login-card, .appearance-section, .settings-tabs, .dashboard-widget-setting, .overview-inline-hidden-item, .overview-inline-hidden, .notice, .toast, .chart-style-choice, .pool-card, .vhost-card, .vh-backend-card, .entity, .settings-filter-row, .stream-backend-row, .file-log-advanced-parser, .metric-info-popover), +html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-style=rounded], [data-corner-style=extra]) :is(.backend-column-options, .renewal-meta-item) { + border-radius: var(--rp-radius-surface) !important; +} + +html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-style=rounded], [data-corner-style=extra]) :is(button:not(.nav):not(.file-log-tab):not(.vhost-backend-trigger), input:not([type=checkbox]):not([type=radio]):not([type=range]), select, textarea, .pill, .vhost-backend-protocol-badge, .overview-widget-type, .badge, .quiet, .outline, .primary, .code, .command, .dashboard-widget-toggle, .settings-tabs button, .overview-inline-restore), +html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-style=rounded], [data-corner-style=extra]) :is(.nav, .file-log-tab, .vhost-backend-trigger, .backend-column-options, .docker-service-quick-actions a, .docker-service-quick-actions button, .docker-view-toggle, .provider-picker, .renewal-meta-item) { + border-radius: var(--rp-radius-control) !important; +} + +#panel-overview .stat-meter>.overview-meter { + position: relative; + display: block; + width: 100%; + height: 10px; + margin: 9px 0 8px; + overflow: hidden; + border-radius: var(--rp-progress-radius); + background: var(--rp-progress-track); +} + +#panel-overview .overview-meter-fill { + display: block; + width: 0; + height: 100%; + border-radius: inherit; + background: var(--rp-progress-fill); + transition: width .18s; +} + +#panel-overview .system-card .system-memory-meter { + display: block; + width: 100%; + height: 5px; + margin: 6px 0 9px; + overflow: hidden; + background: var(--rp-progress-track); + border-radius: var(--rp-progress-radius); +} + +#panel-overview .stat-meter>small, +.file-log-search-help-popover code { + display: block; + overflow-wrap: anywhere; +} + +#panel-overview .stat-donut .overview-donut-row { + display: flex; + gap: 10px; + align-items: center; + min-width: 0; + margin: 4px 0 2px; +} + +#panel-overview .stat-donut .overview-donut-svg { + display: block; + width: 62px; + height: 62px; + flex: 0 0 62px; + overflow: visible; + filter: var(--chart-svg-filter); +} + +#panel-overview .overview-donut-svg circle { + fill: none; + stroke-width: 9; + transform: rotate(-90deg); + transform-origin: 32px 32px; +} + +#panel-overview .overview-donut-track { + stroke: var(--line); +} + +#panel-overview .stat-donut .overview-donut-legend { + display: flex; + flex-direction: column; + gap: 6px; + min-width: 0; +} + +#panel-overview .overview-donut-legend>span { + display: flex; + align-items: center; + gap: 5px; + flex-wrap: wrap; + line-height: 1.25; + overflow-wrap: anywhere; +} + +#panel-overview .overview-donut-legend i { + width: 8px; + height: 8px; + flex: 0 0 8px; + border-radius: 50%; + background: var(--chart-accent); +} + +#panel-overview .overview-donut-legend b { + color: var(--text); + font-family: var(--mono); + font-weight: 500; +} + +#panel-overview .overview-wide-series-3, +#panel-overview .www-ring-3, +.www-dot-3, +.cache-io-line { + stroke: var(--chart-quaternary); +} + +#panel-overview .overview-wide-legend .overview-wide-key-3, +#panel-overview .www-ring-legend .www-dot-3, +#panel-overview .www-segment-3 { + background: var(--chart-quaternary); +} + +.page-intro .overview-samples-toolbar { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 6px 12px; + flex-wrap: wrap; + max-width: 100%; + margin: 0 0 0 auto; +} + +.page-intro .overview-samples-toolbar .function-live-chart-samples select { + min-width: 100px; +} + +.page-intro .overview-samples-toolbar button { + width: auto; + flex: none; + min-height: 28px; + padding: 5px 10px; + white-space: nowrap; +} + +.page-intro .overview-samples-toolbar .fine { + font-size: 10px; + color: var(--muted); + white-space: nowrap; +} + +#panel-overview .stat-donut .overview-donut-info { + display: flex; + flex-direction: column; + justify-content: center; + gap: 4px; + min-width: 0; + flex: 1 1 0; +} + +#panel-overview .stat-donut .overview-donut-info>strong { + display: block; + line-height: 1.1; + margin: 0; +} + +#panel-overview .stat-donut .overview-donut-info>.overview-donut-legend { + gap: 2px; +} + +#panel-overview .www-ring-legend { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + column-gap: 7px; + row-gap: 5px; +} + +#panel-overview .www-ring-legend span { + white-space: nowrap; + font-size: 10px; +} + +#panel-overview .www-ring-arc { + stroke-width: 9; +} + +#panel-overview .www-segmented { + display: flex; + width: 100%; + height: 10px; + margin: 9px 0 8px; + background: var(--rp-progress-track); + border-radius: var(--rp-progress-radius); + overflow: hidden; +} + +#panel-overview .www-segmented span { + display: block; + height: 100%; + transition: width .18s; +} + +#panel-overview .www-segment-4, +#panel-overview .cache-mix-other-key i { + background: var(--muted); +} + +#panel-overview .www-top-bars { + display: grid; + gap: 8px; + margin-top: 12px; + min-height: 90px; +} + +#panel-overview .www-top-row { + display: grid; + grid-template-columns: minmax(60px, 1fr) minmax(30px, 2fr) auto; + gap: 8px; + align-items: center; + min-width: 0; + font: 11px var(--mono); + font-family: var(--mono); + font-variant-numeric: tabular-nums; +} + +#panel-overview .www-top-row>span, +.file-log-column-picker .log-column-option span { + overflow: hidden; + text-overflow: ellipsis; +} + +#panel-overview .www-top-meter { + height: 7px; + background: var(--rp-progress-track); + border-radius: var(--rp-progress-radius); + overflow: hidden; +} + +#panel-overview .www-top-meter span { + display: block; + background: var(--rp-progress-fill); + border-radius: inherit; + height: 100%; +} + +#panel-overview .www-top-row b { + font-weight: 500; + color: var(--text); +} + +#panel-web-server .www-service-panel { + min-width: 0; + margin-top: 10px; +} + +#panel-web-server .www-service-split { + color: var(--muted); + font: 11px var(--mono); + overflow-wrap: anywhere; + margin: 4px 10px 8px; +} + +#panel-overview .dashboard-overview-grid.is-aligned[data-overview-layout-mode=custom] { + grid-auto-flow: row dense; +} + +#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="2"] { + --overview-widget-placement: auto/span 2; +} + +#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="3"] { + --overview-widget-placement: auto/span 3; +} + +#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="4"] { + --overview-widget-placement: auto/span 4; +} + +#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="6"] { + --overview-widget-placement: auto/span 6; +} + +#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="12"] { + --overview-widget-placement: 1/-1; +} + +#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget]:is([data-dashboard-span="2"], [data-dashboard-span="3"], [data-dashboard-span="4"], [data-dashboard-span="6"], [data-dashboard-span="12"]) { + grid-column: var(--overview-widget-placement); +} + +#panel-overview .dashboard-overview-grid.is-aligned[data-overview-layout-mode=custom]>[data-dashboard-widget].overview-row-start { + grid-column-start: auto; +} + +#panel-overview .overview-inline-width { + display: inline-flex; + align-items: center; + justify-content: center; + white-space: nowrap; + font: 11px var(--mono); + color: var(--text); + gap: 3px; + flex: 0 0 auto; +} + +#panel-overview .overview-inline-width-value { + min-width: 31px; + text-align: center; +} + +#panel-overview :is(.system-card, .connection-card) :is(.card-heading h2, .card-heading .meta, .memory-line, .chart-footer, .connection-legend), +:is(.overview-wide-summary, .overview-wide-legend, .overview-donut-legend, .www-method-legend, .stat-chart-legend, .mini-chart-legend, .connection-legend, .traffic-protocol-legend, .traffic-chart-legend, .function-live-chart-legend, .stream-chart-legend, .chart-footer, .vhost-chart-head, .network-interface-rate), +#panel-overview .overview-wide-chart>.fine, +#panel-overview .stat-chart>small { + font-family: var(--mono); + font-variant-numeric: tabular-nums; +} + +#panel-overview :is(.system-card, .connection-card) .card-heading h2 { + letter-spacing: normal; +} + +.overview-presets-dialog { + width: calc(min(730px, 100vw - 24px)); + max-height: calc(100dvh - 24px); + overflow-y: auto; +} + +.overview-presets-content { + display: grid; + gap: 14px; + padding: 14px; +} + +.overview-presets-create, +.overview-presets-list, +.appearance-settings-grid>label { + display: grid; + gap: 7px; +} + +.overview-presets-create-controls, +.listener-active-items { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.overview-presets-create-controls input { + min-width: 150px; + flex: 1 1 240px; +} + +.overview-presets-row { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 8px; + padding: 9px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, var(--radius, 8px)); + background: var(--panel); +} + +.overview-presets-row strong { + font-size: 13px; + overflow-wrap: anywhere; +} + +.overview-presets-actions, +.renewal-actions { + display: flex; + flex-wrap: wrap; + gap: 5px; +} + +#panel-overview .activity-card .card-heading h2, +#panel-overview .overview-wide-chart .card-heading h2, +#panel-overview :is(.stat-chart, .stat-donut)>span:first-child, +.mini-live-chart>.mini-live-chart-head>span:first-child { + font-family: var(--sans); + font-size: 12px; + font-weight: 650; + line-height: 1.35; + letter-spacing: normal; + text-transform: uppercase; +} + +#panel-overview :is(.system-card, .connection-card) .overview-chart-heading { + font-family: var(--sans); + font-size: 12px; + font-weight: 650; + line-height: 1.35; + letter-spacing: normal; + text-transform: uppercase; + color: var(--text); + margin: 0 0 5px; +} + +.function-live-chart-heading>strong, +.traffic-chart-card>h3.subheading, +.vhost-chart-head>span:first-child { + font-family: var(--sans); + font-size: 12px; + font-weight: 650; + line-height: 1.35; + letter-spacing: normal; + text-transform: uppercase; + color: var(--muted); +} + +#panel-overview :is(.overview-wide-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stat-chart-legend, .mini-chart-legend, .connection-legend, .traffic-protocol-legend, .stream-traffic-legend), +:is(.overview-wide-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stat-chart-legend, .mini-chart-legend, .connection-legend, .traffic-protocol-legend, .traffic-chart-legend, .function-live-chart-legend, .stream-chart-legend, .stream-traffic-legend) { + font-family: var(--mono); + font-size: 9px; + font-variant-numeric: tabular-nums; +} + +#panel-overview :is(.overview-wide-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stat-chart-legend, .mini-chart-legend, .connection-legend, .traffic-protocol-legend, .stream-traffic-legend) :is(span, b, strong, small, li, em), +:is(.overview-wide-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stat-chart-legend, .mini-chart-legend, .connection-legend, .traffic-protocol-legend, .traffic-chart-legend, .function-live-chart-legend, .stream-chart-legend, .stream-traffic-legend) :is(span, b, strong, small, li, em) { + font-size: inherit; + font-family: inherit; +} + +html[data-dashboard-density] #panel-overview .stat :is(.stat-chart-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stream-traffic-legend), +html[data-dashboard-density] #panel-overview .stat :is(.stat-chart-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stream-traffic-legend) :is(span, b, small, strong, li) { + font-size: 9px; + font-family: var(--mono); +} + +#panel-overview .dashboard-overview-grid>.card .mini-heading>h3, +#panel-overview .dashboard-overview-grid>.card .overview-chart-heading, +#panel-overview .dashboard-overview-grid>.stat>span:first-child { + font-family: var(--sans); + font-size: 12px; + font-weight: 600; + line-height: 1.35; + letter-spacing: normal; + text-transform: none; + color: var(--muted); +} + +#panel-overview .dashboard-overview-grid>.card:is(.overview-wide-chart, .activity-card, .pool-card) .card-heading h2 { + font-family: var(--sans); + font-size: 12px; + font-weight: 600; + line-height: 1.35; + letter-spacing: normal; + text-transform: none; +} + +#panel-overview .dashboard-overview-grid>[data-dashboard-widget] :is(small, .mini-metrics dt, .mini-metrics dd, .mini-live-chart-head, .memory-line, .chart-footer, .small-label, .meta, .fine) { + font-family: var(--mono); + font-variant-numeric: tabular-nums; + text-transform: none; +} + +#panel-overview .dashboard-overview-grid .mini-live-chart>.mini-live-chart-head>span:first-child { + font-family: var(--mono); + font-size: 10px; + font-weight: 400; + line-height: 1.35; + letter-spacing: normal; + text-transform: none; +} + +#panel-overview .stat-cache-outcomes .cache-mix-header { + display: flex; + justify-content: space-between; + align-items: baseline; + gap: 6px; + min-width: 0; +} + +#panel-overview .stat-cache-outcomes .cache-mix-header>span { + flex: 1 1 auto; + min-width: 0; + color: var(--muted); + font-size: 11px; +} + +#panel-overview .stat-cache-outcomes .cache-mix-caption { + flex: 0 0 auto; + margin-left: auto; + color: var(--muted); + font: 500 10px/1.35 var(--mono); + font-variant-numeric: tabular-nums; + text-align: right; + white-space: nowrap; +} + +#panel-overview .stat-cache-outcomes .cache-mix-bar { + display: flex; + width: 100%; + height: 12px; + margin: 7px 0 9px; + border-radius: var(--rp-progress-radius); + background: var(--rp-progress-track); + overflow: hidden; +} + +#panel-overview .cache-mix-bar>span { + display: block; + height: 100%; + min-width: 0; +} + +#panel-overview .cache-mix-other { + background: var(--muted); + opacity: .65; +} + +#panel-overview .cache-mix-legend { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 5px 8px; + line-height: 1.25; +} + +#panel-overview .cache-mix-legend>span { + display: flex; + align-items: center; + gap: 4px; + min-width: 0; +} + +#panel-overview .cache-mix-legend i { + display: inline-block; + flex: 0 0 7px; + width: 7px; + height: 7px; + border-radius: var(--rp-radius-control, 2px); +} + +#panel-overview .stream-traffic-legend .legend-tx i, +#panel-overview .overview-wide-legend .overview-wide-key-1, +#panel-overview .overview-donut-miss-key i, +#panel-overview .www-ring-legend .www-dot-1, +#panel-overview .www-segment-1, +#panel-overview .cache-mix-miss, +#panel-overview .cache-mix-miss-key i { + background: var(--chart-secondary); +} + +#panel-overview .cache-mix-legend b { + display: flex; + flex-direction: column; + align-items: flex-end; + margin-left: auto; + color: var(--text); + font: 500 10px var(--mono); + overflow-wrap: anywhere; + text-align: right; +} + +#panel-overview .cache-mix-legend .cache-mix-percent { + color: var(--muted); + font: 400 10px var(--mono); +} diff --git a/src/admin/web/css/40-editors.css b/src/admin/web/css/40-editors.css new file mode 100644 index 0000000..43fda6c --- /dev/null +++ b/src/admin/web/css/40-editors.css @@ -0,0 +1,1944 @@ +/* Shared service editors: Vhosts, backends, Web Server, Streams, routing and ACL pickers. + * Order is defined in manifest.json; responsive rules are centralized in 70-responsive.css. + */ + +.host-chip-editor.field-invalid, +.listener-choices.field-invalid { + border-color: var(--red) !important; + box-shadow: 0 0 0 1px rgba(255, 170, 162, .38); +} + +.vhost-layout { + grid-template-columns: minmax(220px, .7fr) minmax(0, 2fr); + gap: 12px; + align-items: start; + display: block; +} + +.vh-field { + min-width: 0; + display: flex; + flex-direction: column; + gap: 4px; + margin: 0; + font-size: 11px; + color: var(--muted); +} + +.php-switch-row .php-switch-label span, +.chart-footer>span, +.cache-stats .stat, +.traffic-chart-card, +#panel-overview .backend-mode-bar span, +#panel-overview .stats-grid>.stat, +#panel-overview .dashboard-overview-grid>.activity-card svg, +#panel-overview .dashboard-overview-grid>[data-dashboard-widget], +.dashboard-density-setting>.fine, +.dashboard-density-setting>select, +.vh-form, +.vhost-layout>.card, +.location-header-editor, +.location-preview-grid>div, +.vhost-card-identity, +.web-service-pages, +.vhost-backend-popup-field, +.vh-setting-section>.vh-grid>.vh-acl-profile-picker, +.certificate-card-identity, +.docker-agent-card, +.docker-agent-settings, +.docker-passive-health-editor .docker-checkbox-field, +#panel-settings .settings-pages, +#panel-tls .certificate-subheading>div, +#panel-tls .certificate-workspace-heading>div, +.docker-profile-field, +.refresh-settings-grid>label:not(.check), +.cache-operation-dialog .cache-dialog-heading>div:first-child, +#panel-traffic [data-traffic-page], +.logging-grid label, +.file-logs-filter label, +.config-history-json-columns section { + min-width: 0; +} + +.vh-group { + min-width: 0; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 6px); + margin: 8px 0; + padding: 9px; +} + +.vh-group>summary { + font-weight: 600; + padding: 0 5px; + color: var(--text); + cursor: pointer; + margin-bottom: 6px; + font-size: 11px; +} + +.vh-grid { + display: grid; + align-items: start; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 7px; +} + +.vh-field input, +.vh-field select { + width: 100%; + min-width: 0; + max-width: 100%; + padding: 6px 7px; + font-size: 12px; + margin: 0; + box-sizing: border-box; +} + +.vh-field textarea { + width: 100%; + min-width: 0; + max-width: 100%; + padding: 6px 7px; + font-size: 12px; + margin: 0; + box-sizing: border-box; + resize: vertical; + font-family: var(--mono); +} + +.vh-check { + font-size: 11px; + min-height: 28px; + margin: 0; + align-items: center; + gap: 6px; +} + +.vh-enable-control { + display: grid !important; + grid-template-columns: auto minmax(0, 1fr) auto; + grid-template-rows: auto auto; + align-items: center; + gap: 2px 10px; + margin-top: 5px !important; + padding: 11px 12px; + border: 1px solid var(--pill-live-line); + background: var(--panel2); + box-shadow: inset 3px 0 0 var(--green); +} + +.vh-enable-control>input { + grid-column: 1; + grid-row: 1/3; + width: 18px; + height: 18px; + accent-color: var(--green); +} + +.vh-enable-control>span { + grid-column: 2; + grid-row: 1; + color: var(--text); + font-size: 12px; + font-weight: 700; +} + +.vh-enable-hint { + grid-column: 2; + grid-row: 2; + color: var(--muted); + font-size: 10px; +} + +.vh-enable-state { + grid-column: 3; + grid-row: 1/3; + padding: 3px 7px; + border: 1px solid var(--pill-live-line); + color: var(--green); + font: 600 10px var(--mono); + text-transform: uppercase; + letter-spacing: .04em; +} + +.vh-enable-control.is-disabled { + border-color: var(--danger-line-strong); + box-shadow: inset 3px 0 0 var(--red); +} + +.vh-enable-control.is-disabled .vh-enable-state, +.vhost-quick-toggle.is-disable, +.acl-picker-policy.is-deny { + border-color: var(--danger-line); + color: var(--red); +} + +.vh-members { + display: grid; + gap: 12px; +} + +.vh-row { + padding: 8px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 5px); + min-width: 0; + background: var(--bg); +} + +.vh-member>.quiet, +.file-log-raw-json, +.cache-maintenance-info .metric-info-popover .fine+code { + margin-top: 6px; +} + +.vh-member details>summary { + cursor: pointer; + color: var(--muted); + padding: 3px 0; +} + +.backend-card-heading { + align-items: flex-start; + gap: 10px; +} + +.backend-table-tools { + display: flex; + align-items: center; + justify-content: flex-end; + flex-wrap: wrap; + gap: 7px; + margin-left: auto; + --toolbar-control-height: min(var(--button-min-height), var(--ui-field-min-height)); +} + +.backend-sort-control { + display: flex; + align-items: center; + gap: 6px; + color: var(--muted); + font-size: 11px; +} + +.backend-sort-control select { + width: auto; + min-width: 120px; +} + +.backend-column-options { + position: absolute; + z-index: 20; + top: calc(100% + 6px); + right: 0; + display: grid; + grid-template-columns: repeat(2, minmax(145px, 1fr)); + gap: 1px 12px; + min-width: 330px; + max-height: 330px; + overflow: auto; + padding: 10px 12px 12px; + border: 1px solid var(--line); + background: var(--panel); + box-shadow: 0 12px 28px var(--shadow-strong); +} + +.backend-column-options .check { + margin-top: 4px !important; + font-size: 11px; +} + +.location-summary { + overflow-wrap: anywhere; + cursor: pointer; + padding: 10px 12px; + font: 600 11px/1.45 var(--mono); + color: var(--text); +} + +.vh-health-summary { + overflow-wrap: anywhere; + grid-column: 1/-1; + color: var(--muted); + font: 10px var(--mono); +} + +.vh-live-summary { + overflow-wrap: anywhere; + font: 10px var(--mono); + line-height: 1.7; + color: var(--muted); + margin: 5px 0; +} + +.app-startup-detail, +#panel-overview .stats-grid .stat>small, +.overview-inline-hidden-item>span:not(.overview-widget-type), +#panel-overview .www-method-legend, +.vhost-layout .entity-details, +.vh-backend-endpoint, +.acl-policy-condition span, +.listener-active-address, +.acl-profile-attached p, +.stream-backend-status-row>span:first-child, +.docker-agent-security .fine, +.docker-runtime .fine, +#panel-tls .certificate-operation-heading strong, +#panel-tls .renewal-entry-identity strong, +.certificate-api-credential-info code, +.certificate-api-credential-info small, +.certificate-bulk-list .check { + overflow-wrap: anywhere; +} + +.vh-form input:disabled { + opacity: .5; +} + +.edit-form input:not([type=checkbox]):not([type=radio]), +.edit-form select, +.edit-form textarea { + padding: var(--ui-field-pad-y) var(--ui-field-pad-x); + font-size: var(--ui-field-font-size); + min-height: var(--ui-field-min-height); +} + +.vhost-dialog::backdrop, +.audit-dialog::backdrop, +.logs-dialog::backdrop { + background: var(--shadow-strong); + backdrop-filter: blur(2px); +} + +.vh-form>.card-heading { + margin-bottom: 5px; + position: sticky; + top: -14px; + z-index: 3; + background: var(--panel); + padding: 9px 0; +} + +.vh-form>.card-heading h2 { + flex: 1; +} + +.vh-tabs { + display: flex; + flex-direction: row; + margin-inline-start: 0; + gap: 3px; + position: sticky; + top: 29px; + z-index: 2; + background: var(--panel); + padding: 5px 0 8px; + border-bottom: 1px solid var(--line); + margin-bottom: 9px; + flex-wrap: nowrap; + overflow-x: auto; + overscroll-behavior-x: contain; + scrollbar-width: thin; +} + +.vh-tabs button { + width: auto; + flex: 0 1 auto; + font-size: 10px; + padding: 6px 9px; + white-space: nowrap; +} + +.vh-tabs button.active { + color: var(--text); + background: var(--nav-selected-bg); + border-color: var(--green); +} + +.vh-pages-grid, +.vhost-detail span, +.vhost-backend-popup-field>span, +.cache-varnish-grace-field label { + display: block; +} + +.vh-form .form-actions { + position: sticky; + bottom: -14px; + background: var(--panel); + padding: 10px 0; + border-top: 1px solid var(--line); + z-index: 2; +} + +.vh-header-row { + display: grid; + grid-template-columns: 88px minmax(110px, 1fr) minmax(140px, 2fr) auto; + gap: 5px; + align-items: end; + margin: 6px 0; +} + +.vh-header-rows+h3, +.certificate-card>.entity-actions, +.docker-agent-grid, +#panel-cache>article.card+article.card { + margin-top: 10px; +} + +.named-listener-row .vh-grid, +.transform-diagnostics { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.listener-choices { + flex-wrap: wrap; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 8px; +} + +.listener-choices>.fine { + flex-basis: 100%; + grid-column: 1/-1; + margin: 0 0 2px; +} + +.listener-choices>label { + border: 1px solid var(--line); + padding: 7px; + border-radius: var(--rp-radius-control, 4px); + font-size: 11px; + display: flex; + min-height: 44px; + align-items: center; + margin: 0; +} + +dialog.vhost-dialog, +dialog.audit-dialog, +dialog.config-history-dialog, +dialog.config-history-json-dialog, +dialog.logs-dialog { + border-radius: 0; + background: var(--bg); + color: var(--text); + border: 1px solid var(--line); + padding: 0; + box-shadow: 0 18px 70px var(--shadow-strong); +} + +.vh-location { + border: 1px solid var(--line); + background: var(--panel); + min-width: 0; +} + +.vh-locations { + display: grid; + gap: 9px; +} + +.location-actions { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 7px; + justify-content: flex-end; + padding-top: 4px; +} + +.location-header-toolbar { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 7px; + justify-content: flex-start; +} + +.location-toolbar { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 7px; + justify-content: space-between; + margin-bottom: 8px; +} + +.location-toolbar-bottom { + justify-content: flex-start; + margin: 9px 0 0; +} + +.location-body { + border-top: 1px solid var(--line); + padding: 12px; +} + +.location-section { + border: 1px solid var(--line); + min-width: 0; +} + +.location-section>summary { + cursor: pointer; + padding: 9px 10px; + color: var(--muted); + font: 600 10px var(--mono); + letter-spacing: .35px; +} + +.location-section>div { + border-top: 1px solid var(--line); + padding: 10px; +} + +.location-return-box { + padding-top: 3px; +} + +.acl-rule { + display: grid; + grid-template-columns: minmax(100px, 1fr) minmax(140px, 1fr) auto; + align-items: end; + gap: 7px; + margin-block: 5px; +} + +.location-header-editor h3 { + font-size: 12px; + margin: 8px 0; +} + +.location-header-editor .vh-header-row { + grid-template-columns: 100px minmax(120px, 1fr) minmax(120px, 1.3fr) auto; + display: block; + border: 1px solid var(--line); + background: var(--panel); + padding: 9px; +} + +.location-header-direction { + border: 1px solid var(--line); + background: var(--panel2); + padding: 10px; + min-width: 0; +} + +.location-header-direction-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + margin-bottom: 8px; +} + +p, +.php-origin-errors .fine, +.overview-presets-content>p, +.location-header-direction-head h3, +.redirect-listeners .check, +.vh-backend-health-editor .fine, +.acl-policy-preview .fine, +.listener-protocol-settings-actions button, +.vh-setting-section>.fine, +.vh-setting-section>.vh-grid>.fine, +.vh-override-fields>.fine, +.acme-card label, +.docker-agent-listener-choices, +#panel-tls .certificate-section-intro h3, +#panel-tls .certificate-subheading h3, +#panel-tls .certificate-library-footer p, +.cache-operation-dialog .card, +.file-log-search-help-popover p, +.file-logs-table-toolbar .fine, +.cache-maintenance-help .subheading { + margin: 0; +} + +.location-header-direction-head p { + margin: 0; + max-width: 56ch; + text-align: right; +} + +.vh-header-empty { + margin: 8px 0; + padding: 9px 10px; + border: 1px dashed var(--line); +} + +.location-header-editor .vh-header-rows, +.security-policy-card #host-policy-list { + display: grid; + gap: 8px; +} + +.vh-header-rule-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 8px; +} + +.vh-header-rule-title { + font: 600 10px var(--mono); + color: var(--muted); + text-transform: uppercase; + letter-spacing: .45px; +} + +.vh-header-rule-fields { + display: grid; + align-items: end; + gap: 8px; + grid-template-columns: minmax(100px, .55fr) minmax(140px, 1fr) minmax(180px, 1.35fr); +} + +.vh-header-row.is-remove .vh-header-rule-fields { + grid-template-columns: minmax(100px, .55fr) minmax(140px, 1fr) minmax(180px, 1.35fr); +} + +.edit-form>.fine, +.location-add-header, +.certificate-state-pills, +.cache-varnish-info .metric-info-popover .fine+.fine, +.cache-maintenance-info .metric-info-popover code+.fine { + margin-top: 8px; +} + +.location-preview { + border: 1px solid var(--line); + background: var(--panel2); + padding: 11px 12px; +} + +.location-preview h4, +.config-history-month h4 { + margin: 0 0 8px; + font-size: 12px; +} + +.location-preview-disabled { + margin: 0 0 8px; + color: var(--red); +} + +.location-preview-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 7px 14px; + margin: 0; +} + +.location-preview-grid dt { + color: var(--muted); + font: 10px var(--mono); + margin-bottom: 2px; +} + +.location-preview-grid dd { + margin: 0; + font-size: 11px; + overflow-wrap: anywhere; +} + +.location-doc-intro { + padding: 10px 12px; + border: 1px solid var(--line); + background: var(--panel2); +} + +.location-doc-section { + border-top: 1px solid var(--line); + padding-top: 10px; +} + +.location-doc-section h3 { + margin: 0 0 7px; + font-size: 12px; +} + +.location-doc-section p+p { + margin-top: 7px !important; +} + +.location-doc-examples p { + font-family: var(--mono); + font-size: 10px; + padding: 8px 9px; + background: var(--panel2); + border-left: 2px solid var(--accent); +} + +.vhost-list-tools { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; + margin-left: auto; + --toolbar-control-height: min(var(--button-min-height), var(--ui-field-min-height)); +} + +.vhost-card { + border: 1px solid var(--line); + background: var(--panel); + padding: 14px; + min-width: 0; +} + +.vhost-card-head { + display: flex; + justify-content: space-between; + gap: 14px; + align-items: flex-start; +} + +.vhost-policy-pills { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; + margin-top: 8px; +} + +.vhost-title-line { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; +} + +.vhost-hosts { + margin-top: 4px; + font: 11px/1.45 var(--mono); + color: var(--muted); + overflow-wrap: anywhere; +} + +.vhost-detail-strip { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 1px; + margin-top: 12px; + border: 1px solid var(--line); +} + +.vhost-detail { + min-width: 0; + padding: 9px 10px; + background: var(--bg); +} + +.vhost-detail b { + display: block; + margin-top: 3px; + font-size: 11px; + font-weight: 600; + overflow-wrap: anywhere; +} + +.vhost-live-grid { + display: grid; + grid-template-columns: repeat(6, minmax(0, 1fr)); + gap: 8px; + margin-top: 10px; +} + +.vhost-live-metric { + min-width: 0; + padding: 9px 10px; + border: 1px solid var(--line); +} + +.vhost-live-metric span { + display: block; + color: var(--muted); + font-size: 10px; + text-transform: uppercase; + letter-spacing: .04em; +} + +.vhost-live-metric strong { + display: block; + margin-top: 4px; + font: 600 13px var(--mono); + overflow-wrap: anywhere; +} + +.vhost-card .vh-health-summary { + margin: 9px 0 0; + grid-column: auto; +} + +.acl-profile-layout { + display: grid; + gap: 14px; + align-items: start; + grid-template-columns: minmax(260px, .72fr) minmax(440px, 1.28fr); +} + +.host-chip-field>span:first-child { + display: block; + color: var(--muted); + font-size: 11px; + margin-bottom: 7px; +} + +.host-chip-editor { + min-height: 43px; + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 6px; + padding: 6px; + border: 1px solid var(--field-line); + background: var(--field-bg); +} + +.host-chip-editor input { + flex: 1 1 210px; + width: auto; + min-width: 180px; + border: 0; + outline: 0; + padding: 5px 6px; + background: transparent; +} + +.host-chip { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 4px 5px 4px 8px; + border: 1px solid var(--line); + background: var(--panel); + font: 10px var(--mono); +} + +.host-chip button { + border: 0; + background: transparent; + color: var(--muted); + padding: 0 3px; + font-size: 15px; + line-height: 1; +} + +.vh-www-panel { + padding: 12px; + border: 1px solid var(--line); + background: var(--panel2); + min-width: 0; +} + +.vh-www-panel.is-enabled, +.vh-www-choice.is-selected { + border-color: var(--green); + box-shadow: inset 3px 0 0 var(--green); +} + +.vh-www-header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 8px 18px; +} + +.vh-www-title { + font-size: 12px; + color: var(--text); +} + +.vh-www-host-count { + padding: 3px 7px; + border: 1px solid var(--line); + color: var(--muted); + font: 10px var(--mono); +} + +.vh-www-header .vh-www-toggle { + margin: 0; + min-height: 0; + display: flex; + align-items: center; + color: var(--text); + font-size: 11px; +} + +.vh-www-help, +.vh-www-inactive { + margin: 9px 0 0; + font-size: 11px; +} + +.vh-www-details, +.certificate-bulk-assign { + margin-top: 12px; + border-top: 1px solid var(--line); + padding-top: 12px; +} + +.vh-www-option-title { + font-size: 11px; + font-weight: 600; + margin: 0 0 8px; + color: var(--text); +} + +.vh-www-domain-pair { + padding: 9px; + border: 1px solid var(--line); + background: var(--panel); +} + +.vh-www-domain-label { + display: block; + font: 11px var(--mono); + color: var(--text); + margin-bottom: 8px; + overflow-wrap: anywhere; +} + +.vh-www-pair-directions { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; +} + +.vh-www-choice { + min-width: 0; + display: flex; + align-items: flex-start; + gap: 10px; + padding: 11px; + border: 1px solid var(--line); + background: var(--field-bg); + cursor: pointer; +} + +.vh-www-choice:focus-within, +.vh-www-choice:hover, +.log-column-picker[open]>summary { + border-color: var(--green); +} + +.vh-www-choice input { + margin: 3px 0 0; + accent-color: var(--green); + flex: 0 0 auto; +} + +.vh-www-choice-copy { + display: flex; + flex-direction: column; + gap: 5px; + min-width: 0; +} + +.vh-www-choice-copy strong { + font-size: 12px; + overflow-wrap: anywhere; + color: var(--text); +} + +.vh-www-route { + color: var(--muted); + font: 11px var(--mono); + overflow-wrap: anywhere; +} + +.vh-www-no-hosts { + margin: 6px 0; + font-size: 11px; +} + +.vh-www-other { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 6px; + margin-top: 12px; + font-size: 11px; + color: var(--muted); +} + +.vh-www-alias { + font: 11px var(--mono); + padding: 4px 6px; + background: var(--panel); + border: 1px solid var(--line); +} + +.acl-profile-rule-rows:empty:before { + content: "—"; + display: block; + color: var(--muted); + padding: 8px 0; +} + +.acl-profile-rule { + padding: 6px 0; + border-bottom: 1px solid var(--line); + grid-template-columns: minmax(100px, .6fr) minmax(105px, .6fr) minmax(175px, 1.5fr) auto; +} + +.acl-profile-rule label>span { + display: block; + color: var(--muted); + font-size: 10px; + margin-bottom: 4px; +} + +.acl-profile-bulk { + margin-top: 9px; + border-top: 1px solid var(--line); + padding-top: 8px; +} + +.acl-profile-bulk>summary, +.file-log-raw-json>summary { + cursor: pointer; + color: var(--muted); +} + +.stream-page-actions { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px 12px; + margin: 0 0 10px; +} + +.stream-card .vhost-hosts { + text-transform: none; +} + +.stream-live-grid { + grid-template-columns: repeat(6, minmax(0, 1fr)); +} + +.listener-address-chip-editor { + min-height: 52px; +} + +.redirect-listeners { + display: grid; + gap: .55rem; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); +} + +.redirect-rules, +.stream-backend-list { + display: grid; + gap: 1rem; +} + +.redirect-rule { + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 12px); + padding: 1rem; + margin: 0; + min-width: 0; +} + +.redirect-card .vhost-detail-strip { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.frontend-protocol-controls { + margin-block-start: 16px; + padding-block-start: 12px; + border-top: 1px solid var(--line); +} + +.frontend-protocol-controls>.small-label { + display: block; + margin-block-end: 10px; +} + +.protocol-toggle-row { + display: flex; + flex-wrap: wrap; + gap: 8px 18px; + align-items: center; + min-width: 0; +} + +.protocol-toggle-row .check { + margin: 0; + min-width: min(280px, 100%); + margin-top: 0 !important; +} + +.protocol-toggle-row .vh-check { + margin: 0; + min-width: min(280px, 100%); +} + +.vh-backend-card { + padding: 0; + overflow: hidden; + background: var(--panel); +} + +.vh-backend-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 10px 12px; + border-bottom: 1px solid var(--line); +} + +.vh-backend-identity, +.docker-service-meta { + display: grid; + gap: 2px; + min-width: 0; +} + +html[data-dashboard-density=ultra] #panel-overview .dashboard-overview-grid>.card .card-heading h2, +.vh-backend-title { + font-size: 13px; +} + +.vh-backend-main, +#panel-tls .certificate-renewal-card { + padding: 12px; +} + +.vh-backend-switches { + display: flex; + flex-wrap: wrap; + gap: 6px 16px; + padding-top: 2px; +} + +.vh-backend-switches .vh-check { + margin: 0; + min-height: 26px; +} + +.vh-backend-connection { + padding-top: 8px; + border-top: 1px dashed var(--line); +} + +.vh-backend-advanced { + margin: 0 12px 10px; + padding-top: 8px; + border-top: 1px solid var(--line); +} + +.vh-backend-health-editor { + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 8px); + padding: 15px; + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 11px; + background: var(--surface, transparent); +} + +.vh-backend-health-editor h3 { + margin: 0; + font-size: 14px; +} + +.vh-backend-health-editor>.vh-field { + max-width: 620px; +} + +#redirect-listeners>.listener-choices, +.vh-backend-health-editor select, +.appearance-scale-control input[type=range], +.logging-builder-action button { + width: 100%; +} + +.vh-backend-health-panel { + border-top: 1px solid var(--line); + padding-top: 12px; +} + +.vh-backend-actions { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px 12px; + padding: 0 12px 12px; +} + +.vh-backend-actions .fine { + flex: 1 1 320px; + margin: 0; +} + +.vh-local-risk-gate { + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 5px); + padding: 8px; +} + +.vh-local-risk-gate .fine, +#panel-tls .certificate-section-intro p, +#panel-tls .certificate-subheading p, +#panel-tls .certificate-workspace-heading p { + margin: 5px 0 0; +} + +#web-service-dialog .web-service-page, +#panel-tls .renewal-message { + padding-top: 2px; +} + +.backend-sort-direction { + min-width: 36px; + font-size: 16px; + line-height: 1; +} + +.backend-table-tools input, +.backend-table-tools select, +.vhost-list-tools input[type=search] { + box-sizing: border-box; + height: var(--toolbar-control-height); + min-height: var(--toolbar-control-height); + padding-block: 0; +} + +.acl-policy-builder { + margin-top: 12px; + padding: 12px; + border: 1px solid var(--line); + background: var(--bg); +} + +.acl-policy-preview { + margin-top: 12px; + padding: 12px; + border: 1px solid var(--line); + background: var(--bg); + display: grid; + gap: 9px; +} + +.acl-policy-preview-head { + display: flex; + justify-content: space-between; + gap: 10px; + align-items: center; +} + +.acl-policy-step-head, +.certificate-card-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; +} + +.acl-policy-step-head>div { + display: flex; + align-items: flex-start; + gap: 10px; + justify-content: flex-start; +} + +.acl-policy-preview-head>span, +.acl-policy-step-head strong { + display: block; + font-size: 12px; + color: var(--text); +} + +.acl-policy-step-head p, +.config-history-detail-head h3 { + margin: 3px 0 0; +} + +.acl-policy-step { + display: inline-grid; + place-items: center; + flex: 0 0 24px; + width: 24px; + height: 24px; + border: 1px solid var(--line); + background: var(--panel); + color: var(--muted); + font: 11px var(--mono); +} + +.acl-policy-modes { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; + margin-top: 10px; +} + +.acl-policy-mode-card { + position: relative; + display: flex !important; + align-items: flex-start; + gap: 9px; + margin: 0 !important; + padding: 10px; + border: 1px solid var(--field-line); + background: var(--field-bg); + cursor: pointer; +} + +.acl-policy-mode-card span { + display: grid; + gap: 3px; +} + +.acl-policy-mode-card b { + color: var(--text); + font-size: 12px; +} + +.acl-policy-mode-card small { + color: var(--muted); + line-height: 1.45; +} + +.acl-policy-custom-controls { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0 10px; + margin-top: 6px; + padding-top: 8px; + border-top: 1px solid var(--line); +} + +.acl-policy-switch { + display: inline-flex !important; + align-items: center; + gap: 7px; + margin: 0 !important; + white-space: nowrap; +} + +.acl-policy-country-list.is-disabled { + opacity: .55; +} + +.acl-profile-rule-action-badge { + display: flex; + align-items: center; + min-height: var(--ui-field-min-height); + padding: 7px 9px; + border: 1px solid var(--line); + background: var(--panel); + font-size: 11px; + font-weight: 700; +} + +.acl-policy-preview-head b { + padding: 4px 7px; + border: 1px solid var(--line); + background: var(--panel); + font: 700 10px var(--mono); +} + +.acl-policy-preview-flow { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 6px; +} + +.acl-policy-condition { + display: inline-flex; + flex-wrap: wrap; + gap: 5px; + align-items: center; + max-width: 100%; + padding: 7px 9px; + border: 1px solid var(--line); + background: var(--panel); + font-size: 11px; +} + +.acl-policy-condition.allow { + border-left: 3px solid var(--green); +} + +.acl-policy-condition.deny, +#panel-tls .renewal-entry[data-state=failed] { + border-left: 3px solid var(--red); +} + +.acl-policy-or { + color: var(--muted); + font: 700 10px var(--mono); +} + +.acl-profile-card .entity-details { + white-space: pre-line; +} + +.listener-active-summary { + margin-top: 10px; + padding: 10px 12px; + border: 1px solid var(--line); + background: var(--panel2); + display: grid; + gap: 8px; +} + +.listener-active-title { + font-size: 10px; + color: var(--muted); + text-transform: uppercase; + letter-spacing: .45px; +} + +.listener-active-item { + display: inline-flex; + align-items: center; + gap: 7px; + min-width: 0; + padding: 7px 9px; + border: 1px solid var(--line); + background: var(--panel); +} + +.listener-active-protocol { + font: 600 10px var(--mono); + color: var(--accent); +} + +.listener-active-feature { + padding: 2px 5px; + border: 1px solid var(--line); + font: 600 9px var(--mono); + color: var(--muted); +} + +.location-header-editor .vh-header-rule-head>button { + margin-left: auto; + flex: 0 0 auto; +} + +.listener-protocol-settings-actions { + display: flex; + gap: 14px; + flex-wrap: wrap; + align-items: center; + margin-block: 10px; +} + +.acl-profile-attached { + margin-block: 6px; +} + +.acl-profile-attached summary { + cursor: pointer; + color: var(--muted); + font-size: 12px; +} + +.stream-backend-pool { + display: grid; + gap: .8rem; + min-width: 0; +} + +.stream-backend-row { + min-width: 0; + border: 1px solid var(--line, #454d59); + border-radius: var(--rp-radius-surface, .65rem); + padding: .85rem; +} + +.stream-backend-row>.quiet { + margin-top: .6rem; +} + +.stream-backend-status { + display: grid; + gap: .4rem; + padding: .75rem 0; +} + +.stream-backend-status-row { + display: flex; + flex-wrap: wrap; + gap: .5rem; + align-items: center; + justify-content: space-between; + font-size: .85em; +} + +.vh-enable-control.vh-enable-no-hint { + grid-template-rows: auto; +} + +.vh-enable-control.vh-enable-no-hint>:is(input, .vh-enable-state) { + grid-row: 1; +} + +.vhost-backend-protocol-badge { + display: inline-flex; + gap: 4px; + align-items: baseline; + max-width: 100%; + min-width: 0; + border: 1px solid var(--pill-line); + border-radius: var(--rp-radius-control, 4px); + background: var(--panel2); + color: var(--muted); + padding: 3px 7px; + font: 10px var(--mono); + overflow-wrap: anywhere; + text-align: left; +} + +html.app-loading, +.vhost-backend-protocol-badge:is(:hover, [aria-expanded=true]) { + background: var(--bg); +} + +.vhost-backend-protocol-badge[data-observed=true] { + color: var(--green); + border-color: var(--pill-live-line); +} + +.vhost-detail.vhost-backend-trigger { + appearance: none; + display: block; + width: 100%; + min-width: 0; + margin: 0; + border-radius: 0; + color: var(--text); + text-align: left; + font: inherit; + cursor: pointer; + border: 0; + background: var(--bg); +} + +.vhost-live-metric.vhost-backend-trigger { + appearance: none; + display: block; + width: 100%; + min-width: 0; + margin: 0; + border-radius: 0; + color: var(--text); + text-align: left; + font: inherit; + cursor: pointer; + border: 1px solid var(--line); + background: transparent; +} + +.dashboard-widget-toggle:hover, +.vhost-backend-trigger:is(:hover, [aria-expanded=true]), +.vhost-backend-popup-close:hover { + background: var(--panel2); +} + +.vhost-backend-popup { + position: fixed; + z-index: 1600; + max-height: calc(min(360px, 100dvh - 24px)); + overflow: auto; + padding: 12px; + background: var(--panel); + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 0); + box-shadow: 0 12px 35px var(--shadow-strong); + color: var(--text); + font-size: 11px; +} + +.vhost-backend-popup-heading { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; + margin: 0 0 10px; +} + +.vhost-backend-popup h4 { + margin: 0; + font-size: 12px; + font-weight: 650; + overflow-wrap: anywhere; +} + +.vhost-backend-popup-close { + flex: 0 0 auto; + display: grid; + place-items: center; + width: 25px; + height: 25px; + padding: 0; + border: 1px solid var(--line); + background: var(--bg); + color: var(--text); + cursor: pointer; + font: 18px/1 var(--sans); + border-radius: var(--rp-radius-control, 0); +} + +.vhost-backend-popup-list { + display: grid; + gap: 6px; + padding: 0; + margin: 0; + list-style: none; +} + +.vhost-backend-popup-row { + padding: 8px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 0); + background: var(--bg); + min-width: 0; +} + +.vhost-backend-popup-name { + display: block; + margin-bottom: 6px; + overflow-wrap: anywhere; + font: 650 11px var(--mono); +} + +.vhost-backend-popup-columns { + display: grid; + grid-template-columns: minmax(0, 1.75fr) minmax(0, 1fr); + gap: 6px 10px; +} + +.vhost-backend-popup-field>span:first-child { + font-size: 9px; + color: var(--muted); +} + +.vhost-backend-popup-field>span:last-child { + font: 10px/1.35 var(--mono); + overflow-wrap: anywhere; +} + +.vhost-backend-popup-hint { + margin: 10px 0 0; + line-height: 1.4; +} + +.vh-acl-profile-picker>strong { + display: block; + margin-bottom: 8px; +} + +.acl-picker-details { + border: 1px solid var(--field-line); + background: var(--field-bg); + border-radius: var(--rp-radius-control, 8px); + min-width: 0; +} + +.acl-picker-details[open], +.acl-picker-option:has(input:focus-visible), +.acl-picker-option:hover { + border-color: var(--border-hover); +} + +.acl-picker-summary { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 12px; + font-size: 12px; + cursor: pointer; + user-select: none; + min-height: 21px; +} + +.acl-picker-heading { + flex: 1 1 auto; + font-weight: 600; + min-width: 0; +} + +.acl-picker-counter { + color: var(--accent); + border: 1px solid var(--pill-line); + border-radius: var(--rp-radius-control, 12px); + padding: 2px 7px; + font: 11px var(--mono); + margin-left: auto; +} + +.acl-picker-dropdown { + padding: 10px; + border-top: 1px solid var(--line); +} + +.acl-picker-search, +.docker-agent-grid .acl-picker-search { + width: 100%; + box-sizing: border-box; + min-height: 34px; + height: 34px; + margin: 0 0 9px; +} + +.acl-picker-options { + display: grid; + max-height: 270px; + overflow-y: auto; + gap: 5px; +} + +.acl-picker-option { + display: flex; + align-items: flex-start; + gap: 10px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-control, 6px); + background: var(--panel); + padding: 9px 10px; + cursor: pointer; + min-width: 0; +} + +.acl-picker-option:has(input:checked) { + border-color: var(--pill-live-line); + background: var(--nav-selected-bg); +} + +.acl-picker-option input[type=checkbox] { + flex: 0 0 auto; + width: 16px !important; + height: 16px !important; + min-height: 16px !important; + margin: 2px 0 0; +} + +.acl-picker-option input:disabled { + opacity: .35; +} + +.acl-picker-option:has(input:disabled) { + cursor: not-allowed; + opacity: .6; +} + +.acl-picker-option-content { + display: grid; + flex: 1; + min-width: 0; + gap: 5px; +} + +.acl-picker-option-heading { + display: flex; + flex-wrap: wrap; + gap: 5px 9px; + align-items: center; +} + +.acl-picker-option-heading strong { + font-size: 12px; + color: var(--text); + overflow-wrap: anywhere; +} + +.acl-picker-id { + font: 10px var(--mono); + color: var(--muted); + overflow-wrap: anywhere; +} + +.acl-picker-policy { + font: 10px var(--mono); + line-height: 1.4; + border-radius: var(--rp-radius-control, 12px); + padding: 2px 7px; + border: 1px solid var(--line); + color: var(--muted); + white-space: nowrap; +} + +.acl-picker-rule-preview { + display: block; + line-height: 1.45; + overflow-wrap: anywhere; + color: var(--muted); + font-size: 11px; +} + +.acl-picker-selected { + display: flex; + gap: 6px; + flex-wrap: wrap; + margin: 8px 0 0; + min-width: 0; +} + +.acl-picker-tag { + display: inline-flex; + align-items: center; + max-width: 100%; + gap: 7px; + padding: 2px 4px 2px 9px; + border-radius: var(--rp-radius-control, 15px); + border: 1px solid var(--pill-live-line); + background: var(--nav-selected-bg); + font-size: 11px; +} + +.acl-picker-tag>span { + overflow-wrap: anywhere; + min-width: 0; +} + +.acl-picker-tag .acl-picker-remove { + border: none; + border-radius: var(--rp-radius-control, 50%); + background: transparent; + color: var(--muted); + min-width: 20px; + height: 20px; + font-size: 16px; + padding: 0; + cursor: pointer; +} + +.acl-picker-tag .acl-picker-remove:hover { + background: var(--panel2); + color: var(--text); +} + +.acl-picker-limit { + display: block; + padding-top: 7px; +} + +.acl-stack-warning { + margin: 8px 0 0; + font-size: 11px; + line-height: 1.5; + color: var(--notice-text); +} + +.vh-setting-section { + display: grid; + gap: 10px; + min-width: 0; + box-sizing: border-box; + padding: 13px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 6px); + background: var(--panel2); +} + +.vh-setting-section>h3 { + font-size: 12px; + color: var(--text); + margin: 0; + padding-bottom: 8px; + border-bottom: 1px solid var(--line); +} + +.vh-setting-section>.vh-grid, +.stream-backend-section>.vh-grid, +.web-service-setting-section>.vh-grid { + gap: 10px; + min-width: 0; +} + +.vh-setting-section>.vh-grid>.vh-wide { + min-width: 0; + margin-top: 0; +} + +.vh-certificate-select-row { + display: flex; + align-items: flex-end; + flex-wrap: wrap; + gap: 8px 12px; +} + +.vh-certificate-select-row>.vh-field { + flex: 1 1 240px; +} + +.vh-certificate-select-row>button { + flex: 0 0 auto; + min-height: 32px; +} + +.vh-setting-section>.vh-grid>.vh-check { + align-self: center; +} + +.vh-country-rates .vh-row>button, +.vh-ua-rates .vh-row>button { + align-self: end; + justify-self: start; +} + +.editor-identity-section .check, +.web-service-setting-section .check { + margin-top: 0 !important; + align-self: center; +} + +.editor-frontend-certificate { + min-width: 0; + gap: 10px; + margin-bottom: 12px; +} + +.editor-identity-section>.vh-grid { + min-width: 0; + gap: 10px; +} + +.stream-backend-row>.vh-grid { + gap: 12px; +} + +.vh-form :is(input, select, textarea):disabled { + opacity: .55; + cursor: not-allowed; +} + +.vh-override-fields { + display: grid; + grid-template-columns: minmax(0, 1fr); + align-items: start; + gap: 8px; + padding-top: 12px; + border-top: 1px solid var(--line); + min-width: 0; +} + +.vh-override-fields>.vh-check, +.vh-override-fields>.vh-field { + min-width: 0; + width: 100%; + margin: 0 !important; +} + +.web-service-preset-toggles { + display: grid; + align-content: start; + gap: 8px; + min-width: 0; +} + +.web-service-preset-toggles>.check { + margin: 0 !important; + min-width: 0; +} diff --git a/src/admin/web/css/50-settings.css b/src/admin/web/css/50-settings.css new file mode 100644 index 0000000..c294748 --- /dev/null +++ b/src/admin/web/css/50-settings.css @@ -0,0 +1,1186 @@ +/* Settings, certificates, integrations and Docker agent/service management. + * Order is defined in manifest.json; responsive rules are centralized in 70-responsive.css. + */ + +.settings-tabs button.has-unsaved:after { + content: ""; + display: inline-block; + width: 6px; + height: 6px; + margin-left: 7px; + border-radius: 50%; + background: currentColor; + vertical-align: middle; + opacity: .78; +} + +.session-user { + margin: 17px 0 12px; +} + +.security-note { + margin: 22px 0; +} + +.settings-shortcuts { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 12px; + border: 1px solid var(--line); + flex-wrap: wrap; + margin-bottom: 10px; +} + +.settings-shortcuts>div { + flex: 1; + min-width: 170px; +} + +.acme-card { + margin-bottom: 14px; +} + +.acme-card .vh-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; +} + +.settings-tabs { + padding: 8px; + margin-bottom: 12px; + border: 1px solid var(--line); + background: var(--panel); + border-radius: 0; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); + width: 100%; + gap: 5px; +} + +.settings-tabs button { + padding: 8px 10px; + font-size: 12px; + border: 1px solid transparent; + background: transparent; + border-radius: 0; + width: 100%; + min-width: 0; + white-space: normal; + text-align: center; +} + +.settings-tabs button[aria-selected=true] { + background: var(--accent); + color: var(--selected-ink); + border-color: var(--accent); + font-weight: 700; +} + +.settings-pages { + min-width: 0; + width: 100%; + max-width: none; +} + +.settings-page>.edit-form+.edit-form { + margin-top: var(--ui-gap); +} + +.settings-page>.card { + padding: var(--ui-card); + border-radius: 0; +} + +.provider-hint { + padding: 0 18px; +} + + + +.renewal-entry { + display: grid; + grid-template-columns: 1fr 2fr auto; + gap: 8px; + padding: 9px 0; + border-bottom: 1px solid var(--line); + align-items: center; +} + +body[data-density=compact] .sidebar nav, +.certificate-coverage { + margin-top: 14px; +} + +.certificate-coverage>.small-label { + display: block; + margin-bottom: 7px; +} + +.certificate-domain-pills .pill { + max-width: 100%; + overflow-wrap: anywhere; +} + +.certificate-coverage-unknown { + color: var(--muted) !important; + font: 10px/1.5 var(--mono) !important; +} + +.certificate-details { + margin-top: 12px; + border-top: 1px solid var(--line); + border-bottom: 1px solid var(--line); +} + +.certificate-details summary { + cursor: pointer; + padding: 9px 0; + color: var(--muted); + font: 10px var(--mono); + letter-spacing: .7px; +} + +.certificate-details-body { + padding: 0 0 8px; +} + +.certificate-details .entity-details { + margin: 4px 0; +} + +.docker-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 16px; + --toolbar-control-height: min(var(--button-min-height), var(--ui-field-min-height)); +} + +.docker-agent-observations, +.docker-agent-settings-list { + display: grid; + gap: 16px; +} + +.docker-agent-status-badge { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 88px; + text-align: center; + min-height: 32px; + box-sizing: border-box; +} + +.docker-agent-summary { + display: flex; + flex-wrap: wrap; + gap: 7px 18px; + margin: 8px 0 16px; + padding: 10px 12px; + border: 1px solid var(--line); + background: var(--panel2); + font-size: 12px; + color: var(--muted); +} + +.docker-agent-security { + display: flex; + align-items: center; + gap: 10px; + padding: 9px 11px; + margin: 4px 0 10px; + border-left: 2px solid var(--line); + background: var(--panel2); + min-width: 0; +} + +.docker-agent-error { + display: grid; + gap: 5px; + padding: 12px; + border: 1px solid var(--danger-line); + background: rgba(255, 170, 162, .04); +} + +.docker-service-row { + display: grid; + border: 1px solid var(--line); + background: var(--panel2); + min-width: 0; + gap: 8px; + padding: 10px 12px; +} + +.docker-service-row.is-unpublished { + opacity: .72; +} + +.docker-service-row.is-view-hidden { + border-style: dashed; + opacity: .58; +} + +.docker-service-head { + display: grid; + align-items: center; + min-width: 0; + grid-template-columns: auto minmax(260px, 1fr) minmax(300px, 1.25fr); + gap: 12px; +} + +.docker-service-meta span { + color: var(--muted); + font-size: 12px; + overflow-wrap: anywhere; +} + +.docker-runtime { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 10px; + min-width: 0; + text-align: right; +} + +.docker-route-grid { + display: grid; + gap: 10px; + align-items: start; + grid-template-columns: minmax(190px, 1fr) minmax(250px, 1.35fr) minmax(210px, 1fr) minmax(120px, .65fr); +} + +.docker-health-grid>label, +.docker-route-grid>label:not(.check), +.docker-acl-grid>label:not(.check), +.docker-timeout-grid>label { + display: grid; + gap: 5px; + min-width: 0; +} + +.stat>span, +.appearance-chart-preview>span, +.docker-health-grid>label>span, +.docker-route-grid>label:not(.check)>span { + color: var(--muted); + font-size: 11px; +} + +.docker-health-editor, +.docker-acl-editor, +.docker-protocol-editor, +.docker-timeout-editor { + border-top: 1px solid var(--line); + padding-top: 9px; +} + +.docker-health-editor>summary, +.docker-acl-editor>summary, +.docker-protocol-editor>summary, +.docker-timeout-editor>summary { + cursor: pointer; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + user-select: none; +} + +.docker-health-grid { + display: grid; + grid-template-columns: repeat(5, minmax(120px, 1fr)); + gap: 10px; + margin-top: 12px; +} + +.docker-passive-health-editor .docker-health-grid { + grid-template-columns: repeat(4, minmax(180px, 1fr)); + align-items: start; +} + +.docker-passive-health-editor .docker-checkbox-field .vh-check { + width: max-content; + max-width: 100%; +} + +.docker-passive-health-editor .docker-checkbox-field input[type=checkbox] { + width: auto; +} + +.docker-passive-health-editor .docker-passive-5xx-warning, +.docker-passive-health-editor .docker-passive-slow-warning { + grid-column: span 2; + margin: 0; + min-width: 0; +} + +.docker-agent-listener-field { + grid-column: 1/-1; + display: grid; + gap: 6px; + min-width: 0; + margin-top: 2px; + padding-top: 4px; + border-top: 1px solid var(--line); +} + +.docker-agent-field-label { + font-size: 11px; + color: var(--muted); + min-height: 14px; + line-height: 14px; +} + +.docker-agent-listener-choices>label { + min-height: 38px; +} + +.docker-agent-grid .mono, +.docker-agent-summary .mono { + font-family: var(--mono); + font-size: 11px; +} + +.docker-toolbar input[type=search] { + width: calc(min(280px, 38vw)); + box-sizing: border-box; + height: var(--toolbar-control-height); + min-height: var(--toolbar-control-height); + padding-block: 0; +} + +#panel-settings .settings-shortcuts { + grid-column: 1/-1; + width: 100%; +} + +#panel-settings .settings-filter-row { + grid-column: 1/-1; + display: flex; + align-items: end; + justify-content: space-between; + gap: 12px; + width: 100%; + padding: 8px 10px; + border: 1px solid var(--line); + background: var(--panel); +} + +#panel-settings .settings-filter { + display: grid; + gap: 5px; + width: min(520px, 100%); + --toolbar-control-height: min(var(--button-min-height), var(--ui-field-min-height)); +} + +#panel-settings .settings-filter input, +.file-logs-filter input, +.file-logs-filter select { + width: 100%; + box-sizing: border-box; + height: var(--toolbar-control-height); + min-height: var(--toolbar-control-height); + padding-block: 0; +} + +#panel-settings .settings-tabs { + display: flex; + flex-direction: column; + position: sticky; + top: 12px; + margin: 0; + gap: 2px; +} + +#panel-settings .settings-tabs button { + width: 100%; + text-align: left; + white-space: normal; +} + +#panel-settings .settings-tabs.sub-tabs { + position: static; + top: auto; +} + +.docker-live-switch { + align-self: center; + white-space: nowrap; +} + +.docker-service-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + padding-top: 10px; + border-top: 1px solid var(--line); +} + +.docker-acl-grid { + display: grid; + grid-template-columns: repeat(4, minmax(140px, 1fr)); + gap: 10px; + margin-top: 12px; + align-items: start; +} + +.docker-timeout-grid { + display: grid; + grid-template-columns: repeat(4, minmax(140px, 1fr)); + gap: 10px; + margin-top: 12px; +} + +.docker-acl-grid textarea { + width: 100%; + min-height: 86px; + font-family: var(--mono); + align-self: start; + box-sizing: border-box; +} + +.security-page-heading { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 18px; + margin-bottom: 14px; +} + +.security-page-heading h2 { + margin: 3px 0 4px; +} + +.security-page-heading p { + margin: 0; + max-width: 760px; +} + +.security-stack, +.file-logs-workspace { + display: grid; + gap: 14px; +} + +.security-account-card .session-user { + margin: 4px 0 0; +} + +.security-account-card .security-note { + margin: 10px 0 14px; +} + +.security-editor { + border-top: 1px solid var(--line); + margin-top: 14px; + padding-top: 10px; +} + +.security-editor>summary { + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; + gap: 14px; + user-select: none; +} + +.security-editor>summary>span { + font-weight: 700; +} + +.security-inline-form { + margin-top: 14px; + padding: 0; + border: 0; + background: transparent; +} + +.audit-dialog-card { + border: 0; + padding: 12px; + background: var(--bg); +} + +.audit-dialog .table-scroll { + max-height: calc(min(68dvh, 680px)); + overflow: auto; + border-top: 1px solid var(--line); +} + +.audit-dialog table, +.logs-dialog table { + font: 11px var(--mono); +} + +.audit-dialog td { + padding: 7px 8px; + max-width: 440px; + overflow: hidden; + text-overflow: ellipsis; +} + +.audit-dialog th { + padding: 7px 8px; + max-width: 440px; + overflow: hidden; + text-overflow: ellipsis; + position: sticky; + top: 0; + background: var(--panel); +} + +.docker-agent-overview { + display: flex; + flex-wrap: wrap; + gap: 7px 18px; + margin: 8px 0 10px; + color: var(--muted); + font-size: 12px; +} + +.docker-agent-details { + margin: 0 0 12px; + border-top: 1px solid var(--line); + padding-top: 8px; +} + +.docker-agent-details>summary { + cursor: pointer; + color: var(--muted); + font-size: 11px; + user-select: none; +} + +.docker-agent-details .docker-agent-summary { + margin: 9px 0 0; +} + +.docker-service-head-actions { + display: flex; + align-items: center; + justify-content: flex-end; + flex-wrap: wrap; + gap: 8px 10px; + min-width: 0; +} + +.docker-service-head-actions .docker-runtime { + flex: 1 1 230px; +} + +.docker-publish-field { + display: inline-flex; + align-items: center; + gap: 8px; + min-height: 30px; + padding: 0; + color: var(--text); + font-size: 12px; + font-weight: 500; + white-space: nowrap; + cursor: pointer; +} + +.docker-publish-field input[type=checkbox].docker-publish { + -webkit-appearance: none; + position: relative; + flex: 0 0 30px; + width: 30px; + height: 16px; + margin: 0; + padding: 0; + border: 1px solid var(--field-line); + border-radius: min(3px, var(--rp-radius-control, 3px)); + background: var(--field-bg); + cursor: pointer; + vertical-align: middle; +} + +.docker-publish-field .docker-publish::before { + content: ""; + position: absolute; + inset: 2px auto 2px 2px; + width: 10px; + background: var(--muted); + border-radius: min(1px, var(--rp-radius-control, 1px)); + transition: transform .12s ease; +} + +.docker-publish-field .docker-publish:checked { + background: var(--field-bg); + border-color: var(--accent); +} + +.docker-publish-field .docker-publish:checked::before { + transform: translateX(14px); + background: var(--accent); +} + +.docker-publish-field .docker-publish:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 3px; +} + +.docker-publish-field:has(.docker-publish:disabled) { + opacity: .55; + cursor: wait; +} + +.docker-publish-field .docker-publish:disabled { + cursor: wait; +} + +.docker-service-quick-actions { + display: inline-flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; +} + +.docker-service-editor>summary { + cursor: pointer; + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px; + user-select: none; +} + +.docker-service-editor>summary>span { + font-weight: 700; + font-size: 12px; +} + +.docker-service-editor .docker-config-hint { + text-align: right; + overflow-wrap: anywhere; +} + +.docker-service-editor[open]>summary { + margin-bottom: 12px; +} + +body[data-density=compact] .docker-service-row { + padding: 6px 8px; + gap: 4px; +} + +body[data-density=compact] .docker-service-head, +body[data-density=compact] .security-stack { + gap: 7px; +} + +body[data-density=compact] .docker-agent-overview { + gap: 4px 12px; + margin: 4px 0 6px; + font-size: 10px; +} + +body[data-density=compact] .docker-agent-details { + margin-bottom: 6px; + padding-top: 5px; +} + +body[data-density=compact] .docker-service-editor { + padding-top: 5px; +} + +body[data-density=compact] .docker-service-editor[open]>summary { + margin-bottom: 7px; +} + +body[data-density=compact] .security-editor { + margin-top: 8px; + padding-top: 6px; +} + +.docker-agent-grid>label { + display: grid; + align-content: start; + gap: 6px; + min-width: 0; + margin: 0 !important; +} + +.docker-agent-grid>label>span:first-child { + min-height: 14px; + line-height: 14px; +} + +.docker-agent-grid input:not([type=checkbox]), +.docker-agent-grid select { + width: 100%; + min-width: 0; + min-height: 38px; + margin: 0; + box-sizing: border-box; +} + +.docker-agent-grid>label>small, +.docker-alias-field>small, +.docker-checkbox-field>small { + margin: 0; + line-height: 1.35; +} + +.docker-agent-heading-actions { + display: flex; + align-items: center; + gap: 9px; +} + +.docker-agent-heading-actions .check { + margin: 0 !important; + min-height: 32px; + margin-top: 0 !important; +} + +.docker-agent-heading-actions>.quiet { + min-height: 32px; + box-sizing: border-box; +} + +.docker-field { + display: grid; + align-content: start; + gap: 5px; + min-width: 0; + margin: 0 !important; +} + +.docker-field>span:first-child { + min-height: 14px; + line-height: 14px; + color: var(--muted); + font-size: 11px; +} + +.docker-acl-grid .docker-field input:not([type=checkbox]), +.docker-acl-grid .docker-field select, +.docker-route-grid .docker-field input:not([type=checkbox]), +.docker-route-grid .docker-field select { + width: 100%; + min-width: 0; + min-height: 38px; + height: 38px; + align-self: start; + box-sizing: border-box; +} + +.docker-checkbox-field .vh-check { + min-height: 28px; + margin: 0 !important; + align-items: center; +} + +.php-origin-errors>.check, +.docker-toolbar .check, +.cache-toolbar-status .check { + margin: 0 !important; +} + +.docker-switch-row { + display: grid; + justify-content: start; + align-items: start; + gap: 8px; + min-width: 0; + grid-column: 1/-1; + grid-template-columns: repeat(auto-fit, minmax(min(155px, 100%), 1fr)); + overflow-x: visible; + padding-bottom: 0; +} + +.docker-preserve-field { + align-self: start; + padding-bottom: 0; + white-space: nowrap; +} + +.docker-acl-grid>.docker-field { + align-self: start; +} + +.vh-header-row button, +.docker-alias-field>span:first-child, +#panel-tls .renewal-entry:last-child { + margin-bottom: 0; +} + +.docker-alias-editor { + min-height: 38px; + padding: 3px 5px; + gap: 4px; + box-sizing: border-box; +} + +.docker-route-grid .docker-alias-editor input:not([type=checkbox]) { + flex: 1 1 180px; + width: auto; + min-width: 150px; + min-height: 30px; + height: 30px; + padding: 2px 4px; +} + +#panel-tls .certificate-workspace-heading { + align-items: flex-start; + margin-bottom: 12px; +} + +#panel-tls .certificate-tabs { + grid-template-columns: repeat(3, minmax(0, 1fr)); + margin-bottom: 16px; +} + +#panel-tls .certificate-section-intro { + margin: 2px 0 12px; +} + +#panel-tls .certificate-safety { + margin: 8px 0 12px; +} + +#panel-tls .certificate-library-footer { + display: grid; + gap: 8px; + margin-top: 14px; + padding-top: 12px; + border-top: 1px solid var(--line); +} + +#panel-tls .certificate-operation-card { + display: grid; + gap: 10px; + margin-bottom: 12px; + border-left: 3px solid var(--line); +} + +#panel-tls .certificate-operation-card[data-state=running] { + border-left-color: var(--accent); +} + +#panel-tls .certificate-operation-card[data-state=complete], +#panel-tls .certificate-operation-card[data-state=ready] { + border-left-color: var(--green); +} + +#panel-tls .certificate-operation-card[data-state=failed], +#panel-tls .certificate-operation-card[data-state=unavailable] { + border-left-color: var(--red); +} + +#panel-tls .certificate-operation-heading, +#panel-tls .renewal-entry-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; +} + +#panel-tls .certificate-operation-heading>div { + display: grid; + gap: 6px; + min-width: 0; +} + +#panel-tls .certificate-log-path { + margin: 0; + padding: 9px 10px; + border: 1px solid var(--line); + background: var(--bg); + font-family: var(--mono); + overflow-wrap: anywhere; +} + +#panel-tls .renewal-entry { + display: grid; + grid-template-columns: 1fr; + gap: 10px; + padding: 13px; + margin-bottom: 10px; + border: 1px solid var(--line); + background: var(--bg); + align-items: stretch; +} + +#panel-tls .renewal-entry-identity { + display: grid; + gap: 3px; + min-width: 0; +} + +#panel-tls .renewal-entry .fine { + grid-column: auto; + margin: 0; +} + +#panel-tls .renewal-meta { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; +} + +#panel-tls .renewal-meta-item { + display: grid; + gap: 5px; + padding: 9px; + border: 1px solid var(--line); +} + +#panel-tls .renewal-meta-item strong { + font-size: 12px; + font-weight: 600; + overflow-wrap: anywhere; +} + +#panel-tls .renewal-actions { + justify-content: flex-end; + padding-top: 2px; +} + +#panel-tls .certificate-generator-card>.provider-picker { + display: grid; + gap: 6px; + font-size: 12px; + font-weight: 600; +} + +#panel-tls .certificate-generator-card>.provider-hint { + padding: 0; + margin: 7px 0 12px; +} + +#panel-tls .certificate-editor-card { + max-width: none; + margin: 0; + padding: 14px 0 0; + border: 0; + border-top: 1px solid var(--line); + box-shadow: none; +} + +#panel-tls details.certificate-editor-card>summary { + padding: 0 0 12px; +} + +#panel-tls .certificate-editor-card .vh-grid { + gap: 10px 14px; +} + +.certificate-api-credentials { + display: grid; + gap: 7px; + margin-top: 10px; +} + +.certificate-api-credential-item { + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 4px); + background: var(--panel2); +} + +.certificate-api-row-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + gap: 4px; +} + +.certificate-api-details { + display: grid; + gap: 7px; + padding: 10px 12px; + border-top: 1px solid var(--line); +} + +.certificate-api-detail-line { + display: grid; + gap: 4px; + grid-template-columns: minmax(120px, 1fr) minmax(0, 2fr); + overflow-wrap: anywhere; +} + +.certificate-api-credential-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 8px 9px; +} + +.certificate-api-credential-info { + min-width: 0; + display: grid; + gap: 3px; +} + +.appearance-settings-card { + max-width: 980px; +} + +.appearance-section { + margin-top: 14px; + padding: 12px; + border: 1px solid var(--line); + background: var(--panel2); +} + +.appearance-section-heading { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + margin-bottom: 12px; + padding-bottom: 9px; + border-bottom: 1px solid var(--line); +} + +.appearance-section-heading h3 { + margin: 2px 0 0; + font-size: 14px; +} + +.appearance-settings-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; + align-items: start; +} + +.appearance-scale-control>span:first-child { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.appearance-scale-control output { + color: var(--accent); + font-weight: 700; + font-variant-numeric: tabular-nums; +} + +.appearance-actions { + display: flex; + justify-content: flex-end; + margin-top: 14px; + padding-top: 12px; + border-top: 1px solid var(--line); +} + +.certificate-bulk-list { + display: grid; + gap: 8px; + max-height: 250px; + overflow-y: auto; + margin: 10px 0; +} + +.docker-agent-security-hint, +.docker-agent-summary>span, +.docker-fingerprint { + min-width: 0; + max-width: 100%; + overflow-wrap: anywhere; + word-break: break-word; +} + +.docker-protocol-options { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 12px; +} + +.docker-protocol-options>* { + display: grid; + align-content: center; + min-width: 130px; + flex: 1 1 145px; + border-radius: var(--rp-radius-control, 6px); + border: 1px solid var(--line); + background: var(--panel2); + padding: 8px 12px; + min-height: 49px; + box-sizing: border-box; +} + +.docker-protocol-fixed { + gap: 4px; +} + +.named-listener-row legend, +html[data-dashboard-density=normal] #panel-overview .stat>span, +.vh-member details, +.acl-picker-no-selection, +.docker-protocol-fixed strong, +.docker-protocol-options .docker-checkbox-field .vh-check { + font-size: 11px; +} + +.docker-protocol-options .docker-checkbox-field>span:first-child { + color: var(--text); + font-size: 11px; +} + +.docker-agent-grid .docker-agent-fingerprint { + min-width: 0; + width: 100%; + box-sizing: border-box; +} + +.docker-backend-ip-settings { + display: grid; + align-content: start; + gap: 12px; + min-width: 0; +} + +.docker-service-certificate-bar { + display: flex; + justify-content: flex-start; + min-width: 0; +} + +.docker-service-certificate-bar>.check { + margin: 0 !important; + align-items: flex-start; + font-size: 11px; + line-height: 1.35; + overflow-wrap: anywhere; +} + +.switch-grid>.check, +.vh-form .vh-grid>.vh-check:not(.vh-enable-control), +.vh-form .vh-www-header .vh-www-toggle, +.docker-protocol-options .check, +.docker-service-head .check, +.docker-switch-row .check { + margin-top: 0 !important; +} + +.docker-switch-row .docker-checkbox-field { + min-width: 0; + max-width: none; +} + +.refresh-settings-grid>.check { + grid-column: 1/-1; + display: flex; + align-items: center; + gap: 8px; + margin: 0; + min-width: 0; +} + +.refresh-settings-grid select { + width: 100%; + min-width: 0; +} diff --git a/src/admin/web/css/60-monitoring.css b/src/admin/web/css/60-monitoring.css new file mode 100644 index 0000000..89eb0e2 --- /dev/null +++ b/src/admin/web/css/60-monitoring.css @@ -0,0 +1,1078 @@ +/* Traffic, cache, transformations, live/stored logs and configuration history. + * Order is defined in manifest.json; responsive rules are centralized in 70-responsive.css. + */ + +.certificate-card .pill, +.cache-miss-key, +.file-logs-table th .file-log-sort-button:hover, +.file-logs-table th[aria-sort=ascending] .file-log-sort-button, +.file-logs-table th[aria-sort=descending] .file-log-sort-button { + color: var(--accent); +} + +.log-column-picker { + position: relative; + font-size: 10px; +} + +.log-column-picker>summary { + list-style: none; + cursor: pointer; + user-select: none; + padding: 6px 9px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-control, 4px); + min-height: 30px; + display: flex; + align-items: center; +} + +.log-column-options { + position: absolute; + z-index: 80; + inset-inline-end: 0; + top: calc(100% + 4px); + width: calc(min(190px, 100vw - 24px)); + overflow: visible; + padding: 4px; + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 1px; + background: var(--panel2); + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 5px); + box-shadow: 0 10px 26px var(--shadow-mid); +} + +.log-column-option { + min-width: 0; + min-height: 22px; + margin: 0 !important; + padding: 3px 5px; + gap: 7px; + border-radius: var(--rp-radius-control, 3px); + line-height: 1.2; + white-space: nowrap; +} + +.log-column-option:hover { + background: var(--panel); +} + +.logs-card th { + background: var(--panel); + position: sticky; + top: 0; +} + +.logs-card .table-scroll { + max-height: 260px; +} + +.cache-table td { + overflow-wrap: anywhere; + max-width: 260px; + vertical-align: top; +} + +.logs-dialog .logs-card { + border: 0; + padding: 0 12px; + background: var(--bg); +} + +.logs-dialog .logs-card>.card-heading { + display: block; + padding: 8px 0; + margin: 0; +} + +.log-tools { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; + width: 100%; + --toolbar-control-height: min(var(--button-min-height), var(--ui-field-min-height)); +} + +.log-tools input:not([type=checkbox]):not([type=radio]) { + width: auto; + max-width: 180px; + font-size: 11px; + flex: 1; + min-width: 110px; + box-sizing: border-box; + height: var(--toolbar-control-height); + min-height: var(--toolbar-control-height); + padding-block: 0; +} + +.log-tools select { + width: auto; + max-width: 180px; + font-size: 11px; + box-sizing: border-box; + height: var(--toolbar-control-height); + min-height: var(--toolbar-control-height); + padding-block: 0; +} + +.log-tools button { + font-size: 10px; + padding: 6px 9px; +} + +.logs-dialog .table-scroll { + max-height: calc(min(60dvh, 580px)); + overflow: auto; + border-top: 1px solid var(--line); +} + +.logs-dialog td { + padding: 6px 7px; + max-width: 360px; + overflow: hidden; + text-overflow: ellipsis; +} + +.logs-dialog th { + padding: 6px 7px; + max-width: 360px; + overflow: hidden; + text-overflow: ellipsis; + position: sticky; + top: 0; + background: var(--panel); +} + +.log-footnote { + padding: 8px 12px; + font-size: 10px; +} + +.logs-dialog.docked { + position: fixed; + inset: auto 12px 10px auto; + margin: 0; + width: calc(min(1180px, 100vw - 24px)); + max-height: 46dvh; + z-index: 20; +} + +.logs-dialog.docked .table-scroll { + max-height: calc(46dvh - 160px); +} + +.cache-toolbar { + flex-wrap: wrap; + display: flex; + align-items: center; + gap: 10px; + padding: 12px; +} + +.cache-toolbar label { + margin: 0; + display: flex; + gap: 7px; + align-items: center; +} + +.cache-toolbar select { + max-width: 300px; + min-width: 130px; + margin: 0; +} + +.cache-domain-filter { + display: flex; + align-items: center; + gap: 7px; + margin: 0; + color: var(--muted); + font-size: 11px; +} + +.cache-domain-filter select { + width: auto; + min-width: 150px; + padding: 7px 28px 7px 9px; + margin: 0; +} + +.cache-stats strong { + font-size: 22px; +} + +.cache-stats progress { + width: 100%; + height: 5px; + margin: 6px 0; +} + +html[data-dashboard-density] #panel-overview .dashboard-overview-grid>.card.activity-card .card-heading h2, +html[data-dashboard-density] #panel-overview .dashboard-overview-grid>.card.overview-wide-chart .card-heading h2, +html[data-dashboard-density] #panel-overview .stat:is(.stat-chart, .stat-donut)>span:first-child, +html[data-dashboard-density] #panel-overview .dashboard-overview-grid>.card.pool-card .card-heading h2, +.cache-table { + font-size: 12px; +} + +.cache-reasons { + min-width: 190px; +} + +.cache-reason { + display: inline-block; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 4px); + padding: 2px 5px; + margin: 1px 4px 2px 0; + font-size: 10px; +} + +#panel-cache thead th { + position: sticky; + top: 0; + background: var(--panel); + z-index: 1; +} + +#panel-cache .cache-table .cache-reasons { + white-space: normal; + min-width: 210px; + max-width: 300px; +} + +#panel-cache .cache-copy-cell { + cursor: copy; +} + +.vhost-dialog .vh-form, +.cache-operation-dialog>.card { + border: 0; + padding: 14px; +} + +.cache-operation-dialog .cache-dialog-heading { + margin-bottom: 12px; + padding: 9px 0; +} + +.window-heading>button[data-i18n="close"], +.cache-operation-dialog .cache-dialog-heading button { + margin-inline-start: auto; + flex: 0 0 auto; +} + +.traffic-donut-layout { + display: grid; + grid-template-columns: 148px minmax(0, 1fr); + gap: 14px; + align-items: center; +} + +.traffic-donut { + position: relative; + width: 148px; + aspect-ratio: 1; + border-radius: 50%; + background: var(--chart-surface); + box-shadow: inset 0 0 0 1px var(--line); +} + +.traffic-donut:before { + content: ""; + position: absolute; + inset: 23%; + border-radius: 50%; + background: var(--chart-surface-alt); + box-shadow: 0 0 0 1px var(--line); +} + +.traffic-donut>span { + position: absolute; + inset: 0; + z-index: 1; + display: grid; + place-items: center; + font: 700 16px var(--mono); +} + +#panel-traffic .traffic-tabs { + margin: 12px 0 14px; +} + +.traffic-bar-row { + display: grid; + grid-template-columns: minmax(120px, 1.5fr) minmax(120px, 3fr) minmax(72px, auto); + gap: 10px; + align-items: center; + min-width: 0; + font-family: var(--mono); + font-variant-numeric: tabular-nums; +} + +.traffic-bar-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font: 11px var(--mono); +} + +.traffic-bar-track { + height: 10px; + border-radius: var(--rp-progress-radius); + background: var(--chart-surface); + box-shadow: inset 0 0 0 1px var(--line); + overflow: hidden; +} + +.traffic-bar-fill { + display: block; + height: 100%; + min-width: 2px; + border-radius: inherit; + background: var(--rp-progress-fill); +} + +.traffic-bar-value { + text-align: right; + font: 700 10px var(--mono); +} + +.logging-settings-subform { + margin: 0; + min-width: 0; +} + +.logging-format-actions { + display: grid; + grid-template-columns: minmax(220px, 1fr) auto; + gap: 14px; + align-items: end; +} + +.logging-builder-action { + display: grid; + gap: 7px; + justify-items: stretch; + min-width: 180px; +} + +.logging-builder-layout { + display: grid; + grid-template-columns: minmax(0, 1.55fr) minmax(260px, .85fr); + gap: 14px; + min-height: 0; +} + +.logging-builder-layout>section { + min-width: 0; + border: 1px solid var(--line); + padding: 12px; + background: var(--bg); +} + +.logging-preview pre { + margin: 0; + max-height: 420px; + overflow: auto; +} + +.cache-toolbar-status { + margin-left: auto; + display: flex; + align-items: center; + gap: 12px; + white-space: nowrap; +} + +.traffic-line { + fill: none; + vector-effect: non-scaling-stroke; +} + +.traffic-line-total, +#sparkline.traffic-line-total { + stroke: var(--chart-septenary); + stroke-width: 1.25; + opacity: .72; +} + +.traffic-line-h1 { + stroke: var(--chart-accent); + stroke-width: 2; +} + +.traffic-line-h2 { + stroke: var(--chart-secondary); + stroke-width: 2; +} + +.traffic-line-h3 { + stroke: var(--chart-tertiary); + stroke-width: 2; +} + +.traffic-line-transform { + stroke: var(--chart-quaternary); + stroke-width: 2.4; + stroke-dasharray: 5 3; +} + +.transform-reason-list { + display: flex; + flex-wrap: wrap; + gap: 7px; +} + +.cache-transform-badge { + display: inline-flex; + align-items: center; + margin-left: 6px; + padding: 1px 5px; + border: 1px solid var(--pill-line); + border-radius: var(--rp-radius-control, 3px); + color: var(--accent); + font: 9px var(--mono); + text-transform: uppercase; + letter-spacing: .35px; +} + +.cache-toolbar-message { + flex-basis: 100%; + margin-top: 0; +} + +.cache-operation-dialog { + width: calc(min(920px, 100vw - 32px)); +} + +.transform-applied-key, +.transform-applied-line { + stroke: var(--chart-quaternary); + color: var(--chart-quaternary); +} + +.transform-cache-key, +.transform-cache-line { + stroke: var(--chart-secondary); + color: var(--chart-secondary); +} + +.transform-skipped-key, +.transform-skipped-line { + stroke: var(--chart-septenary); + color: var(--chart-septenary); +} + +.transform-error-key, +.transform-error-line { + stroke: var(--chart-quinary); + color: var(--chart-quinary); +} + +.cache-memory-sizing { + margin: 12px 0; + padding: 12px; + border: 1px solid var(--line); + border-radius: var(--rp-radius-surface, 0); + background: var(--panel2); +} + +.cache-memory-sizing .card-heading { + margin-bottom: 6px; +} + +.cache-transient-total-key { + color: var(--chart-accent); +} + +.cache-queue-key { + color: var(--blue); +} + +.cache-io-key { + color: var(--transform-chart); +} + +.config-history-dialog { + width: calc(min(1180px, 100vw - 24px)); + max-width: calc(100vw - 24px); + max-height: calc(100dvh - 28px); + overflow: hidden; +} + +.config-history-dialog[open] { + display: grid; + grid-template-rows: auto minmax(0, 1fr); + height: calc(min(820px, 100dvh - 28px)); +} + +.config-history-layout { + display: grid; + grid-template-columns: minmax(380px, .95fr) minmax(420px, 1.05fr); + grid-template-rows: minmax(0, 1fr); + gap: 10px; + padding: 10px; + min-height: 0; + overflow: hidden; +} + +.config-history-browser { + min-width: 0; + min-height: 0; + overflow-y: auto; + overscroll-behavior: contain; +} + +.config-history-detail { + min-width: 0; + min-height: 0; + overscroll-behavior: contain; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.config-history-months { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 10px; +} + +.config-history-month { + border: 1px solid var(--line); + padding: 9px; + background: var(--panel); +} + +.config-history-days, +.config-history-weekdays { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 3px; +} + +.config-history-weekdays span { + color: var(--muted); + font: 9px var(--mono); + text-align: center; + padding: 2px 0; +} + +.config-history-day { + min-height: 34px; + padding: 4px; + border: 1px solid transparent; + background: transparent; + color: var(--muted); + font: 10px var(--mono); + text-align: left; +} + +button.config-history-day.has-revisions { + cursor: pointer; + color: var(--text); + border-color: var(--line-strong, var(--line)); + background: var(--surface, var(--bg)); +} + +button.config-history-day.has-revisions:hover, +button.config-history-day.is-selected, +.config-history-revision.active { + border-color: var(--accent); + color: var(--accent); +} + +.config-history-day-count { + display: block; + margin-top: 3px; + font-size: 8px; + color: var(--muted); +} + +.config-history-detail-head { + display: flex; + justify-content: space-between; + gap: 10px; + align-items: flex-start; + margin-bottom: 8px; +} + +.config-history-detail-actions { + display: flex; + flex-direction: column; + gap: 7px; + align-items: flex-end; + text-align: right; +} + +.config-history-revisions { + display: flex; + flex: 0 0 auto; + flex-wrap: wrap; + gap: 6px; + margin-bottom: 10px; + max-height: 110px; + overflow-y: auto; +} + +.config-history-revision { + font: 10px var(--mono); +} + +.config-history-diff { + display: grid; + flex: 1 1 auto; + min-height: 0; + align-content: start; + grid-auto-rows: max-content; + gap: 8px; + overflow-y: auto; + padding-right: 4px; + overscroll-behavior: contain; +} + +.config-history-change { + border: 1px solid var(--line); + padding: 8px; + background: var(--panel); +} + +.config-history-change-path { + display: block; + margin-bottom: 6px; + font: 10px var(--mono); + overflow-wrap: anywhere; +} + +.config-history-change-values { + grid-template-columns: 1fr 1fr; + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; +} + +.config-history-change pre { + margin: 3px 0 0; + max-height: 160px; + overflow: auto; + white-space: pre-wrap; + overflow-wrap: anywhere; + font: 10px var(--mono); +} + +.file-logs-table { + max-height: 62vh; +} + +.file-logs-table thead th { + position: sticky; + top: 0; + z-index: 4; + background: var(--panel); + box-shadow: 0 1px 0 var(--line); +} + +.file-logs-table th .file-log-sort-button { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 3px 2px; + font: inherit; + font-weight: inherit; + color: inherit; + border: 0; + background: transparent; + cursor: pointer; + text-align: left; +} + +.file-logs-table .file-log-sort-indicator { + font-size: 11px; + opacity: .8; +} + +.file-logs-table tbody tr:not(.file-log-detail-row) { + cursor: pointer; +} + +.file-logs-table tbody tr.selected>td { + background: var(--panel2); + box-shadow: inset 3px 0 0 var(--accent); +} + +.file-logs-table td { + max-width: 340px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.file-log-detail-row td { + max-width: none; + padding: 0; + background: var(--panel2); + white-space: normal; +} + +.file-log-inline-detail { + border-top: 1px solid var(--line); + border-bottom: 1px solid var(--line); + padding: 8px 10px; +} + +.file-log-inline-detail pre { + margin: 8px 0 0; + overflow: auto; + white-space: pre-wrap; + overflow-wrap: anywhere; + max-height: 24vh; + margin-top: 5px; + line-height: 1.28; +} + +.file-logs-pager { + display: flex; + justify-content: flex-end; + gap: 8px; +} + +.file-logs-launcher { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +.file-logs-heading-actions { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 8px; +} + +.file-logs-table-toolbar { + display: flex; + align-items: center; + gap: 8px; + justify-content: space-between; + min-height: 32px; +} + +.file-log-search-help-popover { + position: absolute; + z-index: 90; + top: calc(100% + 6px); + inset-inline-end: 0; + display: grid; + gap: 7px; + width: calc(min(420px, 100vw - 36px)); + padding: 12px; + border: 1px solid var(--line); + background: var(--panel2); + box-shadow: 0 12px 28px var(--shadow-strong); +} + +.file-logs-filter { + display: grid; + grid-template-columns: minmax(180px, 260px) minmax(260px, 1fr) minmax(110px, 150px); + gap: 10px; + align-items: end; + --toolbar-control-height: min(var(--button-min-height), var(--ui-field-min-height)); +} + +.file-logs-filter .file-log-search { + grid-column: auto; +} + +.file-log-column-picker { + align-self: end; + margin-inline-start: auto; +} + +.file-log-column-picker .log-column-options { + width: calc(min(360px, 100vw - 24px)); + max-height: 420px; + overflow: auto; + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.file-log-field-grid { + display: grid; + gap: 1px; + border: 1px solid var(--line); + margin-top: 5px; +} + +.file-log-field { + display: grid; + grid-template-columns: minmax(140px, .5fr) minmax(0, 1.5fr); + background: var(--panel); + gap: 9px; + padding: 3px 7px; + line-height: 1.25; +} + +.file-log-field code { + overflow-wrap: anywhere; + color: var(--muted); +} + +html[data-table-density=compact] .file-log-field { + padding: 2px 6px; + line-height: 1.18; +} + +html[data-table-density=compact] .file-log-inline-detail pre { + line-height: 1.18; +} + +html[data-table-density=ultra] .file-log-inline-detail { + padding: 5px 7px; +} + +body[data-density=compact] .mini-metrics div, +.acl-policy-mode-card input, +.docker-service-certificate-bar>.check>input, +html[data-table-density=ultra] .file-log-field-grid { + margin-top: 2px; +} + +html[data-table-density=ultra] .file-log-field { + gap: 6px; + padding: 1px 5px; + font-size: 10px; + line-height: 1.08; +} + +html[data-table-density=ultra] .file-log-raw-json { + margin-top: 3px; +} + +html[data-table-density=ultra] .file-log-inline-detail pre { + margin-top: 3px; + line-height: 1.08; +} + +.cache-via-setting { + display: grid; + grid-template-columns: minmax(180px, .8fr) minmax(220px, 1.2fr); + gap: 8px 14px; + align-items: end; + margin-top: 10px; +} + +.cache-varnish-setting { + display: flex; + align-items: center; + gap: 8px; + margin-top: 14px; + position: relative; +} + +.cache-varnish-setting .check { + min-width: 0; + flex: 0 1 auto; + margin: 0 !important; +} + +.cache-varnish-info, +.cache-maintenance-info { + position: static; + flex-shrink: 0; +} + +.cache-varnish-grace-field { + width: min(320px, 100%); + min-width: 0; + margin-top: 12px; +} + +.cache-varnish-grace-field input { + display: block; + width: 100%; + box-sizing: border-box; +} + +.cache-varnish-info .metric-info-popover { + width: calc(min(440px, 100vw - 56px)); + max-height: calc(min(65vh, 450px)); + overflow-y: auto; +} + +.cache-maintenance-help { + display: flex; + align-items: center; + gap: 8px; + position: relative; +} + +.cache-maintenance-info .metric-info-popover { + max-height: calc(min(60vh, 420px)); + overflow-y: auto; + width: calc(min(440px, 100vw - 56px)); +} + +.cache-maintenance-info .metric-info-popover code { + display: block; + white-space: pre-wrap; + overflow-wrap: anywhere; + padding: 7px 0; + font-size: 11px; + line-height: 1.55; +} + +.file-logs-heading-actions .file-log-consolidate { + display: inline-flex; + align-items: center; + gap: .45rem; + padding: .35rem .55rem; + min-height: 0; + color: var(--muted); + font-size: .8rem; + font-weight: 400; + white-space: nowrap; + cursor: pointer; +} + +.file-log-consolidate input { + width: 14px; + height: 14px; + margin: 0; + accent-color: var(--accent); +} + +.page-footer .footer-update:hover, +.certificate-details summary:hover, +.file-log-consolidate:hover, +.file-log-tab:hover, +.config-history-change-values .diff-after { + color: var(--text); +} + +.file-log-tabs { + display: flex; + gap: .25rem; + border-bottom: 1px solid var(--line); + margin: .9rem 0 1rem; +} + +.file-log-tab { + appearance: none; + background: transparent; + border: 0; + border-bottom: 2px solid transparent; + border-radius: 0; + padding: .65rem 1rem; + color: var(--muted); + cursor: pointer; + font: inherit; + font-weight: 500; +} + +.file-log-tab.active { + color: var(--text); + border-bottom-color: var(--accent); +} + +.file-log-advanced-parser h3 { + margin: 0 0 .7rem; + font-size: 1rem; +} + +.file-log-aggregate-copy, +.traffic-copy-value { + appearance: none; + border: 0; + background: transparent; + color: inherit; + font: inherit; + text-align: left; + padding: 0; + margin: 0; + cursor: copy; +} + +.file-log-aggregate-copy:focus-visible, +.file-log-aggregate-copy:hover, +.traffic-copy-value:focus-visible, +.traffic-copy-value:hover { + text-decoration: underline; +} + +.file-log-advanced-parser { + margin: 0 0 1rem; + padding: .75rem 1rem; + border-radius: var(--rp-radius-surface, var(--radius, 12px)); +} + +.file-log-aggregate-form { + display: grid; + grid-template-columns: minmax(220px, 2fr) minmax(140px, 1fr) minmax(120px, 1fr) auto; + gap: .75rem; + align-items: end; + margin-top: .75rem; +} + +.file-log-aggregate-table { + margin-top: .75rem; +} + +.file-log-aggregate-table td:last-child, +.file-log-aggregate-table th:last-child { + width: 8rem; + text-align: right; +} + +#panel-filelogs .file-logs-workspace { + min-width: 0; + max-width: 100%; + grid-template-columns: minmax(0, 1fr); +} + +#panel-filelogs .file-log-aggregate-table, +#panel-filelogs .file-logs-table { + box-sizing: border-box; + min-width: 0; + max-width: 100%; + overflow-x: auto; +} + +.config-history-change-values .diff-before { + color: var(--muted); + text-decoration: line-through; +} + +.config-history-change-values code { + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +.config-history-json-dialog { + width: calc(min(1200px, 100vw - 24px)); + max-width: calc(100vw - 24px); + max-height: calc(100dvh - 28px); + overflow: auto; +} + +.config-history-json-columns { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; + padding: 12px; +} + +.config-history-json-columns pre { + padding: 12px; + margin: 0; + background: var(--panel); + border: 0; + overflow: auto; + max-height: 68dvh; + font: 11px/1.5 var(--mono); +} diff --git a/src/admin/web/css/70-responsive.css b/src/admin/web/css/70-responsive.css new file mode 100644 index 0000000..d8bde4f --- /dev/null +++ b/src/admin/web/css/70-responsive.css @@ -0,0 +1,1680 @@ +/* Responsive rules. Each viewport, container and reduced-motion condition is declared once. + * Historical overrides were resolved before consolidation; preserve the documented order. + */ + +@media (min-width:1201px) { + #panel-overview .stats-grid { + grid-template-columns: repeat(6, minmax(0, 1fr)); + } +} + +@media (max-width:1320px) { + .docker-runtime { + grid-column: 2; + justify-content: flex-start; + text-align: left; + flex-wrap: wrap; + } + + .docker-health-grid { + grid-template-columns: repeat(3, minmax(130px, 1fr)); + } + + .docker-passive-health-editor .docker-health-grid { + grid-template-columns: repeat(2, minmax(180px, 1fr)); + } + + .docker-service-head { + grid-template-columns: auto 1fr; + } + + .docker-service-head-actions { + grid-column: 2; + justify-content: flex-start; + flex-wrap: wrap; + } + + .docker-route-grid { + grid-template-columns: 1fr 1fr; + } + + .docker-alias-field { + grid-column: 1/-1; + } +} + +@media (max-width:1200px) { + .stats-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .header-rule { + grid-template-columns: 80px 1fr auto; + } + + .header-rule .header-value { + grid-column: 2; + grid-row: 2; + } + + .header-rule button { + grid-column: 3; + grid-row: 1/3; + } + + #panel-overview .dashboard-overview-grid { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + #panel-overview .dashboard-overview-grid>.card:not(.pool-card), + #panel-overview .dashboard-overview-grid>.stat { + grid-column: span 2; + } +} + +@media (max-width:1150px) { + .vhost-layout { + grid-template-columns: minmax(190px, .6fr) minmax(0, 1.6fr); + } +} + +@media (max-width:1100px) { + .traffic-wide-chart { + grid-column: span 1; + } + + .traffic-chart-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width:1000px) { + .shell { + grid-template-columns: 166px minmax(0, 1fr); + } + + .sidebar { + padding: 18px 10px; + } + + .vh-grid, + .acme-card .vh-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .docker-acl-grid, + .docker-timeout-grid { + grid-template-columns: 1fr 1fr; + } +} + +@media (max-width:980px) { + .logging-grid { + grid-template-columns: 1fr; + } + + .vhost-live-grid, + .stream-live-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (max-width:900px) { + .logging-builder-layout, + .logging-format-actions, + .acl-policy-custom-controls, + .acl-policy-modes, + .acl-profile-layout { + grid-template-columns: 1fr; + } + + .logging-builder-action .small-label { + text-align: left; + } + + #panel-settings:not([hidden]) { + display: block; + } + + #panel-settings .settings-shortcuts { + margin-bottom: 10px; + } + + #panel-settings .settings-filter-row { + align-items: stretch; + flex-direction: column; + margin-bottom: 10px; + } + + #panel-settings .settings-tabs { + display: flex; + flex-direction: row; + position: static; + overflow-x: auto; + overscroll-behavior-inline: contain; + scrollbar-width: thin; + margin-bottom: 12px; + } + + #panel-settings .settings-tabs button { + flex: 0 0 auto; + width: auto; + min-width: 120px; + white-space: nowrap; + text-align: center; + } + + .acl-policy-mode-custom { + grid-column: auto; + } +} + +@media (max-width:860px) { + .config-history-layout { + display: block; + overflow-y: auto; + } + + .config-history-browser { + max-height: calc(min(300px, 38dvh)); + overflow-y: auto; + margin-bottom: 10px; + } + + .config-history-detail { + display: block; + overflow: visible; + } + + .config-history-revisions { + max-height: none; + } + + .config-history-diff { + overflow: visible; + } + + .config-history-change-values { + grid-template-columns: 1fr; + } + + .config-history-detail-head { + flex-direction: column; + } + + .config-history-detail-actions { + align-items: flex-start; + text-align: left; + } +} + +@media (max-width:850px) { + .sidebar { + border-right: 0; + border-bottom: 1px solid var(--line); + height: auto; + flex-direction: row; + flex-wrap: wrap; + position: sticky; + top: 0; + z-index: 60; + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: 10px 12px; + padding: 10px 12px; + } + + .brand-mark span { + font-size: 15px; + left: 18px; + top: 12px; + } + + nav { + flex-direction: row; + margin-left: auto; + } + + .nav { + gap: 7px; + font-size: 11px; + border-left: 0; + border-bottom: 2px solid transparent; + padding: 7px; + } + + .nav.active { + border-bottom-color: var(--rp-nav-active-color); + } + + h1 { + font-size: 36px; + } + + .brand { + font-size: 14px; + } + + .brand-mark { + width: 34px; + height: 34px; + font-size: 22px; + } + + .diagnostics-grid, + #panel-overview .dashboard-overview-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .diagnostics-grid>.card:last-child, + #panel-overview .dashboard-overview-grid>.card:not(.pool-card) { + grid-column: 1/-1; + } + + .shell { + display: block; + } + + .sidebar nav { + margin-top: 0; + display: flex; + flex-wrap: wrap; + gap: 2px; + } + + .edition, + .sidebar-bottom { + display: none; + } + + .topbar { + height: 46px; + } + + .vhost-dialog { + max-width: calc(100vw - 12px); + } + + body[data-density=compact] .sidebar nav { + margin-top: 0; + } + + body[data-density=compact] .sidebar { + padding: 8px 9px; + } + + body[data-density=compact] .topbar { + height: 42px; + } + + .sidebar .brand { + min-width: 0; + } + + .mobile-menu-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 9px; + min-height: 36px; + padding: 7px 10px; + border: 1px solid var(--line); + background: transparent; + color: var(--muted); + font: 600 11px var(--mono); + } + + .mobile-menu-toggle:hover, + .mobile-menu-toggle[aria-expanded=true] { + color: var(--rp-nav-active-color); + border-color: var(--rp-nav-active-color); + background: var(--rp-nav-active-bg); + } + + .mobile-menu-icon { + display: block; + flex: 0 0 auto; + width: 15px; + height: 1px; + background: currentColor; + box-shadow: 0 -5px currentColor, 0 5px currentColor; + } + + .sidebar .main-navigation { + grid-column: 1/-1; + display: none; + width: 100%; + margin: 0; + padding-top: 10px; + border-top: 1px solid var(--line); + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 4px; + } + + .sidebar.mobile-nav-open .main-navigation { + display: grid; + } + + .sidebar .main-navigation .nav { + width: 100%; + min-width: 0; + justify-content: flex-start; + gap: 8px; + padding: 9px 10px; + border: 1px solid transparent; + border-left-width: 2px; + text-align: left; + } + + .sidebar .main-navigation .nav.active { + border-color: var(--line); + border-left-color: var(--rp-nav-active-color); + background: var(--rp-nav-active-bg); + } + + [dir=rtl] .sidebar .main-navigation .nav.active { + border-left-color: var(--line); + border-right-color: var(--rp-nav-active-color); + } + + #panel-overview .dashboard-overview-grid>.stat { + grid-column: span 1; + } +} + +@media (max-width:800px) { + .vhost-layout { + grid-template-columns: minmax(0, 1fr); + } + + .pool-table { + min-width: 620px; + } + + .file-log-aggregate-form { + grid-template-columns: 1fr; + } +} + +@media (max-width:720px) { + .traffic-chart-grid { + grid-template-columns: 1fr; + } + + .traffic-donut-layout { + grid-template-columns: 120px minmax(0, 1fr); + } + + .traffic-donut { + width: 120px; + } +} + +@media (max-width:700px) { + #panel-tls .certificate-operation-heading, + #panel-tls .certificate-subheading, + #panel-tls .certificate-workspace-heading, + #panel-tls .renewal-entry-head, + .file-logs-launcher { + align-items: stretch; + flex-direction: column; + } + + #panel-tls .certificate-subheading>button, + #panel-tls .certificate-workspace-heading>button { + width: 100%; + } + + #panel-tls .certificate-tabs, + #panel-tls .renewal-meta, + .file-log-column-picker .log-column-options, + .file-log-field, + .file-logs-filter { + grid-template-columns: 1fr; + } + + #panel-tls .renewal-actions { + justify-content: stretch; + } + + #panel-tls .renewal-actions button { + flex: 1 1 100%; + } + + .file-logs-heading-actions, + .file-logs-table-toolbar { + align-items: stretch; + flex-wrap: wrap; + } + + .file-log-limit { + max-width: 160px; + } + + .page-intro-title-row { + gap: 8px; + } + + .file-logs-heading-actions .file-log-consolidate { + margin-inline-end: auto; + } + + .file-log-tab { + padding: .6rem .7rem; + } + + #file-log-view-counts, + #file-log-view-entries, + #panel-filelogs { + box-sizing: border-box; + width: 100%; + } + + #panel-filelogs .file-log-field-grid { + box-sizing: border-box; + min-width: 0; + max-width: 100%; + width: 100%; + } + + #panel-filelogs .file-logs-table { + width: 100%; + max-height: none; + overflow: visible; + } + + #panel-filelogs .file-logs-table-toolbar { + box-sizing: border-box; + min-width: 0; + max-width: 100%; + width: 100%; + align-items: flex-start; + gap: 5px; + } + + #panel-filelogs .file-logs-workspace { + box-sizing: border-box; + width: 100%; + gap: 10px; + padding: 12px; + overflow: visible; + } + + #panel-filelogs .card-heading { + min-width: 0; + flex-wrap: wrap; + gap: 8px; + } + + #panel-filelogs .file-logs-heading-actions { + width: 100%; + justify-content: flex-start; + gap: 4px; + } + + #panel-filelogs .file-logs-heading-actions .file-log-consolidate { + margin-inline-end: auto; + white-space: normal; + } + + #panel-filelogs .file-log-search-help-popover { + width: calc(min(320px, 100vw - 54px)); + } + + #panel-filelogs .file-log-aggregate-form, + #panel-filelogs .file-logs-filter, + #panel-filelogs .file-log-field { + grid-template-columns: minmax(0, 1fr); + } + + #panel-filelogs .file-log-limit { + max-width: none; + } + + #panel-filelogs .file-log-aggregate-form input, + #panel-filelogs .file-log-aggregate-form label, + #panel-filelogs .file-log-aggregate-form select, + #panel-filelogs .file-logs-filter input, + #panel-filelogs .file-logs-filter label, + #panel-filelogs .file-logs-filter select { + min-width: 0; + max-width: 100%; + } + + #panel-filelogs .file-log-tabs { + min-width: 0; + } + + #panel-filelogs .file-log-tab { + flex: 1 1 0; + min-width: 0; + white-space: normal; + } + + #panel-filelogs .file-log-column-picker { + align-self: flex-end; + } + + #panel-filelogs .file-logs-table table, + #panel-filelogs .file-logs-table tbody { + display: block; + width: 100%; + min-width: 0; + white-space: normal; + } + + #panel-filelogs .file-logs-table thead { + display: none; + } + + #panel-filelogs .file-logs-table tbody tr:not(.file-log-detail-row) { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 5px 10px; + position: relative; + min-width: 0; + padding: 10px 12px; + margin-bottom: 8px; + border: 1px solid var(--line); + background: var(--panel); + } + + #panel-filelogs .file-logs-table tbody tr:focus-visible { + outline: 2px solid var(--accent); + outline-offset: -2px; + } + + #panel-filelogs .file-logs-table tbody tr.selected { + border-color: var(--accent); + } + + #panel-filelogs .file-logs-table tbody tr:not(.file-log-detail-row)>td { + display: block; + min-width: 0; + max-width: none; + padding: 0; + border: 0; + overflow: hidden; + text-overflow: unset; + white-space: normal; + overflow-wrap: anywhere; + } + + #panel-filelogs .file-logs-table td[data-mobile-column=record] { + grid-column: 1/-1; + font-weight: 600; + line-height: 1.45; + padding-inline-end: 16px; + max-height: 4.4em; + overflow: hidden; + } + + #panel-filelogs .file-logs-table td[data-mobile-column=event], + #panel-filelogs .file-logs-table td[data-mobile-column=status] { + color: var(--muted); + font-size: 11px; + } + + #panel-filelogs .file-logs-table td[data-mobile-column=time], + #panel-filelogs .file-logs-table td[data-mobile-column=vhost] { + color: var(--muted); + font-size: 11px; + grid-column: 1/-1; + } + + #panel-filelogs .file-logs-table tbody tr:not(.file-log-detail-row)>td:before { + content: attr(data-mobile-label) ": "; + color: var(--text-subtle); + font-weight: 400; + } + + #panel-filelogs .file-logs-table tbody tr:not(.file-log-detail-row)>td[data-mobile-column=record]:before { + content: ""; + } + + #panel-filelogs .file-logs-table tbody tr:not(.file-log-detail-row):after { + content: "+"; + position: absolute; + inset-block-end: 4px; + inset-inline-end: 8px; + color: var(--muted); + font-size: 15px; + pointer-events: none; + } + + #panel-filelogs .file-logs-table tbody tr.selected:after { + content: "-"; + } + + #panel-filelogs .file-logs-table .file-log-detail-row { + display: block; + margin: -8px 0 8px; + border: 1px solid var(--accent); + border-top: 0; + background: var(--panel2); + } + + #panel-filelogs .file-logs-table .file-log-detail-row td { + display: block; + box-sizing: border-box; + width: 100%; + min-width: 0; + max-width: 100%; + white-space: normal; + } + + #panel-filelogs .file-log-inline-detail { + min-width: 0; + padding: 8px; + } + + #panel-filelogs .file-log-inline-detail pre { + max-width: 100%; + overflow-x: auto; + } + + #panel-filelogs .file-log-aggregate-table table { + table-layout: fixed; + } + + #panel-filelogs .file-log-aggregate-table td:first-child { + white-space: normal; + overflow-wrap: anywhere; + } + + #panel-filelogs .file-logs-pager { + justify-content: space-between; + } + + #panel-filelogs .file-logs-table tr.selected td[data-mobile-column=record] { + max-height: none; + overflow-wrap: anywhere; + } + + .dashboard-widget-list { + display: grid; + grid-template-columns: minmax(0, 1fr); + } + + .dashboard-widget-setting { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + min-width: 0; + } + + .page-intro .overview-samples-toolbar { + margin-left: 0; + justify-content: flex-start; + } +} + +@media (max-width:680px) { + .settings-tabs button { + font-size: 11px; + } + + .settings-page>.card { + padding: var(--ui-card); + } +} + +@media (max-width:650px) { + .location-header-editor .vh-header-row { + grid-template-columns: 1fr 1fr; + } + + .location-actions, + .location-toolbar { + align-items: stretch; + } + + .location-actions>button, + .location-toolbar>button { + flex: 1 1 150px; + } + + .auth-top { + padding: 14px; + } + + .auth-main { + padding: 22px 14px; + } + + .location-header-direction-head { + display: block; + } + + .location-header-direction-head p { + text-align: left; + margin-top: 5px; + } + + .location-preview-grid, + .vh-header-row.is-remove .vh-header-rule-fields, + .vh-header-rule-fields, + .cache-via-setting { + grid-template-columns: 1fr; + } + + .traffic-bar-row { + grid-template-columns: minmax(88px, 1.2fr) minmax(90px, 2fr) minmax(60px, auto); + gap: 7px; + } + + .vh-www-pair-directions { + grid-template-columns: minmax(0, 1fr); + } +} + +@media (max-width:640px) { + .acl-rule, + .basic-user { + grid-template-columns: 1fr 1fr; + } + + .renewal-entry, + .acl-profile-rule { + grid-template-columns: 1fr; + } + + .renewal-entry .fine { + grid-column: auto; + } + + .settings-tabs, + .vhost-live-grid, + .stream-live-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .vhost-card-head { + flex-direction: column; + } + + #panel-notfound .card-heading, + #panel-redirects .card-heading, + #panel-vhosts .card-heading, + .backend-card-heading, + #panel-streams .card-heading, + .acl-policy-preview-head, + .acl-policy-step-head { + align-items: stretch; + flex-direction: column; + } + + .vhost-list-tools, + #panel-streams .vhost-list-tools { + width: 100%; + margin-left: 0; + } + + #notfound-search, + #redirect-search, + #vhost-search, + #stream-search { + flex: 1 1 auto; + width: auto; + min-width: 0; + } + + .backend-table-tools { + width: 100%; + justify-content: flex-start; + margin-left: 0; + } + + #pool-search { + flex: 1 1 100%; + width: 100%; + min-width: 0; + } + + .backend-column-options { + right: auto; + left: 0; + } + + .vhost-card-head .entity-actions { + width: 100%; + justify-content: flex-start; + } + + .acl-policy-step-head>button, + .acl-policy-switch { + width: 100%; + } +} + +@media (max-width:620px) { + .nav { + min-width: 0; + } + + .breadcrumb b, +.nav span { + display: none; + } + + .breadcrumb { + font-size: 9px; + letter-spacing: 0; + } + + .breadcrumb span { + margin: 0 5px; + } + + .language-select { + max-width: 110px; + font-size: 10px; + } + + .page-intro, + .settings-shortcuts { + align-items: flex-start; + } + + .page-intro .eyebrow { + letter-spacing: .5px; + font-size: 8px; + } + + .notice { + font-size: 10px; + gap: 8px; + } + + .stats-grid, + .vh-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .logs-card>.card-heading, + .about-repository { + align-items: flex-start; + flex-direction: column; + } + + .log-tools button { + white-space: normal; + max-width: 85px; + } + + .page-footer { + flex-direction: column; + gap: 7px; + } + + .field-grid:where(:not(.docker-health-grid, .docker-acl-grid, .docker-timeout-grid)) { + gap: 0 9px; + } + + .header-rule { + grid-template-columns: 76px minmax(0, 1fr) auto; + } + + .header-rule button { + max-width: 65px; + overflow-wrap: anywhere; + } + + .switch-grid, + .diagnostics-grid, + .acme-card .vh-grid { + grid-template-columns: minmax(0, 1fr); + } + + .form-actions { + gap: 8px; + } + + .outline:where(:not(.docker-open-app, .docker-open-logs)), + .primary:where(:not(.docker-open-app, .docker-open-logs)) { + white-space: normal; + } + + .mini-metrics { + margin-top: 9px; + } + + .memory-line, + .nav b { + font-size: 10px; + } + + .login-section { + padding-top: 25px; + } + + .login-card { + padding: 18px; + } + + .topbar { + gap: 7px; + height: auto; + min-height: 46px; + padding: 7px 0; + } + + main { + padding: 0 10px; + } + + .top-actions { + gap: 4px; + width: 100%; + flex-wrap: wrap; + justify-content: flex-end; + } + + .topbar .breadcrumb { + font-size: 8px; + display: none; + } + + .top-actions .quiet { + padding: 5px; + font-size: 10px; + } + + .page-intro h1 { + font-size: 23px; + } + + .diagnostics-grid>.card:last-child { + grid-column: auto; + } + + .sidebar nav { + width: 100%; + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .vh-header-row { + grid-template-columns: 85px 1fr; + } + + .page-label, + .page-source, + .vh-header-row>:nth-child(3) { + grid-column: 1/-1; + } + + .vhost-dialog .vh-form { + padding: 9px; + } + + .vh-form>.card-heading { + top: -9px; + } + + .vh-tabs { + top: 29px; + } + + .window-heading { + flex-wrap: wrap; + gap: 5px; + } + + .window-heading h2 { + font-size: 14px; + } + + .window-tools { + margin-inline-start: 0; + } + + .logs-dialog .table-scroll { + max-height: 50dvh; + } + + .log-tools select { + max-width: 140px; + flex: 1; + } + + .logs-dialog.docked { + max-height: 55dvh; + } + + .logs-dialog.docked .table-scroll { + max-height: calc(55dvh - 230px); + } + + .page-editor, + .h3-editor .vh-grid { + grid-template-columns: 1fr 1fr; + } + + .named-listener-row .vh-grid, + .about-meta { + grid-template-columns: 1fr; + } + + .cache-toolbar select, + .cache-toolbar>* { + max-width: 100%; + } + + .cache-toolbar label { + flex-wrap: wrap; + } + + .cache-stats strong { + font-size: 19px; + } +} + +@media (max-width:760px) { + .redirect-card .vhost-detail-strip, + .redirect-rule .vh-grid, + .listener-choices, + .docker-health-grid, + .docker-passive-health-editor .docker-health-grid, + .docker-acl-grid, + .docker-timeout-grid, + .docker-route-grid, + .transform-diagnostics, + .appearance-settings-grid, + .config-history-json-columns { + grid-template-columns: 1fr; + } + + .protocol-toggle-row { + display: grid; + grid-template-columns: 1fr; + } + + .protocol-toggle-row .check, + .protocol-toggle-row .vh-check, + .stat-chart .stat-chart-legend span { + min-width: 0; + } + + .vh-backend-head, + .function-live-chart-summary, + .acl-picker-option-heading { + align-items: flex-start; + } + + .docker-runtime { + grid-column: auto; + justify-content: flex-start; + text-align: left; + } + + .docker-passive-health-editor .docker-passive-5xx-warning, + .docker-passive-health-editor .docker-passive-slow-warning, + .docker-alias-field, + .docker-acl-grid .docker-profile-field, + .docker-agent-grid .docker-profile-field { + grid-column: auto; + } + + .docker-agent-security, + .docker-toolbar, + .docker-service-editor>summary, + .security-editor>summary, + .function-live-chart-footer, + .certificate-api-credential-row { + align-items: flex-start; + flex-direction: column; + } + + .docker-toolbar input[type=search], + .docker-service-actions button, + .security-page-heading>button, + .docker-agent-summary>span, + .docker-fingerprint { + width: 100%; + } + + .cache-toolbar-status { + margin-left: 0; + width: 100%; + justify-content: flex-start; + } + + .docker-service-actions { + justify-content: stretch; + } + + .security-page-heading { + flex-direction: column; + } + + .docker-service-editor .docker-config-hint { + text-align: left; + } + + .docker-agent-heading-actions { + flex-wrap: wrap; + justify-content: flex-start; + } + + #docker-agents-form>.form-actions, + .function-live-chart-legend { + justify-content: flex-start; + } + + .network-interface-row-head { + flex-direction: column; + gap: 2px; + align-items: flex-start; + } + + .network-interface-row-head .fine { + white-space: normal; + text-align: left; + } + + .function-live-chart-heading small { + white-space: normal; + } + + .chart-style-palette { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .config-history-json-columns pre { + max-height: 38dvh; + } + + .appearance-settings-grid.dashboard-appearance-grid>.dashboard-density-setting { + grid-template-columns: minmax(0, 1fr); + gap: 7px; + } + + .dashboard-widget-settings-head { + align-items: stretch; + } + + .dashboard-widget-settings-controls { + margin-left: 0; + justify-content: flex-start; + } + + .dashboard-appearance-grid .dashboard-widget-list>.dashboard-widget-setting { + flex-basis: 210px; + width: 210px; + min-width: 210px; + max-width: 210px; + } + + .dashboard-appearance-grid .dashboard-widget-list.is-expanded>.dashboard-widget-setting { + min-width: 0; + max-width: none; + } + + .docker-route-grid .docker-alias-field { + grid-column: 1/-1; + } + + .docker-service-row { + position: relative; + gap: 8px; + padding: 12px; + } + + .docker-service-head { + grid-template-columns: minmax(0, 1fr); + gap: 8px; + align-items: start; + } + + .docker-publish-field { + min-height: 24px; + padding-right: 90px; + } + + .docker-service-meta { + gap: 3px; + } + + .docker-service-meta b, + .docker-service-meta span { + overflow-wrap: anywhere; + } + + .docker-service-head-actions { + grid-column: 1; + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: flex-start; + gap: 8px; + min-width: 0; + } + + .docker-service-head-actions .docker-runtime { + flex: 0 0 auto; + min-height: 0; + width: 100%; + display: block; + text-align: left; + } + + .docker-service-head-actions .docker-runtime .pill { + position: absolute; + top: 12px; + right: 12px; + max-width: 86px; + text-align: center; + } + + .docker-service-head-actions .docker-runtime .fine { + display: block; + font-size: 11px; + line-height: 1.4; + } + + .docker-service-quick-actions { + display: flex; + flex-wrap: wrap; + gap: 6px; + } + + .docker-service-quick-actions>* { + flex: 0 0 auto; + } + + .docker-service-head-actions .docker-view-toggle { + align-self: flex-start; + } + + body[data-density=compact] .docker-service-row { + padding: 8px; + gap: 5px; + } + + body[data-density=compact] .docker-service-head-actions .docker-runtime .pill { + top: 8px; + right: 8px; + } + + .backend-column-options { + width: min(330px, calc(100vw - 32px)); + min-width: 0; + max-width: calc(100vw - 32px); + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .stat-chart .stat-chart-legend { + flex-wrap: wrap; + white-space: normal; + } + + .system-info-heading-actions { + flex-wrap: wrap; + justify-content: flex-end; + min-width: 0; + } + + .system-info-heading-actions .pill { + max-width: 100%; + white-space: normal; + overflow-wrap: anywhere; + } + + #panel-filelogs .file-log-tabs { + max-width: 100%; + overflow-x: auto; + scrollbar-width: thin; + } + + #panel-filelogs .file-log-tab { + flex-shrink: 0; + } +} + +@media (max-width:600px) { + .sidebar .main-navigation .nav span { + display: none; + } + + .brand-mark span { + left: 15px; + top: 11px; + } + + .login-footer span { + margin: 0 5px; + } + + .page-intro { + display: block; + } + + .eyebrow { + font-size: 9px; + letter-spacing: 1.2px; + } + + h1 { + font-size: 34px; + letter-spacing: -1.4px; + } + + .lead { + font-size: 12px; + } + + .meta { + font-size: 9px; + max-width: 106px; + text-align: right; + } + + .section-heading h2 { + font-size: 20px; + } + + .chart-footer, + .login-footer { + font-size: 8px; + } + + .page-footer { + font-size: 8px; + flex-wrap: wrap; + } + + .login-section h1 { + letter-spacing: -2px; + } + + /* The standalone auth heading keeps its later, unconditional font size. */ + .login-section h1:where(:not(.auth-main h1)) { + font-size: 40px; + } + + .toast { + right: 17px; + bottom: 17px; + } + + #config-editor { + font-size: 11px; + } + + .brand { + font-size: 16px; + } + + .sidebar nav { + justify-content: space-between; + } + + .nav { + font-size: 11px; + flex: 1; + justify-content: center; + } + + .login-card .primary { + white-space: normal; + } + + .security-note { + overflow-wrap: anywhere; + } + + .sidebar nav .nav { + min-width: 0; + justify-content: center; + padding: 9px 5px; + } + + .overview-inline-hidden-item { + max-width: 100%; + } + + .vh-setting-section>.vh-grid, + .stream-backend-section>.vh-grid, + .stream-health-probe-fields, + .web-service-setting-section>.vh-grid { + grid-template-columns: minmax(0, 1fr); + } + + .vh-certificate-select-row { + align-items: stretch; + flex-direction: column; + } + + .vh-certificate-select-row>.vh-field, + .vh-certificate-select-row>button { + flex: 0 1 auto; + width: 100%; + } + + .stream-backend-section, + .web-service-setting-section { + padding: 10px; + } +} + +@media (max-width:550px) { + .overview-layout-footer { + flex-wrap: wrap; + } + + .overview-layout-footer p { + width: 100%; + } + + #panel-overview .dashboard-overview-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + #panel-overview .dashboard-overview-grid>.stat { + grid-column: auto/span 1; + } + + #panel-overview .dashboard-overview-grid.is-unlocked>[data-dashboard-widget] { + padding-top: 135px; + } +} + +@media (max-width:480px) { + .page-intro-title-row h1 { + font-size: calc(clamp(18px, 5vw, 24px)); + letter-spacing: -.6px; + } + + .overview-presets-create-controls>button { + flex: 1 1 auto; + } + + .overview-presets-row { + align-items: flex-start; + flex-direction: column; + } + + .overview-presets-actions { + width: 100%; + } +} + +@media (max-width:420px) { + .vh-form .form-actions { + flex-wrap: wrap; + } + + .vh-field input, + .vh-field select:where(:not(.edit-form select)), + .vh-field textarea:where(:not(.edit-form textarea)) { + font-size: 12px; + } + + .vh-setting-section { + padding: 10px; + } + + .backend-column-options { + grid-template-columns: minmax(0, 1fr); + } +} + +@media (max-width:380px) { + .vh-grid { + grid-template-columns: minmax(0, 1fr); + } + + .sidebar nav { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .topbar { + justify-content: flex-end; + } + + .vh-tabs button { + font-size: 10px; + padding: 5px 7px; + } +} + +@media (max-width:360px) { + .nav { + font-size: 10px; + } +} + +@media (max-height:620px) { + .log-column-options { + top: auto; + bottom: calc(100% + 4px); + width: calc(min(360px, 100vw - 24px)); + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (prefers-reduced-motion:reduce) { + .docker-publish-field .docker-publish, + .docker-publish-field .docker-publish::before, + #panel-overview .overview-meter-fill, + #panel-overview .www-segmented span { + transition: none; + } + + .app-startup-spinner { + animation: none; + } + + * { + scroll-behavior: auto !important; + } +} + +@container overview-canvas (max-width:1080px) { + #panel-overview .dashboard-overview-grid.is-aligned { + grid-template-columns: repeat(6, minmax(0, 1fr)); + } + + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget].stat:where(:not([data-dashboard-span])) { + grid-column-end: span 2; + } + + #panel-overview .dashboard-overview-grid.is-aligned>.card:not(.pool-card):where(:not([data-dashboard-span])) { + grid-column-end: span 3; + } + + #panel-overview .dashboard-overview-grid.is-aligned>.card.overview-wide-chart:where(:not([data-dashboard-span])), + #panel-overview .dashboard-overview-grid.is-aligned>.pool-card:where(:not([data-dashboard-span])) { + grid-column: 1/-1; + } + + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="2"] { + --overview-widget-placement: auto/span 2; + } + + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="3"] { + --overview-widget-placement: auto/span 3; + } + + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="4"] { + --overview-widget-placement: auto/span 4; + } + + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="12"], + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="6"] { + --overview-widget-placement: 1/-1; + } +} + +@container overview-canvas (max-width:700px) { + #panel-overview .dashboard-overview-grid.is-aligned { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget].stat { + touch-action: pan-y pinch-zoom; + overflow-wrap: anywhere; + } + + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget].stat:where(:not([data-dashboard-span])) { + grid-column-end: span 1; + } + + #panel-overview .dashboard-overview-grid.is-aligned>.stat.stat-with-action { + padding-right: 11px; + } + + #panel-overview .dashboard-overview-grid.is-aligned .stat-with-action .stat-corner-action { + position: static; + display: block; + width: max-content; + max-width: 100%; + margin: 6px 0 0; + white-space: normal; + } + + html[data-dashboard-density=normal] #panel-overview .dashboard-overview-grid.is-aligned>.stat strong { + font-size: calc(clamp(20px, 5.8vw, 26px)); + } + + html[data-dashboard-density=compact] #panel-overview .dashboard-overview-grid.is-aligned>.stat strong { + font-size: calc(clamp(19px, 5vw, 22px)); + } + + #panel-overview .dashboard-overview-grid.is-aligned>.card:not(.pool-card):where(:not([data-dashboard-span])) { + grid-column: 1/-1; + } + + #panel-overview .network-interface-charts { + max-height: none; + overflow: visible; + overscroll-behavior: auto; + } + + #panel-overview .stat-donut .overview-donut-row { + gap: 6px; + } + + #panel-overview .stat-donut .overview-donut-svg { + width: 48px; + height: 48px; + flex-basis: 48px; + } + + #panel-overview .stat-donut .overview-donut-legend { + gap: 3px; + } + + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="2"].stat { + --overview-widget-placement: auto/span 1; + } + + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="12"], + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="2"]:not(.stat), + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="3"], + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="4"], + #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="6"] { + --overview-widget-placement: 1/-1; + } +} diff --git a/src/admin/web/css/README.md b/src/admin/web/css/README.md new file mode 100644 index 0000000..6ebfe8a --- /dev/null +++ b/src/admin/web/css/README.md @@ -0,0 +1,99 @@ +# Administration stylesheet + +This directory is the source of the shared dashboard and login stylesheet. +`manifest.json` defines its complete cascade order. A normal Cargo build runs +`scripts/build/panel_css.py`, compacts these sources conservatively and embeds +one `panel-style.css` from Cargo's `OUT_DIR` into the binary. + +The browser continues to request `/assets/style_.css` once. The +existing fingerprint, login access rules and cached gzip/Brotli/Zstandard +responses remain in use. Source modules are not separate HTTP assets. The +documentation Reader/API page keeps its independent `../docs.css` stylesheet. + +## Module ownership and order + +| Order | File | Responsibility | +|---|---|---| +| 1 | `00-tokens.css` | Theme palettes, appearance tokens and custom properties shared with JavaScript preferences | +| 2 | `10-base.css` | Reset, typography, buttons, form fields, tables, shared cards, dialogs and overlays | +| 3 | `20-shell.css` | Startup, login, navigation, page structure and editor draft dock | +| 4 | `30-overview.css` | Shared charts/legends and Overview metrics, density, widget layout controls and dashboard presets | +| 5 | `40-editors.css` | Vhosts and backends, Web Server, Streams, redirects, 404 hosts and ACL controls | +| 6 | `50-settings.css` | Settings, certificates, integrations and Docker service/agent management | +| 7 | `60-monitoring.css` | Traffic, cache, transformations, live/stored logs and configuration history | +| 8 | `70-responsive.css` | All viewport/container queries and reduced-motion adjustments, in their required cascade order | + +## Editing rules + +- Update the rule in its owning module. Keep one occurrence of a selector in + each conditional scope; group selectors when they deliberately share one + declaration block and the move preserves cascade behavior. +- Reuse the existing theme and appearance variables. JavaScript also writes + live values such as chart geometry, widget layout and preferences; retain + those variable and class names when reorganizing CSS. +- Place a responsive adjustment inside the existing matching condition in + `70-responsive.css`. A container name needs whitespace before its condition, + for example `@container overview-canvas (max-width: 700px)`. +- Preserve the manifest order and the order of overlapping conditions. The + query order is part of the cascade; do not sort breakpoints mechanically. + In particular, the `760px` condition follows `620px` to preserve the later + Transform and file-log adjustments. Container queries follow viewport rules. +- Explicit dashboard spans set `--overview-widget-placement` on the widget; + one common rule applies it to `grid-column`. Container queries change that + variable for supported span values. Keep the generic no-span defaults + separate so the legacy row-start and custom-layout contracts remain intact. +- Add a new module only when it has a distinct owner and register it once in + the manifest. Missing, repeated and unlisted CSS files fail the build. + `@import`, `@charset`, path traversal and symlinked modules are rejected. +- Group identical declaration bodies within each compatible selector family. + Moving them across intervening rules requires checking specificity and source + order, including combinations of state classes. Keep vendor pseudo-elements + in separate compatible lists; a WebKit/Gecko selector mix can invalidate the + whole rule in another engine. + +The builder removes formatting whitespace beside braces/semicolons and normal +comments. It preserves selector whitespace, escaped tokens, strings, functions +and declaration order. It does not sort, merge or otherwise optimize rules at +build time. No Node.js/npm or runtime stylesheet compiler is required. + +## Verification + +From the repository root: + +```bash +python3 scripts/build/panel_css.py --check +python3 tools/dev/test_panel_css.py +python3 scripts/preflight.py +python3 scripts/audit_css_usage.py --strict +bash dev.sh check +bash dev.sh test +``` + +The first three commands require only Python 3.10+. The optional CSS audit +requires `tinycss2` and `cssselect2`; install them in a development environment, +not on the deployed server. `--structure-json PATH` records repeated selectors, +conditional blocks, properties and identical declaration blocks. `--json PATH` +records selector references. The audit follows JavaScript embedded by Rust and +flags scripts outside those entry points for review. Missing literal class +names still need manual review because JavaScript can construct them. +Strict mode also rejects identical declaration bodies within the same compatible +selector family. Its only body-repetition exception is a single rule per vendor +family when merging would create an incompatible selector list. + +Use browser comparisons for layout changes: cover login and every workspace, +open dialogs and expanded rows, themes, corner styles, dashboard density/width, +hidden/disabled states, viewport breakpoints, container widths and reduced +motion. A source audit alone does not establish visual or runtime equivalence. +Rebuild the Rust binary after every stylesheet change. + +When replacing a previously built checkout with a source ZIP, include the root +`build.rs`. If Cargo reports that `OUT_DIR/panel-style.css` is missing, follow +the [generated-CSS recovery steps](../../../../scripts/README.md#missing-generated-css-during-rust-compilation). +Source preflight does not generate Cargo outputs. The build tracks the generated +bundle as well as its inputs, and rejects a missing or empty output. + +The optional [dashboard browser regression runner](../../../../tools/dev/README-dashboard-css.md) +assembles the actual HTML and JavaScript against synthetic API fixtures and +compares a supplied baseline stylesheet with the built candidate. It includes +breakpoint/container boundaries and dynamic editor states. Its dependencies are +development-only; they are not part of the application build or deployment. diff --git a/src/admin/web/css/manifest.json b/src/admin/web/css/manifest.json new file mode 100644 index 0000000..71586cb --- /dev/null +++ b/src/admin/web/css/manifest.json @@ -0,0 +1,12 @@ +{ + "files": [ + "00-tokens.css", + "10-base.css", + "20-shell.css", + "30-overview.css", + "40-editors.css", + "50-settings.css", + "60-monitoring.css", + "70-responsive.css" + ] +} diff --git a/src/admin/web/style.css b/src/admin/web/style.css deleted file mode 100644 index 7a432bd..0000000 --- a/src/admin/web/style.css +++ /dev/null @@ -1,12666 +0,0 @@ -:root { - color-scheme: dark; - --bg: #131615; - --panel: #1b1f1d; - --panel2: #202622; - --line: #323a34; - --muted: #a0aaa0; - --text: #edf0e9; - --accent: #f69b74; - --green: #bdd293; - --red: #ffaaa2; - --sidebar-bg: #101310; - --brand-secondary: #cfe0ac; - --nav-active-bg: #242b23; - --nav-hover-bg: #20251f; - --text-subtle: #879082; - --separator: #596454; - --border-hover: #89917c; - --field-bg: #131813; - --field-line: #47523f; - --placeholder: #778070; - --row-line: #2b332c; - --outline-line: #717b67; - --chart-grid: #333c33; - --code-bg: #101810; - --code-alt-bg: #101922; - --code-alt-text: #d3e4ec; - --nav-selected-bg: #34412a; - --accent-ink: #221810; - --accent-hover: #ffb090; - --selected-ink: #10251e; - --notice-text: #c9b99c; - --notice-line: #554833; - --notice-bg: #29251c; - --pill-line: #6d5a45; - --pill-live-line: #576847; - --toast-bg: #d0dfb4; - --toast-text: #1c2818; - --toast-line: #ebf2de; - --toast-error-bg: #ffd2bd; - --toast-error-line: #ff9f7a; - --toast-error-text: #42241b; - --danger-line: #644a47; - --danger-line-strong: #79514e; - --warn-line: #786647; - --blue: #8ebcff; - --transform-chart: #d7adff; - --backdrop: rgba(0, 0, 0, .68); - --shadow-mid: #0006; - --shadow-strong: #0009; - --neutral-soft: hsla(0, 0%, 49.8%, .05); - --scrollbar-track: #151a16; - --scrollbar-thumb: #4f5d50; - --scrollbar-thumb-hover: #6b7b69; - --chart-accent: var(--accent); - --chart-secondary: var(--green); - --chart-tertiary: var(--blue); - --chart-quaternary: var(--transform-chart); - --chart-quinary: var(--red); - --chart-senary: var(--notice-text); - --chart-septenary: var(--border-hover); - --chart-octonary: var(--separator); - --chart-surface: var(--panel2); - --chart-surface-alt: var(--panel); - --chart-line-width: 2; - --chart-linecap: round; - --chart-linejoin: round; - --chart-grid-opacity: .28; - --chart-grid-dash: none; - --chart-svg-filter: none; - --rp-progress-fill: var(--chart-accent); - --rp-progress-track: var(--line); - --rp-progress-radius: 3px; - --rp-nav-active-color: var(--brand-secondary); - --rp-nav-active-bg: var(--nav-active-bg); - --rp-nav-hover-bg: var(--nav-hover-bg); - --traffic-h1: var(--chart-accent); - --traffic-h2: var(--chart-secondary); - --traffic-h3: var(--chart-tertiary); - --ui-scale: 1; - --ui-field-font-size: 12px; - --ui-field-pad-y: 6px; - --ui-field-pad-x: 8px; - --ui-field-min-height: 27px; - --ui-check-size: 15px; - --ui-range-height: 18px; - --mono: ui-monospace, SFMono-Regular, Consolas, monospace; - --sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; -} - -html[data-theme=light] { - color-scheme: light; - --bg: #f4f6f2; - --panel: #fff; - --panel2: #edf1eb; - --line: #cbd3c7; - --muted: #5e685f; - --text: #1d241f; - --accent: #b85b38; - --green: #5f783f; - --red: #b64a43; - --sidebar-bg: #eef2ec; - --brand-secondary: #6b8249; - --nav-active-bg: #e1e8dc; - --nav-hover-bg: #e8ede6; - --text-subtle: #687369; - --separator: #9ba69b; - --border-hover: #839083; - --field-bg: #fff; - --field-line: #b7c1b5; - --placeholder: #7a867b; - --row-line: #e0e5dd; - --outline-line: #98a497; - --chart-grid: #d4dbd1; - --code-bg: #edf3e9; - --code-alt-bg: #eef3f6; - --code-alt-text: #2d4049; - --nav-selected-bg: #e1e9db; - --accent-ink: #fff; - --accent-hover: #a84f30; - --selected-ink: #fff; - --notice-text: #685b45; - --notice-line: #d4c2a5; - --notice-bg: #fff9ef; - --pill-line: #cbb28f; - --pill-live-line: #a8ba8d; - --danger-line: #d7a29d; - --danger-line-strong: #c98f89; - --warn-line: #c6b27e; - --blue: #4f77a9; - --transform-chart: #7d4fa3; - --shadow-mid: rgba(31, 39, 33, .16); - --shadow-strong: rgba(31, 39, 33, .26); - --neutral-soft: rgba(70, 78, 72, .05); - --scrollbar-track: #e7ece4; - --scrollbar-thumb: #a9b5a6; - --scrollbar-thumb-hover: #879684; -} - -html:is([data-theme=light], [data-theme=cream]) { - --rp-nav-active-color: color-mix(in srgb, var(--brand-secondary) 45%, var(--text) 55%); -} - -html[data-theme=ultra_dark] { - color-scheme: dark; - --bg: #030404; - --panel: #0b0d0e; - --panel2: #121516; - --line: #292f30; - --muted: #a4afb0; - --text: #f1f4f2; - --accent: #93dfc8; - --logo-accent: #f69b74; - --logo-secondary: #cfe0ac; - --green: #b1dcb0; - --red: #ff9899; - --sidebar-bg: #000; - --brand-secondary: #a9cfb5; - --nav-active-bg: #172422; - --nav-hover-bg: #121819; - --text-subtle: #879695; - --separator: #526261; - --border-hover: #7c9f96; - --field-bg: #090b0c; - --field-line: #445352; - --placeholder: #879593; - --row-line: #202627; - --outline-line: #79968f; - --chart-grid: #303839; - --code-bg: #0e1313; - --code-alt-bg: #101a1d; - --code-alt-text: #d5eae9; - --nav-selected-bg: #213730; - --accent-ink: #071814; - --accent-hover: #b6f2dd; - --selected-ink: #071814; - --notice-text: #e6c99c; - --notice-line: #66533a; - --notice-bg: #201a11; - --pill-line: #625544; - --pill-live-line: #506c59; - --toast-bg: #bcf3da; - --toast-text: #10221a; - --toast-line: #d8ffe8; - --toast-error-bg: #ffd5d0; - --toast-error-line: #ff9c96; - --toast-error-text: #472625; - --danger-line: #754245; - --danger-line-strong: #965257; - --warn-line: #89754c; - --blue: #8bbdfb; - --transform-chart: #cfaeff; - --backdrop: rgba(0, 0, 0, .86); - --shadow-mid: rgba(0, 0, 0, .64); - --shadow-strong: rgba(0, 0, 0, .84); - --neutral-soft: rgba(132, 171, 158, .045); - --scrollbar-track: #060808; - --scrollbar-thumb: #364243; - --scrollbar-thumb-hover: #617373; -} - -html[data-theme=cream] { - color-scheme: light; - --bg: #f2ede3; - --panel: #fcf9f2; - --panel2: #eae3d6; - --line: #d5cabb; - --muted: #6b6257; - --text: #302c27; - --accent: #8c5942; - --green: #5e7655; - --red: #a54645; - --sidebar-bg: #eae2d5; - --brand-secondary: #668367; - --nav-active-bg: #e0d3c0; - --nav-hover-bg: #e6dccc; - --text-subtle: #786e61; - --separator: #afa494; - --border-hover: #968776; - --field-bg: #fffcf7; - --field-line: #baad9a; - --placeholder: #807466; - --row-line: #e2d9cb; - --outline-line: #9c8d7c; - --chart-grid: #ddd3c4; - --code-bg: #f0e8da; - --code-alt-bg: #e7ebe9; - --code-alt-text: #364f4e; - --nav-selected-bg: #d9cbb7; - --accent-ink: #fff; - --accent-hover: #774632; - --selected-ink: #fff; - --notice-text: #765e40; - --notice-line: #d9c3a1; - --notice-bg: #f8efdf; - --pill-line: #c2a78d; - --pill-live-line: #b0bd9d; - --toast-bg: #d6ead2; - --toast-text: #263d29; - --toast-line: #b8d5b6; - --toast-error-bg: #f8ded7; - --toast-error-line: #dca99c; - --toast-error-text: #60352f; - --danger-line: #d2a29a; - --danger-line-strong: #b97870; - --warn-line: #b49c6b; - --blue: #4d7292; - --transform-chart: #83659a; - --backdrop: rgba(37, 27, 16, .44); - --shadow-mid: rgba(64, 49, 34, .13); - --shadow-strong: rgba(64, 49, 34, .24); - --neutral-soft: rgba(104, 91, 69, .045); - --scrollbar-track: #ece4d7; - --scrollbar-thumb: #b5a997; - --scrollbar-thumb-hover: #978a76; -} - -html[data-theme=ultra_dark] :is(.card, .stat, dialog) { - box-shadow: 0 6px 24px rgba(0, 0, 0, .18); -} - -html[data-theme=cream] :is(.card, .stat, dialog) { - box-shadow: 0 6px 20px rgba(78, 58, 38, .055); -} - -* { - box-sizing: border-box; - scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track); - scrollbar-width: thin; -} - -::-webkit-scrollbar { - width: 10px; - height: 10px; -} - -::-webkit-scrollbar-track { - background: var(--scrollbar-track); -} - -::-webkit-scrollbar-thumb { - background: var(--scrollbar-thumb); - border: 2px solid var(--scrollbar-track); -} - -::-webkit-scrollbar-thumb:hover { - background: var(--scrollbar-thumb-hover); -} - -::-webkit-scrollbar-corner { - background: var(--scrollbar-track); -} - -html.app-loading { - background: var(--bg); -} - -html.app-loading .shell { - visibility: hidden; - pointer-events: none; -} - -.app-startup { - position: fixed; - inset: 0; - z-index: 10000; - display: grid; - place-items: center; - padding: 24px; - box-sizing: border-box; - background: var(--bg, #10151b); - color: var(--text, #edf2f7); - text-align: center; -} - -html:not(.app-loading) .app-startup { - display: none; -} - -.app-startup-card { - width: min(100%, 360px); -} - -.app-startup-brand { - font: 700 29px var(--mono, monospace); - letter-spacing: -2px; - margin-bottom: 24px; -} - -.app-startup-brand span { - color: var(--logo-secondary, var(--accent, #65cfa2)); -} - -.app-startup-spinner { - box-sizing: border-box; - width: 40px; - height: 40px; - margin: 0 auto 22px; - border: 3px solid var(--line, #374352); - border-top-color: var(--accent, #65cfa2); - border-radius: 50%; - animation: app-startup-spin .8s linear infinite; -} - -.app-startup h2 { - margin: 0; - font-size: 19px; - font-weight: 600; - line-height: 1.4; -} - -.app-startup-message, -.app-startup-recovery p { - font-size: 13px; - line-height: 1.6; - color: var(--muted, #b5c0cc); -} - -.app-startup-recovery { - visibility: hidden; - animation: app-startup-reveal 0s linear 12s forwards; -} - -.app-startup-recovery .outline:not([hidden]) { - margin-top: 10px; - display: inline-flex; -} - -html.app-startup-failed .app-startup-message, -html.app-startup-failed .app-startup-spinner { - display: none; -} - -html.app-startup-failed .app-startup-recovery { - visibility: visible; - animation: none; -} - -.app-startup-detail { - overflow-wrap: anywhere; -} - -@keyframes app-startup-spin { - to { - transform: rotate(1turn); - } -} - -@keyframes app-startup-reveal { - to { - visibility: visible; - } -} - -body { - margin: 0; - background: var(--bg); - color: var(--text); - font-family: var(--sans); - line-height: 1.5; - zoom: var(--ui-scale, 1); -} - -button, -input, -select, -textarea { - font: inherit; -} - -a, -button, -input, -select, -textarea { - -webkit-tap-highlight-color: transparent; -} - -a, -button { - touch-action: manipulation; -} - -button { - cursor: pointer; - border: 0; -} - -button:disabled { - opacity: .5; - cursor: wait; -} - -[hidden] { - display: none !important; -} - -a { - color: inherit; - text-decoration: none; -} - -h1, -h2, -h3, -p { - margin: 0; -} - -a:focus-visible, -button:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 4px; -} - -h2 { - letter-spacing: -.5px; -} - -.shell { - display: grid; - min-height: calc(100vh / var(--ui-scale, 1)); -} - -.sidebar { - position: sticky; - top: 0; - height: calc(100vh / var(--ui-scale, 1)); - background: var(--sidebar-bg); - border-right: 1px solid var(--line); - display: flex; - flex-direction: column; -} - -.brand { - display: flex; - align-items: center; - font-weight: 780; - line-height: 1.1; -} - -.brand-mark { - font: 700 33px var(--mono); - border: 1px solid var(--logo-accent, var(--accent)); - color: var(--logo-accent, var(--accent)); - place-content: center; - position: relative; - box-sizing: border-box; - padding-right: 2px; -} - -.brand-mark span { - font-size: 22px; - left: 29px; - top: 19px; - color: var(--logo-secondary, var(--brand-secondary)); -} - -.edition { - font: 9px/1.6 var(--mono); - color: var(--muted); - letter-spacing: 1px; -} - -.edition span { - display: block; - color: var(--accent); - margin-top: 5px; -} - -nav { - flex-direction: column; -} - -.local-tag, -.nav, -nav { - display: flex; -} - -.nav { - align-items: center; - gap: 13px; - text-align: left; - background: transparent; - color: var(--muted); - border-left: 2px solid transparent; - border-radius: var(--rp-radius-control, 0); -} - -.nav span { - font: 11px var(--mono); - opacity: .65; -} - -.nav b { - font-weight: 530; -} - -.nav.active { - background: var(--rp-nav-active-bg); - color: var(--rp-nav-active-color); - border-left-color: var(--rp-nav-active-color); -} - -.nav:not(.active):hover { - background: var(--rp-nav-hover-bg); -} - -.sidebar .main-navigation .nav:focus-visible { - outline: 2px solid var(--rp-nav-active-color); - outline-offset: 2px; -} - -.sidebar-bottom { - margin-top: auto; - padding-top: 32px; -} - -.small-label { - font: 10px var(--mono); - color: var(--muted); -} - -.sidebar-bottom p { - font-size: 11px; - line-height: 1.9; - color: var(--text-subtle); - margin: 13px 0 26px; -} - -.local-tag { - font: 9px var(--mono); - color: var(--green); - gap: 8px; - align-items: center; -} - -.local-tag i, -.signal { - display: inline-block; - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--green); -} - -main { - min-width: 0; - width: 100%; -} - -.topbar { - border-bottom: 1px solid var(--line); - display: flex; - justify-content: space-between; - align-items: center; - gap: 14px; -} - -.breadcrumb { - font: 10px var(--mono); - color: var(--muted); - letter-spacing: 1px; -} - -.breadcrumb span { - margin: 0 13px; - color: var(--separator); -} - -.breadcrumb b { - font-weight: 400; - color: var(--text); -} - -.top-actions { - display: flex; - align-items: center; -} - -.version { - font: 10px var(--mono); -} - -.quiet, -.version { - color: var(--muted); -} - -.quiet { - background: transparent; - border: 1px solid var(--line); - border-radius: var(--rp-radius-control, 3px); -} - -.quiet:hover { - color: var(--text); - border-color: var(--border-hover); -} - -.primary { - background: var(--accent); - color: var(--accent-ink); - border: 1px solid var(--accent); - font-weight: 650; - border-radius: var(--rp-radius-control, 3px); - white-space: nowrap; -} - -.primary:hover { - background: var(--accent-hover); -} - -.outline { - display: inline-flex; - justify-content: center; - align-items: center; - border: 1px solid var(--outline-line); - background: transparent; - color: var(--text); - border-radius: var(--rp-radius-control, 3px); - white-space: nowrap; -} - -.outline:hover { - border-color: var(--accent); -} - -.eyebrow { - font: 10px var(--mono); - letter-spacing: 2px; - color: var(--green); - display: flex; - gap: 8px; - align-items: center; - margin-bottom: 16px; -} - -h1 { - font-size: calc(clamp(32px, 3.6vw, 52px)); - line-height: 1.1; - font-weight: 520; - letter-spacing: -2px; -} - -.lead { - font-size: 13px; - line-height: 1.7; - color: var(--muted); - margin-top: 15px; - max-width: 690px; -} - -.page-intro { - display: flex; - justify-content: space-between; - align-items: center; -} - -.notice { - display: flex; - gap: 13px; - line-height: 1.65; - color: var(--notice-text); - border: 1px solid var(--notice-line); - background: var(--notice-bg); - border-radius: var(--rp-radius-surface, 3px); -} - -.notice b { - font-family: var(--mono); - color: var(--accent); -} - -.stats-grid { - display: grid; -} - -.stat { - border: 1px solid var(--line); - background: var(--panel); - position: relative; -} - -.stat:first-child { - border-top: 2px solid var(--accent); -} - -.stat small, -.stat>span { - color: var(--muted); -} - -.stat strong { - display: block; - font-family: var(--mono); - font-weight: 450; - letter-spacing: -1px; -} - -.stat:first-child small { - color: var(--green); -} - -.card { - border: 1px solid var(--line); - background: var(--panel); - min-width: 0; -} - -.card-heading { - display: flex; - align-items: center; - justify-content: space-between; -} - -.card-heading .small-label { - display: block; -} - -.activity-card .card-heading { - flex-wrap: wrap; -} - -.activity-card #chart-peak { - margin-left: auto; - color: var(--muted); - font: 9px var(--mono); - white-space: nowrap; -} - -.pill { - padding: 4px 8px; - border: 1px solid var(--pill-line); - color: var(--accent); - font: 9px var(--mono); - letter-spacing: 1px; -} - -.pill.live { - border-color: var(--pill-live-line); - color: var(--green); -} - -#traffic-chart, -table { - width: 100%; -} - -.chart-grid { - stroke: var(--chart-grid); - stroke-width: 1; - fill: none; - opacity: var(--chart-grid-opacity, .55); - stroke-dasharray: var(--chart-grid-dash, 2 5); -} - -#sparkline { - stroke: var(--chart-accent); - stroke-width: 2; - fill: none; - vector-effect: non-scaling-stroke; -} - -.chart-footer { - display: flex; - justify-content: space-between; - color: var(--muted); - font: 9px var(--mono); - gap: 10px; -} - -.table-scroll { - overflow: auto; -} - -table { - border-collapse: collapse; - text-align: left; - font-size: 11px; - white-space: nowrap; -} - -th { - font: 10px var(--mono); - color: var(--muted); - font-weight: 400; - border-bottom: 1px solid var(--line); -} - -td { - border-bottom: 1px solid var(--row-line); - font-family: var(--mono); - max-width: 260px; - overflow: hidden; - text-overflow: ellipsis; -} - -td:first-child { - color: var(--muted); -} - -.status-good { - color: var(--green); -} - -.status-error { - color: var(--red); -} - -.fine, -.meta { - font-size: 11px; - line-height: 1.7; - color: var(--muted); -} - -.empty { - color: var(--muted); - font-size: 12px; -} - -.section-heading { - display: flex; - justify-content: space-between; - align-items: center; - gap: 10px; -} - -.entity { - border-bottom: 1px solid var(--line); -} - -.entity:first-child { - padding-top: 0; -} - -.entity:last-child { - border-bottom: 0; - padding-bottom: 0; -} - -.entity-top { - display: flex; - justify-content: space-between; - align-items: center; - gap: 12px; -} - -.entity-title { - font-weight: 650; -} - -.entity-details { - font: 11px/1.8 var(--mono); - color: var(--muted); - overflow-wrap: anywhere; -} - -.entity-actions { - display: flex; - flex-wrap: wrap; -} - -.entity-actions button { - padding: 5px 9px; - font-size: 10px; -} - -.entity small { - font: 9px var(--mono); - color: var(--green); -} - -.edit-form label, -.login-card label { - display: block; - color: var(--muted); - font-size: 11px; - margin: 16px 0 7px; -} - -input:not([type=checkbox]):not([type=radio]), -select, -textarea { - display: block; - width: 100%; - background: var(--field-bg); - border: 1px solid var(--field-line); - border-radius: var(--rp-radius-control, 3px); - color: var(--text); - min-width: 0; -} - -input:not([type=checkbox]):not([type=radio]):focus, -select:focus, -textarea:focus { - outline: 1px solid var(--green); - border-color: var(--green); -} - -input::placeholder { - color: var(--placeholder); -} - -input[type=checkbox], -input[type=radio] { - display: inline-block; - width: var(--ui-check-size); - height: var(--ui-check-size); - min-width: var(--ui-check-size); - min-height: var(--ui-check-size); - padding: 0; - margin: 0; - accent-color: var(--green); - vertical-align: middle; -} - -input.field-invalid, -select.field-invalid, -textarea.field-invalid { - border-color: var(--red) !important; - outline: 1px solid rgba(255, 170, 162, .72); -} - -.host-chip-editor.field-invalid, -.listener-choices.field-invalid { - border-color: var(--red) !important; - box-shadow: 0 0 0 1px rgba(255, 170, 162, .38); -} - -.settings-tabs button.has-unsaved:after { - content: ""; - display: inline-block; - width: 6px; - height: 6px; - margin-left: 7px; - border-radius: 50%; - background: currentColor; - vertical-align: middle; - opacity: .78; -} - -button.is-saving { - cursor: progress; -} - -.check { - display: flex !important; - align-items: center; - gap: 9px; - margin-top: 13px !important; -} - -.form-actions { - display: flex; - align-items: center; - flex-wrap: wrap; -} - -#config-editor { - font: 12px/1.7 var(--mono); - resize: vertical; - min-height: 330px; - margin-top: 16px; - tab-size: 2; - white-space: pre; -} - -#settings-page-manual-config>p { - margin-top: 20px; -} - -.manual-config-source { - padding: 12px 16px; - margin-top: 14px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 8px); - overflow-wrap: anywhere; -} - -.manual-config-source p { - margin: 6px 0 0; -} - -.manual-config-path { - font: 12px/1.6 var(--mono); - user-select: text; -} - -.page-footer { - display: flex; - justify-content: space-between; - gap: 15px; - border-top: 1px solid var(--line); - font: 9px var(--mono); - color: var(--muted); - letter-spacing: .5px; -} - -.page-footer b { - color: var(--accent); - font-weight: 400; - margin-left: 9px; -} - -.page-footer .footer-update { - color: var(--green); - text-decoration: underline; - text-underline-offset: 2px; -} - -.page-footer .footer-update:hover { - color: var(--text); -} - -.login-section { - padding: 62px 0 40px; - max-width: 720px; -} - -.login-section h1 { - font-size: calc(clamp(42px, 5vw, 68px)); - letter-spacing: -3px; - max-width: 640px; -} - -.login-section .lead { - font-size: 15px; - max-width: 570px; -} - -.login-card { - background: var(--panel); - border: 1px solid var(--line); - padding: 27px; -} - -.login-card>p { - font-size: 12px; - color: var(--muted); - line-height: 1.7; -} - -.command { - display: block; - padding: 12px 15px; - background: var(--code-bg); - border-left: 2px solid var(--green); - font-size: 13px; - color: var(--green); - margin: 15px 0 20px; -} - -.login-card .primary { - width: 100%; - margin-top: 18px; -} - -.login-card .fine { - font-size: 10px; - margin-top: 16px; -} - -.login-footer { - font: 9px var(--mono); - letter-spacing: 1.7px; - color: var(--muted); - margin-top: 30px; - line-height: 2; -} - -.login-footer span { - margin: 0 11px; - color: var(--accent); -} - -.toast { - position: fixed; - bottom: 26px; - right: 26px; - max-width: calc(min(580px, 100vw - 40px)); - background: var(--toast-bg); - color: var(--toast-text); - border: 1px solid var(--toast-line); - padding: 14px 20px; - font-size: 13px; - z-index: 50; - box-shadow: 0 6px 35px var(--shadow-mid); - white-space: pre-wrap; - overflow-wrap: anywhere; -} - -.toast.error { - background: var(--toast-error-bg); - border-color: var(--toast-error-line); - color: var(--toast-error-text); -} - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - -@media (min-width:1600px) { - main { - padding: 0 58px; - } -} - -@media (max-width:1150px) { - .shell { - grid-template-columns: 184px minmax(0, 1fr); - } - - .sidebar { - padding: 29px 17px; - } - - main { - padding: 0 25px; - } - - .stats-grid { - gap: 10px; - } - - .stat { - padding: 18px 15px; - } - - .card { - padding: 20px; - } -} - -@media (max-width:850px) { - .sidebar { - border-right: 0; - border-bottom: 1px solid var(--line); - } - - .brand { - letter-spacing: 1px; - gap: 9px; - } - - .brand-mark { - letter-spacing: -4px; - } - - .brand-mark span { - font-size: 15px; - left: 18px; - top: 12px; - } - - nav { - flex-direction: row; - margin-left: auto; - gap: 5px; - } - - .nav { - gap: 7px; - font-size: 11px; - border-left: 0; - border-bottom: 2px solid transparent; - } - - .nav.active { - border-bottom-color: var(--rp-nav-active-color); - } - - h1 { - font-size: 36px; - } - - .stats-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .login-section { - padding-top: 42px; - } -} - -@media (max-width:600px) { - main { - padding: 0 17px; - } - - .sidebar { - gap: 10px; - } - - .nav span, - .version { - display: none; - } - - .brand-mark { - width: 33px; - height: 33px; - font-size: 21px; - } - - .brand-mark span { - left: 15px; - top: 11px; - } - - .topbar { - height: 58px; - } - - .breadcrumb { - font-size: 8px; - letter-spacing: .4px; - } - - .breadcrumb span, - .login-footer span { - margin: 0 5px; - } - - .top-actions { - gap: 6px; - } - - .quiet { - font-size: 11px; - padding: 7px 9px; - } - - .page-intro { - display: block; - padding-top: 25px; - } - - .eyebrow { - font-size: 9px; - letter-spacing: 1.2px; - } - - h1 { - font-size: 34px; - letter-spacing: -1.4px; - } - - .lead { - font-size: 12px; - } - - .notice { - font-size: 10px; - } - - .stats-grid { - gap: 10px; - } - - .stat strong { - font-size: 30px; - } - - .card { - padding: 17px; - } - - .card-heading { - gap: 10px; - } - - .card-heading h2 { - font-size: 18px; - } - - .meta { - font-size: 9px; - max-width: 106px; - text-align: right; - } - - .section-heading h2 { - font-size: 20px; - } - - .chart-footer, - .login-footer, - .page-footer { - font-size: 8px; - } - - .page-footer { - flex-wrap: wrap; - } - - .login-section h1 { - letter-spacing: -2px; - } - - .login-card { - padding: 20px; - } - - .toast { - right: 17px; - bottom: 17px; - } - - #config-editor { - font-size: 11px; - } -} - -.brand-mark { - display: flex; - align-items: center; - justify-content: center; - letter-spacing: -3px; -} - -.brand-mark span { - position: static; -} - -.brand { - letter-spacing: -.6px; - font-size: 17px; - gap: 8px; -} - -.brand-mark { - flex-shrink: 0; - width: 47px; - height: 47px; - font-size: 29px; -} - -#policy-rules { - font: 11px/1.6 var(--mono); - resize: vertical; -} - -.session-user { - margin: 17px 0 12px; -} - -.security-note { - margin: 22px 0; -} - -.entity-details { - white-space: pre-wrap; -} - -.login-card { - max-width: 510px; -} - -.signal.status-error { - background: var(--red); -} - -@media (max-width:850px) { - .brand { - font-size: 14px; - } - - .brand-mark { - width: 34px; - height: 34px; - font-size: 22px; - } -} - -@media (max-width:600px) { - .brand { - font-size: 16px; - } - - .sidebar nav { - width: 100%; - justify-content: space-between; - margin-top: 5px; - } - - .nav { - font-size: 11px; - padding: 10px 9px; - flex: 1; - justify-content: center; - } - - .sidebar { - flex-wrap: wrap; - padding: 12px 16px; - } - - .login-section { - padding-top: 28px; - } - - .login-section h1 { - font-size: 40px; - } - - .login-card .primary { - white-space: normal; - } - - .security-note { - overflow-wrap: anywhere; - } -} - -@media (max-width:360px) { - .nav { - font-size: 10px; - padding: 8px 4px; - } - - .sidebar { - padding-left: 12px; - padding-right: 12px; - } -} - -.command, -.fine, -.meta, -.mini-metrics dd { - overflow-wrap: anywhere; -} - -.command { - white-space: normal; -} - -#panel-tls .section-heading { - flex-wrap: wrap; - gap: 12px; -} - -@media (max-width:600px) { - .sidebar nav { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 4px; - } - - .sidebar nav .nav { - min-width: 0; - justify-content: center; - padding: 9px 5px; - } -} - -main { - max-width: none; -} - -.sidebar { - padding-top: 24px; - padding-bottom: 22px; -} - -.edition { - margin: 17px 0 28px; -} - -nav { - gap: 5px; -} - -.top-actions { - gap: 8px; -} - -.language-select { - width: auto; - max-width: 160px; - padding: 6px 9px; - font-size: 11px; -} - -.page-intro .eyebrow { - font-size: 9px; -} - -.notice { - font-size: 10px; -} - -.stats-grid { - grid-template-columns: repeat(6, minmax(0, 1fr)); -} - -.stat>span { - font-size: 11px; -} - -.stat strong { - line-height: 1.3; -} - -h2 { - font-size: 17px; -} - -h3 { - font-size: 14px; - margin-bottom: 10px; -} - -.card-heading { - gap: 10px; -} - -.small-label { - font-size: 9px; - letter-spacing: 1.2px; -} - -#traffic-chart { - height: 107px; - margin: 3px 0; -} - -.system-card h2 { - font: 23px var(--mono); -} - -.system-card h2 small { - font: 10px var(--mono); - color: var(--muted); -} - -#system-chart { - display: block; - width: 100%; - height: 65px; -} - -#system-sparkline { - fill: none; - stroke: var(--chart-secondary); - stroke-width: 2; - vector-effect: non-scaling-stroke; -} - -.memory-line { - display: flex; - justify-content: space-between; - gap: 8px; - font-size: 9px; - color: var(--muted); - margin-top: 4px; -} - -.memory-line b { - font-weight: 400; - font-family: var(--mono); -} - -progress { - display: block; - width: 100%; - height: 5px; - margin: 6px 0 9px; - border: 0; - appearance: none; - -webkit-appearance: none; - background: var(--rp-progress-track); - accent-color: var(--rp-progress-fill); - border-radius: var(--rp-progress-radius); - overflow: hidden; -} - -progress::-webkit-progress-bar { - background: var(--rp-progress-track); - border-radius: var(--rp-progress-radius); -} - -progress::-webkit-progress-value { - background: var(--rp-progress-fill); - border-radius: var(--rp-progress-radius); -} - -progress::-moz-progress-bar { - background: var(--rp-progress-fill); - border-radius: var(--rp-progress-radius); -} - -.diagnostics-grid { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); -} - -.mini-heading { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; -} - -.mini-heading h3 { - font-size: 12px; - margin: 0; -} - -.mini-metrics { - margin: 8px 0 0; -} - -.mini-metrics div { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: 10px; -} - -.mini-metrics dt { - font-size: 10px; - color: var(--muted); -} - -.mini-metrics dd { - font: 10px var(--mono); - margin: 0; - text-align: end; -} - -.mini-live-chart-stack { - display: grid; - gap: 8px; - margin-top: 10px; -} - -.mini-live-chart { - min-width: 0; - margin-top: 10px; - border-top: 1px solid var(--line); - padding-top: 8px; -} - -.mini-live-chart-stack .mini-live-chart { - margin-top: 0; -} - -.mini-live-chart-head { - display: flex; - justify-content: space-between; - align-items: baseline; - gap: 8px; - margin-bottom: 5px; - color: var(--muted); - font: 10px var(--mono); -} - -.mini-live-chart-head b { - color: var(--text); - font-weight: 600; - text-align: right; -} - -.mini-live-chart-actions { - display: inline-flex; - align-items: center; - justify-content: flex-end; - gap: 7px; - min-width: 0; -} - -.mini-live-chart svg { - display: block; - width: 100%; - height: 60px; - overflow: visible; - filter: var(--chart-svg-filter); -} - -.diagnostic-line { - fill: none; - stroke: var(--chart-accent); - stroke-width: var(--chart-line-width); - stroke-linecap: var(--chart-linecap); - stroke-linejoin: var(--chart-linejoin); - vector-effect: non-scaling-stroke; -} - -.cache-hit-line, -.network-tx-line { - stroke: var(--chart-secondary); -} - -.cache-miss-line { - stroke: var(--chart-accent); -} - -.cache-bypass-line { - stroke: var(--chart-tertiary); -} - -.process-rss-line { - stroke: var(--chart-secondary); -} - -.mini-chart-legend { - display: flex; - justify-content: flex-end; - gap: 10px; - margin-top: 3px; - font: 9px var(--mono); - color: var(--muted); -} - -.mini-chart-legend span:before { - content: ""; - display: inline-block; - width: 12px; - height: 2px; - margin-right: 4px; - vertical-align: middle; - background: currentColor; -} - -.cache-hit-key { - color: var(--green); -} - -.cache-miss-key { - color: var(--accent); -} - -.cache-bypass-key { - color: var(--muted); -} - -.log-column-picker { - position: relative; - font-size: 10px; -} - -.log-column-picker>summary { - list-style: none; - cursor: pointer; - user-select: none; - padding: 6px 9px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-control, 4px); - min-height: 30px; - display: flex; - align-items: center; -} - -.log-column-picker>summary::-webkit-details-marker { - display: none; -} - -.log-column-picker[open]>summary { - border-color: var(--green); -} - -.log-column-options { - position: absolute; - z-index: 80; - inset-inline-end: 0; - top: calc(100% + 4px); - width: calc(min(190px, 100vw - 24px)); - overflow: visible; - padding: 4px; - display: grid; - grid-template-columns: minmax(0, 1fr); - gap: 1px; - background: var(--panel2); - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 5px); - box-shadow: 0 10px 26px var(--shadow-mid); -} - -.log-column-option { - min-width: 0; - min-height: 22px; - margin: 0 !important; - padding: 3px 5px; - gap: 7px; - border-radius: var(--rp-radius-control, 3px); - font-size: 10px; - line-height: 1.2; - white-space: nowrap; -} - -.log-column-option:hover, -.logs-card th { - background: var(--panel); -} - -@media (max-height:620px) { - .log-column-options { - top: auto; - bottom: calc(100% + 4px); - width: calc(min(360px, 100vw - 24px)); - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -.logs-card .table-scroll { - max-height: 260px; -} - -.logs-card th { - position: sticky; - top: 0; -} - -th { - padding: 7px 8px; -} - -td { - padding: 8px; -} - -.edit-form label { - margin: 10px 0 5px; -} - -.edit-form .check { - margin-top: 9px !important; -} - -.form-actions { - gap: 9px; - margin-top: 13px; -} - -.entity { - padding: 12px 0; -} - -.entity-details { - margin: 6px 0; - line-height: 1.6; -} - -.entity-actions { - gap: 6px; -} - -.entity-title { - font-size: 13px; -} - -.section-heading { - margin: 16px 0 12px; -} - -.section-heading h2 { - margin-top: 4px; -} - -.subheading { - margin-top: 17px; - margin-bottom: 7px !important; -} - -.field-grid, -.switch-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 0 12px; -} - -.pending-note { - font-size: 11px; - color: var(--accent); - padding: 9px 0; -} - -.header-rule { - display: grid; - grid-template-columns: 86px minmax(90px, 1fr) minmax(80px, 1.2fr) auto; - gap: 6px; - margin: 8px 0; -} - -.header-rule input, -.header-rule select { - padding: 7px !important; - font-size: 11px; -} - -.header-rule button { - font-size: 10px; - padding: 6px; -} - -.header-rules { - margin-bottom: 9px; -} - -.edit-form>.fine { - margin-top: 8px; -} - -.empty { - padding: 17px 3px; -} - -.login-section { - padding-top: 40px; -} - -.login-card { - margin-top: 23px; -} - -.chart-footer { - line-height: 1.5; -} - -.chart-footer>span, -.mini-metrics dd { - min-width: 0; -} - -[dir=rtl] .nav { - text-align: right; - border-left: 0; - border-right: 2px solid transparent; -} - -[dir=rtl] .nav.active { - border-right-color: var(--rp-nav-active-color); -} - -[dir=rtl] table { - text-align: right; -} - -[dir=rtl] #config-editor, -[dir=rtl] .entity-details, -[dir=rtl] input[type=url] { - direction: ltr; - text-align: left; -} - -@media (max-width:1200px) { - main { - padding: 0 18px; - } - - .stats-grid { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - - .diagnostics-grid { - gap: 10px; - } - - .header-rule { - grid-template-columns: 80px 1fr auto; - } - - .header-rule .header-value { - grid-column: 2; - grid-row: 2; - } - - .header-rule button { - grid-column: 3; - grid-row: 1/3; - } -} - -@media (max-width:850px) { - .page-intro { - padding-top: 18px; - } - - .diagnostics-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .diagnostics-grid>.card:last-child { - grid-column: 1/-1; - } -} - -@media (max-width:620px) { - .sidebar { - align-items: flex-start; - flex-direction: column; - gap: 12px; - } - - .nav { - padding: 8px 7px; - min-width: 0; - } - - .breadcrumb b, - .nav span, - .topbar .version { - display: none; - } - - .breadcrumb { - font-size: 9px; - letter-spacing: 0; - } - - .breadcrumb span { - margin: 0 5px; - } - - .language-select { - max-width: 110px; - font-size: 10px; - } - - .page-intro { - align-items: flex-start; - } - - .page-intro .lead { - font-size: 11px; - } - - .page-intro .eyebrow { - letter-spacing: .5px; - font-size: 8px; - } - - .notice { - font-size: 10px; - gap: 8px; - } - - .stats-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 8px; - } - - .stat { - padding: 10px; - } - - .stat strong { - font-size: 24px; - } - - .logs-card>.card-heading { - align-items: flex-start; - flex-direction: column; - } - - .log-tools { - width: 100%; - } - - .log-tools input:not([type=checkbox]):not([type=radio]) { - flex: 1; - min-width: 95px; - width: 95px; - } - - .log-tools select { - width: 95px; - } - - .log-tools button { - white-space: normal; - max-width: 85px; - } - - .card { - padding: 13px; - } - - .page-footer { - flex-direction: column; - gap: 7px; - } - - .field-grid { - gap: 0 9px; - } - - .header-rule { - grid-template-columns: 76px minmax(0, 1fr) auto; - } - - .header-rule button { - max-width: 65px; - overflow-wrap: anywhere; - } - - .switch-grid { - grid-template-columns: minmax(0, 1fr); - } - - .form-actions { - gap: 8px; - } - - .outline, - .primary { - white-space: normal; - } - - .mini-metrics { - margin-top: 9px; - } - - .memory-line { - font-size: 10px; - } - - .login-section { - padding-top: 25px; - } - - .login-card { - padding: 18px; - } - - .topbar { - gap: 7px; - } -} - -.vhost-layout { - grid-template-columns: minmax(220px, .7fr) minmax(0, 2fr); - gap: 12px; - align-items: start; -} - -.vh-field, -.vh-form, -.vh-group, -.vhost-layout>.card { - min-width: 0; -} - -.vh-group { - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 6px); -} - -.vh-group legend, -.vh-group>summary { - font-weight: 600; - padding: 0 5px; - color: var(--text); -} - -.vh-group>summary { - cursor: pointer; - margin-bottom: 6px; -} - -.vh-grid { - display: grid; - align-items: start; -} - -.vh-field { - display: flex; - flex-direction: column; - gap: 4px; - margin: 0; - font-size: 11px; - color: var(--muted); -} - -.vh-field input, -.vh-field select, -.vh-field textarea { - width: 100%; - min-width: 0; - max-width: 100%; - padding: 6px 7px; - font-size: 12px; - margin: 0; - box-sizing: border-box; -} - -.vh-field textarea { - resize: vertical; - font-family: var(--mono); -} - -.vh-check { - font-size: 11px; - min-height: 28px; - margin: 0; - align-items: center; - gap: 6px; -} - -.vh-check input { - flex: 0 0 auto; -} - -.vh-enable-control { - display: grid !important; - grid-template-columns: auto minmax(0, 1fr) auto; - grid-template-rows: auto auto; - align-items: center; - gap: 2px 10px; - margin-top: 5px !important; - padding: 11px 12px; - border: 1px solid var(--pill-live-line); - background: var(--panel2); - box-shadow: inset 3px 0 0 var(--green); -} - -.vh-enable-control>input { - grid-column: 1; - grid-row: 1/3; - width: 18px; - height: 18px; - accent-color: var(--green); -} - -.vh-enable-control>span { - grid-column: 2; - grid-row: 1; - color: var(--text); - font-size: 12px; - font-weight: 700; -} - -.vh-enable-hint { - grid-column: 2; - grid-row: 2; - color: var(--muted); - font-size: 10px; -} - -.vh-enable-state { - grid-column: 3; - grid-row: 1/3; - padding: 3px 7px; - border: 1px solid var(--pill-live-line); - color: var(--green); - font: 600 10px var(--mono); - text-transform: uppercase; - letter-spacing: .04em; -} - -.vh-enable-control.is-disabled { - border-color: var(--danger-line-strong); - box-shadow: inset 3px 0 0 var(--red); -} - -.vh-enable-control.is-disabled .vh-enable-state { - border-color: var(--danger-line); - color: var(--red); -} - -.vh-wide { - grid-column: 1/-1; - min-width: 0; -} - -.vh-members { - display: grid; -} - -.vh-row { - padding: 8px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 5px); - min-width: 0; - background: var(--bg); -} - -.vh-member>.quiet { - margin-top: 6px; -} - -.vh-member details { - font-size: 11px; -} - -.vh-member details>summary { - cursor: pointer; - color: var(--muted); - padding: 3px 0; -} - -.backend-card-heading { - align-items: flex-start; - gap: 10px; -} - -.backend-table-tools { - display: flex; - align-items: center; - justify-content: flex-end; - flex-wrap: wrap; - gap: 7px; - margin-left: auto; -} - -#pool-search { - width: calc(min(250px, 30vw)); - min-width: 170px; -} - -.backend-sort-control { - display: flex; - align-items: center; - gap: 6px; - color: var(--muted); - font-size: 11px; -} - -.backend-sort-control select { - width: auto; - min-width: 120px; -} - -.backend-column-picker { - position: relative; -} - -.backend-column-picker>summary { - list-style: none; - cursor: pointer; - user-select: none; -} - -.backend-column-picker>summary::-webkit-details-marker { - display: none; -} - -.backend-column-options { - position: absolute; - z-index: 20; - top: calc(100% + 6px); - right: 0; - display: grid; - grid-template-columns: repeat(2, minmax(145px, 1fr)); - gap: 1px 12px; - min-width: 330px; - max-height: 330px; - overflow: auto; - padding: 10px 12px 12px; - border: 1px solid var(--line); - background: var(--panel); - box-shadow: 0 12px 28px var(--shadow-strong); -} - -.backend-column-options .check { - margin-top: 4px !important; - font-size: 11px; -} - -.pool-card .fine { - margin: 0 0 8px; -} - -.pool-table { - font-size: 11px; - width: 100%; - min-width: 640px; -} - -.pool-table td:nth-child(n+4) { - font-variant-numeric: tabular-nums; - text-align: right; -} - -.pool-table th:nth-child(n+4) { - text-align: right; -} - -.pool-table td:first-child, -.pool-table td:nth-child(2) { - max-width: 200px; - overflow: hidden; - text-overflow: ellipsis; -} - -#toast[popover], -.app-confirm-message, -.cache-table td, -.location-summary, -.vh-health-summary, -.vh-live-summary, -.vhost-layout .entity-details { - overflow-wrap: anywhere; -} - -.vh-form input:disabled { - opacity: .5; -} - -@media (max-width:1150px) { - .vhost-layout { - grid-template-columns: minmax(190px, .6fr) minmax(0, 1.6fr); - } - - .vh-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -@media (max-width:800px) { - .vhost-layout { - grid-template-columns: minmax(0, 1fr); - } - - .vh-form .form-actions { - position: static; - } - - .vh-group { - padding: 8px; - } - - .pool-table { - min-width: 620px; - } -} - -@media (max-width:420px) { - .vh-grid { - grid-template-columns: minmax(0, 1fr); - } - - .vh-group { - padding: 6px; - } - - .vh-form .form-actions { - flex-wrap: wrap; - } - - .vh-field input, - .vh-field select, - .vh-field textarea { - font-size: 12px; - } -} - -body { - --ui-gap: 5px; - --ui-card: 8px; - --ui-font: 12px; - --ui-control: 27px; - --button-padding: 6px 10px; - --button-font-size: 11px; - --button-min-height: var(--ui-control); - font-size: var(--ui-font); -} - -body[data-density=dense] { - --ui-gap: 4px; - --ui-card: 7px; - --ui-font: 11.5px; - --ui-control: 25px; -} - -body[data-density=comfortable] { - --ui-gap: 13px; - --ui-card: 17px; - --ui-font: 14px; - --ui-control: 36px; -} - -.shell { - grid-template-columns: 182px minmax(0, 1fr); -} - -main { - padding: 0 16px; -} - -.sidebar { - padding: 20px 13px; -} - -.sidebar nav { - margin-top: 22px; -} - -.nav { - padding: 9px 10px; -} - -.topbar { - height: 48px; -} - -.page-intro { - padding: 13px 0 11px; - gap: 12px; -} - -.page-intro h1 { - font-size: 25px; - letter-spacing: -.7px; -} - -.page-intro .eyebrow { - margin-bottom: 4px; -} - -.page-intro .lead { - font-size: 11px; - margin-top: 4px; -} - -.notice { - margin-bottom: var(--ui-gap); - padding: 5px 9px; -} - -.card { - padding: var(--ui-card); -} - -.card-heading { - margin-bottom: 8px; -} - -.card-heading h2 { - font-size: 16px; -} - -.card-heading .small-label { - font-size: 8px; - margin-bottom: 3px; -} - -.diagnostics-grid, -.stats, -.stats-grid { - gap: var(--ui-gap); - margin-bottom: var(--ui-gap); -} - -.stat { - padding: 11px; -} - -.stat p, -.stat small { - font-size: 10px; -} - -.activity-card svg { - height: 105px; -} - -.system-card svg { - height: 80px; -} - -.mini-metrics div { - margin-top: 4px; -} - -.pool-card { - margin-bottom: var(--ui-gap); -} - -.pool-table td, -.pool-table th { - white-space: nowrap; - padding: 5px 7px; -} - -.pool-card>.fine { - font-size: 10px; -} - -.pool-card .table-scroll { - max-height: 205px; -} - -.page-footer { - padding: 12px 0; - margin-top: 8px; -} - -.edit-form label { - margin-top: 9px; -} - -.edit-form input:not([type=checkbox]):not([type=radio]), -.edit-form select, -.edit-form textarea, -input:not([type=checkbox]):not([type=radio]), -select, -textarea { - padding: var(--ui-field-pad-y) var(--ui-field-pad-x); - font-size: var(--ui-field-font-size); - min-height: var(--ui-field-min-height); -} - -.outline, -.primary, -.quiet, -button { - padding: var(--button-padding); - font-size: var(--button-font-size); - min-height: var(--button-min-height); -} - -html[data-button-style=compact] body { - --button-padding: 3px 7px; - --button-font-size: 10px; - --button-min-height: 22px; -} - -.check { - font-size: 11px; -} - -.settings-shortcuts { - display: flex; - align-items: center; - gap: 10px; - padding: 10px 12px; - border: 1px solid var(--line); - flex-wrap: wrap; -} - -.settings-shortcuts>div { - flex: 1; - min-width: 170px; -} - -.vhost-layout { - display: block; -} - -.vh-health-summary { - grid-column: 1/-1; - color: var(--muted); - font: 10px var(--mono); -} - -.audit-dialog, -.logs-dialog, -.vhost-dialog { - color: var(--text); - background: var(--bg); - border: 1px solid var(--line); - padding: 0; - max-width: calc(100vw - 24px); - max-height: calc(100dvh - 28px); - box-shadow: 0 18px 70px var(--shadow-strong); - border-radius: var(--rp-radius-surface, 7px); -} - -.vhost-dialog { - width: 960px; - overflow: auto; -} - -.audit-dialog::backdrop, -.logs-dialog::backdrop, -.vhost-dialog::backdrop { - background: var(--shadow-strong); - backdrop-filter: blur(2px); -} - -.vhost-dialog .vh-form { - border: 0; - padding: 14px; -} - -.vh-form>.card-heading { - margin-bottom: 5px; - position: sticky; - top: -14px; - z-index: 3; - background: var(--panel); - padding: 9px 0; -} - -.vh-form>.card-heading h2 { - flex: 1; -} - -.vh-live-summary { - font: 10px var(--mono); - line-height: 1.7; - color: var(--muted); - margin: 5px 0; -} - -.vh-tabs { - display: flex; - flex-direction: row; - margin-inline-start: 0; - gap: 3px; - position: sticky; - top: 29px; - z-index: 2; - background: var(--panel); - padding: 5px 0 8px; - border-bottom: 1px solid var(--line); - margin-bottom: 9px; -} - -.vh-tabs button { - width: auto; - flex: 0 1 auto; - font-size: 10px; - padding: 6px 9px; -} - -.vh-tabs button.active { - color: var(--text); - background: var(--nav-selected-bg); - border-color: var(--green); -} - -.vh-group { - margin: 8px 0; - padding: 9px; -} - -.vh-group[hidden] { - display: none; -} - -.vh-grid { - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 7px; -} - -.vh-pages-grid { - display: block; -} - -.vh-group legend, -.vh-group>summary { - font-size: 11px; -} - -.vh-form .form-actions { - position: sticky; - bottom: -14px; - background: var(--panel); - padding: 10px 0; - border-top: 1px solid var(--line); - z-index: 2; -} - -.vh-header-row { - display: grid; - grid-template-columns: 88px minmax(110px, 1fr) minmax(140px, 2fr) auto; - gap: 5px; - align-items: end; - margin: 6px 0; -} - -.vh-header-row label span { - font-size: 10px; -} - -.vh-header-row button { - margin-bottom: 0; -} - -.vh-header-rows+h3 { - margin-top: 10px; -} - -.page-editor { - display: grid; - grid-template-columns: minmax(105px, .9fr) minmax(115px, 1fr) minmax(105px, 1fr); - gap: 6px; - align-items: center; - padding: 7px 0; - border-bottom: 1px solid var(--line); -} - -.page-label { - font-size: 11px; - font-weight: 500; -} - -.page-source { - grid-column: 1/-1; -} - -.page-source input { - font-size: 11px; - margin: 6px 0; -} - -.page-source textarea { - font: 11px var(--mono); - min-height: 100px; -} - -.page-source[hidden] { - display: none; -} - -.pages-errors { - margin: 12px 0; -} - -.pages-errors>summary { - font-size: 12px; - cursor: pointer; -} - -.logs-dialog { - width: 1180px; - overflow: visible; -} - -.logs-dialog .logs-card { - border: 0; - padding: 0 12px; - background: var(--bg); -} - -.window-heading { - display: flex; - align-items: center; - gap: 12px; - padding: 10px 12px; - border-bottom: 1px solid var(--line); -} - -.window-heading h2 { - font-size: 16px; -} - -.window-heading>button[data-i18n="close"] { - margin-inline-start: auto; - flex: 0 0 auto; -} - -.window-tools { - margin-inline-start: auto; - display: flex; - gap: 5px; -} - -.logs-dialog .logs-card>.card-heading { - display: block; - padding: 8px 0; - margin: 0; -} - -.logs-dialog .logs-card>.card-heading>div:first-child { - display: none; -} - -.log-tools { - display: flex; - align-items: center; - gap: 6px; - flex-wrap: wrap; - width: 100%; -} - -.log-tools input:not([type=checkbox]):not([type=radio]), -.log-tools select { - width: auto; - max-width: 180px; - font-size: 11px; -} - -.log-tools input:not([type=checkbox]):not([type=radio]) { - flex: 1; - min-width: 110px; -} - -.log-tools button { - font-size: 10px; - padding: 6px 9px; -} - -.logs-dialog .table-scroll { - max-height: calc(min(60dvh, 580px)); - overflow: auto; - border-top: 1px solid var(--line); -} - -.logs-dialog table { - font: 11px var(--mono); -} - -.logs-dialog td, -.logs-dialog th { - padding: 6px 7px; - max-width: 360px; - overflow: hidden; - text-overflow: ellipsis; -} - -.logs-dialog th { - position: sticky; - top: 0; - background: var(--panel); -} - -.log-footnote { - padding: 8px 12px; - font-size: 10px; -} - -.logs-dialog.docked { - position: fixed; - inset: auto 12px 10px auto; - margin: 0; - width: calc(min(1180px, 100vw - 24px)); - max-height: 46dvh; - z-index: 20; -} - -.logs-dialog.docked .table-scroll { - max-height: calc(46dvh - 160px); -} - -#toast { - z-index: 1000; -} - -@media (max-width:1000px) { - .shell { - grid-template-columns: 166px minmax(0, 1fr); - } - - .sidebar { - padding: 18px 10px; - } - - .vh-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -@media (max-width:850px) { - .shell { - display: block; - } - - .sidebar { - height: auto; - flex-direction: row; - flex-wrap: wrap; - } - - .sidebar nav { - margin-top: 0; - display: flex; - flex-wrap: wrap; - gap: 2px; - } - - .nav { - padding: 7px; - } - - .edition, - .sidebar-bottom { - display: none; - } - - .topbar { - height: 46px; - } - - .vhost-dialog { - max-width: calc(100vw - 12px); - } -} - -@media (max-width:620px) { - main { - padding: 0 10px; - } - - .top-actions { - gap: 4px; - } - - .topbar .breadcrumb { - font-size: 8px; - } - - .top-actions .quiet { - padding: 5px; - font-size: 10px; - } - - .page-intro h1 { - font-size: 23px; - } - - .diagnostics-grid { - grid-template-columns: minmax(0, 1fr); - } - - .diagnostics-grid>.card:last-child { - grid-column: auto; - } - - .sidebar nav { - width: 100%; - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - - .nav b { - font-size: 10px; - } - - .vh-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .vh-header-row { - grid-template-columns: 85px 1fr; - } - - .page-label, - .page-source, - .vh-header-row>:nth-child(3) { - grid-column: 1/-1; - } - - .vhost-dialog .vh-form { - padding: 9px; - } - - .vh-form>.card-heading { - top: -9px; - } - - .vh-tabs { - top: 29px; - } - - .window-heading { - flex-wrap: wrap; - gap: 5px; - } - - .window-heading h2 { - font-size: 14px; - } - - .window-tools { - margin-inline-start: 0; - } - - .logs-dialog .table-scroll { - max-height: 50dvh; - } - - .log-tools select { - max-width: 140px; - flex: 1; - } - - .logs-dialog.docked { - max-height: 55dvh; - } - - .logs-dialog.docked .table-scroll { - max-height: calc(55dvh - 230px); - } - - .settings-shortcuts { - align-items: flex-start; - } - - .page-editor { - grid-template-columns: 1fr 1fr; - } -} - -@media (max-width:380px) { - .vh-grid { - grid-template-columns: minmax(0, 1fr); - } - - .sidebar nav { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - - .topbar .breadcrumb { - display: none; - } - - .topbar { - justify-content: flex-end; - } - - .vh-tabs button { - font-size: 10px; - padding: 5px 7px; - } -} - -main { - margin: 0 auto; -} - -.stat strong { - font-size: calc(clamp(20px, 1.8vw, 26px)); - margin: 4px 0; -} - -body[data-density=dense] .stat strong { - font-size: 22px; - margin: 2px 0; -} - -#toast[popover] { - position: fixed; - inset: auto 18px 18px auto; - margin: 0; - width: max-content; - max-width: calc(min(520px, 100vw - 28px)); - padding: 12px 16px; - color: var(--text); - background: var(--panel); - border: 1px solid var(--green); - box-shadow: 0 10px 40px var(--shadow-strong); - pointer-events: none; -} - -#toast[popover].error { - border-color: #d57867; -} - -#toast[popover]::backdrop { - display: none; -} - -.named-listener-row { - border: 1px solid var(--line); - padding: 10px; - margin: 10px 0; - min-width: 0; -} - -.named-listener-row legend { - font-size: 11px; -} - -.named-listener-row .vh-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.h3-editor { - grid-column: 1/-1; - min-width: 0; -} - -.h3-editor>label { - margin-top: 4px; -} - -.h3-editor details { - padding: 7px 0; -} - -.h3-editor summary { - font-size: 11px; - cursor: pointer; -} - -.h3-editor .fine, -.h3-editor .vh-grid { - margin-top: 7px; -} - -.cache-toolbar, -.listener-choices { - flex-wrap: wrap; -} - -.listener-choices>.fine { - flex-basis: 100%; -} - -.listener-choices>label { - border: 1px solid var(--line); - padding: 7px; - border-radius: var(--rp-radius-control, 4px); - font-size: 11px; -} - -@media (max-width:620px) { - .named-listener-row .vh-grid { - grid-template-columns: 1fr; - } - - .h3-editor .vh-grid { - grid-template-columns: 1fr 1fr; - } -} - -#named-listener-rows { - overflow: visible; -} - -.cache-toolbar { - display: flex; - align-items: center; - gap: 10px; - padding: 12px; -} - -.cache-toolbar label { - margin: 0; - display: flex; - gap: 7px; - align-items: center; -} - -.cache-toolbar select { - max-width: 300px; - min-width: 130px; - margin: 0; -} - -.cache-domain-filter { - display: flex; - align-items: center; - gap: 7px; - margin: 0; - color: var(--muted); - font-size: 11px; -} - -.cache-domain-filter select { - width: auto; - min-width: 150px; - padding: 7px 28px 7px 9px; - margin: 0; -} - -.cache-stats .stat { - min-width: 0; -} - -.cache-stats strong { - font-size: 22px; -} - -.cache-stats progress { - width: 100%; - height: 5px; - margin: 6px 0; -} - -#panel-cache>article.card+article.card { - margin-top: 10px; -} - -.cache-table { - font-size: 12px; -} - -.cache-table td { - max-width: 260px; - vertical-align: top; -} - -.cache-reasons { - min-width: 190px; -} - -.cache-reason { - display: inline-block; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 4px); - padding: 2px 5px; - margin: 1px 4px 2px 0; - font-size: 10px; -} - -.acme-card { - margin-bottom: 14px; -} - -.acme-card .vh-grid { - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 10px; -} - -.acme-card label { - margin: 0; -} - -@media (max-width:1000px) { - .acme-card .vh-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -@media (max-width:620px) { - .acme-card .vh-grid { - grid-template-columns: minmax(0, 1fr); - } - - .cache-toolbar select, - .cache-toolbar>* { - max-width: 100%; - } - - .cache-toolbar label { - flex-wrap: wrap; - } - - .cache-stats strong { - font-size: 19px; - } -} - -#cache-domain-scroll { - max-height: 260px; - overflow: auto; - scrollbar-gutter: stable; -} - -#cache-object-scroll { - max-height: calc(min(49vh, 520px)); - overflow: auto; - scrollbar-gutter: stable; -} - -#panel-cache thead th { - position: sticky; - top: 0; - background: var(--panel); - z-index: 1; -} - -#panel-cache .cache-table .cache-reasons { - white-space: normal; - min-width: 210px; - max-width: 300px; -} - -#panel-cache .cache-copy-cell { - cursor: copy; -} - -.settings-tabs { - padding: 8px; - margin-bottom: 12px; - border: 1px solid var(--line); - background: var(--panel); -} - -.settings-tabs button { - padding: 8px 10px; - font-size: 12px; - border: 1px solid transparent; - background: transparent; -} - -.settings-tabs button[aria-selected=true] { - background: var(--accent); - color: var(--selected-ink); - border-color: var(--accent); - font-weight: 700; -} - -.cache-operation-dialog .cache-dialog-heading>div:first-child, -.settings-pages { - min-width: 0; -} - -.settings-page[hidden] { - display: none !important; -} - -.settings-page>.edit-form+.edit-form { - margin-top: var(--ui-gap); -} - -.settings-page>.card { - padding: var(--ui-card); -} - -.provider-hint { - padding: 0 18px; -} - -.metrics-example, -.metrics-preview { - font: 11px/1.55 ui-monospace, SFMono-Regular, monospace; - max-width: 100%; - overflow: auto; - white-space: pre; - padding: 12px; - background: var(--code-alt-bg); - border-radius: var(--rp-radius-surface, 8px); - color: var(--code-alt-text); -} - -.metrics-preview { - max-height: 360px; -} - -.settings-shortcuts { - margin-bottom: 10px; -} - -@media (max-width:680px) { - .settings-tabs { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .settings-tabs button { - white-space: normal; - text-align: start; - font-size: 11px; - } - - .settings-page>.card { - padding: var(--ui-card); - } -} - -dialog.audit-dialog, -dialog.config-history-dialog, -dialog.config-history-json-dialog, -dialog.logs-dialog, -dialog.vhost-dialog { - border-radius: 0; - background: var(--bg); - color: var(--text); - border: 1px solid var(--line); - padding: 0; - box-shadow: 0 18px 70px var(--shadow-strong); -} - -dialog::backdrop { - background: var(--shadow-strong) !important; - backdrop-filter: blur(2px) !important; -} - -#toast[popover], -.settings-page>.card, -.settings-tabs, -.settings-tabs button, -dialog .card, -dialog .vh-form, -dialog .window-heading { - border-radius: 0; -} - -.certificate-provider-hint { - padding: 0 12px; -} - -.cache-operation-dialog>.card { - border: 0; - padding: 14px; -} - -.cache-operation-dialog .cache-dialog-heading { - margin-bottom: 12px; - padding: 9px 0; -} - -.cache-operation-dialog .cache-dialog-heading button { - margin-inline-start: auto; - flex: 0 0 auto; -} - -.vh-location { - border: 1px solid var(--line); - background: var(--panel); - min-width: 0; -} - -.vh-locations { - display: grid; - gap: 9px; -} - -.location-actions, -.location-header-toolbar, -.location-toolbar { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 7px; -} - -.location-toolbar { - justify-content: space-between; - margin-bottom: 8px; -} - -.location-toolbar-bottom { - justify-content: flex-start; - margin: 9px 0 0; -} - -.location-summary { - cursor: pointer; - padding: 10px 12px; - font: 600 11px/1.45 var(--mono); - color: var(--text); -} - -.location-body { - border-top: 1px solid var(--line); - padding: 12px; -} - -.location-section { - border: 1px solid var(--line); - min-width: 0; -} - -.location-section>summary { - cursor: pointer; - padding: 9px 10px; - color: var(--muted); - font: 600 10px var(--mono); - letter-spacing: .35px; -} - -.location-section>div { - border-top: 1px solid var(--line); - padding: 10px; -} - -.location-header-toolbar { - justify-content: flex-start; -} - -.location-actions { - justify-content: flex-end; - padding-top: 4px; -} - -.location-return-box { - padding-top: 3px; -} - -#location-help-dialog { - width: calc(min(760px, 100vw - 28px)); - max-height: calc(min(82vh, 760px)); -} - -#location-help-body { - display: grid; - gap: 8px; - padding: 14px 16px 18px; - overflow: auto; -} - -#location-help-body p { - margin: 0; - line-height: 1.55; -} - -.acl-rule, -.basic-user { - display: grid; - grid-template-columns: minmax(100px, 1fr) minmax(140px, 1fr) auto; - align-items: end; - gap: 7px; - margin-block: 5px; -} - -.basic-user { - grid-template-columns: 1fr 1fr auto auto; -} - -.basic-user .fine { - align-self: center; - font-size: 10px; -} - -.renewal-entry { - display: grid; - grid-template-columns: 1fr 2fr auto; - gap: 8px; - padding: 9px 0; - border-bottom: 1px solid var(--line); - align-items: center; -} - -.renewal-actions { - display: flex; - flex-wrap: wrap; - gap: 5px; -} - -.renewal-entry .fine { - grid-column: 1/-1; -} - -@media (max-width:640px) { - - .acl-rule, - .basic-user { - grid-template-columns: 1fr 1fr; - } - - .renewal-entry { - grid-template-columns: 1fr; - } - - .renewal-entry .fine { - grid-column: auto; - } -} - -.app-confirm-dialog { - width: calc(min(520px, 100vw - 28px)); - max-width: none; - padding: 0; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 4px); - background: var(--panel); - color: var(--text); -} - -.app-confirm-dialog::backdrop { - background: var(--backdrop); - backdrop-filter: blur(2px); -} - -.app-confirm-card { - margin: 0; - border: 0; - padding: 18px; -} - -.app-confirm-message { - margin-top: 14px; - color: var(--text); - white-space: pre-line; -} - -.app-confirm-actions { - justify-content: flex-end; - margin-top: 18px; -} - -.app-confirm-danger { - border-color: var(--red); - color: var(--red); -} - -.app-confirm-danger:hover { - border-color: var(--red); - background: rgba(255, 170, 162, .08); -} - -#certificate-list { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); - gap: 10px; -} - -#certificate-list>.entity { - border: 1px solid var(--line); - padding: 12px; - min-width: 0; -} - -.certificate-card-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 10px; -} - -.certificate-card-identity, -.location-header-editor { - min-width: 0; -} - -.certificate-state-pills { - margin-top: 8px; -} - -.certificate-card .pill { - color: var(--accent); -} - -.certificate-coverage { - margin-top: 14px; -} - -.certificate-coverage>.small-label { - display: block; - margin-bottom: 7px; -} - -.certificate-domain-pills { - margin-top: 0; -} - -.certificate-domain-pills .pill { - max-width: 100%; - overflow-wrap: anywhere; -} - -.certificate-coverage-unknown { - color: var(--muted) !important; - font: 10px/1.5 var(--mono) !important; -} - -.certificate-details { - margin-top: 12px; - border-top: 1px solid var(--line); - border-bottom: 1px solid var(--line); -} - -.certificate-details summary { - cursor: pointer; - padding: 9px 0; - color: var(--muted); - font: 10px var(--mono); - letter-spacing: .7px; -} - -.certificate-details summary:hover { - color: var(--text); -} - -.certificate-details-body { - padding: 0 0 8px; -} - -.certificate-details .entity-details { - margin: 4px 0; -} - -.certificate-card>.entity-actions { - margin-top: 10px; -} - -.auth-page.auth-loading .auth-main, -.auth-page.auth-loading .auth-top { - visibility: hidden; -} - -.auth-page { - min-height: 100vh; -} - -.auth-top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 18px 24px; - border-bottom: 1px solid var(--line); -} - -.auth-main { - max-width: 560px; - margin: auto; - padding: 28px 20px; -} - -.auth-main .login-section { - padding: 0; - max-width: none; -} - -.auth-main .login-card { - width: 100%; - max-width: none; -} - -.auth-main h1 { - font-size: 1.65rem; - margin-bottom: 18px; -} - -.login-section #auth-message:empty { - display: none; -} - -.login-section #auth-message { - margin-top: 14px; - padding: 11px 12px; - border: 1px solid var(--line); - background: var(--panel2); - color: var(--text); - font-size: 12px; - line-height: 1.5; - overflow-wrap: anywhere; -} - -.login-section #auth-message.error { - border-color: rgba(255, 170, 162, .58); - background: rgba(255, 170, 162, .07); - color: var(--red); -} - -.location-header-editor h3 { - font-size: 12px; - margin: 8px 0; -} - -.location-header-editor .vh-header-row { - grid-template-columns: 100px minmax(120px, 1fr) minmax(120px, 1.3fr) auto; -} - -@media (max-width:650px) { - .location-header-editor .vh-header-row { - grid-template-columns: 1fr 1fr; - } - - .location-actions, - .location-toolbar { - align-items: stretch; - } - - .location-actions>button, - .location-toolbar>button { - flex: 1 1 150px; - } - - .auth-top { - padding: 14px; - } - - .auth-main { - padding: 22px 14px; - } -} - -.location-header-direction { - border: 1px solid var(--line); - background: var(--panel2); - padding: 10px; - min-width: 0; -} - -.location-header-direction-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 12px; - margin-bottom: 8px; -} - -.location-header-direction-head h3, -.location-header-direction-head p { - margin: 0; -} - -.location-header-direction-head p { - max-width: 56ch; - text-align: right; -} - -.vh-header-empty { - margin: 8px 0; - padding: 9px 10px; - border: 1px dashed var(--line); -} - -.location-header-editor .vh-header-rows { - display: grid; - gap: 8px; -} - -.location-header-editor .vh-header-row { - display: block; - border: 1px solid var(--line); - background: var(--panel); - padding: 9px; -} - -.vh-header-rule-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - margin-bottom: 8px; -} - -.vh-header-rule-title { - font: 600 10px var(--mono); - color: var(--muted); - text-transform: uppercase; - letter-spacing: .45px; -} - -.vh-header-rule-fields { - display: grid; - align-items: end; - gap: 8px; -} - -.vh-header-row.is-remove .vh-header-rule-fields, -.vh-header-rule-fields { - grid-template-columns: minmax(100px, .55fr) minmax(140px, 1fr) minmax(180px, 1.35fr); -} - -.location-add-header { - margin-top: 8px; -} - -.location-preview { - border: 1px solid var(--line); - background: var(--panel2); - padding: 11px 12px; -} - -.location-preview h4 { - margin: 0 0 8px; - font-size: 12px; -} - -.location-preview-disabled { - margin: 0 0 8px; - color: var(--red); -} - -.location-preview-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 7px 14px; - margin: 0; -} - -.location-preview-grid>div { - min-width: 0; -} - -.location-preview-grid dt { - color: var(--muted); - font: 10px var(--mono); - margin-bottom: 2px; -} - -.location-preview-grid dd { - margin: 0; - font-size: 11px; - overflow-wrap: anywhere; -} - -.location-doc-intro { - padding: 10px 12px; - border: 1px solid var(--line); - background: var(--panel2); -} - -.location-doc-section { - border-top: 1px solid var(--line); - padding-top: 10px; -} - -.location-doc-section h3 { - margin: 0 0 7px; - font-size: 12px; -} - -.location-doc-section p+p { - margin-top: 7px !important; -} - -.location-doc-examples p { - font-family: var(--mono); - font-size: 10px; - padding: 8px 9px; - background: var(--panel2); - border-left: 2px solid var(--accent); -} - -@media (max-width:650px) { - .location-header-direction-head { - display: block; - } - - .location-header-direction-head p { - text-align: left; - margin-top: 5px; - } - - .location-preview-grid, - .vh-header-row.is-remove .vh-header-rule-fields, - .vh-header-rule-fields { - grid-template-columns: 1fr; - } -} - -.settings-tabs { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); - width: 100%; - gap: 5px; -} - -.settings-tabs button { - width: 100%; - min-width: 0; - white-space: normal; - text-align: center; -} - -.settings-page, -.settings-page>.edit-form, -.settings-pages { - width: 100%; - max-width: none; -} - -.logging-grid, -.traffic-chart-grid { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 12px; - margin: 14px 0; -} - -.traffic-chart-card { - min-width: 0; -} - -.traffic-donut-layout { - display: grid; - grid-template-columns: 148px minmax(0, 1fr); - gap: 14px; - align-items: center; -} - -.traffic-donut { - position: relative; - width: 148px; - aspect-ratio: 1; - border-radius: 50%; - background: var(--chart-surface); - box-shadow: inset 0 0 0 1px var(--line); -} - -.traffic-donut:before { - content: ""; - position: absolute; - inset: 23%; - border-radius: 50%; - background: var(--chart-surface-alt); - box-shadow: 0 0 0 1px var(--line); -} - -.traffic-donut>span { - position: absolute; - inset: 0; - z-index: 1; - display: grid; - place-items: center; - font: 700 16px var(--mono); -} - -.traffic-chart-legend { - display: grid; - gap: 6px; - padding: 0; - margin: 0; - list-style: none; - min-width: 0; -} - -.traffic-chart-legend li { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - min-width: 0; - font-size: 11px; -} - -.traffic-chart-legend li>span { - display: flex; - align-items: center; - gap: 7px; - min-width: 0; -} - -.traffic-chart-swatch { - width: 9px; - height: 9px; - border-radius: 50%; - flex: 0 0 auto; -} - -.traffic-chart-label { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.traffic-chart-legend b { - font: 700 10px var(--mono); - flex: 0 0 auto; -} - -#panel-traffic .traffic-tabs { - margin: 12px 0 14px; -} - -#panel-traffic [data-traffic-page] { - min-width: 0; -} - -.traffic-wide-chart { - grid-column: span 2; -} - -.traffic-bar-chart { - display: grid; - gap: 8px; - min-width: 0; -} - -.traffic-bar-row { - display: grid; - grid-template-columns: minmax(120px, 1.5fr) minmax(120px, 3fr) minmax(72px, auto); - gap: 10px; - align-items: center; - min-width: 0; -} - -.traffic-bar-label { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font: 11px var(--mono); -} - -.traffic-bar-track { - height: 10px; - border-radius: var(--rp-progress-radius); - background: var(--chart-surface); - box-shadow: inset 0 0 0 1px var(--line); - overflow: hidden; -} - -.traffic-bar-fill { - display: block; - height: 100%; - min-width: 2px; - border-radius: inherit; - background: var(--rp-progress-fill); -} - -.traffic-bar-value { - text-align: right; - font: 700 10px var(--mono); -} - -@media (max-width:650px) { - .traffic-bar-row { - grid-template-columns: minmax(88px, 1.2fr) minmax(90px, 2fr) minmax(60px, auto); - gap: 7px; - } -} - -.logging-grid label { - min-width: 0; -} - -#settings-page-logging>.edit-form { - width: 100%; - max-width: none; -} - -.logging-settings-subform { - margin: 0; - min-width: 0; -} - -@media (max-width:1100px) { - .traffic-wide-chart { - grid-column: span 1; - } - - .traffic-chart-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -@media (max-width:720px) { - - .logging-grid, - .traffic-chart-grid { - grid-template-columns: 1fr; - } - - .traffic-donut-layout { - grid-template-columns: 120px minmax(0, 1fr); - } - - .traffic-donut { - width: 120px; - } -} - -#vhost-list, -#web-service-list { - display: grid; - gap: 10px; -} - -#panel-notfound .card-heading, -#panel-redirects .card-heading, -#panel-vhosts .card-heading, -#panel-web-server .card-heading { - flex-wrap: wrap; -} - -.vhost-list-tools { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 8px; - margin-left: auto; -} - -#notfound-search, -#redirect-search, -#vhost-search, -#web-service-search { - width: calc(min(260px, 32vw)); - min-width: 180px; -} - -.vhost-card { - border: 1px solid var(--line); - background: var(--panel); - padding: 14px; - min-width: 0; -} - -.vhost-card-head { - display: flex; - justify-content: space-between; - gap: 14px; - align-items: flex-start; -} - -.vhost-card-identity { - min-width: 0; -} - -.vhost-policy-pills, -.vhost-title-line { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 8px; -} - -.vhost-hosts { - margin-top: 4px; - font: 11px/1.45 var(--mono); - color: var(--muted); - overflow-wrap: anywhere; -} - -.vhost-detail-strip { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: 1px; - margin-top: 12px; - border: 1px solid var(--line); -} - -.vhost-detail { - min-width: 0; - padding: 9px 10px; - background: var(--bg); -} - -.vhost-detail b, -.vhost-detail span { - display: block; -} - -.vhost-detail b { - margin-top: 3px; - font-size: 11px; - font-weight: 600; - overflow-wrap: anywhere; -} - -.vhost-live-grid { - display: grid; - grid-template-columns: repeat(6, minmax(0, 1fr)); - gap: 8px; - margin-top: 10px; -} - -.vhost-live-metric { - min-width: 0; - padding: 9px 10px; - border: 1px solid var(--line); -} - -.vhost-live-metric span { - display: block; - color: var(--muted); - font-size: 10px; - text-transform: uppercase; - letter-spacing: .04em; -} - -.vhost-live-metric strong { - display: block; - margin-top: 4px; - font: 600 13px var(--mono); - overflow-wrap: anywhere; -} - -.vhost-card .vh-health-summary { - margin: 9px 0 0; - grid-column: auto; -} - -.vhost-policy-pills { - margin-top: 8px; -} - -.vhost-quick-toggle.is-enable { - border-color: var(--pill-live-line); - color: var(--green); -} - -.vhost-quick-toggle.is-disable { - border-color: var(--danger-line); - color: var(--red); -} - -@media (max-width:980px) { - .logging-grid { - grid-template-columns: 1fr; - } - - .vhost-live-grid { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } -} - -@media (max-width:640px) { - .settings-tabs { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .vhost-card-head { - flex-direction: column; - } - - #panel-notfound .card-heading, - #panel-redirects .card-heading, - #panel-vhosts .card-heading, - .backend-card-heading { - align-items: stretch; - flex-direction: column; - } - - .vhost-list-tools { - width: 100%; - margin-left: 0; - } - - #notfound-search, - #redirect-search, - #vhost-search { - flex: 1 1 auto; - width: auto; - min-width: 0; - } - - .backend-table-tools { - width: 100%; - justify-content: flex-start; - margin-left: 0; - } - - #pool-search { - flex: 1 1 100%; - width: 100%; - min-width: 0; - } - - .backend-column-options { - right: auto; - left: 0; - grid-template-columns: 1fr; - min-width: calc(min(290px, 82vw)); - } - - .vhost-card-head .entity-actions { - width: 100%; - justify-content: flex-start; - } - - .vhost-live-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -.acl-profile-layout { - display: grid; - gap: 14px; - align-items: start; -} - -.acl-profile-layout>.card, -.logging-grid>.card { - border-radius: 0; -} - -.host-chip-field>span:first-child { - display: block; - color: var(--muted); - font-size: 11px; - margin-bottom: 7px; -} - -.host-chip-editor { - min-height: 43px; - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 6px; - padding: 6px; - border: 1px solid var(--field-line); - background: var(--field-bg); -} - -.host-chip-editor:focus-within { - outline: 1px solid var(--green); - border-color: var(--green); -} - -.host-chip-editor input { - flex: 1 1 210px; - width: auto; - min-width: 180px; - border: 0; - outline: 0; - padding: 5px 6px; - background: transparent; -} - -.host-chip { - display: inline-flex; - align-items: center; - gap: 5px; - padding: 4px 5px 4px 8px; - border: 1px solid var(--line); - background: var(--panel); - font: 10px var(--mono); -} - -.host-chip button { - border: 0; - background: transparent; - color: var(--muted); - padding: 0 3px; - font-size: 15px; - line-height: 1; -} - -.host-chip button:hover { - color: var(--red); -} - -.vh-www-panel { - padding: 12px; - border: 1px solid var(--line); - background: var(--panel2); - min-width: 0; -} - -.vh-www-panel.is-enabled { - border-color: var(--green); - box-shadow: inset 3px 0 0 var(--green); -} - -.vh-www-header { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - gap: 8px 18px; -} - -.vh-www-title { - font-size: 12px; - color: var(--text); -} - -.vh-www-host-count { - padding: 3px 7px; - border: 1px solid var(--line); - color: var(--muted); - font: 10px var(--mono); -} - -.vh-www-header .vh-www-toggle { - margin: 0; - min-height: 0; - display: flex; - align-items: center; - color: var(--text); - font-size: 11px; -} - -.vh-www-help, -.vh-www-inactive { - margin: 9px 0 0; - font-size: 11px; -} - -.vh-www-details { - margin-top: 12px; - border-top: 1px solid var(--line); - padding-top: 12px; -} - -.vh-www-option-title { - font-size: 11px; - font-weight: 600; - margin: 0 0 8px; - color: var(--text); -} - -.vh-www-choices { - display: grid; - gap: 10px; -} - -.vh-www-domain-pair { - padding: 9px; - border: 1px solid var(--line); - background: var(--panel); -} - -.vh-www-domain-label { - display: block; - font: 11px var(--mono); - color: var(--text); - margin-bottom: 8px; - overflow-wrap: anywhere; -} - -.vh-www-pair-directions { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 8px; -} - -@media (max-width:650px) { - .vh-www-pair-directions { - grid-template-columns: minmax(0, 1fr); - } -} - -.vh-www-choice { - min-width: 0; - display: flex; - align-items: flex-start; - gap: 10px; - padding: 11px; - border: 1px solid var(--line); - background: var(--field-bg); - cursor: pointer; -} - -.vh-www-choice:focus-within, -.vh-www-choice:hover { - border-color: var(--green); -} - -.vh-www-choice.is-selected { - border-color: var(--green); - box-shadow: inset 3px 0 0 var(--green); -} - -.vh-www-choice input { - margin: 3px 0 0; - accent-color: var(--green); - flex: 0 0 auto; -} - -.vh-www-choice-copy { - display: flex; - flex-direction: column; - gap: 5px; - min-width: 0; -} - -.vh-www-choice-copy strong { - font-size: 12px; - overflow-wrap: anywhere; - color: var(--text); -} - -.vh-www-route { - color: var(--muted); - font: 11px var(--mono); - overflow-wrap: anywhere; -} - -.vh-www-no-hosts { - margin: 6px 0; - font-size: 11px; -} - -.vh-www-other { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 6px; - margin-top: 12px; - font-size: 11px; - color: var(--muted); -} - -.vh-www-alias { - font: 11px var(--mono); - padding: 4px 6px; - background: var(--panel); -} - -.vh-www-alias, -.vhost-chart { - border: 1px solid var(--line); -} - -.vhost-chart { - margin-top: 9px; - background: var(--chart-surface-alt); -} - -.vhost-chart-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - padding: 7px 10px 0; -} - -.vhost-chart svg { - display: block; - width: 100%; - height: 54px; - filter: var(--chart-svg-filter); -} - -.vhost-chart-line { - fill: none; - stroke: var(--chart-accent); - stroke-width: var(--chart-line-width); - stroke-linecap: var(--chart-linecap); - stroke-linejoin: var(--chart-linejoin); - vector-effect: non-scaling-stroke; -} - -.logging-format-actions { - display: grid; - grid-template-columns: minmax(220px, 1fr) auto; - gap: 14px; - align-items: end; -} - -.logging-builder-action { - display: grid; - gap: 7px; - justify-items: stretch; - min-width: 180px; -} - -.logging-builder-action .small-label { - text-align: right; -} - -.logging-builder-action button { - width: 100%; -} - -.logging-builder-layout { - display: grid; - grid-template-columns: minmax(0, 1.55fr) minmax(260px, .85fr); - gap: 14px; - min-height: 0; -} - -.logging-builder-layout>section { - min-width: 0; - border: 1px solid var(--line); - padding: 12px; - background: var(--bg); -} - -.logging-preview pre { - margin: 0; - max-height: 420px; - overflow: auto; -} - -@media (max-width:900px) { - - .acl-profile-layout, - .logging-builder-layout, - .logging-format-actions { - grid-template-columns: 1fr; - } - - .logging-builder-action .small-label { - text-align: left; - } -} - -.acl-profile-rule-rows:empty:before { - content: "—"; - display: block; - color: var(--muted); - padding: 8px 0; -} - -.acl-profile-rule { - padding: 6px 0; - border-bottom: 1px solid var(--line); -} - -.acl-profile-rule label>span { - display: block; - color: var(--muted); - font-size: 10px; - margin-bottom: 4px; -} - -.acl-profile-bulk { - margin-top: 9px; - border-top: 1px solid var(--line); - padding-top: 8px; -} - -.acl-profile-bulk>summary { - cursor: pointer; - color: var(--muted); -} - -.pool-vhost-disabled { - opacity: .55; - background: var(--neutral-soft); -} - -.pool-vhost-disabled td { - color: var(--muted); -} - -#stream-list { - display: grid; - gap: 10px; -} - -#panel-streams .card-heading { - flex-wrap: wrap; -} - -#stream-search { - width: calc(min(260px, 32vw)); - min-width: 180px; -} - -.stream-page-actions { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 8px 12px; - margin: 0 0 10px; -} - -.stream-card .vhost-hosts { - text-transform: none; -} - -.stream-live-grid { - grid-template-columns: repeat(6, minmax(0, 1fr)); -} - -.stream-chart-rx, -.stream-chart-tx { - fill: none; - stroke-width: var(--chart-line-width); - stroke-linecap: var(--chart-linecap); - stroke-linejoin: var(--chart-linejoin); - vector-effect: non-scaling-stroke; -} - -.stream-chart-rx { - stroke: var(--chart-accent); -} - -.stream-chart-tx { - stroke: var(--chart-secondary); -} - -.stream-chart-legend { - display: flex; - justify-content: flex-end; - flex-wrap: wrap; - gap: 12px; - padding: 0 10px 7px; - color: var(--muted); - font: 10px var(--mono); -} - -.stream-socket-editor input[hidden] { - display: none; -} - -.listener-address-chip-editor { - min-height: 52px; -} - -@media (max-width:980px) { - .stream-live-grid { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } -} - -@media (max-width:640px) { - #panel-streams .card-heading { - align-items: stretch; - flex-direction: column; - } - - #panel-streams .vhost-list-tools { - width: 100%; - margin-left: 0; - } - - #stream-search { - flex: 1 1 auto; - width: auto; - min-width: 0; - } - - .stream-live-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -.redirect-listeners { - display: grid; - gap: .55rem; - grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); -} - -.redirect-listeners .check { - margin: 0; -} - -.redirect-rules { - display: grid; - gap: 1rem; -} - -.redirect-rule { - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 12px); - padding: 1rem; - margin: 0; - min-width: 0; -} - -.redirect-rule legend { - padding: 0 .4rem; - font-weight: 700; -} - -.redirect-card .vhost-detail-strip { - grid-template-columns: repeat(3, minmax(0, 1fr)); -} - -@media (max-width:760px) { - - .redirect-card .vhost-detail-strip, - .redirect-rule .vh-grid { - grid-template-columns: 1fr; - } -} - -.vh-tabs { - flex-wrap: nowrap; - overflow-x: auto; - overscroll-behavior-x: contain; - scrollbar-width: thin; -} - -.vh-tabs button { - white-space: nowrap; -} - -.frontend-protocol-controls { - margin-block-start: 16px; - padding-block-start: 12px; - border-top: 1px solid var(--line); -} - -.frontend-protocol-controls>.small-label { - display: block; - margin-block-end: 10px; -} - -.protocol-toggle-row { - display: flex; - flex-wrap: wrap; - gap: 8px 18px; - align-items: center; - min-width: 0; -} - -.protocol-toggle-row .check, -.protocol-toggle-row .vh-check { - margin: 0; - min-width: min(280px, 100%); -} - -.listener-choices { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); - gap: 8px; -} - -.listener-choices>.fine { - grid-column: 1/-1; - margin: 0 0 2px; -} - -.listener-choices>label { - display: flex; - min-height: 44px; - align-items: center; - margin: 0; -} - -.vh-members { - gap: 12px; -} - -.vh-backend-card { - padding: 0; - overflow: hidden; - background: var(--panel); -} - -.vh-backend-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 10px 12px; - border-bottom: 1px solid var(--line); -} - -.vh-backend-identity { - display: grid; - gap: 2px; - min-width: 0; -} - -.vh-backend-title { - font-size: 13px; -} - -.docker-agent-security .fine, -.vh-backend-endpoint { - overflow-wrap: anywhere; -} - -.vh-backend-main { - padding: 12px; -} - -.vh-backend-switches { - display: flex; - flex-wrap: wrap; - gap: 6px 16px; - padding-top: 2px; -} - -.vh-backend-switches .vh-check { - margin: 0; - min-height: 26px; -} - -.vh-backend-connection { - padding-top: 8px; - border-top: 1px dashed var(--line); -} - -.vh-backend-advanced { - margin: 0 12px 10px; - padding-top: 8px; - border-top: 1px solid var(--line); -} - -.auth-advanced>summary, -.vh-backend-advanced>summary { - cursor: pointer; - color: var(--muted); - font-size: 11px; - font-weight: 600; - padding: 4px 0 8px; -} - -.vh-backend-health-editor { - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 8px); - padding: 15px; - display: grid; - grid-template-columns: minmax(0, 1fr); - gap: 11px; - background: var(--surface, transparent); -} - -.vh-backend-health-editor h3 { - margin: 0; - font-size: 14px; -} - -.vh-backend-health-editor .fine { - margin: 0; -} - -.vh-backend-health-editor>.vh-field { - max-width: 620px; -} - -#redirect-listeners>.listener-choices, -.vh-backend-health-editor select { - width: 100%; -} - -.vh-backend-health-panel { - border-top: 1px solid var(--line); - padding-top: 12px; -} - -.vh-backend-health-fields { - margin-top: 4px; -} - -.vh-backend-actions { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 8px 12px; - padding: 0 12px 12px; -} - -.vh-backend-actions .fine { - flex: 1 1 320px; - margin: 0; -} - -.vh-local-risk-gate { - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 5px); - padding: 8px; -} - -.vh-local-risk-gate .fine { - margin: 5px 0 0; -} - -.auth-subcard { - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 6px); - padding: 10px; - display: grid; - gap: 8px; -} - -.auth-subcard>h3 { - margin: 0; - font-size: 12px; -} - -.auth-advanced { - border-top: 1px solid var(--line); - padding-top: 4px; -} - -.vh-cache-options[hidden] { - display: none; -} - -@media (max-width:760px) { - .protocol-toggle-row { - display: grid; - grid-template-columns: 1fr; - } - - .protocol-toggle-row .check, - .protocol-toggle-row .vh-check { - min-width: 0; - } - - .listener-choices { - grid-template-columns: 1fr; - } - - .vh-backend-head { - align-items: flex-start; - } -} - -.docker-toolbar { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - margin-bottom: 16px; -} - -.toolbar-actions { - display: flex; - gap: 8px; - flex-wrap: wrap; - justify-content: flex-end; -} - -.docker-agent-observations, -.docker-agent-settings-list { - display: grid; - gap: 16px; -} - -.docker-agent-card, -.docker-agent-settings { - min-width: 0; -} - -.docker-agent-status-badge { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 88px; - text-align: center; -} - -.docker-agent-summary { - display: flex; - flex-wrap: wrap; - gap: 7px 18px; - margin: 8px 0 16px; - padding: 10px 12px; - border: 1px solid var(--line); - background: var(--panel2); - font-size: 12px; - color: var(--muted); -} - -.docker-agent-security { - display: flex; - align-items: center; - gap: 10px; - padding: 9px 11px; - margin: 4px 0 10px; - border-left: 2px solid var(--line); - background: var(--panel2); - min-width: 0; -} - -.docker-agent-error { - display: grid; - gap: 5px; - padding: 12px; - border: 1px solid var(--danger-line); - background: rgba(255, 170, 162, .04); -} - -.docker-service-list { - display: grid; - gap: 10px; -} - -.docker-service-row { - display: grid; - border: 1px solid var(--line); - background: var(--panel2); - min-width: 0; -} - -.docker-service-row.is-unpublished { - opacity: .72; -} - -.docker-service-row.is-view-hidden { - border-style: dashed; - opacity: .58; -} - -.docker-service-head { - display: grid; - align-items: center; - min-width: 0; -} - -.docker-service-meta { - display: grid; - gap: 2px; - min-width: 0; -} - -.docker-service-meta span { - color: var(--muted); - font-size: 12px; - overflow-wrap: anywhere; -} - -.docker-runtime { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 10px; - min-width: 0; - text-align: right; -} - -.docker-runtime .fine { - overflow-wrap: anywhere; -} - -.docker-route-grid { - display: grid; - gap: 10px; -} - -.docker-health-grid>label, -.docker-route-grid>label:not(.check) { - display: grid; - gap: 5px; - min-width: 0; -} - -.docker-health-grid>label>span, -.docker-route-grid>label:not(.check)>span { - color: var(--muted); - font-size: 11px; -} - -.docker-health-grid input:not([type=checkbox]), -.docker-health-grid select, -.docker-route-grid input:not([type=checkbox]), -.docker-route-grid select { - min-width: 0; - width: 100%; -} - -.docker-health-editor { - border-top: 1px solid var(--line); - padding-top: 9px; -} - -.docker-health-editor>summary { - cursor: pointer; - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - user-select: none; -} - -.docker-health-editor>summary::marker { - color: var(--muted); -} - -.docker-health-grid { - display: grid; - grid-template-columns: repeat(5, minmax(120px, 1fr)); - gap: 10px; - margin-top: 12px; -} - -.docker-passive-health-editor .docker-health-grid { - grid-template-columns: repeat(4, minmax(180px, 1fr)); - align-items: start; -} - -.docker-passive-health-editor .docker-checkbox-field { - min-width: 0; -} - -.docker-passive-health-editor .docker-checkbox-field .vh-check { - width: max-content; - max-width: 100%; -} - -.docker-passive-health-editor .docker-checkbox-field input[type=checkbox] { - width: auto; -} - -.docker-passive-health-editor .docker-passive-5xx-warning, -.docker-passive-health-editor .docker-passive-slow-warning { - grid-column: span 2; - margin: 0; - min-width: 0; -} - -.docker-agent-grid { - margin-top: 10px; -} - -.docker-agent-listener-field { - grid-column: 1/-1; - display: grid; - gap: 6px; - min-width: 0; -} - -.docker-agent-field-label { - font-size: 11px; - color: var(--muted); -} - -.docker-agent-listener-choices { - margin: 0; -} - -.docker-agent-listener-choices>label { - min-height: 38px; -} - -.docker-agent-grid .mono, -.docker-agent-summary .mono { - font-family: var(--mono); - font-size: 11px; -} - -.pill.warn { - border-color: var(--warn-line); - color: var(--accent); -} - -.pill.danger { - border-color: var(--danger-line-strong); - color: var(--red); -} - -.docker-toolbar input[type=search] { - width: calc(min(280px, 38vw)); -} - -.cache-toolbar-status { - margin-left: auto; - display: flex; - align-items: center; - gap: 12px; - white-space: nowrap; -} - -@media (max-width:1320px) { - .docker-runtime { - grid-column: 2; - justify-content: flex-start; - text-align: left; - flex-wrap: wrap; - } - - .docker-preserve-field { - padding-bottom: 0; - } - - .docker-health-grid { - grid-template-columns: repeat(3, minmax(130px, 1fr)); - } - - .docker-passive-health-editor .docker-health-grid { - grid-template-columns: repeat(2, minmax(180px, 1fr)); - } -} - -@media (max-width:760px) { - .docker-service-head { - align-items: stretch; - } - - .docker-runtime { - grid-column: auto; - justify-content: flex-start; - text-align: left; - } - - .docker-health-grid, - .docker-passive-health-editor .docker-health-grid, - .docker-route-grid { - grid-template-columns: 1fr; - } - - .docker-passive-health-editor .docker-passive-5xx-warning, - .docker-passive-health-editor .docker-passive-slow-warning { - grid-column: auto; - } - - .docker-agent-security, - .docker-toolbar { - align-items: flex-start; - flex-direction: column; - } - - .docker-toolbar input[type=search] { - width: 100%; - } - - .cache-toolbar-status { - margin-left: 0; - width: 100%; - justify-content: flex-start; - } -} - -#panel-settings:not([hidden]) { - display: grid; - grid-template-columns: minmax(180px, 220px) minmax(0, 1fr); - gap: 12px; - align-items: start; -} - -#panel-settings .settings-shortcuts { - grid-column: 1/-1; - width: 100%; -} - -#panel-settings .settings-filter-row { - grid-column: 1/-1; - display: flex; - align-items: end; - justify-content: space-between; - gap: 12px; - width: 100%; - padding: 8px 10px; - border: 1px solid var(--line); - background: var(--panel); -} - -#panel-settings .settings-filter { - display: grid; - gap: 5px; - width: min(520px, 100%); -} - -#panel-settings .settings-filter input { - width: 100%; -} - -#panel-settings .settings-tabs { - display: flex; - flex-direction: column; - position: sticky; - top: 12px; - margin: 0; - gap: 2px; -} - -#panel-settings .settings-tabs button { - width: 100%; - text-align: left; - white-space: normal; -} - -#panel-settings .settings-tabs.sub-tabs { - position: static; - top: auto; -} - -#panel-settings .settings-pages { - min-width: 0; -} - -@media (max-width:900px) { - #panel-settings:not([hidden]) { - display: block; - } - - #panel-settings .settings-shortcuts { - margin-bottom: 10px; - } - - #panel-settings .settings-filter-row { - align-items: stretch; - flex-direction: column; - margin-bottom: 10px; - } - - #panel-settings .settings-tabs { - display: flex; - flex-direction: row; - position: static; - overflow-x: auto; - overscroll-behavior-inline: contain; - scrollbar-width: thin; - margin-bottom: 12px; - } - - #panel-settings .settings-tabs button { - flex: 0 0 auto; - width: auto; - min-width: 120px; - white-space: nowrap; - text-align: center; - } -} - -.docker-live-switch { - align-self: center; - white-space: nowrap; -} - -.docker-service-row.is-dirty { - border-color: var(--accent); - box-shadow: inset 3px 0 0 var(--accent); -} - -.docker-service-actions { - display: flex; - justify-content: flex-end; - gap: 8px; -} - -.docker-acl-editor, -.docker-protocol-editor, -.docker-timeout-editor { - border-top: 1px solid var(--line); - padding-top: 9px; -} - -.docker-acl-editor>summary, -.docker-protocol-editor>summary, -.docker-timeout-editor>summary { - cursor: pointer; - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - user-select: none; -} - -.docker-acl-grid, -.docker-timeout-grid { - display: grid; - grid-template-columns: repeat(4, minmax(140px, 1fr)); - gap: 10px; - margin-top: 12px; -} - -.docker-acl-grid>label:not(.check), -.docker-timeout-grid>label { - display: grid; - gap: 5px; - min-width: 0; -} - -.docker-acl-grid textarea { - width: 100%; - min-height: 86px; - font-family: var(--mono); -} - -@media (max-width:1000px) { - - .docker-acl-grid, - .docker-timeout-grid { - grid-template-columns: 1fr 1fr; - } -} - -@media (max-width:760px) { - - .docker-acl-grid, - .docker-timeout-grid { - grid-template-columns: 1fr; - } - - .docker-service-actions { - justify-content: stretch; - } - - .docker-service-actions button { - width: 100%; - } -} - -.security-page-heading { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 18px; - margin-bottom: 14px; -} - -.security-page-heading h2 { - margin: 3px 0 4px; -} - -.security-page-heading p { - margin: 0; - max-width: 760px; -} - -.security-stack { - display: grid; - gap: 14px; -} - -.security-account-card .session-user { - margin: 4px 0 0; -} - -.security-account-card .security-note { - margin: 10px 0 14px; -} - -.security-editor { - border-top: 1px solid var(--line); - margin-top: 14px; - padding-top: 10px; -} - -.security-editor>summary { - cursor: pointer; - display: flex; - justify-content: space-between; - align-items: center; - gap: 14px; - user-select: none; -} - -.security-editor>summary>span { - font-weight: 700; -} - -.security-inline-form { - margin-top: 14px; - padding: 0; - border: 0; - background: transparent; -} - -.security-policy-card #host-policy-list { - display: grid; - gap: 8px; -} - -.audit-dialog { - width: calc(min(1040px, 100vw - 24px)); - max-height: calc(100dvh - 28px); - overflow: hidden; -} - -.audit-dialog-card { - border: 0; - padding: 12px; - background: var(--bg); -} - -.audit-dialog .table-scroll { - max-height: calc(min(68dvh, 680px)); - overflow: auto; - border-top: 1px solid var(--line); -} - -.audit-dialog table { - font: 11px var(--mono); -} - -.audit-dialog td, -.audit-dialog th { - padding: 7px 8px; - max-width: 440px; - overflow: hidden; - text-overflow: ellipsis; -} - -.audit-dialog th { - position: sticky; - top: 0; - background: var(--panel); -} - -.docker-agent-overview { - display: flex; - flex-wrap: wrap; - gap: 7px 18px; - margin: 8px 0 10px; - color: var(--muted); - font-size: 12px; -} - -.docker-agent-details { - margin: 0 0 12px; - border-top: 1px solid var(--line); - padding-top: 8px; -} - -.docker-agent-details>summary { - cursor: pointer; - color: var(--muted); - font-size: 11px; - user-select: none; -} - -.docker-agent-details .docker-agent-summary { - margin: 9px 0 0; -} - -.docker-service-row { - gap: 8px; - padding: 10px 12px; -} - -.docker-service-head { - grid-template-columns: auto minmax(260px, 1fr) minmax(300px, 1.25fr); - gap: 12px; -} - -.docker-service-head-actions { - display: flex; - align-items: center; - justify-content: flex-end; - flex-wrap: wrap; - gap: 8px 10px; - min-width: 0; -} - -.docker-service-head-actions .docker-runtime { - flex: 1 1 230px; -} - -.docker-service-head-actions .docker-view-toggle { - flex: 0 0 auto; -} - -.docker-publish-field { - display: inline-flex; - align-items: center; - gap: 8px; - min-height: 30px; - padding: 0; - color: var(--text); - font-size: 12px; - font-weight: 500; - white-space: nowrap; - cursor: pointer; -} - -.docker-publish-field input[type=checkbox].docker-publish { - appearance: none; - -webkit-appearance: none; - position: relative; - flex: 0 0 30px; - width: 30px; - height: 16px; - margin: 0; - padding: 0; - border: 1px solid var(--field-line); - border-radius: min(3px, var(--rp-radius-control, 3px)); - background: var(--field-bg); - cursor: pointer; - vertical-align: middle; -} - -.docker-publish-field .docker-publish::before { - content: ""; - position: absolute; - inset: 2px auto 2px 2px; - width: 10px; - background: var(--muted); - border-radius: min(1px, var(--rp-radius-control, 1px)); - transition: transform .12s ease; -} - -.docker-publish-field .docker-publish:checked { - background: var(--field-bg); - border-color: var(--accent); -} - -.docker-publish-field .docker-publish:checked::before { - transform: translateX(14px); - background: var(--accent); -} - -.docker-publish-field .docker-publish:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 3px; -} - -.docker-publish-field:has(.docker-publish:disabled) { - opacity: .55; - cursor: wait; -} - -.docker-publish-field .docker-publish:disabled { - cursor: wait; -} - -.docker-service-quick-actions { - display: inline-flex; - align-items: center; - flex-wrap: wrap; - gap: 8px; -} - -.docker-open-logs { - white-space: nowrap; -} - -@media (prefers-reduced-motion: reduce) { - - .docker-publish-field .docker-publish, - .docker-publish-field .docker-publish::before { - transition: none; - } -} - -.docker-service-editor { - border-top: 1px solid var(--line); - padding-top: 8px; -} - -.docker-service-editor>summary { - cursor: pointer; - display: flex; - align-items: center; - justify-content: space-between; - gap: 14px; - user-select: none; -} - -.docker-service-editor>summary>span { - font-weight: 700; - font-size: 12px; -} - -.docker-service-editor .docker-config-hint { - text-align: right; - overflow-wrap: anywhere; -} - -.docker-service-editor[open]>summary { - margin-bottom: 12px; -} - -.docker-service-actions { - padding-top: 10px; - border-top: 1px solid var(--line); -} - -@media (max-width:1320px) { - .docker-service-head { - grid-template-columns: auto 1fr; - } - - .docker-service-head-actions { - grid-column: 2; - justify-content: flex-start; - flex-wrap: wrap; - } -} - -@media (max-width:760px) { - .security-page-heading { - flex-direction: column; - } - - .security-page-heading>button { - width: 100%; - } - - .docker-service-editor>summary, - .docker-service-head-actions, - .security-editor>summary { - align-items: flex-start; - flex-direction: column; - } - - .docker-service-head { - grid-template-columns: 1fr; - } - - .docker-service-head-actions { - grid-column: auto; - } - - .docker-service-editor .docker-config-hint { - text-align: left; - } -} - -.traffic-line { - fill: none; - vector-effect: non-scaling-stroke; -} - -.traffic-line-total { - stroke: var(--chart-septenary); - stroke-width: 1.25; - opacity: .72; -} - -.traffic-line-h1 { - stroke: var(--chart-accent); - stroke-width: 2; -} - -.traffic-line-h2 { - stroke: var(--chart-secondary); - stroke-width: 2; -} - -.traffic-line-h3 { - stroke: var(--chart-tertiary); - stroke-width: 2; -} - -.traffic-line-transform { - stroke: var(--chart-quaternary); - stroke-width: 2.4; - stroke-dasharray: 5 3; -} - -.traffic-protocol-legend { - display: flex; - flex-wrap: wrap; - gap: 7px 16px; - margin: 2px 0 7px; - color: var(--muted); - font: 9px var(--mono); -} - -.traffic-protocol-legend span { - display: inline-flex; - align-items: center; - gap: 5px; -} - -.traffic-protocol-legend i { - width: 12px; - height: 2px; - display: inline-block; - background: var(--muted); -} - -.traffic-protocol-legend .legend-h1 i { - background: var(--accent); -} - -.traffic-protocol-legend .legend-h2 i { - background: var(--green); -} - -.traffic-protocol-legend .legend-h3 i { - background: var(--blue); -} - -.traffic-protocol-legend .legend-transform i { - background: var(--transform-chart); -} - -.traffic-protocol-legend b { - color: var(--text); - font-weight: 600; -} - -body[data-density=compact] { - --ui-gap: 2px; - --ui-card: 5px; - --ui-font: 10.75px; - --ui-control: 22px; - line-height: 1.32; -} - -body[data-density=compact] .shell { - grid-template-columns: 164px minmax(0, 1fr); -} - -body[data-density=compact] main { - padding: 0 10px; -} - -body[data-density=compact] .sidebar { - padding: 14px 9px; -} - -body[data-density=compact] .sidebar nav { - margin-top: 14px; -} - -body[data-density=compact] .nav { - padding: 6px 7px; -} - -body[data-density=compact] .topbar { - height: 42px; -} - -body[data-density=compact] .page-intro { - padding: 7px 0 6px; - gap: 7px; -} - -body[data-density=compact] .page-intro h1 { - font-size: 20px; -} - -body[data-density=compact] .page-intro .lead { - font-size: 10px; - margin-top: 2px; -} - -body[data-density=compact] .card-heading { - margin-bottom: 5px; -} - -body[data-density=compact] .card-heading h2 { - font-size: 14px; -} - -body[data-density=compact] .stat { - padding: 6px 7px; -} - -body[data-density=compact] .stat strong { - font-size: 18px; - margin: 1px 0; -} - -body[data-density=compact] .stat p, -body[data-density=compact] .stat small { - font-size: 9px; -} - -body[data-density=compact] .activity-card svg { - height: 84px; -} - -body[data-density=compact] .system-card svg { - height: 66px; -} - -body[data-density=compact] .mini-metrics div { - margin-top: 2px; -} - -body[data-density=compact] .pool-table td, -body[data-density=compact] .pool-table th { - padding: 3px 5px; - font-size: 10px; -} - -body[data-density=compact] .pool-card .table-scroll { - max-height: 170px; -} - -body[data-density=compact] .outline, -body[data-density=compact] .primary, -body[data-density=compact] .quiet, -body[data-density=compact] button, -body[data-density=compact] input:not([type=checkbox]):not([type=radio]), -body[data-density=compact] select, -body[data-density=compact] textarea { - padding: 3px 7px; - font-size: 10px; -} - -body[data-density=compact] .docker-service-row { - padding: 6px 8px; - gap: 4px; -} - -body[data-density=compact] .docker-service-head { - gap: 7px; -} - -body[data-density=compact] .docker-agent-overview { - gap: 4px 12px; - margin: 4px 0 6px; - font-size: 10px; -} - -body[data-density=compact] .docker-agent-details { - margin-bottom: 6px; - padding-top: 5px; -} - -body[data-density=compact] .docker-service-editor { - padding-top: 5px; -} - -body[data-density=compact] .docker-service-editor[open]>summary { - margin-bottom: 7px; -} - -body[data-density=compact] .security-stack { - gap: 7px; -} - -body[data-density=compact] .security-editor { - margin-top: 8px; - padding-top: 6px; -} - -body[data-density=compact] .page-footer { - padding: 7px 0; - margin-top: 4px; -} - -#sparkline.traffic-line-total { - stroke: var(--chart-septenary); - stroke-width: 1.25; - opacity: .72; -} - -#sparkline-h1.traffic-line-h1 { - stroke: var(--chart-accent); -} - -#sparkline-h2.traffic-line-h2 { - stroke: var(--chart-secondary); -} - -#sparkline-h3.traffic-line-h3 { - stroke: var(--chart-tertiary); -} - -.docker-open-app { - white-space: nowrap; -} - -@media (max-width:850px) { - body[data-density=compact] .sidebar nav { - margin-top: 0; - } - - body[data-density=compact] .sidebar { - padding: 8px 9px; - } - - body[data-density=compact] .topbar { - height: 42px; - } -} - -.network-interface-charts { - display: grid; - max-height: 260px; - overflow: auto; -} - -.network-interface-row-head { - display: flex; - justify-content: space-between; - gap: 10px; -} - -.network-interface-row-head b { - min-width: 0; - overflow-wrap: anywhere; - font-size: 10px; -} - -.network-interface-empty { - margin: 0; - padding: 8px 0; -} - -.docker-agent-grid>label { - display: grid; - align-content: start; - gap: 6px; - min-width: 0; - margin: 0 !important; -} - -.docker-agent-field-label, -.docker-agent-grid>label>span:first-child { - min-height: 14px; - line-height: 14px; -} - -.docker-agent-grid input:not([type=checkbox]), -.docker-agent-grid select { - width: 100%; - min-width: 0; - min-height: 38px; - margin: 0; - box-sizing: border-box; -} - -.docker-agent-grid>label>small { - margin: 0; - line-height: 1.35; -} - -.docker-agent-heading-actions { - display: flex; - align-items: center; - gap: 9px; -} - -.docker-agent-heading-actions .check { - margin: 0 !important; - min-height: 32px; -} - -.docker-agent-heading-actions>.quiet, -.docker-agent-status-badge { - min-height: 32px; - box-sizing: border-box; -} - -.docker-agent-listener-field { - margin-top: 2px; - padding-top: 4px; - border-top: 1px solid var(--line); -} - -#docker-agent-add { - display: inline-flex; - margin-top: 14px; -} - -#docker-agents-form>.form-actions { - justify-content: flex-start; - margin-top: 16px; - padding-top: 14px; - border-top: 1px solid var(--line); -} - -@media (max-width:760px) { - .docker-agent-heading-actions { - flex-wrap: wrap; - justify-content: flex-start; - } - - #docker-agents-form>.form-actions { - justify-content: flex-start; - } -} - -.network-interface-charts { - gap: 8px; - padding-right: 0; -} - -.network-interface-row { - min-width: 0; - padding: 0; - border: 0; - border-radius: 0; - background: transparent; -} - -.network-interface-row+.network-interface-row { - border-top: 1px solid var(--line); - padding-top: 8px; -} - -.network-interface-row-head { - margin-bottom: 5px; -} - -.network-interface-row-head b { - color: var(--text); - font: 600 10px var(--mono); -} - -.network-interface-row-head .fine { - flex: 0 0 auto; - text-align: right; - white-space: nowrap; - font: 10px var(--mono); -} - -.network-interface-row svg { - display: block; - width: 100%; - height: 60px; - overflow: visible; -} - -.docker-route-grid { - align-items: start; -} - -.docker-field { - display: grid; - align-content: start; - gap: 5px; - min-width: 0; - margin: 0 !important; -} - -.docker-field>span:first-child { - min-height: 14px; - line-height: 14px; - color: var(--muted); - font-size: 11px; -} - -.docker-acl-grid .docker-field input:not([type=checkbox]), -.docker-acl-grid .docker-field select, -.docker-route-grid .docker-field input:not([type=checkbox]), -.docker-route-grid .docker-field select { - width: 100%; - min-width: 0; - min-height: 38px; - height: 38px; - align-self: start; - box-sizing: border-box; -} - -.docker-checkbox-field .vh-check { - min-height: 28px; - margin: 0 !important; - align-items: center; -} - -.check>span { - display: inline-flex; - align-items: center; - min-height: 15px; - line-height: 1.25; -} - -.check input[type=checkbox] { - flex: 0 0 auto; - align-self: center; - transform: translateY(-1px); -} - -.cache-toolbar-status .check, -.docker-toolbar .check { - margin: 0 !important; -} - -.docker-switch-row { - display: grid; - justify-content: start; - align-items: start; - gap: 8px; - min-width: 0; -} - -.transform-diagnostics { - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.transform-reason-list { - display: flex; - flex-wrap: wrap; - gap: 7px; -} - -.cache-transform-badge { - display: inline-flex; - align-items: center; - margin-left: 6px; - padding: 1px 5px; - border: 1px solid var(--pill-line); - border-radius: var(--rp-radius-control, 3px); - color: var(--accent); - font: 9px var(--mono); - text-transform: uppercase; - letter-spacing: .35px; -} - -.docker-preserve-field { - align-self: start; - padding-bottom: 0; - white-space: nowrap; -} - -.docker-acl-grid { - align-items: start; -} - -.docker-acl-grid>.docker-field { - align-self: start; -} - -.docker-acl-grid textarea { - align-self: start; - box-sizing: border-box; -} - -@media (max-width:760px) { - .network-interface-row-head { - flex-direction: column; - gap: 2px; - } - - .network-interface-row-head .fine { - white-space: normal; - text-align: left; - } -} - -.docker-route-grid { - grid-template-columns: minmax(190px, 1fr) minmax(250px, 1.35fr) minmax(210px, 1fr) minmax(120px, .65fr); -} - -.docker-alias-field { - grid-column: span 2; -} - -.docker-alias-field>span:first-child { - margin-bottom: 0; -} - -.docker-alias-editor { - min-height: 38px; - padding: 3px 5px; - gap: 4px; - box-sizing: border-box; -} - -.docker-route-grid .docker-alias-editor input:not([type=checkbox]) { - flex: 1 1 180px; - width: auto; - min-width: 150px; - min-height: 30px; - height: 30px; - padding: 2px 4px; -} - -.docker-alias-field>small, -.docker-checkbox-field>small { - margin: 0; - line-height: 1.35; -} - -@media (max-width:1320px) { - .docker-route-grid { - grid-template-columns: 1fr 1fr; - } - - .docker-alias-field, - .docker-switch-row { - grid-column: 1/-1; - } -} - -@media (max-width:760px) { - - .docker-route-grid, - .transform-diagnostics { - grid-template-columns: 1fr; - } - - .docker-alias-field { - grid-column: auto; - } -} - -#panel-tls .certificate-workspace-heading { - align-items: flex-start; - margin-bottom: 12px; -} - -#panel-tls .certificate-subheading>div, -#panel-tls .certificate-workspace-heading>div { - min-width: 0; -} - -#panel-tls .certificate-section-intro p, -#panel-tls .certificate-subheading p, -#panel-tls .certificate-workspace-heading p { - margin: 5px 0 0; -} - -#panel-tls .certificate-tabs { - grid-template-columns: repeat(3, minmax(0, 1fr)); - margin-bottom: 16px; -} - -#panel-tls .certificate-page[hidden] { - display: none !important; -} - -#panel-tls .certificate-section-intro { - margin: 2px 0 12px; -} - -#panel-tls .certificate-section-intro h3, -#panel-tls .certificate-subheading h3 { - margin: 0; -} - -#panel-tls .certificate-safety { - margin: 8px 0 12px; -} - -#panel-tls .certificate-inventory-card { - padding: 14px; -} - -#panel-tls .certificate-library-footer { - display: grid; - gap: 8px; - margin-top: 14px; - padding-top: 12px; - border-top: 1px solid var(--line); -} - -#panel-tls .certificate-library-footer p { - margin: 0; -} - -#panel-tls .certificate-operation-card { - display: grid; - gap: 10px; - margin-bottom: 12px; - border-left: 3px solid var(--line); -} - -#panel-tls .certificate-operation-card[data-state=running] { - border-left-color: var(--accent); -} - -#panel-tls .certificate-operation-card[data-state=complete], -#panel-tls .certificate-operation-card[data-state=ready] { - border-left-color: var(--green); -} - -#panel-tls .certificate-operation-card[data-state=failed], -#panel-tls .certificate-operation-card[data-state=unavailable] { - border-left-color: var(--red); -} - -#panel-tls .certificate-operation-heading { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 12px; -} - -#panel-tls .certificate-operation-heading>div { - display: grid; - gap: 6px; - min-width: 0; -} - -#panel-tls .certificate-operation-heading strong { - overflow-wrap: anywhere; -} - -#panel-tls .certificate-operation-card[data-state=failed] .certificate-operation-heading strong, -#panel-tls .certificate-operation-card[data-state=failed] .pill, -#panel-tls .certificate-operation-card[data-state=unavailable] .certificate-operation-heading strong, -#panel-tls .certificate-operation-card[data-state=unavailable] .pill { - color: var(--red); -} - -#panel-tls .certificate-operation-card[data-state=complete] .pill, -#panel-tls .certificate-operation-card[data-state=ready] .pill { - color: var(--green); -} - -#panel-tls .certificate-log-path { - margin: 0; - padding: 9px 10px; - border: 1px solid var(--line); - background: var(--bg); - font-family: var(--mono); - overflow-wrap: anywhere; -} - -#panel-tls .certificate-renewal-card { - padding: 12px; -} - -#panel-tls .renewal-entry { - display: grid; - grid-template-columns: 1fr; - gap: 10px; - padding: 13px; - margin-bottom: 10px; - border: 1px solid var(--line); - background: var(--bg); - align-items: stretch; -} - -#panel-tls .renewal-entry:last-child { - margin-bottom: 0; -} - -#panel-tls .renewal-entry[data-state=failed] { - border-left: 3px solid var(--red); -} - -#panel-tls .renewal-entry-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 12px; -} - -#panel-tls .renewal-entry-identity { - display: grid; - gap: 3px; - min-width: 0; -} - -#panel-tls .renewal-entry-identity strong { - overflow-wrap: anywhere; -} - -#panel-tls .renewal-entry .fine { - grid-column: auto; - margin: 0; -} - -#panel-tls .renewal-meta { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 8px; -} - -#panel-tls .renewal-meta-item { - display: grid; - gap: 5px; - padding: 9px; - border: 1px solid var(--line); -} - -#panel-tls .renewal-meta-item strong { - font-size: 12px; - font-weight: 600; - overflow-wrap: anywhere; -} - -#panel-tls .renewal-message { - padding-top: 2px; -} - -#panel-tls .renewal-actions { - justify-content: flex-end; - padding-top: 2px; -} - -#panel-tls .certificate-generator-card { - padding: 14px; -} - -#panel-tls .certificate-generator-card>.provider-picker { - display: grid; - gap: 6px; - font-size: 12px; - font-weight: 600; -} - -#panel-tls .certificate-generator-card>.provider-hint { - padding: 0; - margin: 7px 0 12px; -} - -#panel-tls .certificate-editor-card { - max-width: none; - margin: 0; - padding: 14px 0 0; - border: 0; - border-top: 1px solid var(--line); - box-shadow: none; -} - -#panel-tls .certificate-editor-card[hidden] { - display: none !important; -} - -#panel-tls details.certificate-editor-card>summary { - padding: 0 0 12px; -} - -#panel-tls .certificate-editor-card .vh-grid { - gap: 10px 14px; -} - -@media (max-width:700px) { - - #panel-tls .certificate-operation-heading, - #panel-tls .certificate-subheading, - #panel-tls .certificate-workspace-heading, - #panel-tls .renewal-entry-head { - align-items: stretch; - flex-direction: column; - } - - #panel-tls .certificate-subheading>button, - #panel-tls .certificate-workspace-heading>button { - width: 100%; - } - - #panel-tls .certificate-tabs, - #panel-tls .renewal-meta { - grid-template-columns: 1fr; - } - - #panel-tls .renewal-actions { - justify-content: stretch; - } - - #panel-tls .renewal-actions button { - flex: 1 1 100%; - } -} - -.operation-message:empty { - display: none; -} - -.operation-message { - margin: 14px 0 0; - padding: 11px 12px; - border: 1px solid var(--line); - background: var(--panel2); - color: var(--text); - font-size: 12px; - line-height: 1.5; - overflow-wrap: anywhere; -} - -.operation-message.error { - border-color: rgba(255, 170, 162, .58); - background: rgba(255, 170, 162, .07); - color: var(--red); -} - -.cache-toolbar-message { - flex-basis: 100%; - margin-top: 0; -} - -.cache-operation-dialog { - width: calc(min(920px, 100vw - 32px)); -} - -.cache-operation-dialog .card { - margin: 0; -} - -.stat-with-action { - padding-right: 116px; -} - -.stat-corner-action { - position: absolute; - top: 10px; - right: 10px; - margin: 0; -} - -.stat-chart strong { - margin-bottom: 3px; -} - -.stat-chart svg { - display: block; - width: 100%; - height: 26px; - margin-top: 2px; -} - -.stat-chart .stat-chart-legend { - display: flex; - flex-wrap: nowrap; - gap: 6px 10px; - margin-top: 2px; - white-space: nowrap; -} - -.stat-chart .stat-chart-legend span { - display: inline-flex; - align-items: center; - gap: 5px; -} - -.stat-chart .stat-chart-legend i { - width: 8px; - height: 8px; - border-radius: 999px; - display: inline-block; - background: currentColor; -} - -.stat-chart .stat-chart-legend .legend-h1 i { - background: var(--traffic-h1); -} - -.stat-chart .stat-chart-legend .legend-h2 i { - background: var(--traffic-h2); -} - -.stat-chart .stat-chart-legend .legend-h3 i { - background: var(--traffic-h3); -} - -.stat-chart .stat-chart-legend b { - font-family: var(--mono); - font-weight: 500; -} - -.proto-h1-line { - stroke: var(--traffic-h1); -} - -.proto-h1-line, -.proto-h2-line, -.proto-h3-line { - fill: none; - stroke-width: var(--chart-line-width); - stroke-linecap: var(--chart-linecap); -} - -.proto-h2-line { - stroke: var(--traffic-h2); -} - -.proto-h3-line { - stroke: var(--traffic-h3); -} - -.stat-chart .chart-grid { - opacity: var(--chart-grid-opacity, .28); -} - -.function-live-chart { - width: 100%; - padding: 0; - overflow: hidden; -} - -.function-live-chart-summary { - display: flex; - align-items: center; - gap: 12px; - min-height: 52px; - padding: 13px 15px; - cursor: pointer; - list-style: none; - user-select: none; -} - -.function-live-chart-summary::-webkit-details-marker { - display: none; -} - -.function-live-chart-summary:before { - content: "▸"; - flex: 0 0 auto; - color: var(--muted); - font: 12px var(--mono); -} - -.function-live-chart[open]>.function-live-chart-summary:before { - content: "▾"; -} - -.function-live-chart-heading { - display: grid; - gap: 3px; - min-width: 0; - flex: 1 1 auto; -} - -.function-live-chart-heading strong { - font-size: 13px; - font-weight: 650; -} - -.function-live-chart-heading small { - color: var(--muted); - font: 9px var(--mono); -} - -.function-live-chart-summary>b { - flex: 0 0 auto; - color: var(--text); - font: 11px var(--mono); - font-weight: 650; - white-space: nowrap; -} - -.function-live-chart-body { - border-top: 1px solid var(--line); - padding: 14px 15px 12px; -} - -.function-live-chart svg { - display: block; - width: 100%; - height: 180px; - overflow: visible; -} - -.function-live-line { - fill: none; - stroke-width: 2.2; - vector-effect: non-scaling-stroke; -} - -.transform-applied-key, -.transform-applied-line { - stroke: var(--chart-quaternary); - color: var(--chart-quaternary); -} - -.transform-cache-key, -.transform-cache-line { - stroke: var(--chart-secondary); - color: var(--chart-secondary); -} - -.transform-skipped-key, -.transform-skipped-line { - stroke: var(--chart-septenary); - color: var(--chart-septenary); -} - -.transform-error-key, -.transform-error-line { - stroke: var(--chart-quinary); - color: var(--chart-quinary); -} - -.function-live-chart-footer { - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px 18px; - margin-top: 8px; -} - -.function-live-chart-samples { - display: inline-flex; - align-items: center; - gap: 7px; - color: var(--muted); - font: 9px var(--mono); - white-space: nowrap; -} - -.function-live-chart-samples select { - min-width: 116px; - padding: 5px 25px 5px 8px; - font: 10px var(--mono); -} - -.function-live-chart-legend { - display: flex; - flex-wrap: wrap; - justify-content: flex-end; - gap: 7px 16px; - color: var(--muted); - font: 9px var(--mono); -} - -.function-live-chart-legend span { - display: inline-flex; - align-items: center; - gap: 5px; -} - -.function-live-chart-legend i { - display: inline-block; - width: 14px; - height: 2px; - background: currentColor; -} - -.function-live-chart-legend b { - color: var(--text); - font-weight: 650; -} - -@media (max-width:760px) { - .function-live-chart-summary { - align-items: flex-start; - } - - .function-live-chart-heading small { - white-space: normal; - } - - .function-live-chart-footer { - align-items: flex-start; - flex-direction: column; - } - - .function-live-chart-legend { - justify-content: flex-start; - } -} - -.runtime-information-card { - margin-top: 0; -} - -.runtime-information-card h3 { - margin: 16px 0 8px; - font-size: 12px; - font-weight: 600; -} - -.runtime-information-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 12px; -} - -.runtime-information-grid>div { - min-width: 0; - display: grid; - gap: 5px; -} - -.runtime-information-grid code { - overflow-wrap: anywhere; - white-space: normal; -} - -.payload-split { - display: flex !important; - align-items: center; - gap: 10px; - font: 9px var(--mono) !important; -} - -.payload-split span { - display: inline-flex; - align-items: center; - gap: 3px; - white-space: nowrap; -} - -.connection-card .chart-footer b, -.connection-legend b, -.payload-split b { - color: var(--text); - font-weight: 650; -} - -.connection-card h2 { - display: flex; - align-items: baseline; - gap: 6px; -} - -.connection-card h2 small { - color: var(--muted); - font: 9px var(--mono); - font-weight: 400; -} - -.connection-card svg { - display: block; - width: 100%; - height: 100px; - overflow: visible; -} - -.connection-exchange-line, -.connection-open-line { - fill: none; - vector-effect: non-scaling-stroke; - stroke-width: 2; -} - -.connection-open-line { - stroke: var(--chart-accent); -} - -.connection-exchange-line { - stroke: var(--chart-secondary); -} - -.connection-legend { - display: flex; - flex-wrap: wrap; - gap: 7px 14px; - margin: 4px 0 8px; - color: var(--muted); - font: 9px var(--mono); -} - -.connection-legend span { - display: inline-flex; - align-items: center; - gap: 5px; -} - -.connection-legend i { - display: inline-block; - width: 14px; - height: 2px; - background: currentColor; -} - -.connection-open-key { - color: var(--accent) !important; -} - -.connection-exchange-key { - color: var(--green) !important; -} - -.connection-legend span span { - color: var(--muted); -} - -.network-interface-row-head { - align-items: center; -} - -.network-interface-head-actions { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 7px; - min-width: 0; -} - -.network-interface-hide { - min-height: 24px; - padding: 3px 7px; - font: 9px var(--mono); -} - -.network-interface-hidden { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 6px; - padding-top: 8px; - border-top: 1px solid var(--line); - color: var(--muted); - font: 9px var(--mono); -} - -.network-interface-hidden .quiet { - min-height: 24px; - padding: 3px 7px; - font: 9px var(--mono); -} - -.certificate-api-credentials { - display: grid; - gap: 7px; - margin-top: 10px; -} - -#certificate-api-cancel-edit[hidden], -#certificate-api-edit-note[hidden], -#certificate-api-token-field[hidden] { - display: none; -} - -.certificate-api-credential-item { - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 4px); - background: var(--panel2); -} - -.certificate-api-row-actions { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: flex-end; - gap: 4px; -} - -.certificate-api-details { - display: grid; - gap: 7px; - padding: 10px 12px; - border-top: 1px solid var(--line); -} - -.certificate-api-detail-line { - display: grid; - gap: 4px; - grid-template-columns: minmax(120px, 1fr) minmax(0, 2fr); - overflow-wrap: anywhere; -} - -.certificate-api-credential-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 8px 9px; -} - -.certificate-api-credential-info { - min-width: 0; - display: grid; - gap: 3px; -} - -.certificate-api-credential-info code, -.certificate-api-credential-info small { - overflow-wrap: anywhere; -} - -@media (max-width:760px) { - - .certificate-api-credential-row, - .network-interface-row-head { - align-items: flex-start; - } - - .certificate-api-credential-row { - flex-direction: column; - } -} - -.cache-memory-sizing { - margin: 12px 0; - padding: 12px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 0); - background: var(--panel2); -} - -.cache-memory-sizing .card-heading { - margin-bottom: 6px; -} - -.cache-memory-sizing .form-actions { - flex-wrap: wrap; -} - -#cache-memory-warning { - color: var(--notice-text); - font-weight: 600; -} - -.mini-heading-actions { - display: inline-flex; - align-items: center; - justify-content: flex-end; - gap: 7px; - min-width: 0; -} - -.metric-info { - position: relative; -} - -.metric-info>summary { - width: 18px; - height: 18px; - display: inline-grid; - place-items: center; - list-style: none; - cursor: pointer; - border: 1px solid var(--line); - border-radius: 50%; - color: var(--muted); - background: var(--panel2); - font: 700 10px var(--mono); - user-select: none; -} - -.metric-info>summary::-webkit-details-marker { - display: none; -} - -.metric-info>summary:focus-visible, -.metric-info>summary:hover, -.metric-info[open]>summary { - color: var(--text); - border-color: var(--border-hover); - outline: none; -} - -.metric-info-popover { - position: absolute; - z-index: 40; - top: calc(100% + 7px); - right: 0; - width: calc(min(360px, 100vw - 44px)); - padding: 10px 11px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 6px); - background: var(--panel2); - box-shadow: 0 12px 30px var(--shadow-mid); -} - -.metric-info-popover .fine { - margin: 0; - line-height: 1.5; -} - -.cache-hot-line, -.cache-transient-total-line { - stroke: var(--chart-accent); -} - -.cache-fill-line { - stroke: var(--chart-secondary); -} - -.cache-cold-line, -.cache-queue-line { - stroke: var(--chart-tertiary); -} - -.cache-io-line { - stroke: var(--chart-quaternary); -} - -.cache-transient-total-key { - color: var(--chart-accent); -} - -.cache-fill-key { - color: var(--green); -} - -.cache-queue-key { - color: var(--blue); -} - -.cache-io-key { - color: var(--transform-chart); -} - -.web-service-pages { - min-width: 0; -} - -#web-service-dialog .web-service-page { - padding-top: 2px; -} - -.manual-config-tools { - display: flex; - flex-wrap: wrap; - gap: 8px; - align-items: center; -} - -.config-history-dialog { - width: calc(min(1180px, 100vw - 24px)); - max-width: calc(100vw - 24px); - max-height: calc(100dvh - 28px); - overflow: hidden; -} - -.config-history-dialog[open] { - display: grid; - grid-template-rows: auto minmax(0, 1fr); - height: calc(min(820px, 100dvh - 28px)); -} - -.config-history-layout { - display: grid; - grid-template-columns: minmax(380px, .95fr) minmax(420px, 1.05fr); - grid-template-rows: minmax(0, 1fr); - gap: 10px; - padding: 10px; - min-height: 0; - overflow: hidden; -} - -.config-history-browser, -.config-history-detail { - min-width: 0; - min-height: 0; - overflow-y: auto; - overscroll-behavior: contain; -} - -.config-history-detail { - display: flex; - flex-direction: column; - overflow: hidden; -} - -.config-history-months { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 10px; -} - -.config-history-month { - border: 1px solid var(--line); - padding: 9px; - background: var(--panel); -} - -.config-history-month h4 { - margin: 0 0 8px; - font-size: 12px; -} - -.config-history-days, -.config-history-weekdays { - display: grid; - grid-template-columns: repeat(7, 1fr); - gap: 3px; -} - -.config-history-weekdays span { - color: var(--muted); - font: 9px var(--mono); - text-align: center; - padding: 2px 0; -} - -.config-history-day { - min-height: 34px; - padding: 4px; - border: 1px solid transparent; - background: transparent; - color: var(--muted); - font: 10px var(--mono); - text-align: left; -} - -button.config-history-day.has-revisions { - cursor: pointer; - color: var(--text); - border-color: var(--line-strong, var(--line)); - background: var(--surface, var(--bg)); -} - -button.config-history-day.has-revisions:hover, -button.config-history-day.is-selected { - border-color: var(--accent); - color: var(--accent); -} - -.config-history-day-count { - display: block; - margin-top: 3px; - font-size: 8px; - color: var(--muted); -} - -.config-history-detail-head { - display: flex; - justify-content: space-between; - gap: 10px; - align-items: flex-start; - margin-bottom: 8px; -} - -.config-history-detail-head h3 { - margin: 3px 0 0; -} - -.config-history-detail-actions { - display: flex; - flex-direction: column; - gap: 7px; - align-items: flex-end; - text-align: right; -} - -.config-history-detail-actions button { - white-space: nowrap; -} - -.config-history-revisions { - display: flex; - flex: 0 0 auto; - flex-wrap: wrap; - gap: 6px; - margin-bottom: 10px; - max-height: 110px; - overflow-y: auto; -} - -.config-history-revision { - font: 10px var(--mono); -} - -.config-history-revision.active { - border-color: var(--accent); - color: var(--accent); -} - -.config-history-diff { - display: grid; - flex: 1 1 auto; - min-height: 0; - align-content: start; - grid-auto-rows: max-content; - gap: 8px; - overflow-y: auto; - padding-right: 4px; - overscroll-behavior: contain; -} - -.config-history-change { - border: 1px solid var(--line); - padding: 8px; - background: var(--panel); -} - -.config-history-change-path { - display: block; - margin-bottom: 6px; - font: 10px var(--mono); - overflow-wrap: anywhere; -} - -.config-history-change-values { - grid-template-columns: 1fr 1fr; -} - -.config-history-change pre { - margin: 3px 0 0; - max-height: 160px; - overflow: auto; - white-space: pre-wrap; - overflow-wrap: anywhere; - font: 10px var(--mono); -} - -@media (max-width:860px) { - .config-history-layout { - display: block; - overflow-y: auto; - } - - .config-history-browser { - max-height: calc(min(300px, 38dvh)); - overflow-y: auto; - margin-bottom: 10px; - } - - .config-history-detail { - display: block; - overflow: visible; - } - - .config-history-revisions { - max-height: none; - } - - .config-history-diff { - overflow: visible; - } - - .config-history-change-values { - grid-template-columns: 1fr; - } - - .config-history-detail-head { - flex-direction: column; - } - - .config-history-detail-actions { - align-items: flex-start; - text-align: left; - } -} - -.mobile-menu-toggle { - display: none; -} - -@media (max-width:850px) { - .sidebar { - position: sticky; - top: 0; - z-index: 60; - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - align-items: center; - gap: 10px 12px; - padding: 10px 12px; - } - - .sidebar .brand { - min-width: 0; - } - - .mobile-menu-toggle { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 9px; - min-height: 36px; - padding: 7px 10px; - border: 1px solid var(--line); - background: transparent; - color: var(--muted); - font: 600 11px var(--mono); - } - - .mobile-menu-toggle:hover, - .mobile-menu-toggle[aria-expanded=true] { - color: var(--rp-nav-active-color); - border-color: var(--rp-nav-active-color); - background: var(--rp-nav-active-bg); - } - - .mobile-menu-icon { - display: block; - flex: 0 0 auto; - width: 15px; - height: 1px; - background: currentColor; - box-shadow: 0 -5px currentColor, 0 5px currentColor; - } - - .sidebar .main-navigation { - grid-column: 1/-1; - display: none; - width: 100%; - margin: 0; - padding-top: 10px; - border-top: 1px solid var(--line); - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 4px; - } - - .sidebar.mobile-nav-open .main-navigation { - display: grid; - } - - .sidebar .main-navigation .nav { - width: 100%; - min-width: 0; - justify-content: flex-start; - gap: 8px; - padding: 9px 10px; - border: 1px solid transparent; - border-left-width: 2px; - text-align: left; - } - - .sidebar .main-navigation .nav.active { - border-color: var(--line); - border-left-color: var(--rp-nav-active-color); - background: var(--rp-nav-active-bg); - } - - [dir=rtl] .sidebar .main-navigation .nav.active { - border-left-color: var(--line); - border-right-color: var(--rp-nav-active-color); - } -} - -@media (max-width:600px) { - .sidebar .main-navigation .nav span { - display: none; - } -} - -.about-dialog { - width: calc(min(560px, 100vw - 24px)); - overflow: hidden; -} - -.about-dialog .window-heading button { - margin-inline-start: auto; -} - -.about-card { - display: grid; - gap: 16px; - margin: 0; - padding: 18px; - border: 0; - box-shadow: none; -} - -.about-product { - display: flex; - align-items: center; - gap: 12px; -} - -.about-product>div { - display: grid; - gap: 4px; -} - -.about-product strong { - font: 600 16px var(--mono); -} - -.about-meta { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 1px; - margin: 0; - border: 1px solid var(--line); - background: var(--line); -} - -.about-meta>div { - min-width: 0; - padding: 10px 12px; - background: var(--panel); -} - -.about-meta dt { - margin-bottom: 5px; - color: var(--muted); - font: 9px var(--mono); - letter-spacing: .8px; - text-transform: uppercase; -} - -.about-meta dd { - margin: 0; - overflow-wrap: anywhere; -} - -.about-card>p { - margin: 0; - color: var(--muted); - line-height: 1.65; -} - -.about-repository { - justify-content: space-between; - gap: 12px; - width: 100%; - white-space: normal; - text-align: left; -} - -.about-repository span:last-child { - color: var(--muted); - font: 10px var(--mono); - overflow-wrap: anywhere; -} - -@media (max-width:620px) { - .topbar { - height: auto; - min-height: 46px; - padding: 7px 0; - } - - .topbar .breadcrumb { - display: none; - } - - .top-actions { - width: 100%; - flex-wrap: wrap; - justify-content: flex-end; - } - - .about-meta { - grid-template-columns: 1fr; - } - - .about-repository { - align-items: flex-start; - flex-direction: column; - } -} - -.file-logs-workspace { - display: grid; - gap: 14px; -} - -.file-logs-filter label { - min-width: 0; -} - -.file-logs-table { - max-height: 62vh; -} - -.file-logs-table thead th { - position: sticky; - top: 0; - z-index: 4; - background: var(--panel); - box-shadow: 0 1px 0 var(--line); -} - -.file-logs-table th .file-log-sort-button { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 3px 2px; - font: inherit; - font-weight: inherit; - color: inherit; - border: 0; - background: transparent; - cursor: pointer; - text-align: left; -} - -.file-logs-table th .file-log-sort-button:hover, -.file-logs-table th[aria-sort=ascending] .file-log-sort-button, -.file-logs-table th[aria-sort=descending] .file-log-sort-button { - color: var(--accent); -} - -.file-logs-table .file-log-sort-indicator { - font-size: 11px; - opacity: .8; -} - -.file-logs-table tbody tr:not(.file-log-detail-row) { - cursor: pointer; -} - -.file-logs-table tbody tr.selected>td { - background: var(--panel2); - box-shadow: inset 3px 0 0 var(--accent); -} - -.file-logs-table td { - max-width: 340px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.file-log-detail-row td { - max-width: none; - padding: 0; - background: var(--panel2); - white-space: normal; -} - -.file-log-inline-detail { - border-top: 1px solid var(--line); - border-bottom: 1px solid var(--line); -} - -.file-log-inline-detail pre { - margin: 8px 0 0; - overflow: auto; - white-space: pre-wrap; - overflow-wrap: anywhere; -} - -.file-logs-pager { - display: flex; - justify-content: flex-end; - gap: 8px; -} - -.file-logs-launcher { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; -} - -@media (max-width:1100px) { - .file-logs-filter { - grid-template-columns: repeat(3, minmax(140px, 1fr)); - } -} - -@media (max-width:700px) { - .file-logs-filter .file-log-search { - grid-column: auto; - } - - .file-logs-launcher { - align-items: stretch; - flex-direction: column; - } -} - -.top-log-menu { - position: relative; -} - -.file-log-search-help>summary, -.top-log-menu>summary { - list-style: none; - cursor: pointer; - user-select: none; -} - -.file-log-search-help>summary::-webkit-details-marker, -.top-log-menu>summary::-webkit-details-marker { - display: none; -} - -.file-log-search-help[open]>summary, -.top-log-menu[open]>summary { - color: var(--text); - border-color: var(--border-hover); -} - -.top-log-menu-items { - position: absolute; - z-index: 120; - top: calc(100% + 6px); - inset-inline-end: 0; - display: grid; - gap: 4px; - min-width: 170px; - padding: 6px; - border: 1px solid var(--line); - background: var(--panel); - box-shadow: 0 12px 28px var(--shadow-strong); -} - -.top-log-menu-items .quiet { - width: 100%; - text-align: start; - white-space: nowrap; -} - -.file-logs-heading-actions, -.file-logs-table-toolbar { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 8px; -} - -.file-log-search-help { - position: relative; -} - -.file-log-search-help-popover { - position: absolute; - z-index: 90; - top: calc(100% + 6px); - inset-inline-end: 0; - display: grid; - gap: 7px; - width: calc(min(420px, 100vw - 36px)); - padding: 12px; - border: 1px solid var(--line); - background: var(--panel2); - box-shadow: 0 12px 28px var(--shadow-strong); -} - -.file-log-search-help-popover p, -.file-logs-table-toolbar .fine { - margin: 0; -} - -.file-log-search-help-popover code { - display: block; - overflow-wrap: anywhere; -} - -.file-logs-filter { - display: grid; - grid-template-columns: minmax(180px, 260px) minmax(260px, 1fr) minmax(110px, 150px); - gap: 10px; - align-items: end; -} - -.file-logs-filter .file-log-search { - grid-column: auto; -} - -.file-logs-filter input, -.file-logs-filter select { - width: 100%; -} - -.file-logs-table-toolbar { - justify-content: space-between; - min-height: 32px; -} - -.file-log-column-picker { - align-self: end; - margin-inline-start: auto; -} - -.file-log-column-picker .log-column-options { - width: calc(min(360px, 100vw - 24px)); - max-height: 420px; - overflow: auto; - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.dashboard-widget-setting-label, -.file-log-column-picker .log-column-option span { - overflow: hidden; - text-overflow: ellipsis; -} - -.file-log-field-grid { - display: grid; - gap: 1px; - border: 1px solid var(--line); -} - -.file-log-field { - display: grid; - grid-template-columns: minmax(140px, .5fr) minmax(0, 1.5fr); - background: var(--panel); -} - -.file-log-field code { - overflow-wrap: anywhere; - color: var(--muted); -} - -.file-log-field span { - min-width: 0; - overflow-wrap: anywhere; -} - -.file-log-raw-json>summary { - cursor: pointer; - color: var(--muted); -} - -.backend-sort-direction { - min-width: 36px; - font-size: 16px; - line-height: 1; -} - -@media (max-width:700px) { - - .file-logs-heading-actions, - .file-logs-table-toolbar { - align-items: stretch; - flex-wrap: wrap; - } - - .file-log-column-picker .log-column-options, - .file-log-field, - .file-logs-filter { - grid-template-columns: 1fr; - } - - .file-log-limit { - max-width: 160px; - } - - .file-log-field { - gap: 3px; - } -} - -.appearance-settings-card { - max-width: 980px; -} - -.appearance-section { - margin-top: 14px; - padding: 12px; - border: 1px solid var(--line); - background: var(--panel2); -} - -.appearance-section-heading { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 12px; - margin-bottom: 12px; - padding-bottom: 9px; - border-bottom: 1px solid var(--line); -} - -.appearance-section-heading h3 { - margin: 2px 0 0; - font-size: 14px; -} - -.appearance-section-reset { - flex: 0 0 auto; -} - -.appearance-settings-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 16px; - align-items: start; -} - -.appearance-settings-grid>label { - display: grid; - gap: 7px; -} - -.appearance-scale-control>span:first-child { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} - -.appearance-scale-control output { - color: var(--accent); - font-weight: 700; - font-variant-numeric: tabular-nums; -} - -.appearance-scale-control input[type=range] { - width: 100%; -} - -.appearance-chart-preview { - min-height: 118px; - display: grid; - grid-template-rows: 1fr auto; - gap: 6px; - padding: 10px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 0); - background: var(--chart-surface); -} - -.appearance-chart-preview svg { - width: 100%; - height: 78px; - filter: var(--chart-svg-filter); -} - -.appearance-chart-preview polyline { - fill: none; - stroke-width: var(--chart-line-width); - stroke-linecap: var(--chart-linecap); - stroke-linejoin: var(--chart-linejoin); - vector-effect: non-scaling-stroke; -} - -.appearance-chart-primary { - stroke: var(--chart-accent); -} - -.appearance-chart-secondary { - stroke: var(--chart-secondary); -} - -.appearance-chart-preview>span { - color: var(--muted); - font-size: 11px; -} - -.chart-style-palette { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 6px; - margin-top: 10px; -} - -.chart-style-choice { - display: grid; - grid-template-columns: 42px minmax(0, 1fr); - align-items: center; - gap: 8px; - min-height: 34px; - padding: 5px 7px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-control, 0); - background: var(--panel); - color: var(--text); - text-align: left; - font-size: 10px; -} - -.chart-style-choice:hover { - border-color: var(--border-hover); - background: var(--nav-hover-bg); -} - -.chart-style-choice.active { - border-color: var(--accent); - box-shadow: inset 3px 0 0 var(--accent); -} - -.chart-style-swatches { - display: grid; - grid-template-columns: repeat(4, 1fr); - height: 16px; - border: 1px solid var(--line); -} - -.chart-style-swatches i:first-child { - background: var(--sw1); -} - -.chart-style-swatches i:nth-child(2) { - background: var(--sw2); -} - -.chart-style-swatches i:nth-child(3) { - background: var(--sw3); -} - -.chart-style-swatches i:nth-child(4) { - background: var(--sw4); -} - -.chart-style-choice[data-chart-preset=default] { - --sw1: var(--accent); - --sw2: var(--green); - --sw3: var(--blue); - --sw4: var(--transform-chart); -} - -.chart-style-choice[data-chart-preset=soft] { - --sw1: #e8a998; - --sw2: #95c293; - --sw3: #8daef6; - --sw4: #b49de6; -} - -.chart-style-choice[data-chart-preset=technical] { - --sw1: #79c9ff; - --sw2: #9adf90; - --sw3: #ffd56e; - --sw4: #c39cff; -} - -.chart-style-choice[data-chart-preset=high_contrast] { - --sw1: #ffd54f; - --sw2: #8dff80; - --sw3: #6fd9ff; - --sw4: #ff78d7; -} - -.chart-style-choice[data-chart-preset=neon] { - --sw1: #2cf6ff; - --sw2: #7eff85; - --sw3: #ff5cd0; - --sw4: #8b7cff; -} - -.chart-style-choice[data-chart-preset=monochrome] { - --sw1: #f2f2f2; - --sw2: #b8b8b8; - --sw3: #858585; - --sw4: #595959; -} - -.chart-style-choice[data-chart-preset=warm] { - --sw1: #ff9c5c; - --sw2: #f4c46b; - --sw3: #ff7b72; - --sw4: #f4a261; -} - -.chart-style-choice[data-chart-preset=cool] { - --sw1: #5fb9ff; - --sw2: #45d3c3; - --sw3: #7d8fff; - --sw4: #70d7ff; -} - -.dashboard-widget-settings { - display: grid; - min-width: 0; - gap: 5px; -} - -.dashboard-widget-list { - min-width: 0; - grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); - margin-top: 4px; -} - -.dashboard-widget-setting { - grid-template-columns: minmax(0, 1fr) auto; - min-height: 34px; - border: 1px solid var(--line); - background: var(--panel); -} - -.dashboard-widget-toggle { - display: flex; - align-items: center; - gap: 7px; - min-width: 0; - min-height: 26px; - padding: 3px 6px; - border: 0; - background: transparent; - color: var(--text); - text-align: left; -} - -.dashboard-widget-toggle:hover { - background: var(--panel2); -} - -.dashboard-widget-toggle.is-hidden { - color: var(--muted); -} - -.dashboard-widget-visibility { - flex: 0 0 8px; - width: 8px; - height: 8px; - border: 1px solid var(--green); - border-radius: 50%; - background: var(--green); -} - -.dashboard-widget-toggle.is-hidden .dashboard-widget-visibility { - border-color: var(--muted); - background: transparent; -} - -.dashboard-widget-setting-label { - white-space: nowrap; - font-size: 11px; -} - -.dashboard-widget-setting-actions { - display: inline-flex; - gap: 2px; -} - -.dashboard-widget-setting-actions button { - min-width: 24px; - min-height: 24px; - padding: 1px 5px; - border-radius: 0; -} - -.appearance-actions { - display: flex; - justify-content: flex-end; - margin-top: 14px; - padding-top: 12px; - border-top: 1px solid var(--line); -} - -html[data-chart-style=default] { - --chart-accent: var(--accent); - --chart-secondary: var(--green); - --chart-tertiary: var(--blue); - --chart-quaternary: var(--transform-chart); - --chart-quinary: var(--red); - --chart-senary: var(--notice-text); - --chart-septenary: var(--border-hover); - --chart-octonary: var(--separator); - --chart-surface: var(--panel2); - --chart-surface-alt: var(--panel); - --chart-line-width: 2; - --chart-linecap: round; - --chart-linejoin: round; - --chart-grid-opacity: .28; - --chart-grid-dash: 2 5; - --chart-svg-filter: none; -} - -html[data-chart-style=soft] { - --chart-accent: #e8a998; - --chart-secondary: #95c293; - --chart-tertiary: #8daef6; - --chart-quaternary: #b49de6; - --chart-quinary: #d99490; - --chart-senary: #d0b78e; - --chart-septenary: #99a790; - --chart-octonary: #728070; - --chart-surface: color-mix(in srgb, var(--panel2) 88%, #fff 12%); - --chart-surface-alt: color-mix(in srgb, var(--panel) 92%, #fff 8%); - --chart-line-width: 2.8; - --chart-linecap: round; - --chart-linejoin: round; - --chart-grid-opacity: .42; - --chart-grid-dash: 1 0; - --chart-svg-filter: saturate(.88) brightness(1.04); -} - -html[data-chart-style=technical] { - --chart-accent: #79c9ff; - --chart-secondary: #9adf90; - --chart-tertiary: #ffd56e; - --chart-quaternary: #c39cff; - --chart-quinary: #ff8c7b; - --chart-senary: #b8d0d7; - --chart-septenary: #7e9aac; - --chart-octonary: #6a7884; - --chart-surface: color-mix(in srgb, var(--panel2) 90%, #0b1a27 10%); - --chart-surface-alt: color-mix(in srgb, var(--panel) 90%, #09151f 10%); - --chart-line-width: 1.35; - --chart-linecap: square; - --chart-linejoin: miter; - --chart-grid-opacity: .82; - --chart-grid-dash: 3 4; - --chart-svg-filter: saturate(.76) contrast(1.08); -} - -html[data-chart-style=high_contrast] { - --chart-accent: #ffd54f; - --chart-secondary: #8dff80; - --chart-tertiary: #6fd9ff; - --chart-quaternary: #ff78d7; - --chart-quinary: #ff8b6f; - --chart-senary: #f2e48f; - --chart-septenary: #d7f0d9; - --chart-octonary: #cfcfcf; - --chart-surface: color-mix(in srgb, var(--panel2) 94%, #000 6%); - --chart-surface-alt: color-mix(in srgb, var(--panel) 94%, #000 6%); - --chart-line-width: 3; - --chart-grid-opacity: .95; - --chart-grid-dash: 1 0; - --chart-svg-filter: saturate(1.6) contrast(1.22) brightness(1.08); -} - -html[data-chart-style=cool], -html[data-chart-style=high_contrast], -html[data-chart-style=monochrome], -html[data-chart-style=neon], -html[data-chart-style=warm] { - --chart-linecap: round; - --chart-linejoin: round; -} - -html[data-chart-style=neon] { - --chart-accent: #2cf6ff; - --chart-secondary: #7eff85; - --chart-tertiary: #ff5cd0; - --chart-quaternary: #8b7cff; - --chart-quinary: #ffb454; - --chart-senary: #ffe66b; - --chart-septenary: #7edcff; - --chart-octonary: #d8b3ff; - --chart-surface: #0b1117; - --chart-surface-alt: #101720; - --chart-line-width: 2.45; - --chart-grid-opacity: .62; - --chart-grid-dash: 2 4; - --chart-svg-filter: saturate(1.25) brightness(1.12); -} - -html[data-chart-style=monochrome] { - --chart-accent: color-mix(in srgb, var(--text) 92%, #fff 8%); - --chart-secondary: color-mix(in srgb, var(--muted) 80%, var(--text) 20%); - --chart-tertiary: color-mix(in srgb, var(--separator) 72%, var(--text) 28%); - --chart-quaternary: color-mix(in srgb, var(--line) 45%, var(--text) 55%); - --chart-quinary: color-mix(in srgb, var(--muted) 55%, #fff 45%); - --chart-senary: color-mix(in srgb, var(--outline-line) 58%, var(--text) 42%); - --chart-septenary: color-mix(in srgb, var(--field-line) 70%, var(--text) 30%); - --chart-octonary: color-mix(in srgb, var(--scrollbar-thumb) 75%, var(--text) 25%); - --chart-surface: color-mix(in srgb, var(--panel2) 96%, var(--text) 4%); - --chart-surface-alt: color-mix(in srgb, var(--panel) 96%, var(--text) 4%); - --chart-line-width: 2.1; - --chart-grid-opacity: .4; - --chart-grid-dash: 2 3; - --chart-svg-filter: saturate(0) contrast(1.05); -} - -html[data-chart-style=warm] { - --chart-accent: #ff9c5c; - --chart-secondary: #f4c46b; - --chart-tertiary: #ff7b72; - --chart-quaternary: #f4a261; - --chart-quinary: #e76f51; - --chart-senary: #d9a15e; - --chart-septenary: #c9895c; - --chart-octonary: #b37258; - --chart-surface: color-mix(in srgb, var(--panel2) 88%, #3d1f10 12%); - --chart-surface-alt: color-mix(in srgb, var(--panel) 90%, #32180d 10%); - --chart-line-width: 2.4; - --chart-grid-opacity: .36; - --chart-grid-dash: 1 0; - --chart-svg-filter: saturate(1.1) brightness(1.04); -} - -html[data-chart-style=cool] { - --chart-accent: #5fb9ff; - --chart-secondary: #45d3c3; - --chart-tertiary: #7d8fff; - --chart-quaternary: #70d7ff; - --chart-quinary: #54a0ff; - --chart-senary: #8bcbe0; - --chart-septenary: #6ea6bf; - --chart-octonary: #7d91a9; - --chart-surface: color-mix(in srgb, var(--panel2) 88%, #0d2740 12%); - --chart-surface-alt: color-mix(in srgb, var(--panel) 90%, #0d2033 10%); - --chart-line-width: 2.2; - --chart-grid-opacity: .46; - --chart-grid-dash: 2 4; - --chart-svg-filter: saturate(1.08) brightness(1.03); -} - -html[data-chart-style] { - --traffic-h1: var(--chart-accent); - --traffic-h2: var(--chart-secondary); - --traffic-h3: var(--chart-tertiary); -} - -:is(.stat-chart, .activity-card, .system-card, .function-live-chart) svg, -:is(.traffic-donut, .traffic-bar-chart span) { - filter: var(--chart-svg-filter); -} - -html[data-dashboard-density=compact] #panel-overview .stats-grid, -html[data-dashboard-density=normal] #panel-overview .stats-grid, -html[data-dashboard-density=ultra] #panel-overview .stats-grid { - gap: var(--ui-gap); -} - -html[data-dashboard-density=normal] #panel-overview .stat { - padding: 14px; -} - -html[data-dashboard-density=normal] #panel-overview .stat>span { - font-size: 11px; -} - -html[data-dashboard-density=normal] #panel-overview .stat strong { - font-size: 26px; - margin: 6px 0 4px; - line-height: 1.3; -} - -html[data-dashboard-density=normal] #panel-overview .stat small { - font-size: 9px; -} - -html[data-dashboard-density=compact] #panel-overview .stat { - padding: 9px 10px; -} - -html[data-dashboard-density=compact] #panel-overview .stat>span { - font-size: 11px; - line-height: 1.35; -} - -html[data-dashboard-density=compact] #panel-overview .stat strong { - font-size: 22px; - margin: 3px 0 2px; - line-height: 1.2; -} - -html[data-dashboard-density=compact] #panel-overview .stat small { - font-size: 10px; - line-height: 1.35; -} - -html[data-dashboard-density=compact] #panel-overview .payload-split { - font-size: 10px !important; -} - -html[data-dashboard-density=ultra] #panel-overview .stat { - padding: 7px 8px; -} - -html[data-dashboard-density=ultra] #panel-overview .stat>span { - font-size: 9px; - line-height: 1.08; -} - -html[data-dashboard-density=ultra] #panel-overview .stat strong { - font-size: 18px; - margin: 2px 0 1px; - line-height: 1.08; -} - -html[data-dashboard-density=ultra] #panel-overview .stat small { - font-size: 7.5px; - line-height: 1.12; -} - -html[data-dashboard-density=ultra] #panel-overview .stat-chart svg { - height: 24px; -} - -html[data-dashboard-density=normal] #panel-overview .stat-chart svg { - height: 34px; -} - -html[data-dashboard-density=compact] #panel-overview .stat-chart svg { - height: 28px; -} - -html[data-table-density=compact] :is(table th, table td) { - padding-top: 6px; - padding-bottom: 6px; - font-size: 11px; - line-height: 1.28; -} - -html[data-table-density=compact] .file-log-field { - padding: 2px 6px; - line-height: 1.18; -} - -html[data-table-density=compact] .file-log-inline-detail pre { - line-height: 1.18; -} - -html[data-table-density=ultra] :is(table th, table td) { - padding-top: 3px; - padding-bottom: 3px; - font-size: 10px; - line-height: 1.15; -} - -html[data-table-density=ultra] .file-log-inline-detail { - padding: 5px 7px; -} - -html[data-table-density=ultra] .file-log-field-grid { - margin-top: 2px; -} - -html[data-table-density=ultra] .file-log-field { - gap: 6px; - padding: 1px 5px; - font-size: 10px; - line-height: 1.08; -} - -html[data-table-density=ultra] .file-log-raw-json { - margin-top: 3px; -} - -html[data-table-density=ultra] .file-log-inline-detail pre { - margin-top: 3px; - line-height: 1.08; -} - -.file-log-inline-detail { - padding: 8px 10px; -} - -.file-log-field-grid { - margin-top: 5px; -} - -.file-log-field { - gap: 9px; - padding: 3px 7px; - line-height: 1.25; -} - -.file-log-raw-json { - margin-top: 6px; -} - -.file-log-inline-detail pre { - max-height: 24vh; - margin-top: 5px; - line-height: 1.28; -} - -@media (max-width:760px) { - .appearance-settings-grid { - grid-template-columns: 1fr; - } - - .chart-style-palette { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -.draft-dock { - position: fixed; - right: 14px; - bottom: 14px; - z-index: 900; - width: calc(min(330px, 100vw - 28px)); - padding: 0; - border: 1px solid var(--border-hover); - border-radius: var(--rp-radius-surface, 0); - background: var(--panel); - box-shadow: 0 12px 32px var(--shadow-strong); -} - -.draft-dock-heading { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - min-height: 34px; - padding: 7px 9px; - border-bottom: 1px solid var(--line); - background: var(--panel2); -} - -.draft-dock.is-collapsed .draft-dock-heading { - border-bottom: 0; -} - -.draft-dock-heading>strong { - font: 700 10px var(--mono); - letter-spacing: .12em; - text-transform: uppercase; -} - -.draft-dock-heading .pill { - min-width: 24px; - padding: 2px 6px; - text-align: center; - border-radius: var(--rp-radius-control, 0); -} - -.draft-dock-heading-actions { - display: inline-flex; - align-items: center; - gap: 5px; -} - -.draft-dock-toggle { - display: inline-grid; - place-items: center; - width: 26px; - min-width: 26px; - height: 24px; - padding: 0; - border: 1px solid var(--line); - border-radius: var(--rp-radius-control, 0); - background: var(--panel); - color: var(--text); - font: 700 14px var(--mono); -} - -.draft-dock-toggle:hover { - border-color: var(--border-hover); - background: var(--nav-hover-bg); -} - -.draft-dock-list { - display: grid; - gap: 0; - max-height: calc(min(42vh, 360px)); - overflow: auto; -} - -.draft-dock-list[hidden] { - display: none; -} - -.draft-dock-item { - display: grid; - grid-template-columns: minmax(0, 1fr) 32px; - gap: 0; - align-items: stretch; - border-bottom: 1px solid var(--line); -} - -.draft-dock-item:last-child { - border-bottom: 0; -} - -.draft-dock-open, -.draft-dock-remove { - min-height: 42px; - border: 0; - border-radius: 0; - background: transparent; -} - -.draft-dock-open { - min-width: 0; - padding: 6px 9px; - text-align: left; - border-right: 1px solid var(--line); -} - -.draft-dock-open:hover, -.draft-dock-remove:hover { - background: var(--nav-hover-bg); - color: var(--text); -} - -.draft-dock-open:hover { - box-shadow: inset 3px 0 0 var(--accent); -} - -.draft-dock-identity { - display: grid; - min-width: 0; - gap: 1px; -} - -.draft-dock-meta { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - min-width: 0; -} - -.draft-dock-identity small { - color: var(--muted); - font: 9px var(--mono); - text-transform: uppercase; - letter-spacing: .08em; -} - -.draft-dock-identity time { - flex: 0 0 auto; - color: var(--text-subtle); - font: 9px var(--mono); -} - -.draft-dock-identity b { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 12px; - font-weight: 650; -} - -.draft-dock-remove { - padding: 0; - font-size: 18px; - color: var(--muted); -} - -html[data-chart-style] :is(#workspace, dialog) svg polyline { - stroke-width: var(--chart-line-width); - stroke-linecap: var(--chart-linecap); - stroke-linejoin: var(--chart-linejoin); -} - -#panel-settings .settings-filter, -.backend-table-tools, -.docker-toolbar, -.file-logs-filter, -.log-tools, -.vhost-list-tools { - --toolbar-control-height: min(var(--button-min-height), var(--ui-field-min-height)); -} - -#panel-settings .settings-filter input, -.backend-table-tools input, -.backend-table-tools select, -.docker-toolbar input[type=search], -.file-logs-filter input, -.file-logs-filter select, -.log-tools input:not([type=checkbox]):not([type=radio]), -.log-tools select, -.vhost-list-tools input[type=search] { - box-sizing: border-box; - height: var(--toolbar-control-height); - min-height: var(--toolbar-control-height); - padding-block: 0; -} - -.acl-profile-layout { - grid-template-columns: minmax(260px, .72fr) minmax(440px, 1.28fr); -} - -.acl-policy-builder, -.acl-policy-preview { - margin-top: 12px; - padding: 12px; - border: 1px solid var(--line); - background: var(--bg); -} - -.acl-policy-preview-head, -.acl-policy-step-head, -.acl-policy-step-head>div { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 10px; -} - -.acl-policy-step-head>div, -.php-switch-row { - justify-content: flex-start; -} - -.acl-policy-preview-head>span, -.acl-policy-step-head strong { - display: block; - font-size: 12px; - color: var(--text); -} - -.acl-policy-step-head p { - margin: 3px 0 0; -} - -.acl-policy-step { - display: inline-grid; - place-items: center; - flex: 0 0 24px; - width: 24px; - height: 24px; - border: 1px solid var(--line); - background: var(--panel); - color: var(--muted); - font: 11px var(--mono); -} - -.acl-policy-modes { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 8px; - margin-top: 10px; -} - -.acl-policy-mode-card { - position: relative; - display: flex !important; - align-items: flex-start; - gap: 9px; - margin: 0 !important; - padding: 10px; - border: 1px solid var(--field-line); - background: var(--field-bg); - cursor: pointer; -} - -.acl-policy-mode-card:has(input:checked) { - outline: 1px solid var(--green); - border-color: var(--green); -} - -.acl-policy-mode-card input { - margin-top: 2px; -} - -.acl-policy-mode-card span { - display: grid; - gap: 3px; -} - -.acl-policy-mode-card b { - color: var(--text); - font-size: 12px; -} - -.acl-policy-mode-card small { - color: var(--muted); - line-height: 1.45; -} - -.acl-policy-mode-custom { - grid-column: 1/-1; -} - -.acl-policy-custom-controls { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 0 10px; - margin-top: 6px; - padding-top: 8px; - border-top: 1px solid var(--line); -} - -.acl-policy-custom-controls[hidden] { - display: none; -} - -.acl-policy-switch { - display: inline-flex !important; - align-items: center; - gap: 7px; - margin: 0 !important; - white-space: nowrap; -} - -.acl-policy-country-list.is-disabled { - opacity: .55; -} - -.acl-profile-rule-action-badge { - display: flex; - align-items: center; - min-height: var(--ui-field-min-height); - padding: 7px 9px; - border: 1px solid var(--line); - background: var(--panel); - font-size: 11px; - font-weight: 700; -} - -.acl-policy-preview-head .is-allow, -.acl-profile-rule-action-badge.is-allow { - color: var(--green); -} - -.acl-policy-preview-head .is-deny, -.acl-profile-rule-action-badge.is-deny { - color: var(--red); -} - -.acl-policy-preview { - display: grid; - gap: 9px; -} - -.acl-policy-preview-head { - align-items: center; -} - -.acl-policy-preview-head b { - padding: 4px 7px; - border: 1px solid var(--line); - background: var(--panel); - font: 700 10px var(--mono); -} - -.acl-policy-preview-flow { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 6px; -} - -.acl-policy-condition { - display: inline-flex; - flex-wrap: wrap; - gap: 5px; - align-items: center; - max-width: 100%; - padding: 7px 9px; - border: 1px solid var(--line); - background: var(--panel); - font-size: 11px; -} - -.acl-policy-condition b { - font-size: 10px; - color: var(--muted); -} - -.acl-policy-condition span, -.listener-active-address { - overflow-wrap: anywhere; -} - -.acl-policy-condition.allow { - border-left: 3px solid var(--green); -} - -.acl-policy-condition.deny { - border-left: 3px solid var(--red); -} - -.acl-policy-or { - color: var(--muted); - font: 700 10px var(--mono); -} - -.acl-policy-preview .fine { - margin: 0; -} - -.acl-profile-card .entity-details { - white-space: pre-line; -} - -@media (max-width:900px) { - - .acl-policy-custom-controls, - .acl-policy-modes, - .acl-profile-layout { - grid-template-columns: 1fr; - } - - .acl-policy-mode-custom { - grid-column: auto; - } -} - -@media (max-width:640px) { - - .acl-policy-preview-head, - .acl-policy-step-head { - align-items: stretch; - flex-direction: column; - } - - .acl-policy-step-head>button, - .acl-policy-switch { - width: 100%; - } -} - -.listener-active-summary { - margin-top: 10px; - padding: 10px 12px; - border: 1px solid var(--line); - background: var(--panel2); - display: grid; - gap: 8px; -} - -.listener-active-title { - font-size: 10px; - color: var(--muted); - text-transform: uppercase; - letter-spacing: .45px; -} - -.listener-active-items { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.listener-active-item { - display: inline-flex; - align-items: center; - gap: 7px; - min-width: 0; - padding: 7px 9px; - border: 1px solid var(--line); - background: var(--panel); -} - -.listener-active-protocol { - font: 600 10px var(--mono); - color: var(--accent); -} - -.listener-active-feature { - padding: 2px 5px; - border: 1px solid var(--line); - font: 600 9px var(--mono); - color: var(--muted); -} - -.cache-via-setting { - display: grid; - grid-template-columns: minmax(180px, .8fr) minmax(220px, 1.2fr); - gap: 8px 14px; - align-items: end; - margin-top: 10px; -} - -.location-header-editor .vh-header-rule-head>button { - margin-left: auto; - flex: 0 0 auto; -} - -@media (max-width:650px) { - .cache-via-setting { - grid-template-columns: 1fr; - } -} - -.php-switch-row { - display: flex; - align-items: center; - gap: 10px; - min-width: 0; - min-height: 30px; - margin: 4px 0; -} - -.php-switch-row .php-switch-label { - display: inline-flex !important; - flex: 0 1 auto; - align-items: center; - gap: 9px; - min-width: 0; - margin: 0 !important; - line-height: 1.5; -} - -.php-switch-row .php-switch-label input[type=checkbox] { - position: static; - flex: 0 0 var(--ui-check-size); - margin: 0; -} - -.php-switch-row .php-switch-label span { - min-width: 0; -} - -.php-switch-row .php-field-info { - flex: 0 0 auto; -} - -.php-switch-row .php-field-info .metric-info-popover { - right: auto; - left: 0; -} - -.php-reference pre { - overflow-x: auto; - white-space: pre; - padding: 10px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 4px); - background: var(--panel2); - font: 12px/1.6 var(--mono); -} - -.php-profile-help-divider { - width: 100%; - height: 0; - margin: 16px 0 12px; - border: 0; - border-top: 1px solid var(--line); -} - -.php-origin-errors { - grid-column: 1/-1; - display: grid; - gap: 8px; - padding: 10px 0; - border-top: 1px solid var(--line); -} - -.php-origin-errors>label { - max-width: 430px; -} - -.php-origin-errors>.check { - margin: 0 !important; -} - -.php-origin-errors .fine { - margin: 0; -} - -.page-intro-copy, -.page-intro-title-row { - min-width: 0; - width: 100%; -} - -.page-intro-title-row { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: flex-end; - gap: 10px; -} - -.page-intro-title-row h1 { - min-width: 0; - flex: 1 1 auto; - margin-right: auto; -} - -@media (max-width:700px) { - .page-intro-title-row { - gap: 8px; - } -} - -@media (max-width:480px) { - .page-intro-title-row h1 { - font-size: calc(clamp(18px, 5vw, 24px)); - letter-spacing: -.6px; - } -} - -.system-info-heading-actions { - display: flex; - align-items: center; - gap: 8px; - margin-left: auto; - flex-shrink: 0; -} - -.system-info-heading-actions .pill { - white-space: nowrap; -} - -.cache-varnish-setting { - display: flex; - align-items: center; - gap: 8px; - margin-top: 14px; - position: relative; -} - -.cache-varnish-setting .check { - min-width: 0; - flex: 0 1 auto; - margin: 0 !important; -} - -.cache-varnish-info { - position: static; - flex-shrink: 0; -} - -.cache-varnish-grace-field { - width: min(320px, 100%); - min-width: 0; - margin-top: 12px; -} - -.cache-varnish-grace-field label { - display: block; -} - -.cache-varnish-grace-field input { - display: block; - width: 100%; - box-sizing: border-box; -} - -.cache-varnish-info .metric-info-popover { - width: calc(min(440px, 100vw - 56px)); - max-height: calc(min(65vh, 450px)); - overflow-y: auto; -} - -.cache-varnish-info .metric-info-popover .fine+.fine { - margin-top: 8px; -} - -.cache-maintenance-help { - display: flex; - align-items: center; - gap: 8px; - position: relative; -} - -.cache-maintenance-help .subheading { - margin: 0; -} - -.cache-maintenance-info { - position: static; - flex-shrink: 0; -} - -.cache-maintenance-info .metric-info-popover { - max-height: calc(min(60vh, 420px)); - overflow-y: auto; - width: calc(min(440px, 100vw - 56px)); -} - -.cache-maintenance-info .metric-info-popover code { - display: block; - white-space: pre-wrap; - overflow-wrap: anywhere; - padding: 7px 0; - font-size: 11px; - line-height: 1.55; -} - -.cache-maintenance-info .metric-info-popover .fine+code { - margin-top: 6px; -} - -.cache-maintenance-info .metric-info-popover code+.fine { - margin-top: 8px; -} - -.file-logs-heading-actions .file-log-consolidate { - display: inline-flex; - align-items: center; - gap: .45rem; - padding: .35rem .55rem; - min-height: 0; - color: var(--muted); - font-size: .8rem; - font-weight: 400; - white-space: nowrap; - cursor: pointer; -} - -.file-log-consolidate input { - width: 14px; - height: 14px; - margin: 0; - accent-color: var(--accent); -} - -.file-log-consolidate:hover { - color: var(--text); -} - -.file-log-tabs { - display: flex; - gap: .25rem; - border-bottom: 1px solid var(--line); - margin: .9rem 0 1rem; -} - -.file-log-tab { - appearance: none; - background: transparent; - border: 0; - border-bottom: 2px solid transparent; - border-radius: 0; - padding: .65rem 1rem; - color: var(--muted); - cursor: pointer; - font: inherit; - font-weight: 500; -} - -.file-log-tab:hover { - color: var(--text); -} - -.file-log-tab.active { - color: var(--text); - border-bottom-color: var(--accent); -} - -.file-log-tab:focus-visible { - outline: 2px solid var(--accent); - outline-offset: -2px; -} - -#file-log-view-counts[hidden], -#file-log-view-entries[hidden] { - display: none !important; -} - -.file-log-advanced-parser h3 { - margin: 0 0 .7rem; - font-size: 1rem; -} - -@media (max-width:700px) { - .file-logs-heading-actions .file-log-consolidate { - margin-inline-end: auto; - } - - .file-log-tab { - padding: .6rem .7rem; - } -} - -.file-log-aggregate-copy, -.traffic-copy-value { - appearance: none; - border: 0; - background: transparent; - color: inherit; - font: inherit; - text-align: left; - padding: 0; - margin: 0; - cursor: copy; -} - -.file-log-aggregate-copy:focus-visible, -.file-log-aggregate-copy:hover, -.traffic-copy-value:focus-visible, -.traffic-copy-value:hover { - text-decoration: underline; -} - -.file-log-advanced-parser { - margin: 0 0 1rem; - padding: .75rem 1rem; - border-radius: var(--rp-radius-surface, var(--radius, 12px)); -} - -.file-log-aggregate-form { - display: grid; - grid-template-columns: minmax(220px, 2fr) minmax(140px, 1fr) minmax(120px, 1fr) auto; - gap: .75rem; - align-items: end; - margin-top: .75rem; -} - -.file-log-aggregate-table { - margin-top: .75rem; -} - -.file-log-aggregate-table td:last-child, -.file-log-aggregate-table th:last-child { - width: 8rem; - text-align: right; -} - -@media (max-width:800px) { - .file-log-aggregate-form { - grid-template-columns: 1fr; - } -} - -@media (max-width:700px) { - - #file-log-view-counts, - #file-log-view-entries, - #panel-filelogs, - #panel-filelogs .file-log-field-grid, - #panel-filelogs .file-logs-table, - #panel-filelogs .file-logs-table-toolbar, - #panel-filelogs .file-logs-workspace { - box-sizing: border-box; - min-width: 0; - max-width: 100%; - width: 100%; - } - - #panel-filelogs .file-logs-workspace { - gap: 10px; - padding: 12px; - overflow: visible; - } - - #panel-filelogs .card-heading { - min-width: 0; - flex-wrap: wrap; - gap: 8px; - } - - #panel-filelogs .file-logs-heading-actions { - width: 100%; - justify-content: flex-start; - gap: 4px; - } - - #panel-filelogs .file-logs-heading-actions .file-log-consolidate { - margin-inline-end: auto; - white-space: normal; - } - - #panel-filelogs .file-log-search-help-popover { - width: calc(min(320px, 100vw - 54px)); - } - - #panel-filelogs .file-log-aggregate-form, - #panel-filelogs .file-logs-filter { - grid-template-columns: minmax(0, 1fr); - } - - #panel-filelogs .file-log-limit { - max-width: none; - } - - #panel-filelogs .file-log-aggregate-form input, - #panel-filelogs .file-log-aggregate-form label, - #panel-filelogs .file-log-aggregate-form select, - #panel-filelogs .file-logs-filter input, - #panel-filelogs .file-logs-filter label, - #panel-filelogs .file-logs-filter select { - min-width: 0; - max-width: 100%; - } - - #panel-filelogs .file-log-tabs { - min-width: 0; - } - - #panel-filelogs .file-log-tab { - flex: 1 1 0; - min-width: 0; - white-space: normal; - } - - #panel-filelogs .file-logs-table-toolbar { - align-items: flex-start; - gap: 5px; - } - - #panel-filelogs .file-log-column-picker { - align-self: flex-end; - } - - #panel-filelogs .file-logs-table { - max-height: none; - } - - #panel-filelogs .file-logs-table table, - #panel-filelogs .file-logs-table tbody { - display: block; - width: 100%; - min-width: 0; - white-space: normal; - } - - #panel-filelogs .file-logs-table thead { - display: none; - } - - #panel-filelogs .file-logs-table tbody tr:not(.file-log-detail-row) { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); - gap: 5px 10px; - position: relative; - min-width: 0; - padding: 10px 12px; - margin-bottom: 8px; - border: 1px solid var(--line); - background: var(--panel); - } - - #panel-filelogs .file-logs-table tbody tr:focus-visible { - outline: 2px solid var(--accent); - outline-offset: -2px; - } - - #panel-filelogs .file-logs-table tbody tr.selected { - border-color: var(--accent); - } - - #panel-filelogs .file-logs-table tbody tr:not(.file-log-detail-row)>td { - display: block; - min-width: 0; - max-width: none; - padding: 0; - border: 0; - overflow: hidden; - text-overflow: unset; - white-space: normal; - overflow-wrap: anywhere; - } - - #panel-filelogs .file-logs-table td[data-mobile-column=record] { - grid-column: 1/-1; - font-weight: 600; - line-height: 1.45; - padding-inline-end: 16px; - } - - #panel-filelogs .file-logs-table td[data-mobile-column=event], - #panel-filelogs .file-logs-table td[data-mobile-column=status], - #panel-filelogs .file-logs-table td[data-mobile-column=time], - #panel-filelogs .file-logs-table td[data-mobile-column=vhost] { - color: var(--muted); - font-size: 11px; - } - - #panel-filelogs .file-logs-table tbody tr:not(.file-log-detail-row)>td:before { - content: attr(data-mobile-label) ": "; - color: var(--text-subtle); - font-weight: 400; - } - - #panel-filelogs .file-logs-table tbody tr:not(.file-log-detail-row)>td[data-mobile-column=record]:before { - content: ""; - } - - #panel-filelogs .file-logs-table tbody tr:not(.file-log-detail-row):after { - content: "+"; - position: absolute; - inset-block-end: 4px; - inset-inline-end: 8px; - color: var(--muted); - font-size: 15px; - pointer-events: none; - } - - #panel-filelogs .file-logs-table tbody tr.selected:after { - content: "-"; - } - - #panel-filelogs .file-logs-table .file-log-detail-row { - display: block; - margin: -8px 0 8px; - border: 1px solid var(--accent); - border-top: 0; - background: var(--panel2); - } - - #panel-filelogs .file-logs-table .file-log-detail-row td { - display: block; - box-sizing: border-box; - width: 100%; - min-width: 0; - max-width: 100%; - white-space: normal; - } - - #panel-filelogs .file-log-field { - grid-template-columns: minmax(0, 1fr); - } - - #panel-filelogs .file-log-inline-detail { - min-width: 0; - padding: 8px; - } - - #panel-filelogs .file-log-inline-detail pre { - max-width: 100%; - overflow-x: auto; - } - - #panel-filelogs .file-log-aggregate-table { - box-sizing: border-box; - min-width: 0; - max-width: 100%; - overflow-x: auto; - } - - #panel-filelogs .file-log-aggregate-table table { - table-layout: fixed; - } - - #panel-filelogs .file-log-aggregate-table td:first-child { - white-space: normal; - overflow-wrap: anywhere; - } - - #panel-filelogs .file-logs-pager { - justify-content: space-between; - } -} - -#file-log-view-counts, -#file-log-view-entries, -#panel-filelogs, -#panel-filelogs .file-logs-workspace { - min-width: 0; - max-width: 100%; -} - -#panel-filelogs .file-logs-workspace { - grid-template-columns: minmax(0, 1fr); -} - -#panel-filelogs .file-log-aggregate-table, -#panel-filelogs .file-logs-table { - box-sizing: border-box; - min-width: 0; - max-width: 100%; - overflow-x: auto; -} - -@media (max-width:700px) { - #panel-filelogs .file-logs-table { - overflow: visible; - } - - #panel-filelogs .file-logs-table td[data-mobile-column=time], - #panel-filelogs .file-logs-table td[data-mobile-column=vhost] { - grid-column: 1/-1; - } - - #panel-filelogs .file-logs-table td[data-mobile-column=record] { - max-height: 4.4em; - overflow: hidden; - } - - #panel-filelogs .file-logs-table tr.selected td[data-mobile-column=record] { - max-height: none; - overflow-wrap: anywhere; - } -} - -.listener-protocol-settings-actions { - display: flex; - gap: 14px; - flex-wrap: wrap; - align-items: center; - margin-block: 10px; -} - -.listener-protocol-settings-actions button { - margin: 0; -} - -.listener-protocol-settings-actions button[hidden] { - display: none; -} - -.config-history-change-values { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px; -} - -.config-history-change-values .diff-before { - color: var(--muted); - text-decoration: line-through; -} - -.config-history-change-values .diff-after { - color: var(--text); -} - -.config-history-change-values code { - white-space: pre-wrap; - overflow-wrap: anywhere; -} - -.config-history-json-dialog { - width: calc(min(1200px, 100vw - 24px)); - max-width: calc(100vw - 24px); - max-height: calc(100dvh - 28px); - overflow: auto; -} - -.config-history-json-columns { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 12px; - padding: 12px; -} - -.config-history-json-columns section { - min-width: 0; -} - -.config-history-json-columns pre { - padding: 12px; - margin: 0; - background: var(--panel); - border: 0; - overflow: auto; - max-height: 68dvh; - font: 11px/1.5 var(--mono); -} - -@media (max-width:760px) { - .config-history-json-columns { - grid-template-columns: 1fr; - } - - .config-history-json-columns pre { - max-height: 38dvh; - } -} - -.acl-profile-attached { - margin-block: 6px; -} - -.acl-profile-attached summary { - cursor: pointer; - color: var(--muted); - font-size: 12px; -} - -#panel-overview .stats-grid .stat>small, -.acl-profile-attached p, -.stream-backend-status-row>span:first-child { - overflow-wrap: anywhere; -} - -.stream-backend-pool { - display: grid; - gap: .8rem; - min-width: 0; -} - -.stream-backend-list { - display: grid; - gap: 1rem; -} - -.stream-backend-row { - min-width: 0; - border: 1px solid var(--line, #454d59); - border-radius: var(--rp-radius-surface, .65rem); - padding: .85rem; -} - -.stream-backend-row>legend { - font-weight: 650; - padding: 0 .35rem; -} - -.stream-backend-row>.quiet { - margin-top: .6rem; -} - -.stream-backend-status { - display: grid; - gap: .4rem; - padding: .75rem 0; -} - -.stream-backend-status-row { - display: flex; - flex-wrap: wrap; - gap: .5rem; - align-items: center; - justify-content: space-between; - font-size: .85em; -} - -[data-dashboard-widget^=stream_][hidden] { - display: none !important; -} - -.dashboard-widget-toggle:focus-visible { - outline: 2px solid var(--accent); - outline-offset: -2px; -} - -.vh-enable-control.vh-enable-no-hint { - grid-template-rows: auto; -} - -.vh-enable-control.vh-enable-no-hint>:is(input, .vh-enable-state) { - grid-row: 1; -} - -#panel-overview .backend-mode-bar span, -#panel-overview .stats-grid>.stat { - min-width: 0; -} - -#panel-overview .stream-traffic-legend .legend-rx i { - background: var(--chart-accent); -} - -#panel-overview .stream-traffic-legend .legend-tx i { - background: var(--chart-secondary); -} - -#panel-overview .stats-grid .stat-chart>.stat-chart-legend { - flex-wrap: wrap; - white-space: normal; -} - -@media (min-width:1201px) { - #panel-overview .stats-grid { - grid-template-columns: repeat(6, minmax(0, 1fr)); - } -} - -#panel-overview .backend-mode-bar { - display: flex; - height: 7px; - width: 100%; - overflow: hidden; - gap: 2px; - border-radius: var(--rp-radius-surface, 5px); - background: var(--line); - margin: 7px 0 5px; -} - -#panel-overview .backend-mode-h1 { - background: var(--traffic-h1); -} - -#panel-overview .backend-mode-auto { - background: var(--traffic-h2); -} - -#panel-overview .backend-mode-compat { - background: var(--traffic-h2); - opacity: .5; -} - -#panel-overview .backend-mode-h2 { - background: var(--traffic-h3); -} - -#panel-overview .backend-mode-bar[aria-label] { - background-clip: padding-box; -} - -.vh-origin-protocol-info, -.vh-origin-protocol-warning { - color: var(--muted); -} - -.overview-layout-dialog { - width: calc(min(1180px, 100vw - 24px)); - max-height: calc(100dvh - 24px); - overflow: auto; -} - -.overview-layout-dialog>.window-heading { - position: sticky; - top: 0; - z-index: 2; - background: var(--bg); - border-bottom: 1px solid var(--line); -} - -.overview-layout-tile { - border: 1px solid var(--line); - background: var(--panel); - cursor: grab; -} - -.overview-layout-tile:active { - cursor: grabbing; -} - -.overview-layout-tile.is-dragging { - opacity: .4; -} - -.overview-layout-tile.is-drop-target { - outline: 2px solid var(--accent); - outline-offset: -2px; -} - -.overview-layout-tile-toolbar { - padding: 4px; - border-bottom: 1px solid var(--line); -} - -.overview-layout-tile-toolbar button { - min-height: 28px; - min-width: 28px; - padding: 3px 7px; -} - -.overview-layout-grip { - padding: 0 5px; - color: var(--muted); - user-select: none; -} - -.overview-layout-hide { - margin-inline-start: auto; - font-size: 11px; -} - -.overview-layout-hidden-item { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - border: 1px solid var(--line); - padding: 8px; - background: var(--panel); - min-width: 0; -} - -.overview-layout-hidden-item span { - font-size: 12px; - min-width: 0; - overflow-wrap: anywhere; -} - -.overview-layout-hidden-item button { - flex-shrink: 0; -} - -.overview-layout-footer { - position: sticky; - bottom: 0; - display: flex; - align-items: center; - justify-content: flex-end; - gap: 8px; - padding: 12px; - border-top: 1px solid var(--line); - background: var(--bg); - z-index: 2; -} - -.overview-layout-footer p { - margin: 0 auto 0 0; - font-size: 11px; - overflow-wrap: anywhere; -} - -@media (max-width:550px) { - .overview-layout-footer { - flex-wrap: wrap; - } - - .overview-layout-footer p { - width: 100%; - } -} - -#panel-overview .dashboard-overview-grid { - display: grid; - grid-template-columns: repeat(6, minmax(0, 1fr)); - gap: var(--ui-gap); - align-items: stretch; -} - -#panel-overview .dashboard-overview-grid>.activity-card svg, -#panel-overview .dashboard-overview-grid>[data-dashboard-widget] { - min-width: 0; -} - -#panel-overview .dashboard-overview-grid>[hidden] { - display: none !important; -} - -#panel-overview .dashboard-overview-grid>.card:not(.pool-card) { - grid-column: span 2; -} - -#panel-overview .dashboard-overview-grid>.pool-card { - grid-column: 1/-1; - margin-bottom: 0; -} - -@media (max-width:1200px) { - #panel-overview .dashboard-overview-grid { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - - #panel-overview .dashboard-overview-grid>.card:not(.pool-card), - #panel-overview .dashboard-overview-grid>.stat { - grid-column: span 2; - } -} - -@media (max-width:850px) { - #panel-overview .dashboard-overview-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - #panel-overview .dashboard-overview-grid>.stat { - grid-column: span 1; - } - - #panel-overview .dashboard-overview-grid>.card:not(.pool-card) { - grid-column: 1/-1; - } -} - -@media (max-width:550px) { - #panel-overview .dashboard-overview-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - #panel-overview .dashboard-overview-grid>.stat { - grid-column: auto/span 1; - } -} - -.overview-layout-tile.is-panel { - grid-column: span 2; -} - -.overview-layout-panel-preview>strong { - font-size: 13px; -} - -.overview-layout-panel-preview svg { - width: 100%; - height: 80px; -} - -.overview-layout-panel-preview small { - color: var(--muted); -} - -.overview-layout-footer #overview-layout-default { - margin-right: auto; -} - -.overview-reset-confirm { - width: calc(min(460px, 100vw - 24px)); - padding: 16px; -} - -.overview-reset-confirm>p { - line-height: 1.5; -} - -@media (max-width:850px) { - .overview-layout-tile.is-panel { - grid-column: span 2; - } -} - -@media (max-width:550px) { - .overview-layout-tile.is-panel { - grid-column: span 1; - } -} - -.overview-widget-type { - display: inline-block; - width: fit-content; - max-width: 100%; - padding: 2px 6px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-control, 4px); - background: var(--panel2); - color: var(--muted); - font-size: 10px; - line-height: 1.25; - overflow-wrap: anywhere; -} - -.dashboard-widget-setting .overview-widget-type { - margin-left: 15px; -} - -.overview-layout-tile-toolbar .overview-widget-type { - margin-left: 3px; -} - -.overview-layout-hidden-item .overview-widget-type { - flex: 0 0 auto; -} - -.overview-inline-actions { - display: inline-flex; - gap: 7px; - align-items: center; - justify-content: flex-end; - flex-wrap: wrap; - max-width: 100%; - margin-left: auto; -} - -.overview-inline-actions[hidden], -.overview-inline-hint[hidden] { - display: none !important; -} - -.overview-inline-hint { - margin: 0 0 10px; -} - -#panel-overview .dashboard-overview-grid.is-unlocked>[data-dashboard-widget] { - position: relative; - outline: 1px dashed var(--accent); - outline-offset: -2px; -} - -#panel-overview .dashboard-overview-grid.is-unlocked>.is-overview-dragging { - opacity: .45; -} - -#panel-overview .dashboard-overview-grid.is-unlocked>.is-overview-drop-before { - box-shadow: inset 4px 0 var(--accent); -} - -#panel-overview .dashboard-overview-grid.is-unlocked>.is-overview-drop-after { - box-shadow: inset -4px 0 var(--accent); -} - -.overview-inline-toolbar { - position: absolute; - top: 3px; - left: 4px; - right: 4px; - z-index: 4; - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 3px; - pointer-events: auto; -} - -#panel-overview .overview-inline-toolbar button { - min-width: 28px; - min-height: 27px; - padding: 2px 7px; - font-size: 12px; -} - -#panel-overview .overview-inline-toolbar .overview-inline-grip { - cursor: grab; - user-select: none; -} - -#panel-overview .overview-inline-toolbar .overview-inline-grip:active { - cursor: grabbing; -} - -#panel-overview .overview-inline-toolbar .overview-widget-type { - margin-left: auto; -} - -@media (max-width:600px) { - .overview-layout-hidden-item { - flex-wrap: wrap; - } -} - -#panel-overview .overview-inline-toolbar .overview-inline-hide { - color: var(--red); - border: 1px solid var(--danger-line); - background: var(--panel); - font-size: 18px; - line-height: 1; -} - -#panel-overview .overview-inline-toolbar .overview-inline-hide:focus-visible, -#panel-overview .overview-inline-toolbar .overview-inline-hide:hover { - border-color: var(--danger-line-strong); - background: var(--notice-bg); -} - -.overview-inline-hidden { - margin: 0 0 10px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 9px); - background: var(--panel); - color: var(--text); -} - -.overview-inline-hidden>summary { - padding: 9px 12px; - cursor: pointer; - color: var(--muted); - font-size: 12px; -} - -.overview-inline-hidden>summary:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -.overview-inline-hidden-list { - display: flex; - flex-wrap: wrap; - gap: 7px; - max-height: calc(min(30vh, 240px)); - overflow-y: auto; - padding: 10px 12px; - border-top: 1px solid var(--line); -} - -.overview-inline-hidden-item { - display: inline-flex; - align-items: center; - gap: 7px; - max-width: 100%; - padding: 4px 7px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 7px); - color: var(--text); - background: var(--panel2); - font-size: 12px; -} - -.overview-inline-hidden-item>span:not(.overview-widget-type) { - overflow-wrap: anywhere; -} - -.overview-inline-hidden-item>button { - flex: 0 0 auto; - min-width: 27px; - min-height: 27px; - padding: 1px 7px; - border-radius: var(--rp-radius-control, 5px); - border: 1px solid var(--field-line); - color: var(--green); - background: var(--panel); -} - -.overview-inline-hidden-item>button:hover { - border-color: var(--accent); -} - -@media (max-width:600px) { - #panel-overview .dashboard-overview-grid.is-unlocked>[data-dashboard-widget] { - padding-top: 82px; - } - - .overview-inline-hidden-item { - max-width: 100%; - } -} - -#panel-overview { - container: overview-canvas / inline-size; -} - -#panel-overview .dashboard-overview-grid.is-aligned { - display: grid; - --overview-gap: var(--ui-gap); - margin: 0; - row-gap: var(--overview-gap); - column-gap: var(--overview-gap); - grid-template-columns: repeat(12, minmax(0, 1fr)); - grid-auto-flow: row; - grid-auto-rows: auto; - align-items: stretch; - align-content: start; - gap: var(--overview-gap); - overflow-anchor: none; -} - -#panel-overview .dashboard-overview-grid.is-aligned.stats-grid { - gap: var(--overview-gap); -} - -#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget] { - grid-column: auto/span 2; - grid-row: auto; - align-self: stretch; - width: 100%; - max-width: 100%; - min-width: 0; - box-sizing: border-box; - margin-block: 0; - position: relative; -} - -#panel-overview .dashboard-overview-grid.is-aligned>.card:not(.pool-card) { - grid-column: auto/span 4; -} - -#panel-overview .dashboard-overview-grid.is-aligned>.card.overview-wide-chart { - grid-column: auto/span 6; -} - -#panel-overview .dashboard-overview-grid.is-aligned>.pool-card { - grid-column: 1/-1; -} - -#panel-overview .dashboard-overview-grid.is-aligned>.card.overview-row-start, -#panel-overview .dashboard-overview-grid.is-aligned>.overview-row-start { - grid-column-start: 1; -} - -@container overview-canvas (max-width: 1080px) { - #panel-overview .dashboard-overview-grid.is-aligned { - grid-template-columns: repeat(6, minmax(0, 1fr)); - } - - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget].stat { - grid-column: auto/span 2; - } - - #panel-overview .dashboard-overview-grid.is-aligned>.card:not(.pool-card) { - grid-column: auto/span 3; - } - - #panel-overview .dashboard-overview-grid.is-aligned>.card.overview-wide-chart, - #panel-overview .dashboard-overview-grid.is-aligned>.pool-card { - grid-column: 1/-1; - } -} - -@container overview-canvas (max-width: 700px) { - #panel-overview .dashboard-overview-grid.is-aligned { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget].stat { - grid-column: auto/span 1; - touch-action: pan-y pinch-zoom; - overflow-wrap: anywhere; - } - - #panel-overview .dashboard-overview-grid.is-aligned>.stat.stat-with-action { - padding-right: 11px; - } - - #panel-overview .dashboard-overview-grid.is-aligned .stat-with-action .stat-corner-action { - position: static; - display: block; - width: max-content; - max-width: 100%; - margin: 6px 0 0; - white-space: normal; - } - - html[data-dashboard-density=normal] #panel-overview .dashboard-overview-grid.is-aligned>.stat strong { - font-size: calc(clamp(20px, 5.8vw, 26px)); - } - - html[data-dashboard-density=compact] #panel-overview .dashboard-overview-grid.is-aligned>.stat strong { - font-size: calc(clamp(19px, 5vw, 22px)); - } - - #panel-overview .dashboard-overview-grid.is-aligned>.card:not(.pool-card) { - grid-column: 1/-1; - } -} - -#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget].overview-row-start { - grid-column-start: 1; -} - -#panel-overview .overview-wide-chart { - padding: 15px; - overflow: hidden; -} - -html[data-dashboard-density=normal] #panel-overview .dashboard-overview-grid>.card:not(.pool-card) { - padding: 13px; -} - -html[data-dashboard-density=compact] #panel-overview .dashboard-overview-grid>.card:not(.pool-card) { - padding: 10px; -} - -html[data-dashboard-density=ultra] #panel-overview .dashboard-overview-grid>.card:not(.pool-card) { - padding: 7px; -} - -html[data-dashboard-density=compact] #panel-overview .dashboard-overview-grid>.card .card-heading h2 { - font-size: 14px; -} - -html[data-dashboard-density=ultra] #panel-overview .dashboard-overview-grid>.card .card-heading h2 { - font-size: 13px; -} - -#panel-overview .overview-wide-chart .card-heading { - min-width: 0; - align-items: baseline; - gap: 10px; -} - -#panel-overview .overview-wide-chart h2 { - min-width: 0; - overflow-wrap: anywhere; -} - -#panel-overview .overview-wide-summary { - color: var(--accent); - font: 500 12px var(--mono); - white-space: nowrap; -} - -#panel-overview .overview-wide-svg { - display: block; - width: 100%; - height: 125px; - margin-block: 13px 9px; -} - -#panel-overview .overview-wide-svg .chart-grid { - vector-effect: non-scaling-stroke; - stroke: var(--line); - fill: none; - stroke-width: 1; - opacity: .6; -} - -#panel-overview .overview-wide-series { - fill: none; - vector-effect: non-scaling-stroke; - stroke-width: 2; - stroke-linejoin: round; - stroke-linecap: round; -} - -#panel-overview .overview-wide-series-0 { - stroke: var(--chart-accent); -} - -#panel-overview .overview-wide-series-1 { - stroke: var(--chart-secondary); -} - -#panel-overview .overview-wide-series-2 { - stroke: var(--chart-tertiary); -} - -#panel-overview .overview-wide-legend { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px 16px; - color: var(--muted); - font-size: 11px; -} - -#panel-overview .overview-wide-legend>span { - display: inline-flex; - align-items: center; - gap: 6px; - min-width: 0; -} - -#panel-overview .overview-wide-legend i { - width: 8px; - height: 8px; - border-radius: 100%; - background: var(--chart-accent); -} - -#panel-overview .overview-wide-legend .overview-wide-key-1 { - background: var(--chart-secondary); -} - -#panel-overview .overview-wide-legend .overview-wide-key-2 { - background: var(--chart-tertiary); -} - -#panel-overview .overview-wide-legend b { - font: 500 11px var(--mono); - color: var(--text); -} - -.dashboard-widget-setting .dashboard-widget-toggle .overview-widget-type { - margin: 0 0 0 auto; - flex: 0 0 auto; -} - -.dashboard-widget-setting .dashboard-widget-setting-actions { - justify-self: end; - flex-shrink: 0; -} - -.overview-layout-tile-toolbar { - flex-wrap: wrap; -} - -.overview-layout-tile-toolbar .overview-widget-type { - margin: 0; -} - -.overview-layout-tile-toolbar .overview-layout-controls { - margin-left: auto; -} - -.overview-layout-hidden-item>button { - margin-left: auto; -} - -.overview-layout-hidden-item>.overview-widget-type { - margin: 0; -} - -#panel-overview .overview-inline-toolbar .overview-widget-type { - margin: 0 auto 0 0; -} - -#panel-overview .overview-inline-toolbar .overview-inline-hide { - margin-left: 0; -} - -.overview-inline-hidden-item>.overview-widget-type { - margin-right: 0; -} - -.overview-inline-hidden-item>.overview-inline-restore { - order: 3; - margin-left: auto; -} - -.dashboard-widget-list { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.dashboard-widget-setting { - display: inline-flex; - align-items: center; - gap: 8px; - flex: 1 1 280px; - min-width: min(280px, 100%); - max-width: 100%; - padding: 4px 6px; -} - -.dashboard-widget-setting .dashboard-widget-toggle { - width: auto; - flex: 1 1 auto; - overflow: hidden; - min-width: 0; - flex-wrap: nowrap; -} - -.dashboard-widget-setting .dashboard-widget-setting-label { - flex: 1 1 auto; - min-width: 0; -} - -.dashboard-widget-setting>.overview-widget-type { - flex: none; - margin: 0; - white-space: nowrap; - overflow-wrap: normal; -} - -.dashboard-widget-setting>.dashboard-widget-setting-actions { - justify-self: end; - white-space: nowrap; -} - -@media (max-width:700px) { - .dashboard-widget-list { - display: grid; - grid-template-columns: minmax(0, 1fr); - } - - .dashboard-widget-setting { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - min-width: 0; - } -} - -.appearance-settings-grid.dashboard-appearance-grid { - grid-template-columns: minmax(0, 1fr); - gap: 12px; -} - -.appearance-settings-grid.dashboard-appearance-grid>.dashboard-density-setting { - display: grid; - grid-template-columns: minmax(145px, 200px) minmax(180px, 290px) minmax(0, 1fr); - align-items: center; - gap: 12px; -} - -.dashboard-density-setting>.fine, -.dashboard-density-setting>select { - min-width: 0; -} - -.dashboard-appearance-grid>.dashboard-widget-settings { - grid-column: 1/-1; - gap: 6px; -} - -.dashboard-widget-settings-head { - display: flex; - align-items: flex-start; - flex-wrap: wrap; - gap: 8px 14px; -} - -.dashboard-widget-settings-intro { - display: grid; - gap: 3px; - min-width: 0; - flex: 1 1 290px; -} - -.dashboard-widget-settings-controls { - display: flex; - flex: 0 0 auto; - align-items: center; - flex-wrap: wrap; - justify-content: flex-end; - gap: 6px; - margin-left: auto; -} - -.dashboard-widget-settings-controls>.dashboard-widget-scroll-button { - min-width: 30px; -} - -.dashboard-appearance-grid .dashboard-widget-list { - display: flex; - flex-wrap: nowrap; - align-items: stretch; - gap: 6px; - max-width: 100%; - min-width: 0; - margin: 0; - padding: 2px 1px 9px; - overflow-x: auto; - overflow-y: hidden; - scrollbar-width: thin; - overscroll-behavior-x: contain; - scroll-snap-type: x proximity; - -webkit-overflow-scrolling: touch; -} - -.dashboard-appearance-grid .dashboard-widget-list>.dashboard-widget-setting { - flex: 0 0 226px; - width: 226px; - min-width: 226px; - max-width: 226px; - min-height: 33px; - box-sizing: border-box; - scroll-snap-align: start; -} - -.dashboard-appearance-grid .dashboard-widget-list.is-expanded { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(min(225px, 100%), 1fr)); - gap: 7px; - overflow: visible; - padding-bottom: 0; - scroll-snap-type: none; -} - -.dashboard-appearance-grid .dashboard-widget-list.is-expanded>.dashboard-widget-setting { - width: 100%; - max-width: none; - min-width: 0; -} - -#settings-page-appearance .dashboard-widget-setting .overview-widget-type, -.dashboard-appearance-grid .dashboard-widget-list.is-expanded~.dashboard-widget-scroll-hint { - display: none; -} - -@media (max-width:760px) { - .appearance-settings-grid.dashboard-appearance-grid>.dashboard-density-setting { - grid-template-columns: minmax(0, 1fr); - gap: 7px; - } - - .dashboard-widget-settings-head { - align-items: stretch; - } - - .dashboard-widget-settings-controls { - margin-left: 0; - justify-content: flex-start; - } - - .dashboard-appearance-grid .dashboard-widget-list>.dashboard-widget-setting { - flex-basis: 210px; - width: 210px; - min-width: 210px; - max-width: 210px; - } - - .dashboard-appearance-grid .dashboard-widget-list.is-expanded>.dashboard-widget-setting { - min-width: 0; - max-width: none; - } -} - -html[data-corner-style=square] { - --rp-radius-surface: 0px; - --rp-radius-control: 0px; - --rp-progress-radius: 0px; -} - -html[data-corner-style=subtle] { - --rp-radius-surface: 5px; - --rp-radius-control: 4px; - --rp-progress-radius: 2px; -} - -html[data-corner-style=rounded] { - --rp-radius-surface: 12px; - --rp-radius-control: 8px; - --rp-progress-radius: 4px; -} - -html[data-corner-style=extra] { - --rp-radius-surface: 18px; - --rp-radius-control: 12px; - --rp-progress-radius: 999px; -} - -html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-style=rounded], [data-corner-style=extra]) :is(.card, .stat, dialog, .login-card, .appearance-section, .settings-tabs, .dashboard-widget-setting, .overview-layout-tile, .overview-layout-hidden-item, .overview-inline-hidden-item, .overview-inline-hidden, .notice, .toast, .chart-style-choice, .pool-card, .vhost-card, .vh-backend-card, .entity, .settings-filter-row, .stream-backend-row, .file-log-advanced-parser, .metric-info-popover) { - border-radius: var(--rp-radius-surface) !important; -} - -html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-style=rounded], [data-corner-style=extra]) :is(button:not(.nav):not(.file-log-tab):not(.vhost-backend-trigger), input:not([type=checkbox]):not([type=radio]):not([type=range]), select, textarea, .pill, .vhost-backend-protocol-badge, .overview-widget-type, .badge, .quiet, .outline, .primary, .code, .command, .dashboard-widget-toggle, .settings-tabs button, .overview-inline-restore) { - border-radius: var(--rp-radius-control) !important; -} - -.acl-profile-rule { - grid-template-columns: minmax(100px, .6fr) minmax(105px, .6fr) minmax(175px, 1.5fr) auto; -} - -@media (max-width:640px) { - .acl-profile-rule { - grid-template-columns: 1fr; - } -} - -.vhost-backend-protocol-badge { - display: inline-flex; - gap: 4px; - align-items: baseline; - max-width: 100%; - min-width: 0; - border: 1px solid var(--pill-line); - border-radius: var(--rp-radius-control, 4px); - background: var(--panel2); - color: var(--muted); - padding: 3px 7px; - font: 10px var(--mono); - overflow-wrap: anywhere; - text-align: left; -} - -.vhost-backend-protocol-badge:is(:hover, [aria-expanded=true]) { - background: var(--bg); -} - -.vhost-backend-protocol-badge:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -.vhost-backend-protocol-badge[data-observed=true] { - color: var(--green); - border-color: var(--pill-live-line); -} - -@container overview-canvas (max-width: 700px) { - #panel-overview .network-interface-charts { - max-height: none; - overflow: visible; - overscroll-behavior: auto; - } -} - -#panel-overview .stat-meter>.overview-meter { - position: relative; - display: block; - width: 100%; - height: 10px; - margin: 9px 0 8px; - overflow: hidden; - border-radius: var(--rp-progress-radius); - background: var(--rp-progress-track); -} - -#panel-overview .overview-meter-fill { - display: block; - width: 0; - height: 100%; - border-radius: inherit; - background: var(--rp-progress-fill); - transition: width .18s; -} - -#panel-overview .system-card .system-memory-meter { - display: block; - width: 100%; - height: 5px; - margin: 6px 0 9px; - overflow: hidden; - background: var(--rp-progress-track); - border-radius: var(--rp-progress-radius); -} - -#panel-overview .stat-meter>small { - display: block; - overflow-wrap: anywhere; -} - -#panel-overview .stat-donut .overview-donut-row { - display: flex; - gap: 10px; - align-items: center; - min-width: 0; - margin: 4px 0 2px; -} - -#panel-overview .stat-donut .overview-donut-svg { - display: block; - width: 62px; - height: 62px; - flex: 0 0 62px; - overflow: visible; - filter: var(--chart-svg-filter); -} - -#panel-overview .overview-donut-svg circle { - fill: none; - stroke-width: 9; - transform: rotate(-90deg); - transform-origin: 32px 32px; -} - -#panel-overview .overview-donut-track { - stroke: var(--line); -} - -#panel-overview .overview-donut-hit { - stroke: var(--chart-accent); -} - -#panel-overview .overview-donut-miss { - stroke: var(--chart-secondary); -} - -#panel-overview .stat-donut .overview-donut-legend { - display: flex; - flex-direction: column; - gap: 6px; - min-width: 0; -} - -#panel-overview .overview-donut-legend>span { - display: flex; - align-items: center; - gap: 5px; - flex-wrap: wrap; - line-height: 1.25; - overflow-wrap: anywhere; -} - -#panel-overview .overview-donut-legend i { - width: 8px; - height: 8px; - flex: 0 0 8px; - border-radius: 50%; - background: var(--chart-accent); -} - -#panel-overview .overview-donut-miss-key i { - background: var(--chart-secondary); -} - -#panel-overview .overview-donut-legend b { - color: var(--text); - font-family: var(--mono); - font-weight: 500; -} - -#panel-overview .overview-wide-series-3 { - stroke: var(--chart-quaternary); -} - -#panel-overview .overview-wide-legend .overview-wide-key-3 { - background: var(--chart-quaternary); -} - -@container overview-canvas (max-width: 700px) { - #panel-overview .stat-donut .overview-donut-row { - gap: 6px; - } - - #panel-overview .stat-donut .overview-donut-svg { - width: 48px; - height: 48px; - flex-basis: 48px; - } - - #panel-overview .stat-donut .overview-donut-legend { - gap: 3px; - } -} - -@media (prefers-reduced-motion:reduce) { - .app-startup-spinner { - animation: none; - } - - * { - scroll-behavior: auto !important; - } - - #panel-overview .overview-meter-fill { - transition: none; - } -} - -.page-intro .page-intro-meta-row { - display: flex; - align-items: center; - justify-content: space-between; - flex-wrap: wrap; - gap: 6px 16px; - min-width: 0; - margin-bottom: 4px; -} - -.page-intro .page-intro-meta-row .eyebrow { - margin-bottom: 0; - flex: 0 0 auto; -} - -.page-intro .overview-samples-toolbar { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 6px 12px; - flex-wrap: wrap; - max-width: 100%; - margin: 0 0 0 auto; -} - -.page-intro .overview-samples-toolbar .function-live-chart-samples select { - min-width: 100px; -} - -.page-intro .overview-samples-toolbar button { - width: auto; - flex: none; - min-height: 28px; - padding: 5px 10px; - white-space: nowrap; -} - -.page-intro .overview-samples-toolbar .fine { - font-size: 10px; - color: var(--muted); - white-space: nowrap; -} - -@media (max-width:700px) { - .page-intro .overview-samples-toolbar { - margin-left: 0; - justify-content: flex-start; - } -} - -#panel-overview .stat-donut .overview-donut-info { - display: flex; - flex-direction: column; - justify-content: center; - gap: 4px; - min-width: 0; - flex: 1 1 0; -} - -#panel-overview .stat-donut .overview-donut-info>strong { - display: block; - line-height: 1.1; - margin: 0; -} - -#panel-overview .stat-donut .overview-donut-info>.overview-donut-legend { - gap: 2px; -} - -#panel-overview .www-ring-legend { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - column-gap: 7px; - row-gap: 5px; -} - -#panel-overview .www-ring-legend span { - white-space: nowrap; - font-size: 10px; -} - -#panel-overview .www-ring-arc { - stroke-width: 9; -} - -#panel-overview .www-ring-0, -.www-dot-0 { - stroke: var(--chart-accent); -} - -#panel-overview .www-ring-1, -.www-dot-1 { - stroke: var(--chart-secondary); -} - -#panel-overview .www-ring-2, -.www-dot-2 { - stroke: var(--chart-tertiary); -} - -#panel-overview .www-ring-3, -.www-dot-3 { - stroke: var(--chart-quaternary); -} - -#panel-overview .www-ring-legend i { - background: var(--chart-accent); -} - -#panel-overview .www-ring-legend .www-dot-1 { - background: var(--chart-secondary); -} - -#panel-overview .www-ring-legend .www-dot-2 { - background: var(--chart-tertiary); -} - -#panel-overview .www-ring-legend .www-dot-3 { - background: var(--chart-quaternary); -} - -#panel-overview .www-segmented { - display: flex; - width: 100%; - height: 10px; - margin: 9px 0 8px; - background: var(--rp-progress-track); - border-radius: var(--rp-progress-radius); - overflow: hidden; -} - -#panel-overview .www-segmented span { - display: block; - height: 100%; - transition: width .18s; -} - -#panel-overview .www-segment-0 { - background: var(--chart-accent); -} - -#panel-overview .www-segment-1 { - background: var(--chart-secondary); -} - -#panel-overview .www-segment-2 { - background: var(--chart-tertiary); -} - -#panel-overview .www-segment-3 { - background: var(--chart-quaternary); -} - -#panel-overview .www-segment-4 { - background: var(--muted); -} - -#panel-overview .www-method-legend { - overflow-wrap: anywhere; -} - -#panel-overview .www-top-bars { - display: grid; - gap: 8px; - margin-top: 12px; - min-height: 90px; -} - -#panel-overview .www-top-row { - display: grid; - grid-template-columns: minmax(60px, 1fr) minmax(30px, 2fr) auto; - gap: 8px; - align-items: center; - min-width: 0; - font: 11px var(--mono); -} - -#panel-overview .www-top-row>span { - overflow: hidden; - text-overflow: ellipsis; -} - -#panel-overview .www-top-meter { - height: 7px; - background: var(--rp-progress-track); - border-radius: var(--rp-progress-radius); - overflow: hidden; -} - -#panel-overview .www-top-meter span { - display: block; - background: var(--rp-progress-fill); - border-radius: inherit; - height: 100%; -} - -#panel-overview .www-top-row b { - font-weight: 500; - color: var(--text); -} - -#panel-web-server .www-service-panel { - min-width: 0; - margin-top: 10px; -} - -#panel-web-server .www-service-split { - color: var(--muted); - font: 11px var(--mono); - overflow-wrap: anywhere; - margin: 4px 10px 8px; -} - -@media (prefers-reduced-motion:reduce) { - #panel-overview .www-segmented span { - transition: none; - } -} - -#panel-overview .dashboard-overview-grid.is-aligned[data-overview-layout-mode=custom] { - grid-auto-flow: row dense; -} - -#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="2"] { - grid-column: auto/span 2; -} - -#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="3"] { - grid-column: auto/span 3; -} - -#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="4"] { - grid-column: auto/span 4; -} - -#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="6"] { - grid-column: auto/span 6; -} - -#panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="12"] { - grid-column: 1/-1; -} - -#panel-overview .dashboard-overview-grid.is-aligned[data-overview-layout-mode=custom]>[data-dashboard-widget].overview-row-start { - grid-column-start: auto; -} - -#panel-overview .dashboard-overview-grid.is-aligned[data-overview-layout-mode=custom] .overview-wide-legend, -#panel-overview .dashboard-overview-grid.is-aligned[data-overview-layout-mode=custom] .stat-chart-legend { - flex-wrap: wrap; - white-space: normal; -} - -#panel-overview .overview-inline-width, -.overview-layout-width-value { - display: inline-flex; - align-items: center; - justify-content: center; - white-space: nowrap; - font: 11px var(--mono); - color: var(--text); -} - -#panel-overview .overview-inline-width { - gap: 3px; - flex: 0 0 auto; -} - -#panel-overview .overview-inline-width-value { - min-width: 31px; - text-align: center; -} - -#panel-overview .dashboard-overview-grid.is-unlocked>[data-dashboard-widget] { - padding-top: 101px; -} - -#panel-overview .overview-inline-toolbar .overview-widget-type { - margin-right: auto; -} - -.overview-layout-tile-toolbar .overview-layout-width-value { - flex: 0 0 auto; - min-width: 30px; - text-align: center; -} - -.overview-layout-tile { - min-width: 0; -} - -.overview-layout-tile-toolbar { - display: grid; - grid-template-columns: max-content minmax(0, 1fr); - gap: 5px 6px; - align-items: center; - min-width: 0; -} - -.overview-layout-tile-toolbar .overview-widget-type { - min-width: 0; - max-width: 100%; - grid-column: 2; -} - -.overview-layout-tile-toolbar .overview-layout-controls { - grid-column: 1/-1; - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 4px 8px; - min-width: 0; - width: 100%; - margin: 0; -} - -.overview-layout-order-actions, -.overview-layout-size-actions { - display: inline-flex; - flex-wrap: wrap; - align-items: center; - gap: 3px; - min-width: 0; -} - -.overview-layout-tile-toolbar .overview-layout-hide { - margin-inline-start: 0; - white-space: normal; - overflow-wrap: anywhere; -} - -.overview-layout-dialog .overview-layout-footer, -.overview-layout-dialog .overview-layout-hidden-item { - flex-wrap: wrap; -} - -.overview-layout-dialog .overview-layout-hidden-item>span:first-child { - flex: 1 1 125px; -} - -.overview-layout-dialog .overview-layout-hidden-item>button { - flex: 0 1 auto; - max-width: 100%; - white-space: normal; - overflow-wrap: anywhere; -} - -@container overview-canvas (max-width: 1080px) { - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="2"] { - grid-column: auto/span 2; - } - - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="3"] { - grid-column: auto/span 3; - } - - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="4"] { - grid-column: auto/span 4; - } - - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="12"], - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="6"] { - grid-column: 1/-1; - } -} - -@container overview-canvas (max-width: 700px) { - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="2"].stat { - grid-column: auto/span 1; - } - - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="12"], - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="2"]:not(.stat), - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="3"], - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="4"], - #panel-overview .dashboard-overview-grid.is-aligned>[data-dashboard-widget][data-dashboard-span="6"] { - grid-column: 1/-1; - } -} - -@media (max-width:550px) { - #panel-overview .dashboard-overview-grid.is-unlocked>[data-dashboard-widget] { - padding-top: 135px; - } -} - -#panel-overview :is(.system-card, .connection-card) :is(.card-heading h2, .card-heading .meta, .memory-line, .chart-footer, .connection-legend) { - font-family: var(--mono); - font-variant-numeric: tabular-nums; -} - -#panel-overview :is(.system-card, .connection-card) .card-heading h2 { - letter-spacing: normal; -} - -.overview-presets-dialog { - width: calc(min(730px, 100vw - 24px)); - max-height: calc(100dvh - 24px); - overflow-y: auto; -} - -.overview-presets-content { - display: grid; - gap: 14px; - padding: 14px; -} - -.overview-presets-content>p { - margin: 0; -} - -.overview-presets-create { - display: grid; - gap: 7px; -} - -.overview-presets-create-controls { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.overview-presets-create-controls input { - min-width: 150px; - flex: 1 1 240px; -} - -.overview-presets-list { - display: grid; - gap: 7px; -} - -.overview-presets-row { - display: flex; - align-items: center; - justify-content: space-between; - flex-wrap: wrap; - gap: 8px; - padding: 9px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, var(--radius, 8px)); - background: var(--panel); -} - -.overview-presets-row strong { - font-size: 13px; - overflow-wrap: anywhere; -} - -.overview-presets-actions { - display: flex; - flex-wrap: wrap; - gap: 5px; -} - -.overview-presets-dialog .overview-layout-footer { - flex-wrap: wrap; -} - -@media (max-width:480px) { - .overview-presets-create-controls>button { - flex: 1 1 auto; - } - - .overview-presets-row { - align-items: flex-start; - flex-direction: column; - } - - .overview-presets-actions { - width: 100%; - } -} - -#panel-overview .activity-card .card-heading h2, -#panel-overview .overview-wide-chart .card-heading h2, -#panel-overview :is(.stat-chart, .stat-donut)>span:first-child, -#panel-overview :is(.system-card, .connection-card) .overview-chart-heading, -.function-live-chart-heading>strong, -.mini-live-chart>.mini-live-chart-head>span:first-child, -.traffic-chart-card>h3.subheading, -.vhost-chart-head>span:first-child { - font-family: var(--sans); - font-size: 12px; - font-weight: 650; - line-height: 1.35; - letter-spacing: normal; - text-transform: uppercase; -} - -html[data-dashboard-density] #panel-overview .dashboard-overview-grid>.card.activity-card .card-heading h2, -html[data-dashboard-density] #panel-overview .dashboard-overview-grid>.card.overview-wide-chart .card-heading h2, -html[data-dashboard-density] #panel-overview .stat:is(.stat-chart, .stat-donut)>span:first-child { - font-size: 12px; -} - -#panel-overview :is(.system-card, .connection-card) .overview-chart-heading { - color: var(--text); - margin: 0 0 5px; -} - -:is(.overview-wide-summary, .overview-wide-legend, .overview-donut-legend, .www-method-legend, .stat-chart-legend, .mini-chart-legend, .connection-legend, .traffic-protocol-legend, .traffic-chart-legend, .function-live-chart-legend, .stream-chart-legend, .chart-footer, .vhost-chart-head, .network-interface-rate) { - font-family: var(--mono); - font-variant-numeric: tabular-nums; -} - -#panel-overview .overview-wide-chart>.fine, -#panel-overview .stat-chart>small, -#panel-overview .www-top-row, -.traffic-bar-row { - font-family: var(--mono); - font-variant-numeric: tabular-nums; -} - -#panel-overview :is(.overview-wide-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stat-chart-legend, .mini-chart-legend, .connection-legend, .traffic-protocol-legend, .stream-traffic-legend), -:is(.overview-wide-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stat-chart-legend, .mini-chart-legend, .connection-legend, .traffic-protocol-legend, .traffic-chart-legend, .function-live-chart-legend, .stream-chart-legend, .stream-traffic-legend) { - font-family: var(--mono); - font-size: 9px; - font-variant-numeric: tabular-nums; -} - -#panel-overview :is(.overview-wide-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stat-chart-legend, .mini-chart-legend, .connection-legend, .traffic-protocol-legend, .stream-traffic-legend) :is(span, b, strong, small, li, em), -:is(.overview-wide-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stat-chart-legend, .mini-chart-legend, .connection-legend, .traffic-protocol-legend, .traffic-chart-legend, .function-live-chart-legend, .stream-chart-legend, .stream-traffic-legend) :is(span, b, strong, small, li, em) { - font-size: inherit; - font-family: inherit; -} - -html[data-dashboard-density] #panel-overview .stat :is(.stat-chart-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stream-traffic-legend), -html[data-dashboard-density] #panel-overview .stat :is(.stat-chart-legend, .overview-donut-legend, .www-ring-legend, .www-method-legend, .stream-traffic-legend) :is(span, b, small, strong, li) { - font-size: 9px; - font-family: var(--mono); -} - -.vhost-detail.vhost-backend-trigger, -.vhost-live-metric.vhost-backend-trigger { - appearance: none; - display: block; - width: 100%; - min-width: 0; - margin: 0; - border-radius: 0; - color: var(--text); - text-align: left; - font: inherit; - cursor: pointer; -} - -.vhost-detail.vhost-backend-trigger { - border: 0; - background: var(--bg); -} - -.vhost-live-metric.vhost-backend-trigger { - border: 1px solid var(--line); - background: transparent; -} - -.vhost-backend-trigger:is(:hover, [aria-expanded=true]) { - background: var(--panel2); -} - -.vhost-backend-trigger:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -.vhost-backend-popup { - position: fixed; - z-index: 1600; - max-height: calc(min(360px, 100dvh - 24px)); - overflow: auto; - padding: 12px; - background: var(--panel); - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 0); - box-shadow: 0 12px 35px var(--shadow-strong); - color: var(--text); - font-size: 11px; -} - -.vhost-backend-popup-heading { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 10px; - margin: 0 0 10px; -} - -.vhost-backend-popup h4 { - margin: 0; - font-size: 12px; - font-weight: 650; - overflow-wrap: anywhere; -} - -.vhost-backend-popup-close { - flex: 0 0 auto; - display: grid; - place-items: center; - width: 25px; - height: 25px; - padding: 0; - border: 1px solid var(--line); - background: var(--bg); - color: var(--text); - cursor: pointer; - font: 18px/1 var(--sans); - border-radius: var(--rp-radius-control, 0); -} - -.vhost-backend-popup-close:hover { - background: var(--panel2); -} - -.vhost-backend-popup-list { - display: grid; - gap: 6px; - padding: 0; - margin: 0; - list-style: none; -} - -.vhost-backend-popup-row { - padding: 8px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 0); - background: var(--bg); - min-width: 0; -} - -.vhost-backend-popup-name { - display: block; - margin-bottom: 6px; - overflow-wrap: anywhere; - font: 650 11px var(--mono); -} - -.vhost-backend-popup-columns { - display: grid; - grid-template-columns: minmax(0, 1.75fr) minmax(0, 1fr); - gap: 6px 10px; -} - -.vhost-backend-popup-field { - min-width: 0; -} - -.vhost-backend-popup-field>span { - display: block; -} - -.vhost-backend-popup-field>span:first-child { - font-size: 9px; - color: var(--muted); -} - -.vhost-backend-popup-field>span:last-child { - font: 10px/1.35 var(--mono); - overflow-wrap: anywhere; -} - -.vhost-backend-popup-field.has-success>span:last-child { - color: var(--green); -} - -.vhost-backend-popup-field.has-error>span:last-child { - color: var(--red); -} - -.vhost-backend-popup-hint { - margin: 10px 0 0; - line-height: 1.4; -} - -#panel-overview .dashboard-overview-grid>.card .mini-heading>h3, -#panel-overview .dashboard-overview-grid>.card .overview-chart-heading, -#panel-overview .dashboard-overview-grid>.card:is(.overview-wide-chart, .activity-card, .pool-card) .card-heading h2, -#panel-overview .dashboard-overview-grid>.stat>span:first-child { - font-family: var(--sans); - font-size: 12px; - font-weight: 600; - line-height: 1.35; - letter-spacing: normal; - text-transform: none; -} - -#panel-overview .dashboard-overview-grid>[data-dashboard-widget] :is(small, .mini-metrics dt, .mini-metrics dd, .mini-live-chart-head, .memory-line, .chart-footer, .small-label, .meta, .fine) { - font-family: var(--mono); - font-variant-numeric: tabular-nums; - text-transform: none; -} - -#panel-overview .dashboard-overview-grid .mini-live-chart>.mini-live-chart-head>span:first-child { - font-family: var(--mono); - font-size: 10px; - font-weight: 400; - line-height: 1.35; - letter-spacing: normal; - text-transform: none; -} - -html[data-dashboard-density] #panel-overview .dashboard-overview-grid>.card.pool-card .card-heading h2 { - font-size: 12px; -} - -#panel-cache>.card .card-heading h2, -#panel-overview .dashboard-overview-grid .mini-live-chart-head>span:first-child, -#panel-overview .dashboard-overview-grid>.card .mini-heading>h3, -#panel-overview .dashboard-overview-grid>.card .overview-chart-heading, -#panel-overview .dashboard-overview-grid>.card:not(.system-card):not(.connection-card) .card-heading h2, -#panel-overview .dashboard-overview-grid>.stat>span:first-child, -#panel-transform>.card .card-heading h2, -.function-live-chart-heading>strong, -.traffic-chart-card>h3.subheading, -.vhost-chart-head>span:first-child { - color: var(--muted); -} - -.certificate-bulk-assign { - margin-top: 12px; - border-top: 1px solid var(--line); - padding-top: 12px; -} - -.certificate-bulk-list { - display: grid; - gap: 8px; - max-height: 250px; - overflow-y: auto; - margin: 10px 0; -} - -.certificate-bulk-list .check { - overflow-wrap: anywhere; -} - -#panel-overview .stat-cache-outcomes .cache-mix-header { - display: flex; - justify-content: space-between; - align-items: baseline; - gap: 6px; - min-width: 0; -} - -#panel-overview .stat-cache-outcomes .cache-mix-header>span { - flex: 1 1 auto; - min-width: 0; - color: var(--muted); - font-size: 11px; -} - -#panel-overview .stat-cache-outcomes .cache-mix-caption { - flex: 0 0 auto; - margin-left: auto; - color: var(--muted); - font: 500 10px/1.35 var(--mono); - font-variant-numeric: tabular-nums; - text-align: right; - white-space: nowrap; -} - -html[data-dashboard-density=ultra] #panel-overview .stat-cache-outcomes .cache-mix-header>span { - font-size: 9px; -} - -html[data-dashboard-density=ultra] #panel-overview .stat-cache-outcomes .cache-mix-caption { - font-size: 9px; -} - -#panel-overview .stat-cache-outcomes .cache-mix-bar { - display: flex; - width: 100%; - height: 12px; - margin: 7px 0 9px; - border-radius: var(--rp-progress-radius); - background: var(--rp-progress-track); - overflow: hidden; -} - -#panel-overview .cache-mix-bar>span { - display: block; - height: 100%; - min-width: 0; -} - -#panel-overview .cache-mix-hit { - background: var(--chart-accent); -} - -#panel-overview .cache-mix-miss { - background: var(--chart-secondary); -} - -#panel-overview .cache-mix-bypass { - background: var(--chart-tertiary); -} - -#panel-overview .cache-mix-other { - background: var(--muted); - opacity: .65; -} - -#panel-overview .cache-mix-legend { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 5px 8px; - line-height: 1.25; -} - -#panel-overview .cache-mix-legend>span { - display: flex; - align-items: center; - gap: 4px; - min-width: 0; -} - -#panel-overview .cache-mix-legend i { - display: inline-block; - flex: 0 0 7px; - width: 7px; - height: 7px; -} - -#panel-overview .cache-mix-hit-key i { - background: var(--chart-accent); -} - -#panel-overview .cache-mix-miss-key i { - background: var(--chart-secondary); -} - -#panel-overview .cache-mix-bypass-key i { - background: var(--chart-tertiary); -} - -#panel-overview .cache-mix-other-key i { - background: var(--muted); -} - -#panel-overview .cache-mix-legend b { - display: flex; - flex-direction: column; - align-items: flex-end; - margin-left: auto; - color: var(--text); - font: 500 10px var(--mono); - overflow-wrap: anywhere; - text-align: right; -} - -#panel-overview .cache-mix-legend .cache-mix-percent { - color: var(--muted); - font: 400 10px var(--mono); -} - -.docker-agent-security-hint, -.docker-agent-summary>span, -.docker-fingerprint { - min-width: 0; - max-width: 100%; - overflow-wrap: anywhere; - word-break: break-word; -} - -.acl-picker, -.vh-acl-profile-picker { - min-width: 0; - width: 100%; -} - -.vh-acl-profile-picker>strong { - display: block; - margin-bottom: 8px; -} - -.acl-picker-details { - border: 1px solid var(--field-line); - background: var(--field-bg); - border-radius: var(--rp-radius-control, 8px); - min-width: 0; -} - -.acl-picker-details[open] { - border-color: var(--border-hover); -} - -.acl-picker-summary { - display: flex; - align-items: center; - gap: 12px; - padding: 10px 12px; - font-size: 12px; - cursor: pointer; - user-select: none; - min-height: 21px; -} - -.acl-picker-heading { - flex: 1 1 auto; - font-weight: 600; - min-width: 0; -} - -.acl-picker-counter { - color: var(--accent); - border: 1px solid var(--pill-line); - border-radius: var(--rp-radius-control, 12px); - padding: 2px 7px; - font: 11px var(--mono); - margin-left: auto; -} - -.acl-picker-dropdown { - padding: 10px; - border-top: 1px solid var(--line); -} - -.acl-picker-search, -.docker-agent-grid .acl-picker-search { - width: 100%; - box-sizing: border-box; - min-height: 34px; - height: 34px; - margin: 0 0 9px; -} - -.acl-picker-options { - display: grid; - max-height: 270px; - overflow-y: auto; - gap: 5px; -} - -.acl-picker-option { - display: flex; - align-items: flex-start; - gap: 10px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-control, 6px); - background: var(--panel); - padding: 9px 10px; - cursor: pointer; - min-width: 0; -} - -.acl-picker-option:has(input:focus-visible), -.acl-picker-option:hover { - border-color: var(--border-hover); -} - -.acl-picker-option:has(input:checked) { - border-color: var(--pill-live-line); - background: var(--nav-selected-bg); -} - -.acl-picker-empty[hidden], -.acl-picker-limit[hidden], -.acl-picker-option[hidden] { - display: none !important; -} - -.acl-picker-option input[type=checkbox] { - flex: 0 0 auto; - width: 16px !important; - height: 16px !important; - min-height: 16px !important; - margin: 2px 0 0; -} - -.acl-picker-option input:disabled { - opacity: .35; -} - -.acl-picker-option:has(input:disabled) { - cursor: not-allowed; - opacity: .6; -} - -.acl-picker-option-content { - display: grid; - flex: 1; - min-width: 0; - gap: 5px; -} - -.acl-picker-option-heading { - display: flex; - flex-wrap: wrap; - gap: 5px 9px; - align-items: center; -} - -.acl-picker-option-heading strong { - font-size: 12px; - color: var(--text); - overflow-wrap: anywhere; -} - -.acl-picker-id { - font: 10px var(--mono); - color: var(--muted); - overflow-wrap: anywhere; -} - -.acl-picker-policy { - font: 10px var(--mono); - line-height: 1.4; - border-radius: var(--rp-radius-control, 12px); - padding: 2px 7px; - border: 1px solid var(--line); - color: var(--muted); - white-space: nowrap; -} - -.acl-picker-policy.is-deny { - border-color: var(--danger-line); - color: var(--red); -} - -.acl-picker-policy.is-allow { - border-color: var(--pill-live-line); - color: var(--green); -} - -.acl-picker-rule-preview { - display: block; - line-height: 1.45; - overflow-wrap: anywhere; - color: var(--muted); - font-size: 11px; -} - -.acl-picker-selected { - display: flex; - gap: 6px; - flex-wrap: wrap; - margin: 8px 0 0; - min-width: 0; -} - -.acl-picker-tag { - display: inline-flex; - align-items: center; - max-width: 100%; - gap: 7px; - padding: 2px 4px 2px 9px; - border-radius: var(--rp-radius-control, 15px); - border: 1px solid var(--pill-live-line); - background: var(--nav-selected-bg); - font-size: 11px; -} - -.acl-picker-tag>span { - overflow-wrap: anywhere; - min-width: 0; -} - -.acl-picker-tag .acl-picker-remove { - border: none; - border-radius: var(--rp-radius-control, 50%); - background: transparent; - color: var(--muted); - min-width: 20px; - height: 20px; - font-size: 16px; - padding: 0; - cursor: pointer; -} - -.acl-picker-tag .acl-picker-remove:hover { - background: var(--panel2); - color: var(--text); -} - -.acl-picker-no-selection { - font-size: 11px; -} - -.acl-picker-limit { - display: block; - padding-top: 7px; -} - -.acl-stack-warning { - margin: 8px 0 0; - font-size: 11px; - line-height: 1.5; - color: var(--notice-text); -} - -.docker-profile-field { - min-width: 0; -} - -.docker-acl-grid .docker-profile-field { - grid-column: span 2; -} - -.docker-agent-grid .docker-profile-field { - grid-column: span 2; -} - -.docker-protocol-options { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-top: 12px; -} - -.docker-protocol-options>* { - display: grid; - align-content: center; - min-width: 130px; - flex: 1 1 145px; - border-radius: var(--rp-radius-control, 6px); - border: 1px solid var(--line); - background: var(--panel2); - padding: 8px 12px; - min-height: 49px; - box-sizing: border-box; -} - -.docker-protocol-fixed { - gap: 4px; -} - -.docker-protocol-fixed strong { - font-size: 11px; -} - -.docker-protocol-fixed small { - font-size: 10px; -} - -.docker-protocol-options .docker-checkbox-field>span:first-child { - color: var(--text); - font-size: 11px; -} - -.docker-protocol-options .docker-checkbox-field .vh-check { - font-size: 11px; -} - -@media (max-width:760px) { - - .docker-acl-grid .docker-profile-field, - .docker-agent-grid .docker-profile-field { - grid-column: auto; - } - - .docker-agent-summary>span, - .docker-fingerprint { - width: 100%; - } - - .acl-picker-option-heading { - align-items: flex-start; - } -} - -.docker-agent-grid .docker-agent-fingerprint { - min-width: 0; - width: 100%; - box-sizing: border-box; -} - -.vh-setting-section { - display: grid; - gap: 10px; - min-width: 0; - box-sizing: border-box; - padding: 13px; - border: 1px solid var(--line); - border-radius: var(--rp-radius-surface, 6px); - background: var(--panel2); -} - -.vh-setting-section>h3 { - font-size: 12px; - color: var(--text); - margin: 0; - padding-bottom: 8px; - border-bottom: 1px solid var(--line); -} - -.vh-setting-section>.vh-grid { - gap: 10px; - min-width: 0; -} - -.vh-setting-section>.vh-grid>.vh-acl-profile-picker, -.vh-setting-section>.vh-grid>.vh-wide { - min-width: 0; -} - -.vh-setting-section>.fine, -.vh-setting-section>.vh-grid>.fine { - margin: 0; -} - -.docker-backend-ip-settings { - display: grid; - align-content: start; - gap: 12px; - min-width: 0; -} - -.docker-service-certificate-bar { - display: flex; - justify-content: flex-start; - min-width: 0; -} - -.docker-service-certificate-bar>.check { - margin: 0 !important; - align-items: flex-start; - font-size: 11px; - line-height: 1.35; - overflow-wrap: anywhere; -} - -.docker-service-certificate-bar>.check>input { - margin-top: 2px; -} - -@media (max-width:760px) { - .docker-route-grid .docker-alias-field { - grid-column: 1/-1; - } -} - -.vh-certificate-select-row { - display: flex; - align-items: flex-end; - flex-wrap: wrap; - gap: 8px 12px; -} - -.vh-certificate-select-row>.vh-field { - flex: 1 1 240px; -} - -.vh-certificate-select-row>button { - flex: 0 0 auto; - min-height: 32px; -} - -.docker-agent-heading-actions .check, -.docker-protocol-options .check, -.docker-service-head .check, -.docker-switch-row .check, -.form-actions .check, -.protocol-toggle-row .check, -.switch-grid>.check, -.vh-form .vh-grid>.vh-check:not(.vh-enable-control), -.vh-form .vh-www-header .vh-www-toggle { - margin-top: 0 !important; -} - -.form-actions .check { - min-height: var(--button-min-height); - align-items: center; -} - -.switch-grid { - row-gap: 8px; -} - -.vh-setting-section>.vh-grid>.vh-check { - align-self: center; -} - -.vh-setting-section>.vh-grid>.vh-wide { - margin-top: 0; -} - -.vh-country-rates .vh-row>button, -.vh-ua-rates .vh-row>button { - align-self: end; - justify-self: start; -} - -@media (max-width:420px) { - .vh-setting-section { - padding: 10px; - } -} - -.docker-switch-row { - grid-column: 1/-1; - grid-template-columns: repeat(auto-fit, minmax(min(155px, 100%), 1fr)); - overflow-x: visible; - padding-bottom: 0; -} - -.docker-switch-row .docker-checkbox-field { - min-width: 0; - max-width: none; -} - -@media (max-width:600px) { - .vh-setting-section>.vh-grid { - grid-template-columns: minmax(0, 1fr); - } - - .vh-certificate-select-row { - align-items: stretch; - flex-direction: column; - } - - .vh-certificate-select-row>.vh-field, - .vh-certificate-select-row>button { - flex: 0 1 auto; - width: 100%; - } -} - -.stream-backend-section, -.web-service-setting-section { - grid-column: 1/-1; -} - -.stream-backend-section>.vh-grid, -.web-service-setting-section>.vh-grid { - gap: 10px; - min-width: 0; -} - -.editor-identity-section .check, -.web-service-setting-section .check { - margin-top: 0 !important; - align-self: center; -} - -.editor-frontend-certificate, -.editor-identity-section>.vh-grid { - min-width: 0; - gap: 10px; -} - -.editor-frontend-certificate { - margin-bottom: 12px; -} - -.stream-backend-row>.vh-grid { - gap: 12px; -} - -.stream-health-probe-fields[hidden], -.web-service-page[hidden] { - display: none !important; -} - -.vh-form :is(input, select, textarea):disabled { - opacity: .55; - cursor: not-allowed; -} - -@media (max-width:600px) { - - .stream-backend-section, - .web-service-setting-section { - padding: 10px; - } - - .stream-backend-section>.vh-grid, - .stream-health-probe-fields, - .web-service-setting-section>.vh-grid { - grid-template-columns: minmax(0, 1fr); - } -} - -.vh-override-fields { - display: grid; - grid-template-columns: minmax(0, 1fr); - align-items: start; - gap: 8px; - padding-top: 12px; - border-top: 1px solid var(--line); - min-width: 0; -} - -.vh-override-fields>.vh-check, -.vh-override-fields>.vh-field { - min-width: 0; - width: 100%; - margin: 0 !important; -} - -.vh-override-fields>.fine { - margin: 0; -} - -.web-service-preset-toggles { - display: grid; - align-content: start; - gap: 8px; - min-width: 0; -} - -.web-service-preset-toggles>.check { - margin: 0 !important; - min-width: 0; -} - -.refresh-settings-grid>.check { - grid-column: 1/-1; - display: flex; - align-items: center; - gap: 8px; - margin: 0; - min-width: 0; -} - -.refresh-settings-grid>label:not(.check) { - min-width: 0; -} - -.refresh-settings-grid select { - width: 100%; - min-width: 0; -} - -.metric-info>summary { - border-radius: var(--rp-radius-control, 50%); -} - -#panel-overview .cache-mix-legend i { - border-radius: var(--rp-radius-control, 2px); -} - -::-webkit-scrollbar-thumb { - border-radius: var(--rp-radius-control, 999px); -} - -html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-style=rounded], [data-corner-style=extra]):is(.acl-profile-layout>.card, .logging-grid>.card) { - border-radius: var(--rp-radius-surface) !important; -} - -@media (max-width: 760px) { - .docker-service-row { - position: relative; - gap: 8px; - padding: 12px; - } - - .docker-service-head { - grid-template-columns: minmax(0, 1fr); - gap: 8px; - align-items: start; - } - - .docker-publish-field { - min-height: 24px; - padding-right: 90px; - } - - .docker-service-meta { - gap: 3px; - } - - .docker-service-meta b, - .docker-service-meta span { - overflow-wrap: anywhere; - } - - .docker-service-head-actions { - grid-column: 1; - display: flex; - flex-direction: column; - align-items: stretch; - justify-content: flex-start; - gap: 8px; - min-width: 0; - } - - .docker-service-head-actions .docker-runtime { - flex: 0 0 auto; - min-height: 0; - width: 100%; - display: block; - text-align: left; - } - - .docker-service-head-actions .docker-runtime .pill { - position: absolute; - top: 12px; - right: 12px; - max-width: 86px; - text-align: center; - } - - .docker-service-head-actions .docker-runtime .fine { - display: block; - font-size: 11px; - line-height: 1.4; - } - - .docker-service-quick-actions { - display: flex; - flex-wrap: wrap; - gap: 6px; - } - - .docker-service-quick-actions>* { - flex: 0 0 auto; - } - - .docker-service-head-actions .docker-view-toggle { - align-self: flex-start; - } - - body[data-density=compact] .docker-service-row { - padding: 8px; - gap: 5px; - } - - body[data-density=compact] .docker-service-head-actions .docker-runtime .pill { - top: 8px; - right: 8px; - } -} - -html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-style=rounded], [data-corner-style=extra]) :is(.nav, .file-log-tab, .vhost-backend-trigger, .backend-column-options, - .docker-service-quick-actions a, .docker-service-quick-actions button, - .docker-view-toggle, .provider-picker, .renewal-meta-item) { - border-radius: var(--rp-radius-control) !important; -} - -html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-style=rounded], [data-corner-style=extra]) :is(.backend-column-options, .renewal-meta-item) { - border-radius: var(--rp-radius-surface) !important; -} - -@media (max-width: 760px) { - .backend-column-options { - width: min(330px, calc(100vw - 32px)); - min-width: 0; - max-width: calc(100vw - 32px); - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .stat-chart .stat-chart-legend { - flex-wrap: wrap; - white-space: normal; - } - - .stat-chart .stat-chart-legend span { - min-width: 0; - } - - .system-info-heading-actions { - flex-wrap: wrap; - justify-content: flex-end; - min-width: 0; - } - - .system-info-heading-actions .pill { - max-width: 100%; - white-space: normal; - overflow-wrap: anywhere; - } - - #panel-filelogs .file-log-tabs { - max-width: 100%; - overflow-x: auto; - scrollbar-width: thin; - } - - #panel-filelogs .file-log-tab { - flex-shrink: 0; - } -} - -@media (max-width: 420px) { - .backend-column-options { - grid-template-columns: minmax(0, 1fr); - } -} \ No newline at end of file diff --git a/src/transform/tests.rs b/src/transform/tests.rs index 8c8c5f8..5854251 100644 --- a/src/transform/tests.rs +++ b/src/transform/tests.rs @@ -423,7 +423,7 @@ mod tests { #[test] fn css_minifier_preserves_dashboard_corner_selector() { - let input = include_str!("../admin/web/style.css"); + let input = include_str!(concat!(env!("OUT_DIR"), "/panel-style.css")); let result = css::minify(input.as_bytes()).expect("minify panel stylesheet"); let output = std::str::from_utf8(&result).expect("UTF-8 CSS"); assert!(output.contains("html:is([data-corner-style=square],[data-corner-style=subtle],[data-corner-style=rounded],[data-corner-style=extra]) :is("), "lost descendant pseudo-class combinator"); diff --git a/tools/audit/admin_audit.py b/tools/audit/admin_audit.py deleted file mode 100755 index 6fa08e7..0000000 --- a/tools/audit/admin_audit.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 -"""Read-only anonymous admin access probes against a running RustProxy. -No cookie, password or token is sent; response bodies are never printed. -Only HTTP loopback URLs are accepted. Exit 1 on an unexpected disclosure/status. -This is a focused regression probe, NOT a penetration test or full security audit. -""" -from __future__ import annotations -import argparse -import ipaddress -import json -from pathlib import Path -from urllib.parse import urlsplit -from urllib.request import Request, build_opener, ProxyHandler, HTTPRedirectHandler -from urllib.error import HTTPError, URLError - -PRIVATE = ('/api/status','/api/config','/api/backends','/api/traffic','/api/cache','/api/transform','/api/logs','/api/file-logs','/api/file-logs/query','/api/audit', - '/api/events','/api/certificates','/api/acme','/api/auth/session','/metrics','/api/metrics', - '/docs/openapi.json','/docs/init.js','/docs/style.css','/docs/swagger-ui-bundle.js', - '/docs/swagger-ui.css','/docs/licenses.txt','/assets/app.js','/assets/langs.json','/assets/vhosts.js', - '/assets/management.js','/assets/integrations.js','/assets/settings.js','/assets/file-logs.js','/assets/acme.js', - '/assets/traffic-panel.js','/assets/cache-panel.js','/assets/transform-panel.js','/api/does-not-exist') - -class NoRedirect(HTTPRedirectHandler): - def redirect_request(self,*args,**kwargs): - return None - -def base_url(value: str) -> str: - url=urlsplit(value) - if url.scheme!='http' or url.username or url.password or url.path not in ('','/') or url.query or url.fragment: - raise argparse.ArgumentTypeError('Use the HTTP loopback administrator origin, e.g. http://127.0.0.1:9090') - try: - loopback=url.hostname=='localhost' or ipaddress.ip_address(url.hostname).is_loopback - if not loopback or not url.port:raise ValueError() - except (ValueError,TypeError):raise argparse.ArgumentTypeError('Explicit loopback address and port required') - return value.rstrip('/') - -def main(): - parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--url',type=base_url,default='http://127.0.0.1:9090');args=parser.parse_args() - opener=build_opener(ProxyHandler({}),NoRedirect());failed=0;count=0 - def check(path,expected,validate): - nonlocal failed,count - count+=1 - try: - request=Request(args.url+path,headers={'Accept':'application/json'}) - try:response=opener.open(request,timeout=5) - except HTTPError as error:response=error - with response: - status=response.code;headers=response.headers;body=response.read(8388609 if path=='/assets/login-langs.json' else 131073) - ok=status==expected and headers.get('Cache-Control')=='no-store' and validate(body,headers) - except (OSError,ValueError,KeyError,TypeError,URLError):status='unavailable';ok=False - if not ok:failed+=1 - print(f'{"PASS" if ok else "FAIL"} {path}: HTTP {status}; expected {expected}') - def denied(body,headers):return json.loads(body)=={'error':'auth.required'} - for path in PRIVATE:check(path,401,denied) - check('/docs',303,lambda b,h:h.get('Location')=='/?next=docs') - check('/docs/',303,lambda b,h:h.get('Location')=='/?next=docs') - check('/healthz',200,lambda b,h:json.loads(b)=={'status':'ok'}) - check('/api/auth/status',200,lambda b,h:isinstance((v:=json.loads(b)).get('setup_required'),bool) and set(v)=={'setup_required'}) - check('/',200,lambda b,h:b'/assets/login.js' in b and all(x not in b for x in [b'/assets/app.js',b'config-editor',b'loginLead'])) - check('/assets/login-langs.json',200,lambda b,h:all(set(p['translations'])<=set(json.loads((Path(__file__).resolve().parents[2]/'src/admin/web/login-keys.json').read_text())) for p in json.loads(b)['languages'])) - print(f'{count-failed}/{count} anonymous probes passed. No authenticated/mutation/TLS checks performed.') - raise SystemExit(bool(failed)) -if __name__=='__main__':main() diff --git a/tools/dev/test_compression.py b/tools/dev/test_compression.py deleted file mode 100755 index c8f1f04..0000000 --- a/tools/dev/test_compression.py +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import gzip -import hashlib -import io -import time -import zlib - -import brotli -import requests -import zstandard - - -TESTS = { - "identity": "identity", - "gzip": "gzip", - "deflate": "deflate", - "brotli": "br", - "zstd": "zstd", - "all": "zstd, br, gzip, deflate", -} - - -def decompress(data, content_encoding): - encodings = [ - e.strip().lower() - for e in content_encoding.split(",") - if e.strip() and e.strip().lower() != "identity" - ] - - # Content-Encoding usuwa się w odwrotnej kolejności. - for encoding in reversed(encodings): - if encoding == "gzip": - data = gzip.decompress(data) - - elif encoding == "deflate": - try: - data = zlib.decompress(data) - except zlib.error: - # Niektóre serwery wysyłają raw DEFLATE. - data = zlib.decompress(data, -zlib.MAX_WBITS) - - elif encoding == "br": - data = brotli.decompress(data) - - elif encoding == "zstd": - dctx = zstandard.ZstdDecompressor() - with dctx.stream_reader(io.BytesIO(data)) as reader: - data = reader.read() - - else: - raise RuntimeError(f"Nieznany Content-Encoding: {encoding}") - - return data - - -def human_size(size): - if size < 1024: - return f"{size} B" - if size < 1024 * 1024: - return f"{size / 1024:.1f} KB" - return f"{size / 1024 / 1024:.2f} MB" - - -def request_test(url, accept_encoding, verify_ssl=True, timeout=15, revalidate=False): - headers = { - "Accept-Encoding": accept_encoding, - "User-Agent": "compression-tester/1.0", - } - if revalidate: - headers["Cache-Control"] = "no-cache" - - start = time.perf_counter() - - r = requests.get( - url, - headers=headers, - timeout=timeout, - verify=verify_ssl, - stream=True, - allow_redirects=True, - ) - - r.raw.decode_content = False - raw = r.raw.read() - - elapsed = time.perf_counter() - start - - content_encoding = r.headers.get("Content-Encoding", "identity") - - try: - decoded = decompress(raw, content_encoding) - error = None - except Exception as exc: - decoded = b"" - error = str(exc) - - return { - "status": r.status_code, - "requested": accept_encoding, - "received": content_encoding, - "raw_size": len(raw), - "decoded_size": len(decoded), - "time": elapsed, - "decoded": decoded, - "error": error, - "headers": r.headers, - "final_url": r.url, - } - - -def main(): - parser = argparse.ArgumentParser( - description="Tester HTTP Content-Encoding: zstd/br/gzip/deflate" - ) - - parser.add_argument("url", help="URL do przetestowania") - parser.add_argument( - "--insecure", - action="store_true", - help="Wyłącz weryfikację certyfikatu SSL", - ) - parser.add_argument( - "--timeout", - type=int, - default=15, - help="Timeout HTTP w sekundach", - ) - parser.add_argument( - "--revalidate", - action="store_true", - help="Wyslij Cache-Control: no-cache (wymusza rewalidacje i moze resetowac Age do 0)", - ) - - args = parser.parse_args() - - if args.insecure: - requests.packages.urllib3.disable_warnings() - - print() - print(f"URL: {args.url}") - print("=" * 100) - - results = {} - - for name, accept_encoding in TESTS.items(): - try: - result = request_test( - args.url, - accept_encoding, - verify_ssl=not args.insecure, - timeout=args.timeout, - revalidate=args.revalidate, - ) - results[name] = result - except Exception as exc: - results[name] = { - "error": f"REQUEST ERROR: {exc}" - } - - identity_body = None - - if "identity" in results: - identity_body = results["identity"].get("decoded") - - print( - f"{'TEST':<10}" - f"{'HTTP':<7}" - f"{'REQUEST':<27}" - f"{'RESPONSE':<12}" - f"{'RAW':<12}" - f"{'DECODED':<12}" - f"{'RATIO':<10}" - f"{'TIME':<10}" - f"{'BODY'}" - ) - - print("-" * 120) - - failed = False - - for name, result in results.items(): - if result.get("error"): - print(f"{name:<10} ERROR: {result['error']}") - failed = True - continue - - raw_size = result["raw_size"] - decoded_size = result["decoded_size"] - - if decoded_size: - ratio = raw_size / decoded_size - ratio_text = f"{ratio * 100:.1f}%" - else: - ratio_text = "-" - - if identity_body is not None and result["decoded"]: - if result["decoded"] == identity_body: - body_status = "OK" - else: - body_status = "DIFF" - else: - body_status = "-" - - print( - f"{name:<10}" - f"{result['status']:<7}" - f"{result['requested']:<27}" - f"{result['received']:<12}" - f"{human_size(raw_size):<12}" - f"{human_size(decoded_size):<12}" - f"{ratio_text:<10}" - f"{result['time']:.3f}s " - f"{body_status}" - ) - - print() - print("=" * 100) - print("Szczegóły nagłówków:") - print() - - for name, result in results.items(): - if result.get("error"): - continue - - headers = result["headers"] - - print(f"[{name}]") - print(f" URL finalny: {result['final_url']}") - print(f" HTTP status: {result['status']}") - print(f" Content-Encoding: {headers.get('Content-Encoding', '-')}") - print(f" Content-Type: {headers.get('Content-Type', '-')}") - print(f" Content-Length: {headers.get('Content-Length', '-')}") - print(f" Vary: {headers.get('Vary', '-')}") - print(f" Cache-Control: {headers.get('Cache-Control', '-')}") - print(f" Age: {headers.get('Age', '-')}") - print(f" X-Cache: {headers.get('X-Cache', '-')}") - print(f" X-Cache-Hits: {headers.get('X-Cache-Hits', '-')}") - print(f" Cache-Status: {headers.get('Cache-Status', '-')}") - print(f" ETag: {headers.get('ETag', '-')}") - print(f" Server: {headers.get('Server', '-')}") - print() - - print("SHA256 po dekompresji:") - print() - - for name, result in results.items(): - body = result.get("decoded") - - if body: - sha = hashlib.sha256(body).hexdigest() - print(f"{name:<10} {sha}") - - if failed: - raise SystemExit(1) - - -if __name__ == "__main__": - main()