From 962b615c86be6dcb356aa51f7d895c695321e25e Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 9 Sep 2026 09:49:57 -0300 Subject: [PATCH 1/3] refactor(db)!: replace public `QueryRow` construction and properties with factories, getters, and immutable methods; support enum content and improve Database filters. --- composer.json | 2 +- e2e/db-controls.spec.js | 217 ++++++++++ package.json | 4 +- resources/assets/dist/css/debug.min.css | 2 +- resources/assets/dist/js/db.min.js | 2 +- resources/src/panels/db.js | 124 +++++- resources/src/styles/main.css | 15 + resources/tests/db.test.js | 185 ++++++++- src/Capture/CapturePolicy.php | 88 ++-- src/Collector/CollectorCoordinator.php | 41 +- src/Comparison/PanelComparison.php | 60 ++- src/Comparison/PayloadDifference.php | 28 +- src/Comparison/SnapshotComparison.php | 10 +- src/Comparison/SummaryMetricComparison.php | 105 ++++- src/Data/FilterEngine.php | 35 +- src/Data/PageSize.php | 18 +- src/Data/QueryInput.php | 11 + src/Helper/Coerce.php | 2 +- src/Helper/Dump.php | 28 +- src/Helper/Format.php | 5 +- src/Helper/Fqcn.php | 8 +- src/Helper/SensitiveDataRedactor.php | 10 + src/Helper/Table.php | 4 +- src/Helper/Tabs.php | 16 +- src/Helper/Text.php | 4 + src/Helper/Trace.php | 20 + src/Helper/Vocabulary.php | 1 + src/Instrumentation/InstrumentationGuard.php | 26 +- src/Panel/Config/ApplicationConfig.php | 16 +- src/Panel/Config/ConfigCardRenderer.php | 186 ++++++--- src/Panel/Config/ConfigDataNormalizer.php | 10 + src/Panel/Config/ConfigSnapshot.php | 2 + src/Panel/Config/ConfigSummary.php | 4 + src/Panel/Db/DbExplainSupport.php | 50 +++ src/Panel/Db/DbQueryRenderer.php | 28 +- src/Panel/Db/DbSnapshot.php | 6 +- src/Panel/Db/DbSummary.php | 22 +- src/Panel/Db/DbSummaryRenderer.php | 20 +- src/Panel/Db/NPlusOneDetector.php | 59 ++- src/Panel/Db/QueryRow.php | 381 +++++++++++++----- src/Panel/Db/SqlHighlighter.php | 8 +- tests/Capture/CapturePolicyTest.php | 118 ++++-- tests/Collector/CollectorCoordinatorTest.php | 65 +-- tests/Comparison/PanelComparisonTest.php | 11 +- tests/Comparison/PayloadDifferenceTest.php | 7 +- tests/Comparison/SnapshotComparisonTest.php | 11 +- .../SummaryMetricComparisonTest.php | 13 +- tests/Data/FilterEngineTest.php | 35 +- tests/Panel/Config/ConfigCardRendererTest.php | 17 + tests/Panel/Db/DbCaptureTest.php | 105 ++--- tests/Panel/Db/DbExplainSupportTest.php | 63 +++ tests/Panel/Db/DbQueryRendererTest.php | 78 ++-- tests/Panel/Db/NPlusOneDetectorTest.php | 25 +- tests/Panel/Db/QueryRowTest.php | 375 ++++++++++++++--- tests/Provider/DbCaptureProvider.php | 35 ++ tests/Provider/DbQueryRendererProvider.php | 41 ++ tests/Provider/PanelComparisonProvider.php | 37 +- .../SummaryMetricComparisonProvider.php | 12 +- 58 files changed, 2270 insertions(+), 641 deletions(-) create mode 100644 e2e/db-controls.spec.js create mode 100644 src/Panel/Db/DbExplainSupport.php create mode 100644 tests/Panel/Db/DbExplainSupportTest.php create mode 100644 tests/Provider/DbCaptureProvider.php create mode 100644 tests/Provider/DbQueryRendererProvider.php diff --git a/composer.json b/composer.json index 3c39780..8bf914b 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "ui-awesome/html-core-component": "^0.4", "ui-awesome/html-helper": "^0.7", "ui-awesome/html-interop": "^0.4", - "ui-awesome/html-mixin": "^0.8.1", + "ui-awesome/html-mixin": "^0.8.2", "ui-awesome/html-svg": "^0.6" }, "require-dev": { diff --git a/e2e/db-controls.spec.js b/e2e/db-controls.spec.js new file mode 100644 index 0000000..ae51af2 --- /dev/null +++ b/e2e/db-controls.spec.js @@ -0,0 +1,217 @@ +import { readFile, readdir } from "node:fs/promises"; + +import { expect, test } from "@playwright/test"; + +const assets = new Map([ + ["/db.js", new URL("../resources/assets/dist/js/db.min.js", import.meta.url)], + [ + "/dom.min.js", + new URL("../resources/assets/dist/js/dom.min.js", import.meta.url), + ], + [ + "/deep-links.js", + new URL("../resources/src/core/deep-links.js", import.meta.url), + ], + [ + "/debug.css", + new URL("../resources/assets/dist/css/debug.min.css", import.meta.url), + ], +]); + +const fonts = new URL("../resources/assets/dist/fonts/", import.meta.url); +for (const name of await readdir(fonts)) { + assets.set(`/fonts/${name}`, new URL(name, fonts)); +} + +function databaseFixture(dialect, theme, filtered) { + const filterClass = + dialect === "yii2" ? "" : ' class="yii-debug-filter-cell"'; + const rows = ["SELECT", "SELECT", "SELECT", "UPDATE"] + .map( + (verb, index) => ` + ${verb} + 10:21:10.9560.2 ms0 rows1 +
${verb} name FROM demo_items WHERE id = ${index + 1}
+ ${index < 3 ? 'Potential N+1 · 3 similar' : ""} + `, + ) + .join(""); + + return ` + + Database controls regression +
+ ${ + filtered + ? `
+ 1 filter active + + query: + demo_items× + Clear all
` + : "" + } +
+ Potential N+1 queries +
3 similar queries +
+
+ + + + + + + ${rows}
TypeTimeDurationRowsDupQuery
+ `; +} + +for (const dialect of ["yii2", "yii3"]) { + for (const theme of ["light", "dark"]) { + test(`${dialect} ${theme} database controls fit and clear N+1 without reloading`, async ({ + page, + }, testInfo) => { + const errors = []; + page.on("pageerror", (error) => errors.push(error.message)); + await page.route("http://debug.test/**", async (route) => { + const path = new URL(route.request().url()).pathname; + const asset = assets.get(path); + await route.fulfill({ + contentType: path.endsWith(".woff2") + ? "font/woff2" + : path === "/debug.css" + ? "text/css" + : asset + ? "text/javascript" + : "text/html", + body: asset + ? await readFile(asset) + : databaseFixture( + dialect, + theme, + new URL(route.request().url()).searchParams.has("Db[query]"), + ), + }); + }); + await page.goto("http://debug.test/?panel=db&sort=-duration#group-a"); + await expect(page.locator("body")).toHaveAttribute("data-ready", "true"); + await expect(page.locator("#group-a")).toHaveClass( + /yii-debug-deep-link-target/, + ); + + await page.evaluate(() => document.fonts.ready); + const select = page.getByLabel("Filter by Type"); + await select.selectOption("SELECT"); + await select.focus(); + const spacing = await select.evaluate((element) => { + const style = getComputedStyle(element); + const context = document.createElement("canvas").getContext("2d"); + context.font = style.font; + return { + width: element.getBoundingClientRect().width, + text: Math.max( + ...Array.from( + element.options, + (option) => context.measureText(option.text).width, + ), + ), + left: parseFloat(style.paddingLeft), + right: parseFloat(style.paddingRight), + outline: style.outlineWidth, + offset: style.outlineOffset, + shadow: style.boxShadow, + }; + }); + expect(spacing.width).toBeGreaterThanOrEqual( + spacing.text + spacing.left + spacing.right + 16, + ); + expect(spacing.width).toBeLessThanOrEqual( + spacing.text + spacing.left + spacing.right + 32, + ); + expect(spacing.left).toBe(spacing.right); + expect(spacing.outline).toBe("2px"); + expect(spacing.offset).toBe("1px"); + expect(spacing.shadow).toBe("none"); + await select.screenshot({ + path: testInfo.outputPath("sql-type-focus.png"), + }); + + const rowTags = page.locator(".yii-debug-db-n1-row-link"); + const clear = page.locator("[data-yii-debug-n1-clear]"); + const banner = page.getByRole("group", { name: "Active filters" }); + const visibleRows = page.locator(".yii-debug-grid-db tbody tr:visible"); + await rowTags.first().click(); + await expect(visibleRows).toHaveCount(3); + await expect(clear).toBeVisible(); + await expect(banner).toContainText("1 filter active"); + await expect(banner).toContainText("3 similar queries"); + expect( + await banner.evaluate((element) => + element.nextElementSibling.classList.contains( + "yii-debug-db-n1-summary", + ), + ), + ).toBe(true); + await rowTags.nth(1).click(); + await expect(visibleRows).toHaveCount(4); + await expect(clear).toBeHidden(); + await expect(banner).toHaveCount(0); + await expect(page.locator(".yii-debug-deep-link-target")).toHaveCount(0); + expect(page.url()).toBe("http://debug.test/?panel=db&sort=-duration"); + + await page.locator(".yii-debug-db-n1-link").click(); + await expect(visibleRows).toHaveCount(3); + await banner + .getByRole("link", { name: "Clear all active filters" }) + .click(); + await expect(visibleRows).toHaveCount(4); + await expect(page.locator('[aria-current="true"]')).toHaveCount(0); + expect(page.url()).toBe("http://debug.test/?panel=db&sort=-duration"); + await expect(select).toHaveValue("SELECT"); + await expect(banner).toHaveCount(0); + + await page.goto( + "http://debug.test/?panel=db&sort=-duration&Db%5Bquery%5D=demo_items", + ); + await expect(page.locator("body")).toHaveAttribute("data-ready", "true"); + await rowTags.first().click(); + await expect(banner).toHaveCount(1); + await expect(banner).toContainText("2 filters active"); + await expect(banner).toContainText("demo_items"); + expect( + await banner.evaluate((element) => + element.nextElementSibling.classList.contains( + "yii-debug-db-n1-summary", + ), + ), + ).toBe(true); + await banner + .getByRole("link", { name: "Remove N+1: 3 similar queries filter" }) + .click(); + await expect(banner).toContainText("1 filter active"); + await expect(banner.locator("[data-yii-debug-n1-pill]")).toHaveCount(0); + await expect(visibleRows).toHaveCount(4); + expect(new URL(page.url()).searchParams.get("Db[query]")).toBe( + "demo_items", + ); + + await rowTags.first().click(); + await expect(banner).toContainText("2 filters active"); + await banner + .getByRole("link", { name: "Clear all active filters" }) + .click(); + await expect(page.locator("body")).toHaveAttribute("data-ready", "true"); + await expect(banner).toHaveCount(0); + await expect(visibleRows).toHaveCount(4); + expect(page.url()).toBe("http://debug.test/?panel=db&sort=-duration"); + expect(errors).toEqual([]); + }); + } +} diff --git a/package.json b/package.json index ccf2e7c..7e29775 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "check:contrast": "node tools/check-token-contrast.mjs", "check:contrast:strict": "node tools/check-token-contrast.mjs --strict", "test:events": "DEBUG_UI_SEED_FIXTURES=0 playwright test e2e/events.spec.js", - "test:e2e": "playwright test e2e/smoke.spec.js e2e/security.spec.js", + "test:e2e": "playwright test e2e/smoke.spec.js e2e/security.spec.js e2e/db-controls.spec.js", "test:e2e:list": "DEBUG_UI_SEED_FIXTURES=0 playwright test --list", "test:a11y": "playwright test e2e/accessibility.spec.js", "test:security": "playwright test e2e/security.spec.js", @@ -28,7 +28,7 @@ "test:visual:update": "DEBUG_UI_VISUAL_COMPARE=1 playwright test e2e/visual.spec.js --update-snapshots", "test:perf": "DEBUG_UI_SEED_FIXTURES=0 playwright test e2e/large-data.spec.js --project=desktop-1440", "test:perf:live": "playwright test e2e/live-performance.spec.js --project=desktop-1440", - "test:browser": "playwright test e2e/smoke.spec.js e2e/security.spec.js e2e/accessibility.spec.js e2e/visual.spec.js e2e/events.spec.js" + "test:browser": "playwright test e2e/smoke.spec.js e2e/security.spec.js e2e/db-controls.spec.js e2e/accessibility.spec.js e2e/visual.spec.js e2e/events.spec.js" }, "engines": { "node": "^22.12.0 || >=24.0.0" diff --git a/resources/assets/dist/css/debug.min.css b/resources/assets/dist/css/debug.min.css index 14be509..4892b41 100644 --- a/resources/assets/dist/css/debug.min.css +++ b/resources/assets/dist/css/debug.min.css @@ -1 +1 @@ -:root,:host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--yii-debug-font-sans:"IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, blinkmacsystemfont, "Segoe UI", roboto, helvetica, arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";--yii-debug-font-mono:"JetBrains Mono", ui-monospace, sfmono-regular, menlo, monaco, consolas, "Liberation Mono", "Courier New", monospace;--yii-debug-font-display:var(--yii-debug-font-mono);--yii-debug-space-1:4px;--yii-debug-space-2:8px;--yii-debug-space-3:12px;--yii-debug-space-4:16px;--yii-debug-space-5:24px;--yii-debug-space-6:32px;--yii-debug-radius-sm:6px;--yii-debug-radius-md:10px;--yii-debug-radius-lg:14px;--yii-debug-radius-pill:999px;--yii-debug-fs-xs:.75rem;--yii-debug-fs-sm:.8125rem;--yii-debug-fs-base:.9375rem;--yii-debug-fs-lg:1.0625rem;--yii-debug-fs-xl:1.25rem;--yii-debug-fs-2xl:1.5rem;--yii-debug-control-min-size:36px;--yii-debug-panel-padding:var(--yii-debug-space-5);--yii-debug-row-padding-block:10px;--yii-debug-duration-fast:.12s;--yii-debug-duration-normal:.18s;--yii-debug-panel-bg:var(--lightningcss-light,#f2f5f1)var(--lightningcss-dark,#0c120e);--yii-debug-panel-bg-accent:var(--lightningcss-light,#0e7a551a)var(--lightningcss-dark,#2fd08c14);--yii-debug-panel-surface:var(--lightningcss-light,#fffffff0)var(--lightningcss-dark,#121a15eb);--yii-debug-panel-surface-muted:var(--lightningcss-light,#f6f8f5)var(--lightningcss-dark,#101812);--yii-debug-panel-surface-hover:var(--lightningcss-light,#e9f2ec)var(--lightningcss-dark,#1a241d);--yii-debug-panel-border:var(--lightningcss-light,#5d7a6942)var(--lightningcss-dark,#8fa39633);--yii-debug-panel-border-strong:var(--lightningcss-light,#4660515c)var(--lightningcss-dark,#8fa39652);--yii-debug-panel-text:var(--lightningcss-light,#16211a)var(--lightningcss-dark,#e4ede6);--yii-debug-panel-muted:var(--lightningcss-light,#5c6b60)var(--lightningcss-dark,#8fa396);--yii-debug-panel-link:var(--lightningcss-light,#0b6b4a)var(--lightningcss-dark,#4fd39a);--yii-debug-panel-link-hover:var(--lightningcss-light,#0d855b)var(--lightningcss-dark,#7fe4b8);--yii-debug-panel-primary:var(--lightningcss-light,#0e7a55)var(--lightningcss-dark,#2fd08c);--yii-debug-primary-bg:linear-gradient(135deg, var(--yii-debug-panel-primary-strong), var(--yii-debug-panel-primary));--yii-debug-on-primary:var(--lightningcss-light,#fff)var(--lightningcss-dark,#06281b);--yii-debug-panel-primary-strong:var(--lightningcss-light,#0a5c40)var(--lightningcss-dark,#17a673);--yii-debug-focus-ring:var(--lightningcss-light,#075c40)var(--lightningcss-dark,#7fe4b8);--yii-debug-success:var(--lightningcss-light,#0d7d47)var(--lightningcss-dark,#34c97e);--yii-debug-warning:var(--lightningcss-light,#8f5000)var(--lightningcss-dark,#e8a33d);--yii-debug-danger:var(--lightningcss-light,#c93838)var(--lightningcss-dark,#f16a6a);--yii-debug-on-success:var(--lightningcss-light,#fff)var(--lightningcss-dark,#0c120e);--yii-debug-on-warning:var(--lightningcss-light,#fff)var(--lightningcss-dark,#1c1917);--yii-debug-on-danger:var(--lightningcss-light,#fff)var(--lightningcss-dark,#1c1917);--yii-debug-info:var(--lightningcss-light,#205f9d)var(--lightningcss-dark,#5fa8e8);--yii-debug-on-info:var(--yii-debug-panel-bg);--yii-debug-panel-code-bg:var(--lightningcss-light,#ecf1ec)var(--lightningcss-dark,#17211a);--yii-debug-panel-pill:var(--lightningcss-light,#16211a0f)var(--lightningcss-dark,#8fa3962e);--yii-debug-panel-shadow:var(--lightningcss-light,0 22px 60px #16211a14)var(--lightningcss-dark,0 22px 60px #0006);--yii-debug-border:1px solid var(--yii-debug-panel-border);--yii-debug-border-strong:1px solid var(--yii-debug-panel-border-strong);--yii-debug-shadow-sm:0 1px 2px #0f172a0f;--yii-debug-shadow-md:0 10px 24px #0f172a14;--yii-debug-verb-get:var(--lightningcss-light,#0b7285)var(--lightningcss-dark,#4dc4d9);--yii-debug-verb-post:var(--lightningcss-light,#6941c6)var(--lightningcss-dark,#a78bfa);--yii-debug-verb-put:var(--lightningcss-light,#b26a00)var(--lightningcss-dark,#e8a33d);--yii-debug-verb-delete:var(--lightningcss-light,#c93838)var(--lightningcss-dark,#f16a6a);--yii-debug-verb-other:var(--yii-debug-panel-muted);--yii-debug-status-2xx:var(--yii-debug-success);--yii-debug-status-3xx:var(--yii-debug-warning);--yii-debug-status-4xx:var(--lightningcss-light,#c2410c)var(--lightningcss-dark,#fb923c);--yii-debug-status-5xx:var(--yii-debug-danger);--yii-debug-level-error:var(--yii-debug-danger);--yii-debug-level-warning:var(--yii-debug-warning);--yii-debug-level-info:var(--yii-debug-info);--yii-debug-level-trace:var(--yii-debug-panel-muted);--yii-debug-level-profile:var(--lightningcss-light,#9333ea)var(--lightningcss-dark,#b16be8);--yii-debug-level-other:var(--yii-debug-panel-muted);--yii-debug-gauge-fill:var(--yii-debug-panel-primary);--yii-debug-gauge-track:var(--lightningcss-light,#0e7a5529)var(--lightningcss-dark,#2fd08c24);--yii-debug-sql-kw:var(--lightningcss-light,#0a5c40)var(--lightningcss-dark,#4fd39a);--yii-debug-sql-str:var(--lightningcss-light,#8f4e00)var(--lightningcss-dark,#e8a33d);--yii-debug-sql-num:var(--yii-debug-info);--yii-debug-sql-comment:var(--yii-debug-panel-muted);--yii-debug-sql-param:var(--lightningcss-light,#6941c6)var(--lightningcss-dark,#a78bfa);--yii-debug-cat-app:var(--lightningcss-light,#0a6b47)var(--lightningcss-dark,#14b06e);--yii-debug-cat-db:var(--lightningcss-light,#0994b8)var(--lightningcss-dark,#0999be);--yii-debug-cat-view:var(--lightningcss-light,#6941c6)var(--lightningcss-dark,#8f6ce8);--yii-debug-cat-cache:var(--lightningcss-light,#b26a00)var(--lightningcss-dark,#c28300);--yii-debug-cat-mail:var(--lightningcss-light,#b23a67)var(--lightningcss-dark,#d95f8f);--yii-debug-cat-queue:var(--lightningcss-light,#3f4dbd)var(--lightningcss-dark,#6d7ce6);--yii-debug-cat-other:var(--yii-debug-panel-muted)}@media (prefers-color-scheme:dark){:root,:host{--lightningcss-light: ;--lightningcss-dark:initial}}:root[data-yii-debug-theme=light],:host([data-theme=light]){--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}:root[data-yii-debug-theme=dark],:host([data-theme=dark]){--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}@font-face{font-family:JetBrains Mono;font-style:normal;font-display:swap;font-weight:400;src:url(../fonts/jetbrains-mono-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-display:swap;font-weight:500;src:url(../fonts/jetbrains-mono-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-display:swap;font-weight:700;src:url(../fonts/jetbrains-mono-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:400;src:url(../fonts/ibm-plex-sans-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:500;src:url(../fonts/ibm-plex-sans-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:600;src:url(../fonts/ibm-plex-sans-latin-600-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:700;src:url(../fonts/ibm-plex-sans-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}.yii-debug .yii-debug-event-controls{margin-block:var(--yii-debug-space-4);font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-event-groups{align-items:center;gap:var(--yii-debug-space-2);margin-block:var(--yii-debug-space-4);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-event-group{align-items:center;gap:var(--yii-debug-space-2);min-height:var(--yii-debug-control-min-size);padding:var(--yii-debug-space-1) var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);overflow-wrap:anywhere;display:inline-flex}.yii-debug .yii-debug-event-item>summary{cursor:pointer;list-style:none}.yii-debug .yii-debug-event-item>summary:focus-visible{outline:2px solid var(--yii-debug-panel-primary);outline-offset:4px;border-radius:var(--yii-debug-radius-sm)}.yii-debug :is(.yii-debug-event-clock,.yii-debug-event-identity){gap:var(--yii-debug-space-1);display:grid}.yii-debug .yii-debug-event-clock{min-width:105px}.yii-debug .yii-debug-event-time{white-space:nowrap;color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-event-identity{overflow-wrap:anywhere;min-width:0}.yii-debug .yii-debug-event-name:before{content:"+ ";color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-event-item[open] .yii-debug-event-name:before{content:"− "}.yii-debug .yii-debug-event-timing{font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-event-detail-row{display:none}.yii-debug .yii-debug-table tr:has(.yii-debug-event-item[open])+.yii-debug-event-detail-row{display:table-row}.yii-debug .yii-debug-table .yii-debug-event-detail-row>td{padding:0}.yii-debug .yii-debug-event-detail{gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-4);background:var(--yii-debug-panel-surface-muted);white-space:normal;grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.yii-debug .yii-debug-event-detail>div{min-width:0}.yii-debug .yii-debug-event-detail-footer{gap:var(--yii-debug-space-2) var(--yii-debug-space-4);flex-wrap:wrap;grid-column:1/-1;display:flex}.yii-debug .yii-debug-event-metadata{gap:var(--yii-debug-space-2) var(--yii-debug-space-3);grid-template-columns:minmax(75px,.4fr) minmax(0,1fr);display:grid}.yii-debug .yii-debug-event-metadata dt{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-event-metadata dd{margin:0}.yii-debug .yii-debug-event-trace pre{white-space:pre-wrap;overflow-wrap:anywhere}.yii-debug :is(.yii-debug-event-context,.yii-debug-event-trace)>:last-child{margin-bottom:0}@media not (min-width:768px){.yii-debug .yii-debug-event-detail{grid-template-columns:minmax(0,1fr)}}.yii-debug .yii-debug-event-coverage>summary{min-height:var(--yii-debug-control-min-size);padding-block:var(--yii-debug-space-2);cursor:pointer}.yii-debug{background:radial-gradient(circle at top left, var(--yii-debug-panel-bg-accent), transparent 36rem), var(--yii-debug-panel-bg);min-height:100vh;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-base);margin:0;padding:0;line-height:1.5}.yii-debug *,.yii-debug :before,.yii-debug :after{box-sizing:border-box}.yii-debug h1,.yii-debug h2,.yii-debug h3,.yii-debug h4,.yii-debug h5,.yii-debug h6{margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-text);letter-spacing:-.01em;font-weight:700;line-height:1.25}.yii-debug h1{font-size:var(--yii-debug-fs-2xl)}.yii-debug h2{font-size:var(--yii-debug-fs-xl)}.yii-debug h3{font-size:var(--yii-debug-fs-lg)}.yii-debug h4,.yii-debug h5,.yii-debug h6{font-size:var(--yii-debug-fs-base)}.yii-debug p,.yii-debug ul,.yii-debug ol,.yii-debug dl,.yii-debug figure{margin:0 0 var(--yii-debug-space-3)}.yii-debug ul,.yii-debug ol{padding-left:var(--yii-debug-space-5)}.yii-debug strong,.yii-debug b{font-weight:600}.yii-debug a{color:var(--yii-debug-panel-link);text-decoration:none;transition:color .12s}.yii-debug a:hover,.yii-debug a:focus{color:var(--yii-debug-panel-link-hover);text-decoration:underline}.yii-debug code,.yii-debug kbd,.yii-debug samp{border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-code-bg);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-variant-ligatures:none;padding:.1em .35em;font-size:.875em}.yii-debug pre{margin:0 0 var(--yii-debug-space-3);padding:var(--yii-debug-space-3);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-code-bg);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);font-variant-ligatures:none;line-height:1.4;overflow-x:auto}.yii-debug pre code{font-size:inherit;background:0 0;padding:0}.yii-debug :focus-visible{outline:3px solid var(--yii-debug-focus-ring);outline-offset:3px}.yii-debug .yii-debug-page{max-width:1680px;padding:var(--yii-debug-space-4);margin:0 auto}.yii-debug .yii-debug-layout{gap:var(--yii-debug-space-4);grid-template-columns:240px 1fr;align-items:start;display:grid}@media (max-width:959px){.yii-debug .yii-debug-layout{grid-template-columns:1fr}}@media (min-width:960px){.yii-debug .yii-debug-sidebar{top:var(--yii-debug-space-4);max-height:calc(100vh - var(--yii-debug-space-5));position:sticky;overflow-y:auto}}.yii-debug .yii-debug-main{min-width:0;container:yii-debug-main/inline-size}.yii-debug .yii-debug-main>*+:is(h2,.yii-debug-disclosure),.yii-debug .yii-debug-tab-panel>.yii-debug-table-wrap+.yii-debug-disclosure{margin-top:var(--yii-debug-space-5)}.yii-debug .yii-debug-main>.yii-debug-sr-only:first-child+:is(h2,.yii-debug-disclosure){margin-top:0}.yii-debug .yii-debug-mini-toolbar{align-items:center;gap:var(--yii-debug-space-2);margin-bottom:var(--yii-debug-space-4);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-side-section{gap:var(--yii-debug-space-2);margin:0 0 var(--yii-debug-space-3);padding:var(--yii-debug-space-3);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);flex-direction:column;display:flex}.yii-debug .yii-debug-side-section-title{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;font-weight:700}.yii-debug .yii-debug-side-section-link{color:var(--yii-debug-panel-link);font-size:var(--yii-debug-fs-sm);text-decoration:none}.yii-debug .yii-debug-side-section-link:hover,.yii-debug .yii-debug-side-section-link:focus{color:var(--yii-debug-panel-link-hover);text-decoration:underline}.yii-debug .yii-debug-request-nav-row{width:100%;margin-top:var(--yii-debug-space-2);grid-template-columns:repeat(4,1fr);gap:6px;display:grid}.yii-debug .yii-debug-btn-icon{justify-content:center;align-items:center;padding:4px 0;font-size:16px;line-height:1;display:inline-flex}.yii-debug .yii-debug-btn-icon svg{width:1em;height:1em}.yii-debug .yii-debug-request-nav .yii-debug-btn{justify-content:center;width:100%}.yii-debug .yii-debug-brand-bar{align-items:center;gap:var(--yii-debug-space-2);margin-bottom:var(--yii-debug-space-3);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:linear-gradient(180deg, color-mix(in srgb, var(--yii-debug-panel-surface) 96%, transparent), color-mix(in srgb, var(--yii-debug-panel-surface-muted) 88%, transparent));box-shadow:var(--yii-debug-shadow-sm);flex-wrap:wrap;padding:8px 12px;display:flex}.yii-debug .yii-debug-brand-chip{border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 70%, transparent);border-radius:var(--yii-debug-radius-pill);background:color-mix(in srgb, var(--yii-debug-panel-surface), transparent 30%);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);min-height:var(--yii-debug-control-min-size);align-items:center;gap:8px;padding:6px 12px;text-decoration:none;transition:border-color .14s,transform .14s,box-shadow .14s;display:inline-flex}.yii-debug a.yii-debug-brand-chip:hover,.yii-debug a.yii-debug-brand-chip:focus{border-color:var(--yii-debug-panel-primary);box-shadow:var(--yii-debug-shadow-sm);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-brand-icon{flex:none;justify-content:center;align-items:center;width:22px;height:22px;display:inline-flex}.yii-debug .yii-debug-brand-icon svg{width:100%;height:100%}.yii-debug .yii-debug-brand-chip-php .yii-debug-brand-icon{width:32px}.yii-debug .yii-debug-brand-chip-php .yii-debug-brand-icon svg{fill:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-brand-label{color:var(--yii-debug-panel-muted);letter-spacing:.06em;text-transform:uppercase;font-weight:600}.yii-debug .yii-debug-brand-value{color:var(--yii-debug-panel-text);font-weight:700}.yii-debug .yii-debug-brand-chip-mem{margin-left:auto}.yii-debug .yii-debug-brand-chip-config{border:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);text-decoration:none;transition:transform .14s,border-color .14s,color .14s,background-color .14s}.yii-debug .yii-debug-brand-chip-config .yii-debug-brand-icon{justify-content:center;align-items:center;width:14px;height:14px;transition:transform .24s;display:inline-flex}.yii-debug .yii-debug-brand-chip-config .yii-debug-brand-icon svg{width:100%;height:100%}.yii-debug .yii-debug-brand-chip-config:hover,.yii-debug .yii-debug-brand-chip-config:focus-visible{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 50%);color:var(--yii-debug-panel-primary-strong);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-brand-chip-config:hover .yii-debug-brand-icon,.yii-debug .yii-debug-brand-chip-config:focus-visible .yii-debug-brand-icon{transform:rotate(45deg)}.yii-debug .yii-debug-brand-chip-config.is-disabled{opacity:.5;cursor:default;pointer-events:none}.yii-debug .yii-debug-brand-chip-config{margin-left:auto}.yii-debug .yii-debug-brand-bar:has(.yii-debug-brand-chip-mem) .yii-debug-brand-chip-config{margin-left:0}.yii-debug .yii-debug-brand-chip-theme{border:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);cursor:pointer;padding:6px 10px;transition:transform .14s,border-color .14s,color .14s,background-color .14s}.yii-debug .yii-debug-brand-chip-theme:hover,.yii-debug .yii-debug-brand-chip-theme:focus-visible{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 50%);color:var(--yii-debug-panel-primary-strong);transform:translateY(-1px)}.yii-debug .yii-debug-brand-chip-theme .yii-debug-brand-icon{justify-content:center;align-items:center;width:16px;height:16px;transition:transform .24s;display:inline-flex}.yii-debug .yii-debug-brand-chip-theme .yii-debug-brand-icon svg{width:100%;height:100%}.yii-debug .yii-debug-brand-chip-theme:hover .yii-debug-brand-icon{transform:rotate(20deg)}.yii-debug .yii-debug-snapshot-line{min-width:0;font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);align-items:baseline;gap:6px;display:flex}.yii-debug .yii-debug-snapshot-method{color:color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-primary-strong)) 80%, var(--yii-debug-panel-text));flex:none;font-weight:700}.yii-debug .yii-debug-snapshot-url{min-width:0;color:var(--yii-debug-panel-text);text-overflow:ellipsis;white-space:nowrap;flex:0 auto;overflow:hidden}.yii-debug .yii-debug-snapshot-meta{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);align-items:center;gap:6px;display:flex}.yii-debug .yii-debug-snapshot-status{border-radius:var(--yii-debug-radius-pill);padding:1px 6px;font-weight:700;display:inline-block}.yii-debug .yii-debug-snapshot-tag{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-pill);padding:0 6px}.yii-debug .yii-debug-request-hero{margin-bottom:var(--yii-debug-space-4);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);box-shadow:var(--yii-debug-shadow-sm)}.yii-debug .yii-debug-request-overview{margin-bottom:var(--yii-debug-space-4);border:var(--yii-debug-border);border-inline-start:4px solid color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)) 76%, var(--yii-debug-panel-text));border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);overflow:hidden}.yii-debug .yii-debug-request-overview-header{gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-4);grid-template-columns:minmax(0,1fr) auto;align-items:center;display:grid}.yii-debug .yii-debug-request-overview-identity{min-width:0}.yii-debug .yii-debug-request-overview-label,.yii-debug .yii-debug-request-overview-status-label,.yii-debug .yii-debug-panel-heading-eyebrow{color:var(--yii-debug-panel-muted);letter-spacing:.09em;text-transform:uppercase;margin-bottom:5px;font-size:.6875rem;font-weight:700;line-height:1.2;display:block}.yii-debug .yii-debug-request-hero-line{align-items:center;gap:var(--yii-debug-space-2);min-width:0;font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-base);display:flex}.yii-debug .yii-debug-request-hero-method{border-radius:var(--yii-debug-radius-pill);letter-spacing:.04em;flex:none;padding:2px 10px;font-weight:700}.yii-debug .yii-debug-request-hero-url{min-width:0;color:var(--yii-debug-panel-text);text-overflow:ellipsis;white-space:nowrap;flex:auto;overflow:hidden}.yii-debug .yii-debug-request-hero-meta{align-items:center;gap:6px var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);flex-wrap:wrap;margin-top:6px;display:flex}.yii-debug .yii-debug-request-hero-meta:empty{display:none}.yii-debug .yii-debug-request-hero-meta-item{align-items:baseline;gap:var(--yii-debug-space-1);display:inline-flex}.yii-debug .yii-debug-request-hero-meta-label{font-family:var(--yii-debug-font-sans);letter-spacing:.07em;text-transform:uppercase;font-size:.625rem;font-weight:700}.yii-debug .yii-debug-request-hero-meta-value{color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-request-overview-status{text-align:right;min-width:72px}.yii-debug .yii-debug-request-overview-status-value{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-lg);letter-spacing:-.03em;padding:3px 9px;line-height:1.2}.yii-debug .yii-debug-request-overview-metrics{border-block:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);grid-template-columns:minmax(0,1fr) minmax(0,2fr) auto;margin:0;display:grid}.yii-debug .yii-debug-request-overview-metric{min-width:0;padding:var(--yii-debug-space-3) var(--yii-debug-space-4)}.yii-debug .yii-debug-request-overview-metric+:is(.yii-debug .yii-debug-request-overview-metric){border-inline-start:var(--yii-debug-border)}.yii-debug .yii-debug-request-overview-metric dt{color:var(--yii-debug-panel-muted);letter-spacing:.07em;text-transform:uppercase;margin-bottom:2px;font-size:.6875rem;font-weight:700}.yii-debug .yii-debug-request-overview-metric dd{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);overflow-wrap:anywhere;margin:0;font-weight:600}.yii-debug .yii-debug-request-overview-meta{align-items:center;gap:var(--yii-debug-space-2) var(--yii-debug-space-4);padding:10px var(--yii-debug-space-4);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-request-overview-meta-item{align-items:baseline;gap:6px;min-width:0;display:inline-flex}.yii-debug .yii-debug-request-overview-meta-label{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);letter-spacing:.07em;text-transform:uppercase;font-size:.625rem;font-weight:700}.yii-debug .yii-debug-request-overview-tag{max-width:34ch;color:var(--yii-debug-panel-text);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.yii-debug .yii-debug-request-overview-flag,.yii-debug .yii-debug-panel-heading-kind{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-pill);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.07em;text-transform:uppercase;padding:2px 8px;font-size:.625rem;font-weight:700;line-height:1.4}.yii-debug .yii-debug-request-routing-error{border-width:1px 0 0 4px;border-radius:0;margin:0}.yii-debug .yii-debug-request-routing-error p{margin:0}.yii-debug .yii-debug-request-tabs{min-width:0}.yii-debug .yii-debug-request-tabs>.yii-debug-tabs{scrollbar-width:thin;flex-wrap:nowrap;overflow-x:auto}.yii-debug .yii-debug-request-tabs .yii-debug-tab{flex:none}.yii-debug .yii-debug-request-tabs .yii-debug-empty-state{padding:var(--yii-debug-space-4)}.yii-debug .yii-debug-request-tabs .yii-debug-empty-state h2{font-size:var(--yii-debug-fs-base);margin:0}.yii-debug .yii-debug-diagnostic-shell{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);overflow:hidden}.yii-debug .yii-debug-diagnostic-header{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);display:flex}.yii-debug .yii-debug-diagnostic-heading-copy{align-items:baseline;gap:var(--yii-debug-space-2) var(--yii-debug-space-3);flex-wrap:wrap;min-width:0;display:flex}.yii-debug .yii-debug-diagnostic-heading-copy h2{font-size:var(--yii-debug-fs-lg);margin:0}.yii-debug .yii-debug-diagnostic-counts{align-items:center;gap:var(--yii-debug-space-2);display:inline-flex}.yii-debug .yii-debug-diagnostic-count,.yii-debug .yii-debug-diagnostic-total,.yii-debug .yii-debug-header-lane-count,.yii-debug .yii-debug-server-group-count{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums}.yii-debug .yii-debug-diagnostic-count{align-items:baseline;gap:var(--yii-debug-space-1);display:inline-flex}.yii-debug .yii-debug-diagnostic-count+.yii-debug-diagnostic-count:before{width:1px;height:12px;background:var(--yii-debug-panel-border-strong);content:"";margin-inline-end:var(--yii-debug-space-1)}.yii-debug .yii-debug-diagnostic-count-value{color:var(--yii-debug-panel-text);font-weight:700}.yii-debug .yii-debug-diagnostic-filter{background:var(--yii-debug-panel-surface);width:min(280px,42%);max-width:none}.yii-debug .yii-debug-header-lanes{grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.yii-debug .yii-debug-header-lane{min-width:0;position:relative}.yii-debug .yii-debug-header-lane+.yii-debug-header-lane{border-inline-start:2px solid color-mix(in srgb, var(--yii-debug-panel-primary) 36%, var(--yii-debug-panel-border))}.yii-debug .yii-debug-header-lane+.yii-debug-header-lane:before{z-index:1;top:25px;border:2px solid var(--yii-debug-panel-surface);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-primary);content:"";width:8px;height:8px;position:absolute;inset-inline-start:-5px}.yii-debug .yii-debug-header-lane-header{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);min-height:58px;padding:10px var(--yii-debug-space-4);border-bottom:var(--yii-debug-border);display:flex}.yii-debug .yii-debug-header-lane-header h3{font-size:var(--yii-debug-fs-base);margin:1px 0 0}.yii-debug .yii-debug-header-direction{color:var(--yii-debug-panel-primary-strong);font-family:var(--yii-debug-font-mono);letter-spacing:.04em;font-size:.6875rem;font-weight:700;display:block}.yii-debug .yii-debug-diagnostic-ledger{margin:0}.yii-debug .yii-debug-diagnostic-row{border-top:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 58%, transparent);grid-template-columns:minmax(132px,.72fr) minmax(0,1.28fr);align-items:start;display:grid}.yii-debug .yii-debug-diagnostic-row:first-child{border-top:0}.yii-debug .yii-debug-diagnostic-row:hover{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-diagnostic-row dt,.yii-debug .yii-debug-diagnostic-row dd{min-width:0;padding:10px var(--yii-debug-space-4);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);line-height:1.5}.yii-debug .yii-debug-diagnostic-row dt{color:var(--yii-debug-panel-muted);overflow-wrap:anywhere;font-weight:600;overflow:hidden}.yii-debug .yii-debug-diagnostic-row dd{border-inline-start:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 45%, transparent);color:var(--yii-debug-panel-text);overflow-wrap:anywhere;white-space:pre-wrap;margin:0}.yii-debug .yii-debug-header-ledger .yii-debug-diagnostic-row:nth-child(2n),.yii-debug .yii-debug-server-ledger .yii-debug-diagnostic-row:nth-child(2n){background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 72%)}:is(.yii-debug .yii-debug-header-ledger .yii-debug-diagnostic-row:nth-child(2n),.yii-debug .yii-debug-server-ledger .yii-debug-diagnostic-row:nth-child(2n)):hover{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-diagnostic-values{gap:6px;display:grid}.yii-debug .yii-debug-diagnostic-value-count{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:.6875rem}.yii-debug .yii-debug-diagnostic-value-list{gap:var(--yii-debug-space-1);margin:0;padding:0;list-style:none;display:grid}.yii-debug .yii-debug-diagnostic-value-list li+li{padding-top:var(--yii-debug-space-1);border-top:1px dashed var(--yii-debug-panel-border)}.yii-debug .yii-debug-diagnostic-empty-value,.yii-debug .yii-debug-diagnostic-lane-empty,.yii-debug .yii-debug-diagnostic-empty-state,.yii-debug .yii-debug-diagnostic-filter-empty{color:var(--yii-debug-panel-muted);font-style:italic}.yii-debug .yii-debug-diagnostic-lane-empty,.yii-debug .yii-debug-diagnostic-empty-state,.yii-debug .yii-debug-diagnostic-filter-empty{padding:var(--yii-debug-space-4);margin:0}.yii-debug .yii-debug-diagnostic-filter-empty{border-top:var(--yii-debug-border);text-align:center}.yii-debug .yii-debug-server-group-body>.yii-debug-mini-toolbar{padding:var(--yii-debug-space-2) var(--yii-debug-space-4);border-bottom:var(--yii-debug-border);justify-content:flex-end;margin:0}.yii-debug .yii-debug-server-group-body>.yii-debug-mini-toolbar .yii-debug-filter-input{max-width:100%}.yii-debug .yii-debug-server-additional+.yii-debug-server-group,.yii-debug .yii-debug-server-group+.yii-debug-server-group{border-top:var(--yii-debug-border)}.yii-debug .yii-debug-server-group-summary{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);min-height:44px;padding:9px var(--yii-debug-space-4);border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);display:flex}.yii-debug .yii-debug-server-group-summary .yii-debug-server-group-title{font-size:var(--yii-debug-fs-sm);margin:0;font-weight:700}.yii-debug .yii-debug-server-group-summary{cursor:pointer;list-style:none}.yii-debug .yii-debug-server-group-summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-server-group-summary:hover,.yii-debug .yii-debug-server-group-summary:focus-visible{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-server-group-identity{align-items:baseline;gap:var(--yii-debug-space-2);display:inline-flex}.yii-debug .yii-debug-server-group:not([open])>.yii-debug-server-group-summary{border-bottom:0}.yii-debug .yii-debug-server-group-summary .yii-debug-disclosure-hint{margin-inline-start:auto}.yii-debug .yii-debug-diagnostic-row[hidden],.yii-debug .yii-debug-header-lane[hidden],.yii-debug .yii-debug-server-group[hidden],.yii-debug .yii-debug-diagnostic-filter-empty[hidden]{display:none}@container yii-debug-main (width<=900px){.yii-debug .yii-debug-header-lanes{grid-template-columns:minmax(0,1fr)}.yii-debug .yii-debug-header-lane+.yii-debug-header-lane{border-top:2px solid color-mix(in srgb, var(--yii-debug-panel-primary) 36%, var(--yii-debug-panel-border));border-inline-start:0}.yii-debug .yii-debug-header-lane+.yii-debug-header-lane:before{top:-5px;inset-inline-start:var(--yii-debug-space-4)}}@container yii-debug-main (width<=560px){.yii-debug .yii-debug-diagnostic-header{flex-direction:column;align-items:stretch}.yii-debug .yii-debug-diagnostic-filter{width:100%}.yii-debug .yii-debug-diagnostic-row{grid-template-columns:minmax(0,1fr)}.yii-debug .yii-debug-diagnostic-row dt{padding-bottom:var(--yii-debug-space-1)}.yii-debug .yii-debug-diagnostic-row dd{border-inline-start:0;padding-top:0}}.yii-debug .yii-debug-route-inventory-header{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-2);display:flex}.yii-debug .yii-debug-route-inventory-title{align-items:baseline;gap:var(--yii-debug-space-2);display:flex}.yii-debug .yii-debug-route-inventory-title h2{font-size:var(--yii-debug-fs-lg);margin:0}.yii-debug .yii-debug-route-inventory-count{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums}.yii-debug .yii-debug-route-inventory-context{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-3);display:flex}.yii-debug .yii-debug-route-inventory-provenance{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);margin:0}.yii-debug .yii-debug-route-inventory-badges,.yii-debug .yii-debug-route-methods{align-items:center;gap:var(--yii-debug-space-1);flex-wrap:wrap;display:inline-flex}.yii-debug .yii-debug-route-inventory-error{margin-bottom:var(--yii-debug-space-3)}.yii-debug .yii-debug-route-inventory-error p{margin:0}.yii-debug .yii-debug-route-ledger{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);overflow:hidden;container:routes/inline-size}.yii-debug .yii-debug-route-columns,.yii-debug .yii-debug-route-summary{align-items:center;gap:var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);grid-template-columns:24px 88px minmax(0,1fr) minmax(0,1fr) 132px;display:grid}.yii-debug .yii-debug-route-columns{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);font-weight:600}.yii-debug .yii-debug-route-list{margin:0;padding:0;list-style:none}.yii-debug .yii-debug-route-list>li{border-top:var(--yii-debug-border)}.yii-debug .yii-debug-route-list>[data-yii-debug-route-match=true]{box-shadow:inset 3px 0 0 var(--yii-debug-success)}.yii-debug .yii-debug-route-summary{cursor:pointer;min-height:56px;list-style:none}.yii-debug .yii-debug-route-summary:hover{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-route-summary:focus-visible{outline:2px solid var(--yii-debug-focus-ring);outline-offset:-3px}.yii-debug .yii-debug-route-summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-route-summary .yii-debug-disclosure-hint{text-align:right;font-size:.625rem}.yii-debug .yii-debug-route-order{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums}.yii-debug .yii-debug-route-method{border-radius:var(--yii-debug-radius-pill);min-height:20px;font-family:var(--yii-debug-font-mono);letter-spacing:.02em;white-space:nowrap;align-items:center;padding:1px 7px;font-size:.6875rem;font-weight:700;line-height:1;display:inline-flex}.yii-debug .yii-debug-route-methods-any{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-route-pattern,.yii-debug .yii-debug-route-identity{overflow-wrap:anywhere;white-space:normal;min-width:0;font-size:var(--yii-debug-fs-xs)}:is(.yii-debug .yii-debug-route-pattern,.yii-debug .yii-debug-route-identity) code{white-space:normal}.yii-debug .yii-debug-route-pattern,.yii-debug .yii-debug-route-identity code{background:0 0;padding:0}.yii-debug .yii-debug-route-identity{align-items:center;gap:var(--yii-debug-space-2);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-route-metadata{padding:var(--yii-debug-space-4);border-top:var(--yii-debug-border);background:var(--yii-debug-panel-surface);font-size:var(--yii-debug-fs-sm);margin:0}.yii-debug .yii-debug-route-metadata>div{gap:var(--yii-debug-space-4);grid-template-columns:120px minmax(0,1fr);display:grid}.yii-debug .yii-debug-route-metadata>div+div{margin-top:var(--yii-debug-space-3)}.yii-debug .yii-debug-route-metadata dt{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-route-metadata dd{overflow-wrap:anywhere;min-width:0;font-family:var(--yii-debug-font-mono);margin:0}@container routes (width<640px){.yii-debug .yii-debug-route-columns{display:none}.yii-debug .yii-debug-route-summary{gap:var(--yii-debug-space-2);grid-template-columns:24px 72px minmax(0,1fr)}.yii-debug .yii-debug-route-identity{grid-column:3}.yii-debug .yii-debug-route-summary .yii-debug-disclosure-hint{text-align:left;grid-column:3}.yii-debug .yii-debug-route-metadata>div{gap:var(--yii-debug-space-1);grid-template-columns:minmax(0,1fr)}}.yii-debug .yii-debug-route-resolution{margin-top:var(--yii-debug-space-4)}.yii-debug .yii-debug-route-resolution-message{margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-sm)}.yii-debug .yii-debug-route-trace{min-width:560px}.yii-debug .yii-debug-panel-heading{justify-content:space-between;align-items:flex-end;gap:var(--yii-debug-space-3);margin:var(--yii-debug-space-5) 0 var(--yii-debug-space-3);padding-bottom:var(--yii-debug-space-2);border-bottom:var(--yii-debug-border);display:flex}.yii-debug .yii-debug-panel-heading-copy{min-width:0}.yii-debug .yii-debug-panel-heading-copy h2{margin:0}.yii-debug .yii-debug-panel-heading-kind{flex:none;margin-bottom:2px}.yii-debug pre.yii-debug-panel-payload{min-height:180px;padding:var(--yii-debug-space-4);border:var(--yii-debug-border);background:linear-gradient(90deg, color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%), transparent 42%), var(--yii-debug-panel-code-bg);margin-bottom:0;line-height:1.55}@media (max-width:640px){.yii-debug .yii-debug-request-overview-header{gap:var(--yii-debug-space-3);grid-template-columns:minmax(0,1fr)}.yii-debug .yii-debug-request-overview-status{align-items:center;gap:var(--yii-debug-space-2);text-align:left;display:flex}.yii-debug .yii-debug-request-overview-status-label{margin:0}.yii-debug .yii-debug-request-overview-metrics{grid-template-columns:1fr}.yii-debug .yii-debug-request-overview-metric+.yii-debug-request-overview-metric{border-block-start:var(--yii-debug-border);border-inline-start:0}.yii-debug .yii-debug-request-overview-meta-item{max-width:100%}.yii-debug .yii-debug-panel-heading{align-items:flex-start}.yii-debug .yii-debug-route-inventory-header,.yii-debug .yii-debug-route-inventory-context{flex-direction:column;align-items:flex-start}.yii-debug .yii-debug-route-filter{width:100%;max-width:none}}.yii-debug .yii-debug-section-header{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);margin:var(--yii-debug-space-4) 0 var(--yii-debug-space-2);display:flex}.yii-debug .yii-debug-section-header h2,.yii-debug .yii-debug-section-header h3{margin:0}.yii-debug .yii-debug-filter-input{width:240px;max-width:50%;padding:4px var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font:inherit;flex:none}.yii-debug .yii-debug-filter-input::placeholder{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-filter-input:focus{border-color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-stack>*+*{margin-top:var(--yii-debug-space-4)}.yii-debug .yii-debug-grid-2{gap:var(--yii-debug-space-4);grid-template-columns:repeat(auto-fit,minmax(280px,1fr));display:grid}.yii-debug .yii-debug-card{padding:var(--yii-debug-panel-padding);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-md)}.yii-debug .yii-debug-nav{padding:var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);margin:0}.yii-debug .yii-debug-nav ul{flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-nav-group{margin-top:var(--yii-debug-space-3)}.yii-debug .yii-debug-nav-group>.yii-debug-nav{box-shadow:none;background:0 0;border:0;border-radius:0;padding:0}.yii-debug .yii-debug-nav-link{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-2);border-radius:var(--yii-debug-radius-md);color:var(--yii-debug-panel-text);padding:8px 12px;font-weight:600;text-decoration:none;transition:background .12s,color .12s;display:flex}.yii-debug .yii-debug-nav-link:after{content:"›";color:var(--yii-debug-panel-muted);font-size:1.1em;line-height:1}.yii-debug .yii-debug-nav-link:hover,.yii-debug .yii-debug-nav-link:focus{background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-text);text-decoration:none}.yii-debug .yii-debug-nav-link.is-active,.yii-debug .yii-debug-nav-link[aria-current=page]{background:var(--yii-debug-primary-bg);color:var(--yii-debug-on-primary)}:is(.yii-debug .yii-debug-nav-link.is-active,.yii-debug .yii-debug-nav-link[aria-current=page]):after{color:inherit}.yii-debug .yii-debug-btn{justify-content:center;align-items:center;gap:var(--yii-debug-space-1);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);min-height:32px;color:var(--yii-debug-panel-text);font-family:inherit;font-size:var(--yii-debug-fs-sm);letter-spacing:0;text-transform:none;cursor:pointer;padding:6px 14px;font-weight:600;line-height:1;text-decoration:none;transition:border-color .12s,background .12s,color .12s,transform .12s,box-shadow .12s;display:inline-flex}.yii-debug .yii-debug-btn:hover,.yii-debug .yii-debug-btn:focus{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 30%);background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-link-hover);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-btn:active{transform:translateY(0)}.yii-debug .yii-debug-btn[disabled],.yii-debug .yii-debug-btn.is-disabled{opacity:.55;pointer-events:none;transform:none}.yii-debug .yii-debug-btn-primary{background:var(--yii-debug-primary-bg);color:var(--yii-debug-on-primary);border-color:#0000}.yii-debug .yii-debug-btn-primary:is(:hover,:focus){background:var(--yii-debug-panel-primary-strong);color:var(--yii-debug-on-primary)}.yii-debug .yii-debug-btn-ghost{border-color:var(--yii-debug-panel-border);background:0 0}.yii-debug .yii-debug-btn-subtle{background:var(--yii-debug-panel-surface-muted);border-color:#0000}.yii-debug .yii-debug-btn-sm{min-height:28px;font-size:var(--yii-debug-fs-xs);padding:4px 10px}.yii-debug .yii-debug-btn-group{gap:var(--yii-debug-space-1);flex-wrap:wrap;display:inline-flex}.yii-debug .yii-debug-badge{border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-xs);letter-spacing:.01em;align-items:center;padding:2px 10px;font-weight:600;line-height:1.4;display:inline-flex}.yii-debug .yii-debug-badge-success{background:var(--yii-debug-success);color:var(--yii-debug-on-success)}.yii-debug .yii-debug-badge-info{background:var(--yii-debug-info);color:var(--yii-debug-on-info)}.yii-debug .yii-debug-badge-warning{background:var(--yii-debug-warning);color:var(--yii-debug-on-warning)}.yii-debug .yii-debug-badge-danger{background:var(--yii-debug-danger);color:var(--yii-debug-on-danger)}.yii-debug .yii-debug-badge-muted{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-verb-get{--yii-debug-hue:var(--yii-debug-verb-get)}.yii-debug .yii-debug-verb-post{--yii-debug-hue:var(--yii-debug-verb-post)}.yii-debug .yii-debug-verb-put{--yii-debug-hue:var(--yii-debug-verb-put)}.yii-debug .yii-debug-verb-delete{--yii-debug-hue:var(--yii-debug-verb-delete)}.yii-debug .yii-debug-verb-other{--yii-debug-hue:var(--yii-debug-verb-other)}.yii-debug .yii-debug-status-2xx{--yii-debug-hue:var(--yii-debug-status-2xx)}.yii-debug .yii-debug-status-3xx{--yii-debug-hue:var(--yii-debug-status-3xx)}.yii-debug .yii-debug-status-4xx{--yii-debug-hue:var(--yii-debug-status-4xx)}.yii-debug .yii-debug-status-5xx{--yii-debug-hue:var(--yii-debug-status-5xx)}.yii-debug .yii-debug-status-none{--yii-debug-hue:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-level-error{--yii-debug-hue:var(--yii-debug-level-error)}.yii-debug .yii-debug-level-warning{--yii-debug-hue:var(--yii-debug-level-warning)}.yii-debug .yii-debug-level-info{--yii-debug-hue:var(--yii-debug-level-info)}.yii-debug .yii-debug-level-trace{--yii-debug-hue:var(--yii-debug-level-trace)}.yii-debug .yii-debug-level-profile{--yii-debug-hue:var(--yii-debug-level-profile)}.yii-debug .yii-debug-level-other{--yii-debug-hue:var(--yii-debug-level-other)}.yii-debug .yii-debug-request-hero-method,.yii-debug .yii-debug-route-method,.yii-debug .yii-debug-snapshot-status,.yii-debug .yii-debug-level-chip,.yii-debug .yii-debug-db-type,.yii-debug .yii-debug-mail-status,.yii-debug .yii-debug-dump-type,.yii-debug .yii-debug-badge:is(.yii-debug-status-2xx,.yii-debug-status-3xx,.yii-debug-status-4xx,.yii-debug-status-5xx,.yii-debug-status-none){border:1px solid color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)), transparent 65%);background:color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)), transparent 90%);color:color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)) 80%, var(--yii-debug-panel-text))}.yii-debug .yii-debug-method{color:color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)) 80%, var(--yii-debug-panel-text));font-family:var(--yii-debug-font-mono);letter-spacing:.02em;font-weight:700}.yii-debug .yii-debug-level-chip{border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;padding:1px 8px;font-size:.7rem;font-weight:700;display:inline-block}.yii-debug .yii-debug-callout{align-items:flex-start;gap:var(--yii-debug-space-2);margin-bottom:var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:var(--yii-debug-border);border-left:4px solid var(--yii-debug-panel-border-strong);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);display:flex}.yii-debug .yii-debug-callout-info{border-left-color:var(--yii-debug-info)}.yii-debug .yii-debug-callout-success{border-left-color:var(--yii-debug-success)}.yii-debug .yii-debug-callout-warning{border-left-color:var(--yii-debug-warning)}.yii-debug .yii-debug-callout-danger{border-left-color:var(--yii-debug-danger)}.yii-debug .yii-debug-router-callout{flex-direction:column}.yii-debug .yii-debug-router-callout-message{margin:0}.yii-debug .yii-debug-router-summary{gap:4px var(--yii-debug-space-3);margin:0 0 var(--yii-debug-space-3);font-size:var(--yii-debug-fs-sm);grid-template-columns:max-content minmax(0,1fr);display:grid}.yii-debug .yii-debug-router-summary dt{color:var(--yii-debug-panel-muted);letter-spacing:.04em;text-transform:uppercase;font-weight:600;font-size:var(--yii-debug-fs-xs);align-self:center}.yii-debug .yii-debug-router-summary dd{overflow-wrap:anywhere;min-width:0;margin:0}.yii-debug .yii-debug-router-summary code{border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface);min-width:0;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);overflow-wrap:anywhere;padding:1px 8px;display:inline}.yii-debug .yii-debug-table-wrap{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);overflow-x:auto}.yii-debug .yii-debug-table{border-collapse:separate;border-spacing:0;width:100%;min-width:640px;color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);background:0 0;margin:0}.yii-debug .yii-debug-table thead th,.yii-debug .yii-debug-table thead td{border-bottom:var(--yii-debug-border);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 20%);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);letter-spacing:.03em;text-align:left;text-transform:uppercase;vertical-align:middle;padding:10px 12px;font-weight:700}.yii-debug .yii-debug-table thead th a{color:var(--yii-debug-panel-link);font-weight:700;text-decoration:none}.yii-debug .yii-debug-table thead th a:hover,.yii-debug .yii-debug-table thead th a:focus{color:var(--yii-debug-panel-link-hover);text-decoration:none}.yii-debug .yii-debug-table tbody td,.yii-debug .yii-debug-table tbody th{padding:var(--yii-debug-row-padding-block) 12px;border-top:1px solid var(--yii-debug-panel-border);vertical-align:top;overflow-wrap:anywhere}.yii-debug .yii-debug-table tbody th{white-space:nowrap}.yii-debug .yii-debug-table tbody tr:first-child td,.yii-debug .yii-debug-table tbody tr:first-child th{border-top:0}.yii-debug .yii-debug-table tbody tr:nth-child(2n) td,.yii-debug .yii-debug-table tbody tr:nth-child(2n) th{background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 50%)}.yii-debug .yii-debug-table tbody tr:hover td,.yii-debug .yii-debug-table tbody tr:hover th{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-table tfoot td{border-top:var(--yii-debug-border);color:var(--yii-debug-panel-muted);padding:10px 12px}.yii-debug .yii-debug-row-success td,.yii-debug .yii-debug-row-success th{background:color-mix(in srgb, var(--yii-debug-success), var(--yii-debug-panel-surface) 86%)!important}.yii-debug .yii-debug-row-info td,.yii-debug .yii-debug-row-info th{background:color-mix(in srgb, var(--yii-debug-info), var(--yii-debug-panel-surface) 86%)!important}.yii-debug .yii-debug-row-warning td,.yii-debug .yii-debug-row-warning th{background:color-mix(in srgb, var(--yii-debug-warning), var(--yii-debug-panel-surface) 86%)!important}.yii-debug .yii-debug-row-danger td,.yii-debug .yii-debug-row-danger th{background:color-mix(in srgb, var(--yii-debug-danger), var(--yii-debug-panel-surface) 86%)!important}.yii-debug .yii-debug-grid{margin-bottom:var(--yii-debug-space-4)}.yii-debug .yii-debug-grid>.summary{margin:0 0 var(--yii-debug-space-2);padding:0 var(--yii-debug-space-1);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-sm)}.yii-debug .yii-debug-grid>.summary b{color:var(--yii-debug-panel-text);font-weight:700}.yii-debug .yii-debug-grid>.empty{padding:var(--yii-debug-space-5);color:var(--yii-debug-panel-muted);text-align:center;font-style:italic}.yii-debug .yii-debug-grid td.yii-debug-cell-nowrap,.yii-debug .yii-debug-grid td.yii-debug-cell-numeric,.yii-debug .yii-debug-grid td.yii-debug-cell-pill,.yii-debug .yii-debug-table td.yii-debug-cell-nowrap,.yii-debug .yii-debug-table td.yii-debug-cell-numeric,.yii-debug .yii-debug-table td.yii-debug-cell-pill{white-space:nowrap}.yii-debug .yii-debug-grid td.yii-debug-cell-fqcn{overflow-wrap:normal}.yii-debug .yii-debug-disclosure{margin-bottom:var(--yii-debug-space-5);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);overflow:hidden}.yii-debug .yii-debug-disclosure[open]>.yii-debug-disclosure-summary{border-bottom:var(--yii-debug-border)}.yii-debug .yii-debug-disclosure-summary{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);padding:10px var(--yii-debug-space-4);cursor:pointer;list-style:none;transition:background-color .12s;display:flex}.yii-debug .yii-debug-disclosure-summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-disclosure-summary:hover,.yii-debug .yii-debug-disclosure-summary:focus-visible{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-disclosure-title,.yii-debug .yii-debug-request-section-title{color:var(--yii-debug-panel-primary-strong);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.12em;text-transform:uppercase;font-weight:700}.yii-debug .yii-debug-disclosure-hint{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;transition:color .12s}.yii-debug .yii-debug-disclosure-hint [data-yii-debug-hint=expanded]{display:none}.yii-debug .yii-debug-disclosure-summary:hover .yii-debug-disclosure-hint,.yii-debug .yii-debug-disclosure-summary:focus-visible .yii-debug-disclosure-hint{color:var(--yii-debug-panel-link-hover)}.yii-debug details[open]>summary .yii-debug-disclosure-hint [data-yii-debug-hint=collapsed]{display:none}.yii-debug details[open]>summary .yii-debug-disclosure-hint [data-yii-debug-hint=expanded]{display:inline}.yii-debug .yii-debug-disclosure-body{padding:var(--yii-debug-space-4)}.yii-debug .yii-debug-disclosure-body>pre{overflow-wrap:anywhere;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);white-space:pre-wrap;margin:0;line-height:1.6}.yii-debug .yii-debug-cell-more-body{max-height:156px;position:relative;overflow:hidden}.yii-debug .yii-debug-cell-more-body:has(.yii-debug-table){max-height:420px}.yii-debug .yii-debug-cell-more-body:after{content:"";background:linear-gradient(to bottom, transparent, var(--yii-debug-panel-surface));pointer-events:none;height:32px;position:absolute;inset:auto 0 0}.yii-debug .yii-debug-cell-more.is-open .yii-debug-cell-more-body{max-height:none}.yii-debug .yii-debug-cell-more.is-open .yii-debug-cell-more-body:after{content:none}.yii-debug .yii-debug-cell-more-toggle{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);color:var(--yii-debug-panel-link);font-family:inherit;font-size:var(--yii-debug-fs-xs);cursor:pointer;background:0 0;align-items:center;margin-top:6px;padding:2px 10px;font-weight:600;display:inline-flex}.yii-debug .yii-debug-cell-more-toggle:before{content:"";width:1.25em;height:1.25em;margin-right:var(--yii-debug-space-1);background:linear-gradient(currentcolor, currentcolor) center / 8px 2px no-repeat, linear-gradient(currentcolor, currentcolor) center / 2px 8px no-repeat, var(--yii-debug-panel-bg-accent);border-radius:50%;display:inline-block}.yii-debug .yii-debug-cell-more.is-open .yii-debug-cell-more-toggle:before{background:linear-gradient(currentcolor, currentcolor) center / 8px 2px no-repeat, var(--yii-debug-panel-bg-accent)}.yii-debug .yii-debug-cell-more-toggle:hover,.yii-debug .yii-debug-cell-more-toggle:focus{background:var(--yii-debug-panel-bg-accent);border-color:var(--yii-debug-panel-primary);text-decoration:none}.yii-debug .yii-debug-grid td.yii-debug-cell-mono{font-family:var(--yii-debug-font-mono)}.yii-debug td.yii-debug-cell-payload{overflow-wrap:anywhere;width:100%}.yii-debug .yii-debug-grid td.yii-debug-cell-numeric{font-variant-numeric:tabular-nums;text-align:right}.yii-debug a.asc:after,.yii-debug a.desc:after,.yii-debug .yii-debug-sort-asc:after,.yii-debug .yii-debug-sort-desc:after{content:"↑";opacity:.78;margin-left:4px;font-size:.85em}.yii-debug a.desc:after,.yii-debug .yii-debug-sort-desc:after{content:"↓"}.yii-debug .yii-debug-pager{gap:var(--yii-debug-space-1);margin:var(--yii-debug-space-3) 0 0;flex-wrap:wrap;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-pager-item{display:inline-flex}.yii-debug .yii-debug-pager-link,.yii-debug .yii-debug-pager>li>:is(a,span){border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);min-width:40px;min-height:40px;color:var(--yii-debug-panel-link);font-size:var(--yii-debug-fs-sm);justify-content:center;align-items:center;padding:0 10px;font-weight:600;text-decoration:none;transition:border-color .12s,background .12s,color .12s;display:inline-flex}:is(.yii-debug .yii-debug-pager-link,.yii-debug .yii-debug-pager>li>:is(a,span)):hover,:is(.yii-debug .yii-debug-pager-link,.yii-debug .yii-debug-pager>li>:is(a,span)):focus{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 30%);background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-link-hover);text-decoration:none}.yii-debug .yii-debug-pager-item.is-active .yii-debug-pager-link{background:var(--yii-debug-primary-bg);color:var(--yii-debug-on-primary);cursor:default;border-color:#0000}.yii-debug .yii-debug-pager-item.is-disabled .yii-debug-pager-link{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);cursor:not-allowed;pointer-events:none}.yii-debug .yii-debug-field{margin-bottom:var(--yii-debug-space-3);flex-direction:column;gap:4px;display:flex}.yii-debug .yii-debug-field-grid{gap:var(--yii-debug-space-3) var(--yii-debug-space-4);margin-bottom:var(--yii-debug-space-3);grid-template-columns:repeat(auto-fit,minmax(240px,1fr));display:grid}.yii-debug .yii-debug-label{color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);font-weight:600}.yii-debug .yii-debug-input,.yii-debug .yii-debug-select,.yii-debug .yii-debug-textarea{border:1px solid var(--yii-debug-panel-border-strong);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);width:100%;min-height:32px;color:var(--yii-debug-panel-text);font-family:inherit;font-size:var(--yii-debug-fs-sm);padding:6px 10px;transition:border-color .12s,box-shadow .12s}:is(.yii-debug .yii-debug-input,.yii-debug .yii-debug-select,.yii-debug .yii-debug-textarea):hover{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), var(--yii-debug-panel-border-strong) 60%)}:is(.yii-debug .yii-debug-input,.yii-debug .yii-debug-select,.yii-debug .yii-debug-textarea):focus{border-color:var(--yii-debug-panel-primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 82%)}:is(.yii-debug .yii-debug-input,.yii-debug .yii-debug-select,.yii-debug .yii-debug-textarea)::placeholder{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-textarea{resize:vertical;min-height:96px;line-height:1.45}.yii-debug .yii-debug-grid .filters td,.yii-debug .yii-debug-grid td.yii-debug-filter-cell{border-bottom:var(--yii-debug-border);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 30%);padding:6px 12px}.yii-debug .yii-debug-grid .filters input,.yii-debug .yii-debug-grid .filters select,.yii-debug .yii-debug-grid .filters textarea,.yii-debug .yii-debug-grid td.yii-debug-filter-cell input,.yii-debug .yii-debug-grid td.yii-debug-filter-cell select,.yii-debug .yii-debug-grid td.yii-debug-filter-cell textarea{border:1px solid var(--yii-debug-panel-border-strong);border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface);width:100%;min-width:0;min-height:28px;color:var(--yii-debug-panel-text);font:inherit;font-size:var(--yii-debug-fs-xs);padding:4px 8px}:is(.yii-debug .yii-debug-grid .filters input,.yii-debug .yii-debug-grid .filters select,.yii-debug .yii-debug-grid .filters textarea,.yii-debug .yii-debug-grid td.yii-debug-filter-cell input,.yii-debug .yii-debug-grid td.yii-debug-filter-cell select,.yii-debug .yii-debug-grid td.yii-debug-filter-cell textarea):focus{border-color:var(--yii-debug-panel-primary);box-shadow:0 0 0 2px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 84%)}.yii-debug .yii-debug-tabs{gap:var(--yii-debug-space-1);margin:0 0 var(--yii-debug-space-3);border-bottom:var(--yii-debug-border);flex-wrap:wrap;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-tab{display:inline-flex}.yii-debug .yii-debug-tab-link{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-sm);border-bottom:2px solid #0000;align-items:center;padding:8px 14px;font-weight:600;text-decoration:none;transition:color .12s,border-color .12s;display:inline-flex}.yii-debug .yii-debug-tab-link:hover,.yii-debug .yii-debug-tab-link:focus{color:var(--yii-debug-panel-text);text-decoration:none}.yii-debug .yii-debug-tab.is-active .yii-debug-tab-link,.yii-debug .yii-debug-tab-link.is-active{border-bottom-color:var(--yii-debug-panel-primary);color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-tab-panel{padding:var(--yii-debug-space-3) 0 0;display:none}.yii-debug .yii-debug-tab-panel.is-active{display:block}.yii-debug .yii-debug-collapsible{margin-top:var(--yii-debug-space-3);display:none}.yii-debug .yii-debug-collapsible.is-open{display:block}.yii-debug .yii-debug-dropdown{display:inline-block;position:relative}.yii-debug .yii-debug-dropdown-menu{z-index:100;border:var(--yii-debug-border-strong);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);min-width:220px;box-shadow:var(--yii-debug-shadow-md);margin:0;padding:4px;list-style:none;display:none;position:absolute;top:calc(100% + 4px);left:0}.yii-debug .yii-debug-dropdown.is-open .yii-debug-dropdown-menu{display:block}.yii-debug .yii-debug-dropdown-item{border-radius:var(--yii-debug-radius-sm);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);padding:6px 10px;text-decoration:none;display:block}.yii-debug .yii-debug-dropdown-item:hover,.yii-debug .yii-debug-dropdown-item:focus{background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-link-hover);text-decoration:none}.yii-debug .yii-debug-since-previous{white-space:nowrap;align-items:center;gap:4px;display:inline-flex}.yii-debug .yii-debug-since-previous .yii-debug-since-previous-btn{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface);min-width:24px;height:24px;color:var(--yii-debug-panel-link);font-size:var(--yii-debug-fs-xs);justify-content:center;align-items:center;padding:0 6px;font-weight:700;text-decoration:none;display:inline-flex}.yii-debug .yii-debug-table-mono{min-width:0}.yii-debug .yii-debug-table-mono thead th{z-index:1;background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 10%);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.12em;padding:8px 16px;position:sticky;top:0}.yii-debug .yii-debug-table-mono tbody tr{transition:background-color .14s}.yii-debug .yii-debug-table-mono tbody tr:hover td,.yii-debug .yii-debug-table-mono tbody tr:hover th{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-table-mono tbody tr:hover th{box-shadow:inset 3px 0 0 var(--yii-debug-panel-primary);color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-table-mono tbody th,.yii-debug .yii-debug-table-mono tbody td{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);padding:12px 16px;line-height:1.55}.yii-debug .yii-debug-table-mono tbody th{width:1%;min-width:120px;max-width:220px;color:var(--yii-debug-panel-muted);letter-spacing:.02em;white-space:nowrap;text-overflow:ellipsis;font-weight:500;transition:color .14s,box-shadow .14s;overflow:hidden}.yii-debug .yii-debug-table-mono tbody td{color:var(--yii-debug-panel-text);word-break:normal;overflow-wrap:anywhere}.yii-debug .yii-debug-table-mono tbody tr+tr td,.yii-debug .yii-debug-table-mono tbody tr+tr th{border-top:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 55%, transparent)}.yii-debug .yii-debug-table-mono tbody tr:nth-child(2n) td,.yii-debug .yii-debug-table-mono tbody tr:nth-child(2n) th{background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 70%)}.yii-debug .yii-debug-table-vite-overview tbody th{width:200px;min-width:160px;max-width:200px}.yii-debug .yii-debug-vite-component+.yii-debug-vite-component{margin-top:var(--yii-debug-space-6)}.yii-debug .yii-debug-table-wrap:has(>.yii-debug-table-mono){border-radius:var(--yii-debug-radius-md);box-shadow:var(--yii-debug-shadow-sm), inset 0 0 0 1px color-mix(in srgb, var(--yii-debug-panel-border) 60%, transparent);overflow:clip}.yii-debug .yii-debug-table-mono{min-width:0}.yii-debug .yii-debug-table-mono tbody th{overflow-wrap:anywhere;white-space:normal}.yii-debug .yii-debug-table-userswitch{width:auto;min-width:100%}.yii-debug .yii-debug-table-userswitch thead th,.yii-debug .yii-debug-table-userswitch tbody td,.yii-debug .yii-debug-table-userswitch tbody th{overflow-wrap:normal;white-space:nowrap}.yii-debug .yii-debug-table-userswitch tbody tr.filters td input,.yii-debug .yii-debug-table-userswitch td.yii-debug-filter-cell input{width:100%;min-width:0}.yii-debug .yii-debug-col-userswitch-id{font-variant-numeric:tabular-nums;min-width:10ch}.yii-debug .yii-debug-col-userswitch-username{min-width:14ch}.yii-debug .yii-debug-col-userswitch-email{min-width:24ch}.yii-debug .yii-debug-col-userswitch-status{text-align:center;min-width:8ch}.yii-debug .yii-debug-col-userswitch-timestamp{font-variant-numeric:tabular-nums;min-width:18ch}.yii-debug .yii-debug-table-pointer tbody tr{cursor:pointer;transition:background-color .12s}.yii-debug .yii-debug-table-pointer tbody tr:hover{background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 86%)}.yii-debug .yii-debug-timeline-duration{margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);letter-spacing:.04em}.yii-debug .yii-debug-user{gap:var(--yii-debug-space-3);flex-direction:column;display:flex}.yii-debug .yii-debug-user-card{gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-4) var(--yii-debug-space-5);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:radial-gradient(circle at top right, color-mix(in srgb, var(--yii-debug-panel-primary), transparent 80%), transparent 60%), var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);grid-template-columns:auto 1fr;align-items:center;display:grid;position:relative;overflow:hidden}.yii-debug .yii-debug-user-avatar{background:linear-gradient(135deg, var(--yii-debug-panel-primary), var(--yii-debug-info));width:64px;height:64px;box-shadow:0 4px 12px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 50%), inset 0 1px 0 #ffffff38;color:#fff;font-family:var(--yii-debug-font-mono);letter-spacing:-.02em;border-radius:50%;flex:none;justify-content:center;align-items:center;font-size:28px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-user-meta{flex-direction:column;gap:4px;min-width:0;display:flex}.yii-debug .yii-debug-user-name{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xl);letter-spacing:-.01em;margin:0;font-weight:700;line-height:1.2}.yii-debug .yii-debug-user-handle{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);margin:0}.yii-debug .yii-debug-user-tags{flex-wrap:wrap;gap:8px;margin-top:6px;display:flex}.yii-debug .yii-debug-user-status{border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;align-items:center;gap:6px;padding:4px 12px 4px 8px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-user-status:before{content:"";background:currentColor;border-radius:50%;width:7px;height:7px}.yii-debug .yii-debug-user-status-success{background:color-mix(in srgb, var(--yii-debug-success), transparent 84%);color:var(--yii-debug-success)}.yii-debug .yii-debug-user-status-success:before{box-shadow:0 0 0 0 color-mix(in srgb, var(--yii-debug-success), transparent 30%);animation:2.4s ease-in-out infinite yii-debug-user-pulse}.yii-debug .yii-debug-user-status-danger{background:color-mix(in srgb, var(--yii-debug-danger), transparent 84%);color:var(--yii-debug-danger)}.yii-debug .yii-debug-user-status-warning{background:color-mix(in srgb, var(--yii-debug-warning), transparent 84%);color:var(--yii-debug-warning)}.yii-debug .yii-debug-user-status-muted{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-user-pill{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-pill);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;align-items:center;padding:4px 10px;display:inline-flex}.yii-debug .yii-debug-user-section{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);overflow:hidden}.yii-debug .yii-debug-user-section>header{padding:10px var(--yii-debug-space-3);border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;align-items:center;gap:10px;font-weight:700;display:flex}.yii-debug .yii-debug-user-section dl{margin:0;padding:0}.yii-debug .yii-debug-user-section-icon{width:18px;height:18px;color:var(--yii-debug-panel-primary);justify-content:center;align-items:center;display:inline-flex}.yii-debug .yii-debug-user-section-icon svg{width:100%;height:100%}.yii-debug .yii-debug-user-row{gap:var(--yii-debug-space-3);padding:12px var(--yii-debug-space-3);border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 50%, transparent);grid-template-columns:minmax(140px,22%) 1fr;display:grid}.yii-debug .yii-debug-user-row:last-child{border-bottom:0}.yii-debug .yii-debug-user-row dt{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em;align-self:center}.yii-debug .yii-debug-user-row dd{min-width:0;margin:0}.yii-debug .yii-debug-user-value{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);word-break:break-all}.yii-debug .yii-debug-user-empty{color:color-mix(in srgb, var(--yii-debug-panel-muted), transparent 35%);font-family:var(--yii-debug-font-mono)}.yii-debug .yii-debug-user-time{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);align-items:baseline;gap:10px;display:inline-flex}.yii-debug .yii-debug-user-time .yii-debug-user-time-rel{color:var(--yii-debug-panel-text);font-weight:600}.yii-debug .yii-debug-user-time .yii-debug-user-time-abs{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-user-reveal{border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 80%, transparent);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);font:inherit;cursor:pointer;text-align:left;align-items:center;gap:12px;padding:6px 6px 6px 14px;transition:border-color .14s,background .14s;display:inline-flex}.yii-debug .yii-debug-user-reveal:hover,.yii-debug .yii-debug-user-reveal:focus{border-color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-user-reveal .yii-debug-user-mask,.yii-debug .yii-debug-user-reveal .yii-debug-user-real{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm)}.yii-debug .yii-debug-user-reveal .yii-debug-user-mask{letter-spacing:.1em;color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-user-reveal .yii-debug-user-real{overflow-wrap:anywhere;max-width:60ch;color:var(--yii-debug-panel-text);display:none}.yii-debug .yii-debug-user-reveal .yii-debug-user-reveal-cta{border-radius:var(--yii-debug-radius-pill);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 80%);color:var(--yii-debug-panel-primary);font-size:var(--yii-debug-fs-xs);letter-spacing:.08em;text-transform:uppercase;flex:none;padding:3px 10px;font-weight:700}.yii-debug .yii-debug-user-reveal .yii-debug-user-reveal-cta:before{content:"Reveal"}.yii-debug .yii-debug-user-reveal.is-revealed .yii-debug-user-mask{display:none}.yii-debug .yii-debug-user-reveal.is-revealed .yii-debug-user-real{display:inline}.yii-debug .yii-debug-user-reveal.is-revealed .yii-debug-user-reveal-cta{background:color-mix(in srgb, var(--yii-debug-warning), transparent 84%);color:var(--yii-debug-warning)}.yii-debug .yii-debug-user-reveal.is-revealed .yii-debug-user-reveal-cta:before{content:"Hide"}.yii-debug .yii-debug-sr-only{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.yii-debug .yii-debug-table-empty{padding:var(--yii-debug-space-4);border:1px dashed color-mix(in srgb, var(--yii-debug-panel-border) 70%, transparent);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 40%);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;justify-content:center;align-items:center;gap:10px;margin:0;display:flex}.yii-debug .yii-debug-table-empty:before{content:"";opacity:.5;border:1.5px solid;border-radius:50%;width:8px;height:8px}.yii-debug .yii-debug-grid-summary{align-items:center;gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);letter-spacing:.06em;text-transform:uppercase;flex-wrap:wrap;display:flex}.yii-debug .yii-debug-grid-summary strong{color:var(--yii-debug-panel-primary-strong);letter-spacing:0;text-transform:none;margin-right:.35em;font-weight:700}.yii-debug .yii-debug-grid-summary-sep{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-grid-summary-stat-warn{--yii-debug-hue:var(--yii-debug-level-warning)}.yii-debug .yii-debug-grid-summary-stat-danger{--yii-debug-hue:var(--yii-debug-level-error)}.yii-debug .yii-debug-grid-summary-stat-info{--yii-debug-hue:var(--yii-debug-level-info)}.yii-debug .yii-debug-grid-summary-stat-trace{--yii-debug-hue:var(--yii-debug-level-trace)}.yii-debug .yii-debug-grid-summary-stat-warn strong{color:var(--yii-debug-warning)}.yii-debug .yii-debug-grid-summary-stat-danger strong{color:var(--yii-debug-danger)}.yii-debug .yii-debug-grid-summary-stat-2xx{--yii-debug-hue:var(--yii-debug-status-2xx)}.yii-debug .yii-debug-grid-summary-stat-3xx{--yii-debug-hue:var(--yii-debug-status-3xx)}.yii-debug .yii-debug-grid-summary-stat-4xx{--yii-debug-hue:var(--yii-debug-status-4xx)}.yii-debug .yii-debug-grid-summary-stat-5xx{--yii-debug-hue:var(--yii-debug-status-5xx)}.yii-debug a:is(.yii-debug-grid-summary-stat-danger,.yii-debug-grid-summary-stat-warn,.yii-debug-grid-summary-stat-info,.yii-debug-grid-summary-stat-trace,.yii-debug-grid-summary-stat-2xx,.yii-debug-grid-summary-stat-3xx,.yii-debug-grid-summary-stat-4xx,.yii-debug-grid-summary-stat-5xx){border:1px solid color-mix(in srgb, var(--yii-debug-hue), transparent 65%);border-radius:var(--yii-debug-radius-pill);background:color-mix(in srgb, var(--yii-debug-hue), transparent 90%);color:color-mix(in srgb, var(--yii-debug-hue) 80%, var(--yii-debug-panel-text));align-items:center;gap:6px;padding:2px 10px;text-decoration:none;transition:transform .14s,background-color .14s;display:inline-flex}.yii-debug a:is(.yii-debug-grid-summary-stat-danger,.yii-debug-grid-summary-stat-warn,.yii-debug-grid-summary-stat-info,.yii-debug-grid-summary-stat-trace,.yii-debug-grid-summary-stat-2xx,.yii-debug-grid-summary-stat-3xx,.yii-debug-grid-summary-stat-4xx,.yii-debug-grid-summary-stat-5xx) strong{color:inherit;margin-right:0}.yii-debug a:is(.yii-debug-grid-summary-stat-danger,.yii-debug-grid-summary-stat-warn,.yii-debug-grid-summary-stat-info,.yii-debug-grid-summary-stat-trace,.yii-debug-grid-summary-stat-2xx,.yii-debug-grid-summary-stat-3xx,.yii-debug-grid-summary-stat-4xx,.yii-debug-grid-summary-stat-5xx):hover,.yii-debug a:is(.yii-debug-grid-summary-stat-danger,.yii-debug-grid-summary-stat-warn,.yii-debug-grid-summary-stat-info,.yii-debug-grid-summary-stat-trace,.yii-debug-grid-summary-stat-2xx,.yii-debug-grid-summary-stat-3xx,.yii-debug-grid-summary-stat-4xx,.yii-debug-grid-summary-stat-5xx):focus-visible{background:color-mix(in srgb, var(--yii-debug-hue), transparent 80%);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-grid-summary-action{color:var(--yii-debug-panel-primary-strong);letter-spacing:0;text-transform:none;margin-left:auto;font-weight:700;text-decoration:none}.yii-debug .yii-debug-grid-summary-action:hover,.yii-debug .yii-debug-grid-summary-action:focus-visible{color:var(--yii-debug-panel-link-hover);text-decoration:underline}.yii-debug .yii-debug-grid-footer{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);margin-top:var(--yii-debug-space-2);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-grid-count{margin:0}.yii-debug .yii-debug-grid-pagesize{align-items:center;gap:6px;margin-left:auto;display:inline-flex}.yii-debug .yii-debug-grid-pagesize-label{color:var(--yii-debug-panel-muted);letter-spacing:.06em;text-transform:uppercase;font-weight:600}.yii-debug .yii-debug-grid-pagesize-select{padding:3px var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-sm);letter-spacing:0;text-transform:none;cursor:pointer}.yii-debug .yii-debug-grid-pagesize-select:focus{border-color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-db-type{border-radius:var(--yii-debug-radius-pill);min-width:5em;font-family:var(--yii-debug-font-mono);letter-spacing:.04em;text-align:center;text-transform:uppercase;white-space:nowrap;padding:2px 8px;font-size:.7rem;font-weight:700;display:inline-block}.yii-debug .yii-debug-db-sql{font-family:var(--yii-debug-font-mono);overflow-wrap:anywhere}.yii-debug .yii-debug-grid-db .yii-debug-table th:not(:last-child),.yii-debug .yii-debug-grid-db .yii-debug-table td:not(:last-child){white-space:nowrap}.yii-debug .yii-debug-grid-db .yii-debug-table th:last-child,.yii-debug .yii-debug-grid-db .yii-debug-table td:last-child{width:100%}.yii-debug .yii-debug-sql-kw{color:var(--yii-debug-sql-kw);font-weight:700}.yii-debug .yii-debug-sql-str{color:var(--yii-debug-sql-str)}.yii-debug .yii-debug-sql-num{color:var(--yii-debug-sql-num)}.yii-debug .yii-debug-sql-comment{color:var(--yii-debug-sql-comment);font-style:italic}.yii-debug .yii-debug-sql-param{color:var(--yii-debug-sql-param);font-weight:600}.yii-debug .yii-debug-db-explain,.yii-debug .yii-debug-db-trace{margin-top:var(--yii-debug-space-2)}.yii-debug .yii-debug-db-explain-toggle,.yii-debug .yii-debug-db-trace-toggle{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);color:var(--yii-debug-panel-link);font-size:var(--yii-debug-fs-xs);cursor:pointer;background:0 0;align-items:center;gap:6px;padding:2px 10px 2px 6px;font-weight:600;text-decoration:none;transition:background-color .12s,border-color .12s;display:inline-flex}.yii-debug .yii-debug-db-explain-toggle:hover,.yii-debug .yii-debug-db-explain-toggle:focus,.yii-debug .yii-debug-db-trace-toggle:hover,.yii-debug .yii-debug-db-trace-toggle:focus-visible{background:var(--yii-debug-panel-bg-accent);border-color:var(--yii-debug-panel-primary);text-decoration:none}.yii-debug .yii-debug-db-trace-toggle{list-style:none}.yii-debug .yii-debug-db-trace-toggle::-webkit-details-marker{display:none}.yii-debug .yii-debug-db-explain-chevron,.yii-debug .yii-debug-db-trace-chevron{width:10px;font-size:14px;line-height:1;transition:transform .16s;display:inline-block}.yii-debug .yii-debug-db-explain.is-open .yii-debug-db-explain-chevron,.yii-debug .yii-debug-db-trace[open]>.yii-debug-db-trace-toggle .yii-debug-db-trace-chevron{transform:rotate(90deg)}.yii-debug .yii-debug-db-explain.is-loading .yii-debug-db-explain-toggle{opacity:.55;pointer-events:none}.yii-debug .yii-debug-db-explain-text{margin-top:var(--yii-debug-space-2);display:none}.yii-debug .yii-debug-db-explain.is-open .yii-debug-db-explain-text{display:block}.yii-debug .yii-debug-db-explain-text.is-error{padding:var(--yii-debug-space-2) var(--yii-debug-space-3);border-left:3px solid var(--yii-debug-danger);border-radius:var(--yii-debug-radius-sm);background:color-mix(in srgb, var(--yii-debug-danger), transparent 92%);color:var(--yii-debug-danger);font-size:var(--yii-debug-fs-sm);font-weight:600;display:block}.yii-debug .yii-debug-db-explain-all{margin-top:var(--yii-debug-space-3);justify-content:flex-end;display:flex}.yii-debug .yii-debug-db-explain-all-toggle{white-space:nowrap;justify-content:center;min-width:104px}.yii-debug .yii-debug-explain{margin-top:var(--yii-debug-space-3)}.yii-debug .yii-debug-explain-title{margin-bottom:var(--yii-debug-space-2);font-size:var(--yii-debug-fs-lg);font-weight:700}.yii-debug .yii-debug-explain-query{margin-bottom:var(--yii-debug-space-3);padding:var(--yii-debug-space-3);background:var(--yii-debug-panel-code-bg);border-radius:var(--yii-debug-radius-md);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);white-space:pre-wrap;overflow-wrap:break-word}.yii-debug .yii-debug-explain-empty{color:var(--yii-debug-panel-muted);font-style:italic}.yii-debug .yii-debug-empty-state{margin:var(--yii-debug-space-4) 0 0;padding:var(--yii-debug-space-5) var(--yii-debug-space-5);border:1px dashed color-mix(in srgb, var(--yii-debug-panel-border) 80%, transparent);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 30%)}.yii-debug .yii-debug-empty-state h2{margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-lg);font-weight:700}.yii-debug .yii-debug-empty-state p{margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);line-height:1.5}.yii-debug .yii-debug-empty-state p:last-child{margin-bottom:0}.yii-debug .yii-debug-empty-state code{border-radius:var(--yii-debug-radius-sm,4px);background:var(--yii-debug-panel-code-bg);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);padding:.1em .4em}.yii-debug .yii-debug-empty-state-code{margin:0 0 var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-code-bg);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);white-space:pre-wrap;overflow-wrap:break-word;line-height:1.5}.yii-debug .yii-debug-mail-list{gap:var(--yii-debug-space-3);flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-mail-list-item{margin:0;padding:0;list-style:none;display:block}.yii-debug .yii-debug-mail-pager{margin-top:var(--yii-debug-space-4)}.yii-debug .yii-debug-mail-card{gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-4) var(--yii-debug-space-4) var(--yii-debug-space-3);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);grid-template-columns:1fr;transition:transform .16s,border-color .16s,box-shadow .16s;display:grid;position:relative}.yii-debug .yii-debug-mail-card:hover{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 60%);box-shadow:var(--yii-debug-shadow-sm), 0 12px 32px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%);transform:translateY(-1px)}.yii-debug .yii-debug-mail-card-head{gap:var(--yii-debug-space-3);grid-template-columns:auto 1fr auto;align-items:flex-start;display:grid}.yii-debug .yii-debug-mail-avatar{--mail-hue:210;background:linear-gradient(135deg, hsl(var(--mail-hue) 72% 52%), hsl(calc(var(--mail-hue) + 28) 65% 40%));width:44px;height:44px;box-shadow:0 2px 10px hsl(var(--mail-hue) 60% 40% / 32%), inset 0 1px 0 #ffffff38;color:#fff;font-family:var(--yii-debug-font-mono);letter-spacing:-.01em;border-radius:50%;flex:none;justify-content:center;align-items:center;font-size:16px;font-weight:800;display:inline-flex}.yii-debug .yii-debug-mail-headline{flex-direction:column;gap:4px;min-width:0;display:flex}.yii-debug .yii-debug-mail-from{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em;text-overflow:ellipsis;white-space:nowrap;margin:0;overflow:hidden}.yii-debug .yii-debug-mail-subject{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-lg);letter-spacing:-.01em;margin:0;font-weight:700;line-height:1.25}.yii-debug .yii-debug-mail-preview{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-sm);-webkit-line-clamp:1;line-clamp:1;-webkit-box-orient:vertical;margin:2px 0 0;line-height:1.4;display:-webkit-box;overflow:hidden}.yii-debug .yii-debug-mail-meta{flex-direction:column;flex:none;align-items:flex-end;gap:6px;display:flex}.yii-debug .yii-debug-mail-status{border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;align-items:center;gap:6px;padding:3px 10px 3px 8px;font-weight:600;line-height:1;display:inline-flex}.yii-debug .yii-debug-mail-status-dot{background:var(--yii-debug-hue,var(--yii-debug-panel-muted));width:7px;height:7px;box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)), transparent 80%);border-radius:50%;flex:none}.yii-debug .yii-debug-mail-status-ok{--yii-debug-hue:var(--yii-debug-success)}.yii-debug .yii-debug-mail-status-fail{--yii-debug-hue:var(--yii-debug-danger)}.yii-debug .yii-debug-mail-time{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em;cursor:help}.yii-debug .yii-debug-mail-download{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);width:30px;height:30px;color:var(--yii-debug-panel-link);justify-content:center;align-items:center;text-decoration:none;transition:background-color .14s,border-color .14s,color .14s,transform .14s;display:inline-flex}.yii-debug .yii-debug-mail-download svg{width:16px;height:16px}.yii-debug .yii-debug-mail-download:hover,.yii-debug .yii-debug-mail-download:focus-visible{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 50%);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%);color:var(--yii-debug-panel-primary-strong);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-mail-recipients{padding:var(--yii-debug-space-3);border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 60%, transparent);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 40%);flex-direction:column;gap:8px;display:flex}.yii-debug .yii-debug-mail-recipient-group{gap:var(--yii-debug-space-3);grid-template-columns:64px 1fr;align-items:center;display:grid}.yii-debug .yii-debug-mail-recipient-label{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.14em;text-align:right;font-weight:700}.yii-debug .yii-debug-mail-recipient-label[data-role=reply]{color:var(--yii-debug-info)}.yii-debug .yii-debug-mail-recipient-label[data-role=bcc]{color:var(--yii-debug-warning)}.yii-debug .yii-debug-mail-recipient-pills{flex-wrap:wrap;gap:6px;min-width:0;display:flex}.yii-debug .yii-debug-mail-recipient-pill{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);max-width:100%;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);text-overflow:ellipsis;white-space:nowrap;align-items:center;padding:3px 10px;line-height:1.4;display:inline-flex;overflow:hidden}.yii-debug .yii-debug-mail-body{padding:var(--yii-debug-space-4);border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 60%, transparent);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);white-space:pre-wrap;overflow-wrap:break-word;line-height:1.55}.yii-debug .yii-debug-mail-body-empty{color:var(--yii-debug-panel-muted);text-align:center;font-style:italic}.yii-debug .yii-debug-mail-tech{border:1px dashed color-mix(in srgb, var(--yii-debug-panel-border) 70%, transparent);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 30%);overflow:hidden}.yii-debug .yii-debug-mail-tech>summary{padding:10px var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;gap:8px;list-style:none;display:flex}.yii-debug .yii-debug-mail-tech>summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-mail-tech>summary:hover{color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-mail-tech[open]>summary{border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 50%, transparent);color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-mail-tech[open] .yii-debug-mail-tech-chevron{transform:rotate(180deg)}.yii-debug .yii-debug-mail-tech-icon{width:14px;height:14px;color:var(--yii-debug-panel-primary);justify-content:center;align-items:center;display:inline-flex}.yii-debug .yii-debug-mail-tech-icon svg{width:100%;height:100%}.yii-debug .yii-debug-mail-tech-label{text-transform:uppercase;font-weight:700}.yii-debug .yii-debug-mail-tech-charset{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:none;margin-left:4px;padding:2px 8px;font-weight:600}.yii-debug .yii-debug-mail-tech-chevron{width:14px;height:14px;color:var(--yii-debug-panel-muted);justify-content:center;align-items:center;margin-left:auto;transition:transform .2s;display:inline-flex}.yii-debug .yii-debug-mail-tech-chevron svg{width:100%;height:100%}.yii-debug .yii-debug-mail-headers{padding:var(--yii-debug-space-3) var(--yii-debug-space-4);background:var(--yii-debug-panel-code-bg);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);white-space:pre-wrap;overflow-wrap:break-word;margin:0;line-height:1.5;overflow-x:auto}@media (max-width:640px){.yii-debug .yii-debug-mail-card-head{grid-template-columns:auto 1fr}.yii-debug .yii-debug-mail-meta{flex-direction:row;grid-column:1/-1;justify-content:flex-start;align-items:center}.yii-debug .yii-debug-mail-recipient-group{grid-template-columns:1fr;gap:4px}.yii-debug .yii-debug-mail-recipient-label{text-align:left}}.yii-debug .yii-debug-queue-grid-job{flex-direction:column;gap:2px;line-height:1.2;display:flex}.yii-debug .yii-debug-queue-grid-job-link{color:var(--yii-debug-text-strong);white-space:nowrap;font-weight:600;text-decoration:none}.yii-debug .yii-debug-queue-grid-job-link:hover{text-decoration:underline}.yii-debug .yii-debug-queue-grid-job-namespace{color:var(--yii-debug-text-muted);font-family:var(--yii-debug-font-mono);white-space:nowrap;font-size:.78rem}.yii-debug .yii-debug-grid-queue .yii-debug-queue-status,.yii-debug .yii-debug-grid-queue .yii-debug-queue-driver{white-space:nowrap}.yii-debug .yii-debug-queue-job-page{gap:var(--yii-debug-space-3);flex-direction:column;display:flex}.yii-debug .yii-debug-queue-job-head{align-items:center;gap:var(--yii-debug-space-2);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-queue-card{gap:var(--yii-debug-space-2);padding:var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);flex-direction:column;display:flex}.yii-debug .yii-debug-queue-card-head{gap:var(--yii-debug-space-3);grid-template-columns:auto 1fr auto;align-items:flex-start;display:grid}.yii-debug .yii-debug-queue-avatar{--queue-hue:210;border-radius:var(--yii-debug-radius-md);background:linear-gradient(135deg, hsl(var(--queue-hue) 72% 52%), hsl(calc(var(--queue-hue) + 28) 65% 40%));width:40px;height:40px;box-shadow:0 2px 10px hsl(var(--queue-hue) 60% 40% / 32%), inset 0 1px 0 #ffffff38;color:#fff;font-family:var(--yii-debug-font-mono);letter-spacing:-.01em;flex:none;justify-content:center;align-items:center;font-size:16px;font-weight:800;display:inline-flex}.yii-debug .yii-debug-queue-headline{flex-direction:column;gap:2px;min-width:0;display:flex}.yii-debug .yii-debug-queue-class{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-lg);letter-spacing:-.01em;word-break:break-all;margin:0;font-weight:700;line-height:1.25}.yii-debug .yii-debug-queue-namespace{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em}.yii-debug .yii-debug-queue-meta-pills{flex-direction:column;flex:none;align-items:flex-end;gap:6px;display:flex}.yii-debug .yii-debug-queue-status{border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;align-items:center;padding:3px 10px;font-weight:600;line-height:1;display:inline-flex}.yii-debug .yii-debug-queue-status-queued{color:var(--yii-debug-info);background:color-mix(in srgb, var(--yii-debug-info), transparent 86%)}.yii-debug .yii-debug-queue-status-done{color:var(--yii-debug-success);background:color-mix(in srgb, var(--yii-debug-success), transparent 86%)}.yii-debug .yii-debug-queue-status-failed{color:var(--yii-debug-danger);background:color-mix(in srgb, var(--yii-debug-danger), transparent 86%)}.yii-debug .yii-debug-queue-driver{border-radius:var(--yii-debug-radius-pill);border:var(--yii-debug-border);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.05em;text-transform:uppercase;cursor:help;align-items:center;padding:3px 10px;font-weight:600;line-height:1;display:inline-flex}.yii-debug .yii-debug-queue-driver-is-sync{color:var(--yii-debug-success);background:color-mix(in srgb, var(--yii-debug-success), transparent 92%);border-color:color-mix(in srgb, var(--yii-debug-success), transparent 70%)}.yii-debug .yii-debug-queue-driver-is-async{color:var(--yii-debug-warning);background:color-mix(in srgb, var(--yii-debug-warning), transparent 92%);border-color:color-mix(in srgb, var(--yii-debug-warning), transparent 70%)}.yii-debug .yii-debug-queue-hint{margin:var(--yii-debug-space-3) 0;padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border-radius:var(--yii-debug-radius-md);border-left:3px solid var(--yii-debug-warning);background:color-mix(in srgb, var(--yii-debug-warning), transparent 94%);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);line-height:1.5}.yii-debug .yii-debug-queue-tree{padding:var(--yii-debug-space-3);border-radius:var(--yii-debug-radius-sm);background:color-mix(in srgb, var(--yii-debug-panel-text), transparent 96%);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);flex-direction:column;gap:4px;line-height:1.6;display:flex}.yii-debug .yii-debug-queue-tree-row{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.yii-debug .yii-debug-queue-tree-key{color:var(--yii-debug-panel-text);font-weight:700}.yii-debug .yii-debug-queue-tree-key:after{content:":";color:var(--yii-debug-panel-muted);margin-left:1px;font-weight:400}.yii-debug .yii-debug-queue-tree-type{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);text-transform:uppercase;letter-spacing:.05em;background:color-mix(in srgb, var(--yii-debug-panel-text), transparent 92%);border-radius:3px;padding:1px 6px}.yii-debug .yii-debug-queue-tree-class{color:var(--yii-debug-info);cursor:help;font-weight:600}.yii-debug .yii-debug-queue-tree-meta{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-queue-tree-truncated{color:var(--yii-debug-warning);font-size:var(--yii-debug-fs-xs);text-transform:uppercase;letter-spacing:.05em;background:color-mix(in srgb, var(--yii-debug-warning), transparent 86%);border-radius:3px;padding:1px 6px}.yii-debug .yii-debug-queue-tree-value{word-break:break-all}.yii-debug .yii-debug-queue-tree-value-string{color:var(--yii-debug-success)}.yii-debug .yii-debug-queue-tree-value-number{color:var(--yii-debug-info)}.yii-debug .yii-debug-queue-tree-value-bool{color:var(--yii-debug-warning);font-weight:600}.yii-debug .yii-debug-queue-tree-value-null{color:var(--yii-debug-panel-muted);font-style:italic}.yii-debug .yii-debug-queue-tree-collapse{flex-direction:column;gap:4px;display:flex}.yii-debug .yii-debug-queue-tree-summary{cursor:pointer;list-style:revert;flex-wrap:wrap;align-items:baseline;gap:8px;padding:2px 0;display:inline-flex}.yii-debug .yii-debug-queue-tree-summary::-webkit-details-marker{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-queue-tree-children{margin-left:var(--yii-debug-space-4);padding-left:var(--yii-debug-space-3);border-left:1px dashed color-mix(in srgb, var(--yii-debug-panel-text), transparent 80%);flex-direction:column;gap:4px;display:flex}.yii-debug .yii-debug-queue-time{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em}.yii-debug .yii-debug-queue-payload{margin:0}.yii-debug .yii-debug-queue-error{padding:var(--yii-debug-space-3);border-radius:var(--yii-debug-radius-sm);border-left:3px solid var(--yii-debug-danger);background:color-mix(in srgb, var(--yii-debug-danger), transparent 92%);color:var(--yii-debug-danger);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);line-height:1.5}.yii-debug .yii-debug-queue-meta{gap:var(--yii-debug-space-2);padding-top:var(--yii-debug-space-2);border-top:var(--yii-debug-border);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-queue-meta-item{border-radius:var(--yii-debug-radius-pill);background:color-mix(in srgb, var(--yii-debug-panel-text), transparent 94%);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);align-items:center;gap:6px;padding:2px 10px;line-height:1.4;display:inline-flex}.yii-debug .yii-debug-queue-meta-label{color:var(--yii-debug-panel-muted);text-transform:uppercase;letter-spacing:.05em;font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-queue-meta-value{color:var(--yii-debug-panel-text);font-weight:600}.yii-debug .yii-debug-dump{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);overflow:hidden}.yii-debug .yii-debug-dump-card-head{align-items:center;gap:var(--yii-debug-space-2);padding:6px var(--yii-debug-space-3);border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);color:var(--yii-debug-panel-muted);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-dump-index{color:var(--yii-debug-panel-primary-strong);font-weight:700}.yii-debug .yii-debug-dump-type{border-radius:var(--yii-debug-radius-pill);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;--yii-debug-hue:var(--yii-debug-panel-primary);padding:1px 8px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-dump-type[data-type=array]{--yii-debug-hue:var(--yii-debug-info)}.yii-debug .yii-debug-dump-type[data-type=string]{--yii-debug-hue:var(--yii-debug-success)}.yii-debug .yii-debug-dump-type[data-type=number]{--yii-debug-hue:var(--yii-debug-warning)}.yii-debug .yii-debug-dump-type[data-type=bool],.yii-debug .yii-debug-dump-type[data-type=null]{--yii-debug-hue:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-dump-meta{align-items:center;gap:var(--yii-debug-space-2);margin-left:auto;display:inline-flex}.yii-debug .yii-debug-dump-time{color:var(--yii-debug-panel-muted);font-variant-numeric:tabular-nums}.yii-debug .yii-debug-dump-trace{color:var(--yii-debug-panel-link)}.yii-debug .yii-debug-dump-body{padding:var(--yii-debug-space-3);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);line-height:1.5;overflow-x:auto}.yii-debug .yii-debug-dump-body code{white-space:pre;background:0 0;padding:0;display:block}[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color: #DD0000"],[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color:#DD0000"]{color:#fca5a5!important}[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color: #0000BB"],[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color:#0000BB"]{color:#93c5fd!important}[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color: #007700"],[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color:#007700"]{color:#86efac!important}[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color: #FF8000"],[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color:#FF8000"]{color:#fdba74!important}[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color: #000000"],[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color:#000000"]{color:var(--yii-debug-panel-text)!important}.yii-debug .yii-debug-asset-stats{gap:var(--yii-debug-space-3);margin:0 0 var(--yii-debug-space-4);grid-template-columns:repeat(4,minmax(0,1fr));display:grid}.yii-debug .yii-debug-asset-stat{padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);align-items:center;gap:10px;display:flex}.yii-debug .yii-debug-asset-stat[data-kind=bundles] .yii-debug-asset-stat-icon{color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-asset-stat[data-kind=css] .yii-debug-asset-stat-icon{color:var(--yii-debug-info)}.yii-debug .yii-debug-asset-stat[data-kind=js] .yii-debug-asset-stat-icon{color:var(--yii-debug-warning)}.yii-debug .yii-debug-asset-stat[data-kind=deps] .yii-debug-asset-stat-icon{color:var(--yii-debug-success)}.yii-debug .yii-debug-asset-stat-icon{flex:none;justify-content:center;align-items:center;width:26px;height:26px;display:inline-flex}.yii-debug .yii-debug-asset-stat-icon svg{width:100%;height:100%}.yii-debug .yii-debug-asset-stat-value{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xl);font-weight:800;line-height:1}.yii-debug .yii-debug-asset-stat-label{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;margin-left:auto}.yii-debug .yii-debug-asset-list{gap:var(--yii-debug-space-3);flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-asset-list-item{margin:0;padding:0;list-style:none;display:block}.yii-debug .yii-debug-asset-card{gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);scroll-margin-top:var(--yii-debug-space-4);flex-direction:column;transition:transform .16s,border-color .16s,box-shadow .16s;display:flex;position:relative}.yii-debug .yii-debug-asset-card:hover{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 60%);box-shadow:var(--yii-debug-shadow-sm), 0 12px 32px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%);transform:translateY(-1px)}.yii-debug .yii-debug-asset-card:target{border-color:var(--yii-debug-panel-primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 78%), var(--yii-debug-shadow-sm)}.yii-debug .yii-debug-asset-card-head{gap:var(--yii-debug-space-3);grid-template-columns:auto 1fr auto;align-items:center;display:grid}.yii-debug .yii-debug-asset-card-icon{border-radius:var(--yii-debug-radius-md);background:linear-gradient(135deg, color-mix(in srgb, var(--yii-debug-panel-primary), transparent 78%), color-mix(in srgb, var(--yii-debug-info), transparent 78%));width:38px;height:38px;color:var(--yii-debug-panel-primary-strong);flex:none;justify-content:center;align-items:center;display:inline-flex}.yii-debug .yii-debug-asset-card-icon svg{width:22px;height:22px}.yii-debug .yii-debug-asset-card-title{flex-direction:column;gap:2px;min-width:0;display:flex}.yii-debug .yii-debug-asset-card-name{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-lg);letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap;margin:0;font-weight:700;line-height:1.2;overflow:hidden}.yii-debug .yii-debug-asset-card-fqcn{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.01em;text-overflow:ellipsis;white-space:nowrap;margin:0;overflow:hidden}.yii-debug .yii-debug-asset-card-meta{flex-wrap:nowrap;flex:none;justify-content:flex-end;align-items:center;gap:5px;display:inline-flex}.yii-debug .yii-debug-asset-chip{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;align-items:center;gap:4px;padding:2px 8px;font-weight:600;display:inline-flex}.yii-debug .yii-debug-asset-chip strong{color:var(--yii-debug-panel-text);font-weight:800}.yii-debug .yii-debug-asset-chip-css{border-color:color-mix(in srgb, var(--yii-debug-info), transparent 60%);background:color-mix(in srgb, var(--yii-debug-info), transparent 90%);color:var(--yii-debug-info)}.yii-debug .yii-debug-asset-chip-css strong{color:var(--yii-debug-info)}.yii-debug .yii-debug-asset-chip-js{border-color:color-mix(in srgb, var(--yii-debug-warning), transparent 60%);background:color-mix(in srgb, var(--yii-debug-warning), transparent 90%);color:var(--yii-debug-warning)}.yii-debug .yii-debug-asset-chip-js strong{color:var(--yii-debug-warning)}.yii-debug .yii-debug-asset-chip-deps{border-color:color-mix(in srgb, var(--yii-debug-success), transparent 60%);background:color-mix(in srgb, var(--yii-debug-success), transparent 90%);color:var(--yii-debug-success)}.yii-debug .yii-debug-asset-chip-deps strong{color:var(--yii-debug-success)}.yii-debug .yii-debug-asset-card-body{gap:var(--yii-debug-space-4);grid-template-columns:minmax(180px,2fr) minmax(0,3fr);display:grid}.yii-debug .yii-debug-asset-card-body[data-cols="1"]{grid-template-columns:1fr}.yii-debug .yii-debug-asset-section{gap:var(--yii-debug-space-3);flex-direction:column;min-width:0;display:flex}.yii-debug .yii-debug-asset-section-title{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.14em;text-transform:uppercase;margin:0;font-weight:700}.yii-debug .yii-debug-asset-files{flex-direction:column;gap:4px;display:flex}.yii-debug .yii-debug-asset-files+.yii-debug-asset-files{margin-top:4px}.yii-debug .yii-debug-asset-file{border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 60%, transparent);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);grid-template-columns:auto 1fr;align-items:center;gap:6px;min-width:0;padding:4px 8px;display:grid}.yii-debug .yii-debug-asset-file-type{border-radius:var(--yii-debug-radius-pill);min-width:30px;font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em;justify-content:center;align-items:center;padding:0 5px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-asset-file-type-css{background:color-mix(in srgb, var(--yii-debug-info), transparent 84%);color:var(--yii-debug-info)}.yii-debug .yii-debug-asset-file-type-js{background:color-mix(in srgb, var(--yii-debug-warning), transparent 84%);color:var(--yii-debug-warning)}.yii-debug .yii-debug-asset-file-name{color:var(--yii-debug-panel-text);text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.yii-debug .yii-debug-asset-wiring{flex-direction:column;gap:4px;margin:0;display:flex}.yii-debug .yii-debug-asset-wiring-row{border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 60%, transparent);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);grid-template-columns:56px 1fr;align-items:baseline;gap:8px;padding:6px 10px;display:grid}.yii-debug .yii-debug-asset-wiring-label{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.12em;text-transform:uppercase;margin:0;font-weight:700}.yii-debug .yii-debug-asset-wiring-value{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);word-break:break-all;margin:0}.yii-debug .yii-debug-asset-depends{padding:10px var(--yii-debug-space-3);border:1px dashed color-mix(in srgb, var(--yii-debug-success), transparent 50%);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-success), transparent 92%);flex-direction:column;gap:8px;display:flex}.yii-debug .yii-debug-asset-depends-label{color:var(--yii-debug-success);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.14em;text-transform:uppercase;font-weight:700}.yii-debug .yii-debug-asset-depends-list{flex-wrap:wrap;gap:6px;display:flex}.yii-debug .yii-debug-asset-depend{border:1px solid color-mix(in srgb, var(--yii-debug-success), transparent 50%);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-success);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);align-items:center;gap:5px;padding:3px 10px 3px 8px;font-weight:600;text-decoration:none;transition:background-color .14s,color .14s,transform .14s,border-color .14s;display:inline-flex}.yii-debug .yii-debug-asset-depend:hover,.yii-debug .yii-debug-asset-depend:focus-visible{border-color:var(--yii-debug-success);background:color-mix(in srgb, var(--yii-debug-success), transparent 86%);color:var(--yii-debug-success);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-asset-depend-icon{opacity:.85;font-size:12px;line-height:1}.yii-debug .yii-debug-asset-depend-name{white-space:nowrap}@media (max-width:720px){.yii-debug .yii-debug-asset-stats{grid-template-columns:repeat(2,minmax(0,1fr))}.yii-debug .yii-debug-asset-card-body{grid-template-columns:1fr}.yii-debug .yii-debug-asset-card-head{grid-template-columns:auto 1fr}.yii-debug .yii-debug-asset-card-meta{grid-column:1/-1;justify-content:flex-start;max-width:100%}}.yii-debug .yii-debug-history-card{gap:var(--yii-debug-space-2);padding:var(--yii-debug-space-3);border:1px dashed color-mix(in srgb, var(--yii-debug-panel-border) 70%, transparent);border-radius:var(--yii-debug-radius-md);background:radial-gradient(circle at top right, color-mix(in srgb, var(--yii-debug-panel-primary), transparent 86%), transparent 60%), color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 40%);flex-direction:column;display:flex;position:relative}.yii-debug .yii-debug-nav-iconed .yii-debug-nav-link{align-items:center}.yii-debug .yii-debug-nav-iconed .yii-debug-nav-link .yii-debug-nav-link-label{margin-right:auto}.yii-debug .yii-debug-nav-link-icon{width:16px;height:16px;color:var(--yii-debug-panel-primary);flex:none;justify-content:center;align-items:center;margin-right:10px;transition:color .14s,transform .14s;display:inline-flex}.yii-debug .yii-debug-nav-link-icon svg{width:100%;height:100%}.yii-debug .yii-debug-nav-link.is-active .yii-debug-nav-link-icon{color:currentColor}.yii-debug .yii-debug-nav-link-label{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-sm);letter-spacing:.005em;flex:1;font-weight:600;overflow:hidden}.yii-debug .yii-debug-nav-link:not(.is-active){color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-nav-link:not(.is-active) .yii-debug-nav-link-icon{color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 35%)}.yii-debug .yii-debug-nav-link:not(.is-active):after{color:color-mix(in srgb, var(--yii-debug-panel-muted), transparent 40%)}.yii-debug .yii-debug-nav-link:not(.is-active):hover,.yii-debug .yii-debug-nav-link:not(.is-active):focus-visible{color:var(--yii-debug-panel-text);background:var(--yii-debug-panel-surface-hover)}:is(.yii-debug .yii-debug-nav-link:not(.is-active):hover,.yii-debug .yii-debug-nav-link:not(.is-active):focus-visible) .yii-debug-nav-link-icon{color:var(--yii-debug-panel-primary);transform:translate(1px)}:is(.yii-debug .yii-debug-nav-link:not(.is-active):hover,.yii-debug .yii-debug-nav-link:not(.is-active):focus-visible):after{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-tag-link{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);letter-spacing:-.01em;color:var(--yii-debug-panel-primary-strong);white-space:nowrap;border-bottom:1px dotted color-mix(in srgb, var(--yii-debug-panel-primary), transparent 60%);font-weight:700;text-decoration:none;display:inline-block}.yii-debug .yii-debug-tag-link:hover,.yii-debug .yii-debug-tag-link:focus-visible{color:var(--yii-debug-panel-primary);border-bottom-color:var(--yii-debug-panel-primary);text-decoration:none}.yii-debug .yii-debug-url-cell{text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;max-width:clamp(180px,26vw,460px);display:inline-block;overflow:hidden}.yii-debug .yii-debug-table tbody tr.is-cursor{background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 86%);box-shadow:inset 3px 0 0 var(--yii-debug-panel-primary)}.yii-debug .yii-debug-table tbody tr.is-cursor:hover{background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 78%)}.yii-debug .yii-debug-active-filters{margin-bottom:var(--yii-debug-space-3);padding:8px var(--yii-debug-space-3);border:1px solid color-mix(in srgb, var(--yii-debug-info), transparent 60%);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-info), transparent 92%);flex-wrap:wrap;align-items:center;gap:8px;display:flex}.yii-debug .yii-debug-active-filters-label{color:var(--yii-debug-info);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;font-weight:700}.yii-debug .yii-debug-active-filters-list{flex-wrap:wrap;gap:4px;display:inline-flex}.yii-debug .yii-debug-active-filter-pill{border:1px solid color-mix(in srgb, var(--yii-debug-info), transparent 50%);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);align-items:center;gap:4px;padding:2px 6px 2px 8px;text-decoration:none;transition:background-color .12s,border-color .12s;display:inline-flex}.yii-debug .yii-debug-active-filter-pill:hover,.yii-debug .yii-debug-active-filter-pill:focus-visible{border-color:var(--yii-debug-danger);background:color-mix(in srgb, var(--yii-debug-danger), transparent 88%);color:var(--yii-debug-danger);text-decoration:none}.yii-debug .yii-debug-active-filter-attr{color:var(--yii-debug-panel-muted);text-transform:lowercase}.yii-debug .yii-debug-active-filter-sep{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-active-filter-value{color:var(--yii-debug-panel-text);text-overflow:ellipsis;white-space:nowrap;max-width:220px;font-weight:700;overflow:hidden}.yii-debug .yii-debug-active-filter-x{color:var(--yii-debug-panel-muted);margin-left:2px;font-weight:700;line-height:1}.yii-debug .yii-debug-active-filter-pill:hover .yii-debug-active-filter-x,.yii-debug .yii-debug-active-filter-pill:focus-visible .yii-debug-active-filter-x{color:var(--yii-debug-danger)}.yii-debug .yii-debug-active-filters-clear{border:1px solid color-mix(in srgb, var(--yii-debug-danger), transparent 60%);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-danger);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;margin-left:auto;padding:3px 10px;font-weight:700;text-decoration:none;transition:background-color .12s,border-color .12s,transform .12s}.yii-debug .yii-debug-active-filters-clear:hover,.yii-debug .yii-debug-active-filters-clear:focus-visible{border-color:var(--yii-debug-danger);background:color-mix(in srgb, var(--yii-debug-danger), transparent 86%);color:var(--yii-debug-danger);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-col-id,.yii-debug .yii-debug-col-queue-id{white-space:nowrap;min-width:120px}.yii-debug .yii-debug-grid .filters input.yii-debug-col-id-input,.yii-debug .yii-debug-grid .filters input.yii-debug-col-queue-id-input,.yii-debug .yii-debug-grid td.yii-debug-filter-cell input.yii-debug-col-id-input,.yii-debug .yii-debug-grid td.yii-debug-filter-cell input.yii-debug-col-queue-id-input{box-sizing:border-box;width:104px}.yii-debug .yii-debug-grid-history .yii-debug-table tbody td,.yii-debug .yii-debug-grid-history .yii-debug-table tbody th{white-space:nowrap;padding:8px 10px}@media (max-width:1366px){.yii-debug .yii-debug-grid-history .yii-debug-col-ip,.yii-debug .yii-debug-grid-history .yii-debug-col-mail{display:none}.yii-debug .yii-debug-grid-history .yii-debug-url-cell{max-width:200px}}@media (max-width:767px){.yii-debug .yii-debug-grid-event .yii-debug-table thead th,.yii-debug .yii-debug-grid-event .yii-debug-table thead td,.yii-debug .yii-debug-grid-event .yii-debug-table tbody tr:not(.yii-debug-event-detail-row) td,.yii-debug .yii-debug-grid-event .yii-debug-table tbody th,.yii-debug .yii-debug-grid-event .yii-debug-table tfoot td{padding:8px 10px}.yii-debug .yii-debug-grid-event .yii-debug-table .filters td,.yii-debug .yii-debug-grid-event .yii-debug-table td.yii-debug-filter-cell{padding:6px 10px}}.yii-debug th.yii-debug-col-num,.yii-debug td.yii-debug-col-num{text-align:center;white-space:nowrap;width:1%}.yii-debug td.yii-debug-col-num{font-variant-numeric:tabular-nums}.yii-debug td.yii-debug-col-num input,.yii-debug td.yii-debug-col-num select{text-align:center;width:100%;min-width:0}.yii-debug .yii-debug-explain-scroll{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);overflow-x:auto}.yii-debug .yii-debug-explain-table{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);white-space:nowrap;margin:0}.yii-debug .yii-debug-explain-table th,.yii-debug .yii-debug-explain-table td{padding:var(--yii-debug-space-2) var(--yii-debug-space-3)}.yii-debug ul.yii-debug-trace{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);white-space:normal;margin:2px 0 0;padding:0;list-style:none}.yii-debug .yii-debug-muted{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-nowrap{white-space:nowrap}.yii-debug .yii-debug-break{word-break:break-all}.yii-debug .yii-debug-not-set{color:var(--yii-debug-danger);font-style:italic}.yii-debug .yii-debug-gauge{vertical-align:middle;font-variant-numeric:tabular-nums;flex-direction:column;gap:3px;min-width:76px;display:inline-flex}.yii-debug .yii-debug-gauge-bar{border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-gauge-track);height:3px;display:block;overflow:hidden}.yii-debug .yii-debug-gauge-bar:after{content:"";width:var(--yii-debug-gauge,0%);border-radius:inherit;background:var(--yii-debug-gauge-fill);height:100%;display:block}.yii-debug .yii-debug-section{margin-bottom:var(--yii-debug-space-6)}.yii-debug .yii-debug-section:last-child{margin-bottom:0}.yii-debug .yii-debug-section-title{align-items:center;gap:var(--yii-debug-space-2);margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.22em;text-transform:uppercase;font-size:.6875rem;font-weight:500;line-height:1;display:flex}.yii-debug .yii-debug-section-mark{color:var(--yii-debug-panel-primary);font-weight:700}.yii-debug .yii-debug-section-count{border-radius:var(--yii-debug-radius-pill);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 85%);color:var(--yii-debug-panel-primary);align-items:center;margin-left:auto;padding:2px 8px;font-weight:700;display:inline-flex}.yii-debug h1.yii-debug-hero-title{margin:0 0 var(--yii-debug-space-5);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-display);letter-spacing:-.015em;text-transform:lowercase;font-size:2rem;font-weight:400;line-height:1}.yii-debug h1.yii-debug-hero-title:before{content:"// ";color:var(--yii-debug-panel-primary);font-family:var(--yii-debug-font-mono);letter-spacing:0;vertical-align:2px;margin-right:.2em;font-size:.75em;font-weight:700}.yii-debug .yii-debug-readout{gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-6);grid-template-columns:repeat(auto-fit,minmax(210px,1fr));display:grid}.yii-debug .yii-debug-readout-card{gap:var(--yii-debug-space-2);min-height:132px;padding:var(--yii-debug-space-5) var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--yii-debug-panel-border), transparent 30%) 1px, transparent 0) 0 0 / 14px 14px, linear-gradient(150deg, color-mix(in srgb, var(--yii-debug-panel-primary), transparent 92%), transparent 55%), var(--yii-debug-panel-surface);flex-direction:column;transition:border-color .15s,transform .15s,box-shadow .15s;animation:.45s cubic-bezier(.2,.7,.3,1) both yii-debug-readout-in;display:flex;position:relative;overflow:hidden}.yii-debug .yii-debug-readout-card:hover{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 45%);box-shadow:var(--yii-debug-shadow-md);transform:translateY(-2px)}.yii-debug .yii-debug-readout-card:first-child{animation-delay:40ms}.yii-debug .yii-debug-readout-card:nth-child(2){animation-delay:80ms}.yii-debug .yii-debug-readout-card:nth-child(3){animation-delay:.12s}.yii-debug .yii-debug-readout-card:nth-child(4){animation-delay:.16s}.yii-debug .yii-debug-readout-card:nth-child(5){animation-delay:.2s}.yii-debug .yii-debug-readout-card:nth-child(6){animation-delay:.24s}.yii-debug .yii-debug-readout-corner{border:1.5px solid color-mix(in srgb, var(--yii-debug-panel-primary), transparent 40%);pointer-events:none;width:9px;height:9px;transition:border-color .15s;position:absolute}.yii-debug .yii-debug-readout-corner[data-corner=tl]{border-bottom:0;border-right:0;top:8px;left:8px}.yii-debug .yii-debug-readout-corner[data-corner=tr]{border-bottom:0;border-left:0;top:8px;right:8px}.yii-debug .yii-debug-readout-corner[data-corner=bl]{border-top:0;border-right:0;bottom:8px;left:8px}.yii-debug .yii-debug-readout-corner[data-corner=br]{border-top:0;border-left:0;bottom:8px;right:8px}.yii-debug .yii-debug-readout-card:hover .yii-debug-readout-corner{border-color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-readout-label{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.15em;text-transform:uppercase;margin:0;font-size:.6875rem;font-weight:500;line-height:1;display:block;position:relative}.yii-debug .yii-debug-readout-label:before{content:":: ";color:var(--yii-debug-panel-primary);opacity:.85;font-weight:700}.yii-debug .yii-debug-readout-value{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);letter-spacing:-.015em;overflow-wrap:break-word;margin:0;font-size:1.375rem;font-weight:700;line-height:1.15;display:block}.yii-debug .yii-debug-readout-meta{align-items:center;gap:var(--yii-debug-space-2);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.05em;text-transform:lowercase;margin-top:auto;font-size:.6875rem;line-height:1;display:inline-flex}.yii-debug .yii-debug-readout-chip{border-radius:var(--yii-debug-radius-sm);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 80%);color:var(--yii-debug-panel-primary);font-family:var(--yii-debug-font-mono);letter-spacing:.08em;text-transform:uppercase;align-items:center;padding:2px 7px;font-size:.625rem;font-weight:700;display:inline-flex}.yii-debug .yii-debug-readout-chip-muted{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-ext-strip{gap:var(--yii-debug-space-2);padding:var(--yii-debug-space-3);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:linear-gradient(0deg, color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 30%), var(--yii-debug-panel-surface));flex-wrap:wrap;display:flex}.yii-debug .yii-debug-ext-pill{align-items:center;gap:var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);letter-spacing:.1em;text-transform:uppercase;padding:6px 12px 6px 9px;font-size:.6875rem;font-weight:500;line-height:1;transition:border-color .15s,background .15s;display:inline-flex}.yii-debug .yii-debug-ext-pill-dot{background:color-mix(in srgb, var(--yii-debug-panel-muted), transparent 60%);border-radius:999px;width:7px;height:7px;display:inline-block}.yii-debug .yii-debug-ext-pill-label{color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-ext-pill-state{margin-left:var(--yii-debug-space-1);color:var(--yii-debug-panel-muted);letter-spacing:.12em;opacity:.7;font-size:.625rem;font-weight:700}.yii-debug .yii-debug-ext-pill.is-on{border-color:color-mix(in srgb, var(--yii-debug-success), transparent 55%);background:color-mix(in srgb, var(--yii-debug-success), var(--yii-debug-panel-surface) 90%)}.yii-debug .yii-debug-ext-pill.is-on .yii-debug-ext-pill-dot{background:var(--yii-debug-success);box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-success), transparent 75%);animation:2.4s ease-in-out infinite yii-debug-led-pulse}.yii-debug .yii-debug-ext-pill.is-on .yii-debug-ext-pill-state{color:var(--yii-debug-success);opacity:1}.yii-debug .yii-debug-ext-pill.is-off{opacity:.55}.yii-debug .yii-debug-dl{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);margin:0;padding:0;overflow:hidden}.yii-debug .yii-debug-dl-row{gap:var(--yii-debug-space-4);padding:10px var(--yii-debug-space-4);border-bottom:1px solid var(--yii-debug-panel-border);grid-template-columns:minmax(160px,200px) 1fr;align-items:baseline;display:grid}.yii-debug .yii-debug-dl-row:last-child{border-bottom:0}.yii-debug .yii-debug-dl-row dt{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.14em;text-transform:uppercase;margin:0;font-size:.6875rem;font-weight:500}.yii-debug .yii-debug-dl-row dd{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);overflow-wrap:break-word;margin:0;font-size:.8125rem}.yii-debug .yii-debug-package-groups{gap:var(--yii-debug-space-3);display:grid}.yii-debug .yii-debug-package-group{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);overflow:hidden}.yii-debug .yii-debug-package-group-header{justify-content:space-between;align-items:baseline;gap:var(--yii-debug-space-3);border-bottom:var(--yii-debug-border);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 18%);padding:9px 12px;display:flex}.yii-debug .yii-debug-package-vendor{color:var(--yii-debug-panel-primary);font-family:var(--yii-debug-font-mono);letter-spacing:.02em;margin:0;font-size:.75rem;font-weight:700}.yii-debug .yii-debug-package-group-count{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.08em;text-transform:uppercase;font-size:.625rem}.yii-debug .yii-debug-package-list{grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));margin:0;padding:0;display:grid}.yii-debug .yii-debug-package-row{gap:var(--yii-debug-space-2);border-right:1px solid color-mix(in srgb, var(--yii-debug-panel-border), transparent 28%);border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border), transparent 28%);grid-template-columns:minmax(0,1fr) auto;align-items:baseline;min-width:0;padding:7px 11px;display:grid}.yii-debug .yii-debug-package-row dt,.yii-debug .yii-debug-package-row dd{font-family:var(--yii-debug-font-mono);margin:0;font-size:.6875rem;line-height:1.35}.yii-debug .yii-debug-package-name{min-width:0;color:var(--yii-debug-panel-text);overflow-wrap:anywhere;font-weight:500}.yii-debug .yii-debug-package-version{color:var(--yii-debug-panel-primary);font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:700}.yii-debug .yii-debug-cta{align-items:center;gap:var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);letter-spacing:.02em;padding:10px 18px 10px 14px;font-size:.8125rem;font-weight:500;text-decoration:none;transition:border-color .15s,color .15s,transform .15s,box-shadow .15s;display:inline-flex}.yii-debug .yii-debug-cta:hover,.yii-debug .yii-debug-cta:focus{border-color:var(--yii-debug-panel-primary);color:var(--yii-debug-panel-primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 85%);text-decoration:none;transform:translate(3px)}:is(.yii-debug .yii-debug-cta:hover,.yii-debug .yii-debug-cta:focus) .yii-debug-cta-prompt{transform:translate(2px)}.yii-debug .yii-debug-cta-prompt{color:var(--yii-debug-panel-primary);font-weight:700;transition:transform .15s}.yii-debug .yii-debug-cta-external{color:var(--yii-debug-panel-muted);margin-left:auto;font-size:.875rem}.yii-debug .yii-debug-phpinfo-shell{gap:var(--yii-debug-space-4);grid-template-columns:220px minmax(0,1fr);align-items:start;display:grid}.yii-debug .yii-debug-phpinfo-toc{top:var(--yii-debug-space-3);max-height:calc(100vh - 80px);padding:var(--yii-debug-space-3);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);scrollbar-width:thin;align-self:start;position:sticky;overflow-y:auto}.yii-debug .yii-debug-phpinfo-toc-title{margin:0 0 var(--yii-debug-space-2);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);align-items:baseline;gap:6px;display:flex}.yii-debug .yii-debug-phpinfo-toc-title span:first-child{color:var(--yii-debug-panel-text);font-variant-numeric:tabular-nums;font-weight:700}.yii-debug .yii-debug-phpinfo-toc-title-note{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);margin-left:auto}.yii-debug .yii-debug-phpinfo-toc-overview,.yii-debug .yii-debug-phpinfo-toc-group-list{flex-direction:column;gap:2px;margin:0;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-phpinfo-toc-overview{margin-bottom:var(--yii-debug-space-2);padding-bottom:var(--yii-debug-space-2);border-bottom:var(--yii-debug-border)}.yii-debug .yii-debug-phpinfo-toc-groups{flex-direction:column;gap:2px;display:flex}.yii-debug .yii-debug-phpinfo-toc-group{margin:0}.yii-debug .yii-debug-phpinfo-toc-group-summary{border-radius:var(--yii-debug-radius-sm);color:var(--yii-debug-panel-muted);cursor:pointer;font-size:var(--yii-debug-fs-xs);grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:7px;padding:7px 8px;font-weight:600;list-style:none;transition:background-color .12s,color .12s;display:grid}.yii-debug .yii-debug-phpinfo-toc-group-summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-phpinfo-toc-group-summary:before{content:"›";color:var(--yii-debug-panel-muted);font-size:1.1em;line-height:1;transition:transform .12s}.yii-debug .yii-debug-phpinfo-toc-group-summary:hover,.yii-debug .yii-debug-phpinfo-toc-group-summary:focus-visible{background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-phpinfo-toc-group[open]>.yii-debug-phpinfo-toc-group-summary{color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-phpinfo-toc-group[open]>.yii-debug-phpinfo-toc-group-summary:before{transform:rotate(90deg)}.yii-debug .yii-debug-phpinfo-toc-group-count{border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);min-width:22px;color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums;text-align:center;padding:1px 6px}.yii-debug .yii-debug-phpinfo-toc-group-list{margin:2px 0 var(--yii-debug-space-2);padding-left:14px}.yii-debug .yii-debug-phpinfo-toc-link{border-radius:0 var(--yii-debug-radius-sm) var(--yii-debug-radius-sm) 0;color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-sm);border-left:2px solid #0000;padding:7px 10px 7px 12px;text-decoration:none;transition:background-color .12s,color .12s,border-color .12s;display:block}.yii-debug .yii-debug-phpinfo-toc-link:hover,.yii-debug .yii-debug-phpinfo-toc-link:focus-visible{background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-text);text-decoration:none}.yii-debug .yii-debug-phpinfo-toc-link.is-active{border-left-color:var(--yii-debug-panel-primary);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 90%);color:var(--yii-debug-panel-primary-strong);font-weight:700}.yii-debug .yii-debug-phpinfo-main{min-width:0}.yii-debug .yii-debug-phpinfo-search{margin-bottom:var(--yii-debug-space-3);flex-direction:column;gap:6px;display:flex}.yii-debug .yii-debug-phpinfo-search-control{align-items:center;display:flex;position:relative}.yii-debug .yii-debug-phpinfo-search-icon{width:16px;height:16px;color:var(--yii-debug-panel-muted);pointer-events:none;justify-content:center;align-items:center;display:inline-flex;position:absolute;left:12px}.yii-debug .yii-debug-phpinfo-search-icon svg{width:100%;height:100%}.yii-debug .yii-debug-phpinfo-search-input{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);flex:1;padding:9px 12px 9px 36px;transition:border-color .12s,box-shadow .12s}.yii-debug .yii-debug-phpinfo-search-input::placeholder{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-phpinfo-search-input:focus{border-color:var(--yii-debug-panel-primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 80%)}.yii-debug .yii-debug-phpinfo-search-clear{border-radius:var(--yii-debug-radius-sm);color:var(--yii-debug-panel-link);cursor:pointer;font:inherit;font-size:var(--yii-debug-fs-xs);background:0 0;border:0;padding:4px 8px;font-weight:600;position:absolute;right:8px}.yii-debug .yii-debug-phpinfo-search-clear:hover,.yii-debug .yii-debug-phpinfo-search-clear:focus-visible{background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-link-hover)}.yii-debug .yii-debug-phpinfo-search-control:has(.yii-debug-phpinfo-search-clear:not([hidden])) .yii-debug-phpinfo-search-input{padding-right:58px}.yii-debug .yii-debug-phpinfo-search-feedback{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-2);min-height:18px;padding-inline:2px;display:flex}.yii-debug .yii-debug-phpinfo-search-feedback:has(.yii-debug-phpinfo-search-status:empty):has(.yii-debug-phpinfo-search-empty[hidden]){display:none}.yii-debug .yii-debug-phpinfo-search-status,.yii-debug .yii-debug-phpinfo-search-empty{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-phpinfo-search-empty{color:var(--yii-debug-danger)}.yii-debug .yii-debug-phpinfo-section{scroll-margin-top:var(--yii-debug-space-3)}.yii-debug .yii-debug-phpinfo-section+:is(.yii-debug .yii-debug-phpinfo-section){margin-top:var(--yii-debug-space-4)}.yii-debug .yii-debug-phpinfo-section .yii-debug-disclosure{margin-bottom:0}.yii-debug .yii-debug-phpinfo-toc-link[hidden],.yii-debug .yii-debug-phpinfo-toc-group[hidden],.yii-debug .yii-debug-phpinfo-section[hidden],.yii-debug .yii-debug-phpinfo-search-clear[hidden],.yii-debug .yii-debug-phpinfo-search-empty[hidden],.yii-debug .yii-debug-phpinfo-overview-hero-section[hidden],.yii-debug .yii-debug-disclosure[hidden],.yii-debug .yii-debug-phpinfo-extension-group[hidden],.yii-debug .yii-debug-ext-pill[hidden],.yii-debug .yii-debug-table-wrap[hidden],.yii-debug .yii-debug-phpinfo-module tr[hidden]{display:none}@media (max-width:900px){.yii-debug .yii-debug-phpinfo-shell{grid-template-columns:1fr}.yii-debug .yii-debug-phpinfo-toc{max-height:none;position:static;overflow:hidden}.yii-debug .yii-debug-phpinfo-toc-groups{gap:4px var(--yii-debug-space-2);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.yii-debug .yii-debug-phpinfo-toc-group[open]{grid-column:1/-1}.yii-debug .yii-debug-phpinfo-toc-group-list{grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:4px;padding-left:0;display:grid}.yii-debug .yii-debug-phpinfo-toc-link{border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);border-left:0;padding:4px 10px}.yii-debug .yii-debug-phpinfo-toc-link.is-active{background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 80%);border-left:0}}@media (max-width:560px){.yii-debug .yii-debug-phpinfo-toc-groups{grid-template-columns:1fr}}.yii-debug .yii-debug-phpinfo-overview-hero{gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-3);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}@media (max-width:720px){.yii-debug .yii-debug-phpinfo-overview-hero{grid-template-columns:1fr}}.yii-debug .yii-debug-phpinfo-overview-hero-headline{align-items:center;gap:var(--yii-debug-space-3);grid-template-columns:1fr auto;display:grid}.yii-debug .yii-debug-phpinfo-overview-hero-version{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-display,var(--yii-debug-font-mono));letter-spacing:-.02em;font-size:clamp(1.6rem,2.4vw + 1rem,2.4rem);font-weight:800;line-height:1}.yii-debug .yii-debug-phpinfo-overview-hero-mark{border-radius:var(--yii-debug-radius-pill);border:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);min-width:64px;color:var(--yii-debug-panel-primary-strong);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-lg);letter-spacing:.08em;text-transform:lowercase;justify-content:center;align-items:center;padding:6px 14px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-phpinfo-overview-hero-metrics{gap:var(--yii-debug-space-2) var(--yii-debug-space-4);grid-template-columns:repeat(auto-fit,minmax(150px,1fr));margin:0;display:grid}.yii-debug .yii-debug-phpinfo-overview-hero-section{gap:var(--yii-debug-space-3);min-width:0;padding:var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);flex-direction:column;display:flex}.yii-debug .yii-debug-phpinfo-overview-hero-section:first-child{grid-column:1/-1}.yii-debug .yii-debug-phpinfo-extensions{grid-column:1/-1}.yii-debug .yii-debug-phpinfo-extensions>.yii-debug-phpinfo-extension-groups{padding:var(--yii-debug-space-4)}.yii-debug .yii-debug-phpinfo-extension-group-count{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums;padding:1px 7px}.yii-debug .yii-debug-phpinfo-extension-groups{gap:var(--yii-debug-space-3);flex-direction:column;display:flex}.yii-debug .yii-debug-phpinfo-extension-group{gap:var(--yii-debug-space-2);flex-direction:column;display:flex}.yii-debug .yii-debug-phpinfo-extension-group-head{align-items:center;gap:var(--yii-debug-space-2);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);font-weight:600;display:flex}.yii-debug .yii-debug-phpinfo-extension-group-head .yii-debug-phpinfo-extension-group-count{margin-left:auto}.yii-debug .yii-debug-phpinfo-extension-group .yii-debug-ext-pill{scroll-margin-top:var(--yii-debug-space-4)}.yii-debug .yii-debug-phpinfo-extension-group .yii-debug-ext-pill:target,.yii-debug .yii-debug-phpinfo-extension-group .yii-debug-ext-pill.is-search-match{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 45%);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 90%)}.yii-debug .yii-debug-phpinfo-overview-block-head{border-bottom:var(--yii-debug-border);align-items:center;gap:10px;padding-bottom:8px;display:flex}.yii-debug .yii-debug-phpinfo-overview-block-eyebrow{color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);font-weight:600}.yii-debug .yii-debug-phpinfo-overview-hero-metric{flex-direction:column;gap:3px;padding-block:2px;display:flex}.yii-debug .yii-debug-phpinfo-overview-hero-metric dt{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);margin:0;font-weight:600;line-height:1.2}.yii-debug .yii-debug-phpinfo-overview-hero-metric dd{overflow-wrap:break-word;min-width:0;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);margin:0;line-height:1.4}.yii-debug .yii-debug-phpinfo-overview-hero-metric dd code{color:inherit;background:0 0;padding:0}.yii-debug .yii-debug-phpinfo-overview-hero-metric.is-wide{grid-column:1/-1}.yii-debug .yii-debug-phpinfo-overview-pill{border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;border:1px solid #0000;align-items:center;gap:6px;padding:1px 10px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-phpinfo-overview-pill[data-variant=success]{color:var(--yii-debug-success);background:color-mix(in srgb, var(--yii-debug-success), transparent 86%);border-color:color-mix(in srgb, var(--yii-debug-success), transparent 65%)}.yii-debug .yii-debug-phpinfo-overview-pill[data-variant=muted]{color:var(--yii-debug-panel-muted);background:var(--yii-debug-panel-surface-muted);border-color:var(--yii-debug-panel-border)}.yii-debug .yii-debug-phpinfo-overview-token{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);padding:1px 8px}.yii-debug .yii-debug-phpinfo-overview-files{flex-wrap:wrap;gap:4px;display:flex}.yii-debug .yii-debug-phpinfo-overview-files .yii-debug-phpinfo-overview-token{cursor:help;transition:border-color .12s,background-color .12s}.yii-debug .yii-debug-phpinfo-overview-files .yii-debug-phpinfo-overview-token:hover{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 40%);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%)}.yii-debug .yii-debug-phpinfo-module{gap:var(--yii-debug-space-3);flex-direction:column;margin-top:0;display:flex}.yii-debug .yii-debug-phpinfo-module-head{align-items:center;gap:10px;margin:0;padding:2px 2px 0;display:flex}.yii-debug .yii-debug-phpinfo-module-head h2{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xl);text-align:left;background:0 0;margin:0;padding:0;font-weight:600}.yii-debug .yii-debug-phpinfo-table-section{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);overflow:hidden}.yii-debug .yii-debug-phpinfo-table-section-head{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-2);padding:9px var(--yii-debug-space-3);border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-sm);font-weight:600;display:flex}.yii-debug .yii-debug-phpinfo-variable-group>summary{cursor:pointer;list-style:none;transition:background-color .12s}.yii-debug .yii-debug-phpinfo-variable-group>summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-phpinfo-variable-group>summary:hover,.yii-debug .yii-debug-phpinfo-variable-group>summary:focus-visible{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-phpinfo-variable-group>summary>span:first-child{align-items:center;gap:8px;display:inline-flex}.yii-debug .yii-debug-phpinfo-variable-group>summary>span:first-child:before{content:"›";color:var(--yii-debug-panel-muted);font-size:1.1em;line-height:1;transition:transform .12s}.yii-debug .yii-debug-phpinfo-variable-group[open]>summary>span:first-child:before{transform:rotate(90deg)}.yii-debug .yii-debug-phpinfo-table-section-count{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums;flex:none;padding:1px 7px;font-weight:500}.yii-debug .yii-debug-phpinfo-table-scroll{min-width:0;overflow-x:auto}.yii-debug .yii-debug-phpinfo-module .yii-debug-table{table-layout:fixed;min-width:560px}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr>:first-child{width:40%}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr>:nth-child(2):last-child{width:60%}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr>:nth-child(2):not(:last-child),.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr>:nth-child(3){width:30%}.yii-debug .yii-debug-phpinfo-module .yii-debug-table th,.yii-debug .yii-debug-phpinfo-module .yii-debug-table td{text-align:left}.yii-debug .yii-debug-phpinfo-module .yii-debug-table th[scope=row]{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);white-space:normal;font-weight:500}.yii-debug .yii-debug-phpinfo-module .yii-debug-table td{font-family:var(--yii-debug-font-mono)}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr.h>*{z-index:1;background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.03em;text-transform:uppercase;font-weight:700;position:sticky;top:0}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr.is-search-match>*{background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%)!important}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr.has-local-override>td:nth-child(2){box-shadow:inset 2px 0 0 color-mix(in srgb, var(--yii-debug-warning), transparent 25%);color:color-mix(in srgb, var(--yii-debug-warning) 72%, var(--yii-debug-panel-text))}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-phpinfo-table-scroll{overflow:visible}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts{table-layout:auto;min-width:0;display:block}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tbody{grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact{border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border), transparent 25%);grid-template-columns:minmax(130px,.85fr) minmax(0,1.15fr);min-width:0;display:grid}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact:nth-child(odd){border-right:1px solid color-mix(in srgb, var(--yii-debug-panel-border), transparent 25%)}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact.yii-debug-phpinfo-fact-wide{border-right:0;grid-column:1/-1}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact>*{overflow-wrap:anywhere;background:0 0;border:0;width:auto;padding:9px 12px}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact>th{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);font-weight:600}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact>td{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact-subheading{grid-column:1/-1;display:block}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact-subheading>th{border-block:var(--yii-debug-border);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 15%);width:auto;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em;text-align:left;padding:8px 12px;font-weight:600;display:block}.yii-debug .yii-debug-phpinfo-status-pill,.yii-debug .yii-debug-phpinfo-redacted{border-radius:var(--yii-debug-radius-pill);width:-moz-fit-content;width:fit-content;font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.03em;border:1px solid #0000;align-items:center;padding:1px 8px;font-weight:600;display:inline-flex}.yii-debug .yii-debug-phpinfo-status-pill[data-variant=success]{border-color:color-mix(in srgb, var(--yii-debug-success), transparent 65%);background:color-mix(in srgb, var(--yii-debug-success), transparent 88%);color:var(--yii-debug-success)}.yii-debug .yii-debug-phpinfo-status-pill[data-variant=muted]{border-color:var(--yii-debug-panel-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-phpinfo-redacted{border-color:color-mix(in srgb, var(--yii-debug-warning), transparent 65%);background:color-mix(in srgb, var(--yii-debug-warning), transparent 90%);color:color-mix(in srgb, var(--yii-debug-warning) 72%, var(--yii-debug-panel-text));text-transform:uppercase}.yii-debug .yii-debug-phpinfo-table-section.is-note .yii-debug-table.is-note{table-layout:auto;min-width:0}.yii-debug .yii-debug-phpinfo-table-section.is-note .yii-debug-table.is-note td,.yii-debug .yii-debug-phpinfo-table-section.is-note .yii-debug-table.is-note th{width:auto;padding:var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);text-align:left;line-height:1.6}@media (max-width:720px){.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tbody{grid-template-columns:1fr}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact{border-right:0}}@media (max-width:959px){.yii-debug .yii-debug-sidebar{min-width:0}.yii-debug .yii-debug-nav{overscroll-behavior-x:contain;scrollbar-width:thin;overflow-x:auto}.yii-debug .yii-debug-nav ul{gap:var(--yii-debug-space-1);flex-direction:row;width:max-content;min-width:100%}.yii-debug .yii-debug-nav-link{white-space:nowrap;min-height:40px}.yii-debug .yii-debug-nav-link:after{display:none}.yii-debug .yii-debug-nav-link-icon{margin-right:var(--yii-debug-space-1)}}@media (max-width:767px){.yii-debug .yii-debug-page{padding:var(--yii-debug-space-2)}.yii-debug .yii-debug-card{padding:var(--yii-debug-space-3)}}@media (prefers-reduced-motion:reduce){.yii-debug *,.yii-debug :before,.yii-debug :after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}@keyframes yii-debug-user-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.55;transform:scale(.85)}}@keyframes yii-debug-readout-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes yii-debug-led-pulse{0%,to{box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-success), transparent 75%)}50%{box-shadow:0 0 0 6px color-mix(in srgb, var(--yii-debug-success), transparent 90%)}}.yii-debug-tl-filter{align-items:end;gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);flex-wrap:wrap;display:flex}.yii-debug-tl-field{flex-direction:column;gap:4px;min-width:160px;display:flex}.yii-debug-tl-field label{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;font-weight:600}.yii-debug-tl-field input{padding:6px var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font:inherit;font-family:var(--yii-debug-font-mono)}.yii-debug-tl-field input::placeholder{color:var(--yii-debug-panel-muted)}.yii-debug-tl-field input:focus{border-color:var(--yii-debug-panel-primary)}.yii-debug-tl-field-grow{flex:240px}.yii-debug-tl-hint{margin-bottom:var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:1px dashed color-mix(in srgb, var(--yii-debug-panel-border) 70%, transparent);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 40%)}.yii-debug-tl-hint-title{color:var(--yii-debug-panel-text);margin:0 0 6px;font-weight:600}.yii-debug-tl-hint-body{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-sm);margin:0;line-height:1.55}.yii-debug-tl-hint-body em{color:var(--yii-debug-panel-text);font-style:normal;font-weight:600}.yii-debug-tl{--yii-debug-tl-label-col:22%;grid-template-columns:minmax(180px, var(--yii-debug-tl-label-col)) 1fr;border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);grid-template-rows:auto 1fr auto;gap:0;display:grid;overflow:hidden}@media (max-width:1440px){.yii-debug-tl{--yii-debug-tl-label-col:32%}}.yii-debug-tl-axis{border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);grid-column:2/3;height:32px;padding:8px 0;position:relative}.yii-debug-tl-tick{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;white-space:nowrap;pointer-events:none;position:absolute;top:8px;transform:translate(-50%)}.yii-debug-tl-tick:after{content:"";background:var(--yii-debug-panel-border);width:1px;height:8px;position:absolute;top:18px;left:50%}.yii-debug-tl-rows{grid-column:1/3;grid-template-columns:minmax(180px, var(--yii-debug-tl-label-col)) 1fr;background:linear-gradient(to right, transparent calc(var(--yii-debug-tl-label-col) - 1px), var(--yii-debug-panel-border) calc(var(--yii-debug-tl-label-col) - 1px), var(--yii-debug-panel-border) var(--yii-debug-tl-label-col), transparent var(--yii-debug-tl-label-col));display:grid}.yii-debug-tl-row{display:contents}.yii-debug-tl-row:hover .yii-debug-tl-label,.yii-debug-tl-row:hover .yii-debug-tl-track{background:var(--yii-debug-panel-surface-hover)}.yii-debug-tl-row:hover .yii-debug-tl-name{color:var(--yii-debug-panel-text)}.yii-debug-tl-label{min-width:0;padding:8px var(--yii-debug-space-3) 8px calc(var(--yii-debug-space-3) + (var(--depth,0) * 14px));border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 50%, transparent);grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px;transition:background-color .12s;display:grid}.yii-debug-tl-dot{background:var(--yii-debug-cat-db);opacity:.85;border-radius:50%;flex:none;width:6px;height:6px}.yii-debug-tl-name{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);color:var(--yii-debug-panel-muted);text-overflow:ellipsis;white-space:nowrap;transition:color .12s;overflow:hidden}.yii-debug-tl-track{border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 50%, transparent);transition:background-color .12s;position:relative}.yii-debug-tl-bar{background:var(--yii-debug-cat-db);border-radius:4px;min-width:2px;height:18px;position:absolute;top:50%;transform:translateY(-50%);box-shadow:inset 0 1px #ffffff1f,0 1px 2px #0000002e}.yii-debug-tl-bar-duration{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums;white-space:nowrap;justify-self:end;font-weight:500}.yii-debug-tl-memory{grid-template-columns:minmax(180px, var(--yii-debug-tl-label-col)) 1fr auto;padding:var(--yii-debug-space-2) var(--yii-debug-space-3);border-top:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);grid-column:1/3;align-items:center;display:grid}.yii-debug-tl-memory-label{text-transform:uppercase;letter-spacing:.06em}.yii-debug-tl-memory-track{border-radius:var(--yii-debug-radius-sm);background:color-mix(in srgb, var(--yii-debug-panel-surface), transparent 50%);width:100%;color:var(--yii-debug-panel-primary);position:relative;overflow:hidden}.yii-debug-tl-memory-track svg{width:100%;height:100%;display:block}.yii-debug-tl-memory-peak{margin-left:var(--yii-debug-space-2);color:var(--yii-debug-panel-text);font-weight:700}@media (max-width:640px){.yii-debug-tl,.yii-debug-tl-rows{--yii-debug-tl-label-col:52%;grid-template-columns:minmax(140px, var(--yii-debug-tl-label-col)) minmax(0, 1fr)}.yii-debug-tl-label{padding:7px var(--yii-debug-space-2);gap:6px}.yii-debug-tl-memory{grid-template-columns:minmax(140px, var(--yii-debug-tl-label-col)) minmax(0, 1fr)}.yii-debug-tl-memory-peak{text-align:right;grid-column:2;margin:4px 0 0}}.yii-debug .yii-debug-skip-link{top:var(--yii-debug-space-2);left:var(--yii-debug-space-2);z-index:1000;padding:var(--yii-debug-space-2) var(--yii-debug-space-3);border:2px solid var(--yii-debug-focus-ring);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-md);color:var(--yii-debug-panel-text);transform:translateY(calc(-100% - var(--yii-debug-space-4)));font-weight:700;position:fixed}.yii-debug .yii-debug-skip-link:focus-visible{text-decoration:none;transform:translateY(0)}.yii-debug .yii-debug-brand-chip-control{border:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);cursor:pointer;justify-content:center}.yii-debug .yii-debug-brand-chip-control:hover,.yii-debug .yii-debug-brand-chip-control:focus-visible{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 50%);color:var(--yii-debug-panel-primary-strong);transform:translateY(-1px)}.yii-debug :is(h2,h3).yii-debug-has-permalink{align-items:center;gap:var(--yii-debug-space-1);scroll-margin-block-start:var(--yii-debug-space-5);display:flex}.yii-debug .yii-debug-heading-permalink{border-radius:var(--yii-debug-radius-sm);min-width:32px;min-height:32px;color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);opacity:0;transition:color var(--yii-debug-duration-fast) ease, opacity var(--yii-debug-duration-fast) ease, background var(--yii-debug-duration-fast) ease;flex:none;justify-content:center;align-items:center;line-height:1;text-decoration:none;display:inline-flex}.yii-debug .yii-debug-heading-permalink:hover,.yii-debug .yii-debug-heading-permalink:focus-visible{background:var(--yii-debug-panel-bg-accent);color:var(--yii-debug-panel-primary-strong);opacity:1;text-decoration:none}.yii-debug :is(h2,h3).yii-debug-has-permalink:hover>.yii-debug-heading-permalink,.yii-debug :is(h2,h3).yii-debug-has-permalink:focus-within>.yii-debug-heading-permalink,.yii-debug :is(h2,h3).yii-debug-deep-link-target>.yii-debug-heading-permalink{opacity:1}.yii-debug .yii-debug-deep-link-target{outline:2px solid var(--yii-debug-focus-ring);outline-offset:4px}.yii-debug .yii-debug-db-n1-summary{gap:var(--yii-debug-space-2);margin-bottom:var(--yii-debug-space-4);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:1px solid color-mix(in srgb, var(--yii-debug-warning), transparent 55%);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-warning), transparent 92%);display:grid}.yii-debug .yii-debug-db-n1-heading{justify-content:space-between;align-items:baseline;gap:var(--yii-debug-space-2);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-db-n1-list{gap:var(--yii-debug-space-2);flex-wrap:wrap;margin:0;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-db-n1-link,.yii-debug .yii-debug-db-n1-row-link{align-items:center;gap:var(--yii-debug-space-2);min-height:var(--yii-debug-control-min-size);padding:var(--yii-debug-space-1) var(--yii-debug-space-2);border:1px solid color-mix(in srgb, var(--yii-debug-warning), transparent 60%);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:color-mix(in srgb, var(--yii-debug-warning) 72%, var(--yii-debug-panel-text));font-size:var(--yii-debug-fs-xs);font-weight:700;text-decoration:none;display:inline-flex}:is(.yii-debug .yii-debug-db-n1-link,.yii-debug .yii-debug-db-n1-row-link):hover,:is(.yii-debug .yii-debug-db-n1-link,.yii-debug .yii-debug-db-n1-row-link):focus-visible,:is(.yii-debug .yii-debug-db-n1-link,.yii-debug .yii-debug-db-n1-row-link)[aria-current=true]{background:color-mix(in srgb, var(--yii-debug-warning), transparent 84%);color:var(--yii-debug-panel-text);text-decoration:none}.yii-debug .yii-debug-db-n1-fingerprint{max-width:min(56ch,65vw);font-family:var(--yii-debug-font-mono);text-overflow:ellipsis;white-space:nowrap;font-weight:500;display:inline-block;overflow:hidden}.yii-debug .yii-debug-db-n1-row-link{margin-top:var(--yii-debug-space-3)}.yii-debug .yii-debug-grid-db .yii-debug-trace{gap:0;display:grid}.yii-debug .yii-debug-grid-db .yii-debug-trace a{align-items:center;min-height:24px;display:inline-flex}.yii-debug .yii-debug-db-n1-clear[hidden]{display:none}.yii-debug .yii-debug-compare-form{gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-4);grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));align-items:end;display:grid}.yii-debug .yii-debug-compare-overview{gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-4);grid-template-columns:repeat(auto-fit,minmax(min(100%,12rem),1fr));display:grid}.yii-debug .yii-debug-compare-grid{min-width:42rem}.yii-debug :is(.yii-debug-delta-up,.yii-debug-delta-down,.yii-debug-delta-neutral){padding:2px var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums;align-items:center;font-weight:700;display:inline-flex}.yii-debug .yii-debug-delta-up{border-color:color-mix(in srgb, var(--yii-debug-danger), transparent 60%);background:color-mix(in srgb, var(--yii-debug-danger), transparent 90%);color:var(--yii-debug-danger)}.yii-debug .yii-debug-delta-down{border-color:color-mix(in srgb, var(--yii-debug-success), transparent 60%);background:color-mix(in srgb, var(--yii-debug-success), transparent 90%);color:var(--yii-debug-success)}.yii-debug .yii-debug-delta-neutral{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted)}@container yii-debug-main (width<=560px){.yii-debug .yii-debug-section-header,.yii-debug .yii-debug-panel-heading{flex-direction:column;align-items:stretch}.yii-debug .yii-debug-filter-input{width:100%;max-width:none}}@media (max-width:767px){.yii-debug .yii-debug-brand-chip-copy .yii-debug-brand-label{text-overflow:ellipsis;white-space:nowrap;max-width:5.5rem;overflow:hidden}}@media (pointer:coarse){.yii-debug .yii-debug-grid-db .yii-debug-trace a{min-width:44px;min-height:44px}.yii-debug :is(.yii-debug-btn,.yii-debug-brand-chip,.yii-debug-cell-more-toggle,.yii-debug-dropdown-item,.yii-debug-filter-input,.yii-debug-heading-permalink,.yii-debug-server-group-summary,.yii-debug-tab-link){min-height:44px}}@media (forced-colors:active){.yii-debug .yii-debug-card,.yii-debug .yii-debug-brand-bar,.yii-debug .yii-debug-brand-chip,.yii-debug .yii-debug-nav,.yii-debug .yii-debug-table-wrap{box-shadow:none;border-color:canvastext}.yii-debug .yii-debug-nav-link.is-active,.yii-debug .yii-debug-tab-link.is-active{color:highlighttext;background:highlight;border-color:highlight}.yii-debug :focus-visible{outline-color:highlight}} +:root,:host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--yii-debug-font-sans:"IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, blinkmacsystemfont, "Segoe UI", roboto, helvetica, arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";--yii-debug-font-mono:"JetBrains Mono", ui-monospace, sfmono-regular, menlo, monaco, consolas, "Liberation Mono", "Courier New", monospace;--yii-debug-font-display:var(--yii-debug-font-mono);--yii-debug-space-1:4px;--yii-debug-space-2:8px;--yii-debug-space-3:12px;--yii-debug-space-4:16px;--yii-debug-space-5:24px;--yii-debug-space-6:32px;--yii-debug-radius-sm:6px;--yii-debug-radius-md:10px;--yii-debug-radius-lg:14px;--yii-debug-radius-pill:999px;--yii-debug-fs-xs:.75rem;--yii-debug-fs-sm:.8125rem;--yii-debug-fs-base:.9375rem;--yii-debug-fs-lg:1.0625rem;--yii-debug-fs-xl:1.25rem;--yii-debug-fs-2xl:1.5rem;--yii-debug-control-min-size:36px;--yii-debug-panel-padding:var(--yii-debug-space-5);--yii-debug-row-padding-block:10px;--yii-debug-duration-fast:.12s;--yii-debug-duration-normal:.18s;--yii-debug-panel-bg:var(--lightningcss-light,#f2f5f1)var(--lightningcss-dark,#0c120e);--yii-debug-panel-bg-accent:var(--lightningcss-light,#0e7a551a)var(--lightningcss-dark,#2fd08c14);--yii-debug-panel-surface:var(--lightningcss-light,#fffffff0)var(--lightningcss-dark,#121a15eb);--yii-debug-panel-surface-muted:var(--lightningcss-light,#f6f8f5)var(--lightningcss-dark,#101812);--yii-debug-panel-surface-hover:var(--lightningcss-light,#e9f2ec)var(--lightningcss-dark,#1a241d);--yii-debug-panel-border:var(--lightningcss-light,#5d7a6942)var(--lightningcss-dark,#8fa39633);--yii-debug-panel-border-strong:var(--lightningcss-light,#4660515c)var(--lightningcss-dark,#8fa39652);--yii-debug-panel-text:var(--lightningcss-light,#16211a)var(--lightningcss-dark,#e4ede6);--yii-debug-panel-muted:var(--lightningcss-light,#5c6b60)var(--lightningcss-dark,#8fa396);--yii-debug-panel-link:var(--lightningcss-light,#0b6b4a)var(--lightningcss-dark,#4fd39a);--yii-debug-panel-link-hover:var(--lightningcss-light,#0d855b)var(--lightningcss-dark,#7fe4b8);--yii-debug-panel-primary:var(--lightningcss-light,#0e7a55)var(--lightningcss-dark,#2fd08c);--yii-debug-primary-bg:linear-gradient(135deg, var(--yii-debug-panel-primary-strong), var(--yii-debug-panel-primary));--yii-debug-on-primary:var(--lightningcss-light,#fff)var(--lightningcss-dark,#06281b);--yii-debug-panel-primary-strong:var(--lightningcss-light,#0a5c40)var(--lightningcss-dark,#17a673);--yii-debug-focus-ring:var(--lightningcss-light,#075c40)var(--lightningcss-dark,#7fe4b8);--yii-debug-success:var(--lightningcss-light,#0d7d47)var(--lightningcss-dark,#34c97e);--yii-debug-warning:var(--lightningcss-light,#8f5000)var(--lightningcss-dark,#e8a33d);--yii-debug-danger:var(--lightningcss-light,#c93838)var(--lightningcss-dark,#f16a6a);--yii-debug-on-success:var(--lightningcss-light,#fff)var(--lightningcss-dark,#0c120e);--yii-debug-on-warning:var(--lightningcss-light,#fff)var(--lightningcss-dark,#1c1917);--yii-debug-on-danger:var(--lightningcss-light,#fff)var(--lightningcss-dark,#1c1917);--yii-debug-info:var(--lightningcss-light,#205f9d)var(--lightningcss-dark,#5fa8e8);--yii-debug-on-info:var(--yii-debug-panel-bg);--yii-debug-panel-code-bg:var(--lightningcss-light,#ecf1ec)var(--lightningcss-dark,#17211a);--yii-debug-panel-pill:var(--lightningcss-light,#16211a0f)var(--lightningcss-dark,#8fa3962e);--yii-debug-panel-shadow:var(--lightningcss-light,0 22px 60px #16211a14)var(--lightningcss-dark,0 22px 60px #0006);--yii-debug-border:1px solid var(--yii-debug-panel-border);--yii-debug-border-strong:1px solid var(--yii-debug-panel-border-strong);--yii-debug-shadow-sm:0 1px 2px #0f172a0f;--yii-debug-shadow-md:0 10px 24px #0f172a14;--yii-debug-verb-get:var(--lightningcss-light,#0b7285)var(--lightningcss-dark,#4dc4d9);--yii-debug-verb-post:var(--lightningcss-light,#6941c6)var(--lightningcss-dark,#a78bfa);--yii-debug-verb-put:var(--lightningcss-light,#b26a00)var(--lightningcss-dark,#e8a33d);--yii-debug-verb-delete:var(--lightningcss-light,#c93838)var(--lightningcss-dark,#f16a6a);--yii-debug-verb-other:var(--yii-debug-panel-muted);--yii-debug-status-2xx:var(--yii-debug-success);--yii-debug-status-3xx:var(--yii-debug-warning);--yii-debug-status-4xx:var(--lightningcss-light,#c2410c)var(--lightningcss-dark,#fb923c);--yii-debug-status-5xx:var(--yii-debug-danger);--yii-debug-level-error:var(--yii-debug-danger);--yii-debug-level-warning:var(--yii-debug-warning);--yii-debug-level-info:var(--yii-debug-info);--yii-debug-level-trace:var(--yii-debug-panel-muted);--yii-debug-level-profile:var(--lightningcss-light,#9333ea)var(--lightningcss-dark,#b16be8);--yii-debug-level-other:var(--yii-debug-panel-muted);--yii-debug-gauge-fill:var(--yii-debug-panel-primary);--yii-debug-gauge-track:var(--lightningcss-light,#0e7a5529)var(--lightningcss-dark,#2fd08c24);--yii-debug-sql-kw:var(--lightningcss-light,#0a5c40)var(--lightningcss-dark,#4fd39a);--yii-debug-sql-str:var(--lightningcss-light,#8f4e00)var(--lightningcss-dark,#e8a33d);--yii-debug-sql-num:var(--yii-debug-info);--yii-debug-sql-comment:var(--yii-debug-panel-muted);--yii-debug-sql-param:var(--lightningcss-light,#6941c6)var(--lightningcss-dark,#a78bfa);--yii-debug-cat-app:var(--lightningcss-light,#0a6b47)var(--lightningcss-dark,#14b06e);--yii-debug-cat-db:var(--lightningcss-light,#0994b8)var(--lightningcss-dark,#0999be);--yii-debug-cat-view:var(--lightningcss-light,#6941c6)var(--lightningcss-dark,#8f6ce8);--yii-debug-cat-cache:var(--lightningcss-light,#b26a00)var(--lightningcss-dark,#c28300);--yii-debug-cat-mail:var(--lightningcss-light,#b23a67)var(--lightningcss-dark,#d95f8f);--yii-debug-cat-queue:var(--lightningcss-light,#3f4dbd)var(--lightningcss-dark,#6d7ce6);--yii-debug-cat-other:var(--yii-debug-panel-muted)}@media (prefers-color-scheme:dark){:root,:host{--lightningcss-light: ;--lightningcss-dark:initial}}:root[data-yii-debug-theme=light],:host([data-theme=light]){--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}:root[data-yii-debug-theme=dark],:host([data-theme=dark]){--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}@font-face{font-family:JetBrains Mono;font-style:normal;font-display:swap;font-weight:400;src:url(../fonts/jetbrains-mono-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-display:swap;font-weight:500;src:url(../fonts/jetbrains-mono-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-display:swap;font-weight:700;src:url(../fonts/jetbrains-mono-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:400;src:url(../fonts/ibm-plex-sans-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:500;src:url(../fonts/ibm-plex-sans-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:600;src:url(../fonts/ibm-plex-sans-latin-600-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:700;src:url(../fonts/ibm-plex-sans-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}.yii-debug .yii-debug-event-controls{margin-block:var(--yii-debug-space-4);font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-event-groups{align-items:center;gap:var(--yii-debug-space-2);margin-block:var(--yii-debug-space-4);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-event-group{align-items:center;gap:var(--yii-debug-space-2);min-height:var(--yii-debug-control-min-size);padding:var(--yii-debug-space-1) var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);overflow-wrap:anywhere;display:inline-flex}.yii-debug .yii-debug-event-item>summary{cursor:pointer;list-style:none}.yii-debug .yii-debug-event-item>summary:focus-visible{outline:2px solid var(--yii-debug-panel-primary);outline-offset:4px;border-radius:var(--yii-debug-radius-sm)}.yii-debug :is(.yii-debug-event-clock,.yii-debug-event-identity){gap:var(--yii-debug-space-1);display:grid}.yii-debug .yii-debug-event-clock{min-width:105px}.yii-debug .yii-debug-event-time{white-space:nowrap;color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-event-identity{overflow-wrap:anywhere;min-width:0}.yii-debug .yii-debug-event-name:before{content:"+ ";color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-event-item[open] .yii-debug-event-name:before{content:"− "}.yii-debug .yii-debug-event-timing{font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-event-detail-row{display:none}.yii-debug .yii-debug-table tr:has(.yii-debug-event-item[open])+.yii-debug-event-detail-row{display:table-row}.yii-debug .yii-debug-table .yii-debug-event-detail-row>td{padding:0}.yii-debug .yii-debug-event-detail{gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-4);background:var(--yii-debug-panel-surface-muted);white-space:normal;grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.yii-debug .yii-debug-event-detail>div{min-width:0}.yii-debug .yii-debug-event-detail-footer{gap:var(--yii-debug-space-2) var(--yii-debug-space-4);flex-wrap:wrap;grid-column:1/-1;display:flex}.yii-debug .yii-debug-event-metadata{gap:var(--yii-debug-space-2) var(--yii-debug-space-3);grid-template-columns:minmax(75px,.4fr) minmax(0,1fr);display:grid}.yii-debug .yii-debug-event-metadata dt{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-event-metadata dd{margin:0}.yii-debug .yii-debug-event-trace pre{white-space:pre-wrap;overflow-wrap:anywhere}.yii-debug :is(.yii-debug-event-context,.yii-debug-event-trace)>:last-child{margin-bottom:0}@media not (min-width:768px){.yii-debug .yii-debug-event-detail{grid-template-columns:minmax(0,1fr)}}.yii-debug .yii-debug-event-coverage>summary{min-height:var(--yii-debug-control-min-size);padding-block:var(--yii-debug-space-2);cursor:pointer}.yii-debug{background:radial-gradient(circle at top left, var(--yii-debug-panel-bg-accent), transparent 36rem), var(--yii-debug-panel-bg);min-height:100vh;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-base);margin:0;padding:0;line-height:1.5}.yii-debug *,.yii-debug :before,.yii-debug :after{box-sizing:border-box}.yii-debug h1,.yii-debug h2,.yii-debug h3,.yii-debug h4,.yii-debug h5,.yii-debug h6{margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-text);letter-spacing:-.01em;font-weight:700;line-height:1.25}.yii-debug h1{font-size:var(--yii-debug-fs-2xl)}.yii-debug h2{font-size:var(--yii-debug-fs-xl)}.yii-debug h3{font-size:var(--yii-debug-fs-lg)}.yii-debug h4,.yii-debug h5,.yii-debug h6{font-size:var(--yii-debug-fs-base)}.yii-debug p,.yii-debug ul,.yii-debug ol,.yii-debug dl,.yii-debug figure{margin:0 0 var(--yii-debug-space-3)}.yii-debug ul,.yii-debug ol{padding-left:var(--yii-debug-space-5)}.yii-debug strong,.yii-debug b{font-weight:600}.yii-debug a{color:var(--yii-debug-panel-link);text-decoration:none;transition:color .12s}.yii-debug a:hover,.yii-debug a:focus{color:var(--yii-debug-panel-link-hover);text-decoration:underline}.yii-debug code,.yii-debug kbd,.yii-debug samp{border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-code-bg);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-variant-ligatures:none;padding:.1em .35em;font-size:.875em}.yii-debug pre{margin:0 0 var(--yii-debug-space-3);padding:var(--yii-debug-space-3);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-code-bg);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);font-variant-ligatures:none;line-height:1.4;overflow-x:auto}.yii-debug pre code{font-size:inherit;background:0 0;padding:0}.yii-debug :focus-visible{outline:3px solid var(--yii-debug-focus-ring);outline-offset:3px}.yii-debug .yii-debug-page{max-width:1680px;padding:var(--yii-debug-space-4);margin:0 auto}.yii-debug .yii-debug-layout{gap:var(--yii-debug-space-4);grid-template-columns:240px 1fr;align-items:start;display:grid}@media (max-width:959px){.yii-debug .yii-debug-layout{grid-template-columns:1fr}}@media (min-width:960px){.yii-debug .yii-debug-sidebar{top:var(--yii-debug-space-4);max-height:calc(100vh - var(--yii-debug-space-5));position:sticky;overflow-y:auto}}.yii-debug .yii-debug-main{min-width:0;container:yii-debug-main/inline-size}.yii-debug .yii-debug-main>*+:is(h2,.yii-debug-disclosure),.yii-debug .yii-debug-tab-panel>.yii-debug-table-wrap+.yii-debug-disclosure{margin-top:var(--yii-debug-space-5)}.yii-debug .yii-debug-main>.yii-debug-sr-only:first-child+:is(h2,.yii-debug-disclosure){margin-top:0}.yii-debug .yii-debug-mini-toolbar{align-items:center;gap:var(--yii-debug-space-2);margin-bottom:var(--yii-debug-space-4);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-side-section{gap:var(--yii-debug-space-2);margin:0 0 var(--yii-debug-space-3);padding:var(--yii-debug-space-3);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);flex-direction:column;display:flex}.yii-debug .yii-debug-side-section-title{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;font-weight:700}.yii-debug .yii-debug-side-section-link{color:var(--yii-debug-panel-link);font-size:var(--yii-debug-fs-sm);text-decoration:none}.yii-debug .yii-debug-side-section-link:hover,.yii-debug .yii-debug-side-section-link:focus{color:var(--yii-debug-panel-link-hover);text-decoration:underline}.yii-debug .yii-debug-request-nav-row{width:100%;margin-top:var(--yii-debug-space-2);grid-template-columns:repeat(4,1fr);gap:6px;display:grid}.yii-debug .yii-debug-btn-icon{justify-content:center;align-items:center;padding:4px 0;font-size:16px;line-height:1;display:inline-flex}.yii-debug .yii-debug-btn-icon svg{width:1em;height:1em}.yii-debug .yii-debug-request-nav .yii-debug-btn{justify-content:center;width:100%}.yii-debug .yii-debug-brand-bar{align-items:center;gap:var(--yii-debug-space-2);margin-bottom:var(--yii-debug-space-3);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:linear-gradient(180deg, color-mix(in srgb, var(--yii-debug-panel-surface) 96%, transparent), color-mix(in srgb, var(--yii-debug-panel-surface-muted) 88%, transparent));box-shadow:var(--yii-debug-shadow-sm);flex-wrap:wrap;padding:8px 12px;display:flex}.yii-debug .yii-debug-brand-chip{border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 70%, transparent);border-radius:var(--yii-debug-radius-pill);background:color-mix(in srgb, var(--yii-debug-panel-surface), transparent 30%);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);min-height:var(--yii-debug-control-min-size);align-items:center;gap:8px;padding:6px 12px;text-decoration:none;transition:border-color .14s,transform .14s,box-shadow .14s;display:inline-flex}.yii-debug a.yii-debug-brand-chip:hover,.yii-debug a.yii-debug-brand-chip:focus{border-color:var(--yii-debug-panel-primary);box-shadow:var(--yii-debug-shadow-sm);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-brand-icon{flex:none;justify-content:center;align-items:center;width:22px;height:22px;display:inline-flex}.yii-debug .yii-debug-brand-icon svg{width:100%;height:100%}.yii-debug .yii-debug-brand-chip-php .yii-debug-brand-icon{width:32px}.yii-debug .yii-debug-brand-chip-php .yii-debug-brand-icon svg{fill:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-brand-label{color:var(--yii-debug-panel-muted);letter-spacing:.06em;text-transform:uppercase;font-weight:600}.yii-debug .yii-debug-brand-value{color:var(--yii-debug-panel-text);font-weight:700}.yii-debug .yii-debug-brand-chip-mem{margin-left:auto}.yii-debug .yii-debug-brand-chip-config{border:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);text-decoration:none;transition:transform .14s,border-color .14s,color .14s,background-color .14s}.yii-debug .yii-debug-brand-chip-config .yii-debug-brand-icon{justify-content:center;align-items:center;width:14px;height:14px;transition:transform .24s;display:inline-flex}.yii-debug .yii-debug-brand-chip-config .yii-debug-brand-icon svg{width:100%;height:100%}.yii-debug .yii-debug-brand-chip-config:hover,.yii-debug .yii-debug-brand-chip-config:focus-visible{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 50%);color:var(--yii-debug-panel-primary-strong);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-brand-chip-config:hover .yii-debug-brand-icon,.yii-debug .yii-debug-brand-chip-config:focus-visible .yii-debug-brand-icon{transform:rotate(45deg)}.yii-debug .yii-debug-brand-chip-config.is-disabled{opacity:.5;cursor:default;pointer-events:none}.yii-debug .yii-debug-brand-chip-config{margin-left:auto}.yii-debug .yii-debug-brand-bar:has(.yii-debug-brand-chip-mem) .yii-debug-brand-chip-config{margin-left:0}.yii-debug .yii-debug-brand-chip-theme{border:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);cursor:pointer;padding:6px 10px;transition:transform .14s,border-color .14s,color .14s,background-color .14s}.yii-debug .yii-debug-brand-chip-theme:hover,.yii-debug .yii-debug-brand-chip-theme:focus-visible{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 50%);color:var(--yii-debug-panel-primary-strong);transform:translateY(-1px)}.yii-debug .yii-debug-brand-chip-theme .yii-debug-brand-icon{justify-content:center;align-items:center;width:16px;height:16px;transition:transform .24s;display:inline-flex}.yii-debug .yii-debug-brand-chip-theme .yii-debug-brand-icon svg{width:100%;height:100%}.yii-debug .yii-debug-brand-chip-theme:hover .yii-debug-brand-icon{transform:rotate(20deg)}.yii-debug .yii-debug-snapshot-line{min-width:0;font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);align-items:baseline;gap:6px;display:flex}.yii-debug .yii-debug-snapshot-method{color:color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-primary-strong)) 80%, var(--yii-debug-panel-text));flex:none;font-weight:700}.yii-debug .yii-debug-snapshot-url{min-width:0;color:var(--yii-debug-panel-text);text-overflow:ellipsis;white-space:nowrap;flex:0 auto;overflow:hidden}.yii-debug .yii-debug-snapshot-meta{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);align-items:center;gap:6px;display:flex}.yii-debug .yii-debug-snapshot-status{border-radius:var(--yii-debug-radius-pill);padding:1px 6px;font-weight:700;display:inline-block}.yii-debug .yii-debug-snapshot-tag{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-pill);padding:0 6px}.yii-debug .yii-debug-request-hero{margin-bottom:var(--yii-debug-space-4);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);box-shadow:var(--yii-debug-shadow-sm)}.yii-debug .yii-debug-request-overview{margin-bottom:var(--yii-debug-space-4);border:var(--yii-debug-border);border-inline-start:4px solid color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)) 76%, var(--yii-debug-panel-text));border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);overflow:hidden}.yii-debug .yii-debug-request-overview-header{gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-4);grid-template-columns:minmax(0,1fr) auto;align-items:center;display:grid}.yii-debug .yii-debug-request-overview-identity{min-width:0}.yii-debug .yii-debug-request-overview-label,.yii-debug .yii-debug-request-overview-status-label,.yii-debug .yii-debug-panel-heading-eyebrow{color:var(--yii-debug-panel-muted);letter-spacing:.09em;text-transform:uppercase;margin-bottom:5px;font-size:.6875rem;font-weight:700;line-height:1.2;display:block}.yii-debug .yii-debug-request-hero-line{align-items:center;gap:var(--yii-debug-space-2);min-width:0;font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-base);display:flex}.yii-debug .yii-debug-request-hero-method{border-radius:var(--yii-debug-radius-pill);letter-spacing:.04em;flex:none;padding:2px 10px;font-weight:700}.yii-debug .yii-debug-request-hero-url{min-width:0;color:var(--yii-debug-panel-text);text-overflow:ellipsis;white-space:nowrap;flex:auto;overflow:hidden}.yii-debug .yii-debug-request-hero-meta{align-items:center;gap:6px var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);flex-wrap:wrap;margin-top:6px;display:flex}.yii-debug .yii-debug-request-hero-meta:empty{display:none}.yii-debug .yii-debug-request-hero-meta-item{align-items:baseline;gap:var(--yii-debug-space-1);display:inline-flex}.yii-debug .yii-debug-request-hero-meta-label{font-family:var(--yii-debug-font-sans);letter-spacing:.07em;text-transform:uppercase;font-size:.625rem;font-weight:700}.yii-debug .yii-debug-request-hero-meta-value{color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-request-overview-status{text-align:right;min-width:72px}.yii-debug .yii-debug-request-overview-status-value{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-lg);letter-spacing:-.03em;padding:3px 9px;line-height:1.2}.yii-debug .yii-debug-request-overview-metrics{border-block:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);grid-template-columns:minmax(0,1fr) minmax(0,2fr) auto;margin:0;display:grid}.yii-debug .yii-debug-request-overview-metric{min-width:0;padding:var(--yii-debug-space-3) var(--yii-debug-space-4)}.yii-debug .yii-debug-request-overview-metric+:is(.yii-debug .yii-debug-request-overview-metric){border-inline-start:var(--yii-debug-border)}.yii-debug .yii-debug-request-overview-metric dt{color:var(--yii-debug-panel-muted);letter-spacing:.07em;text-transform:uppercase;margin-bottom:2px;font-size:.6875rem;font-weight:700}.yii-debug .yii-debug-request-overview-metric dd{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);overflow-wrap:anywhere;margin:0;font-weight:600}.yii-debug .yii-debug-request-overview-meta{align-items:center;gap:var(--yii-debug-space-2) var(--yii-debug-space-4);padding:10px var(--yii-debug-space-4);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-request-overview-meta-item{align-items:baseline;gap:6px;min-width:0;display:inline-flex}.yii-debug .yii-debug-request-overview-meta-label{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);letter-spacing:.07em;text-transform:uppercase;font-size:.625rem;font-weight:700}.yii-debug .yii-debug-request-overview-tag{max-width:34ch;color:var(--yii-debug-panel-text);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.yii-debug .yii-debug-request-overview-flag,.yii-debug .yii-debug-panel-heading-kind{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-pill);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.07em;text-transform:uppercase;padding:2px 8px;font-size:.625rem;font-weight:700;line-height:1.4}.yii-debug .yii-debug-request-routing-error{border-width:1px 0 0 4px;border-radius:0;margin:0}.yii-debug .yii-debug-request-routing-error p{margin:0}.yii-debug .yii-debug-request-tabs{min-width:0}.yii-debug .yii-debug-request-tabs>.yii-debug-tabs{scrollbar-width:thin;flex-wrap:nowrap;overflow-x:auto}.yii-debug .yii-debug-request-tabs .yii-debug-tab{flex:none}.yii-debug .yii-debug-request-tabs .yii-debug-empty-state{padding:var(--yii-debug-space-4)}.yii-debug .yii-debug-request-tabs .yii-debug-empty-state h2{font-size:var(--yii-debug-fs-base);margin:0}.yii-debug .yii-debug-diagnostic-shell{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);overflow:hidden}.yii-debug .yii-debug-diagnostic-header{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);display:flex}.yii-debug .yii-debug-diagnostic-heading-copy{align-items:baseline;gap:var(--yii-debug-space-2) var(--yii-debug-space-3);flex-wrap:wrap;min-width:0;display:flex}.yii-debug .yii-debug-diagnostic-heading-copy h2{font-size:var(--yii-debug-fs-lg);margin:0}.yii-debug .yii-debug-diagnostic-counts{align-items:center;gap:var(--yii-debug-space-2);display:inline-flex}.yii-debug .yii-debug-diagnostic-count,.yii-debug .yii-debug-diagnostic-total,.yii-debug .yii-debug-header-lane-count,.yii-debug .yii-debug-server-group-count{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums}.yii-debug .yii-debug-diagnostic-count{align-items:baseline;gap:var(--yii-debug-space-1);display:inline-flex}.yii-debug .yii-debug-diagnostic-count+.yii-debug-diagnostic-count:before{width:1px;height:12px;background:var(--yii-debug-panel-border-strong);content:"";margin-inline-end:var(--yii-debug-space-1)}.yii-debug .yii-debug-diagnostic-count-value{color:var(--yii-debug-panel-text);font-weight:700}.yii-debug .yii-debug-diagnostic-filter{background:var(--yii-debug-panel-surface);width:min(280px,42%);max-width:none}.yii-debug .yii-debug-header-lanes{grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.yii-debug .yii-debug-header-lane{min-width:0;position:relative}.yii-debug .yii-debug-header-lane+.yii-debug-header-lane{border-inline-start:2px solid color-mix(in srgb, var(--yii-debug-panel-primary) 36%, var(--yii-debug-panel-border))}.yii-debug .yii-debug-header-lane+.yii-debug-header-lane:before{z-index:1;top:25px;border:2px solid var(--yii-debug-panel-surface);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-primary);content:"";width:8px;height:8px;position:absolute;inset-inline-start:-5px}.yii-debug .yii-debug-header-lane-header{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);min-height:58px;padding:10px var(--yii-debug-space-4);border-bottom:var(--yii-debug-border);display:flex}.yii-debug .yii-debug-header-lane-header h3{font-size:var(--yii-debug-fs-base);margin:1px 0 0}.yii-debug .yii-debug-header-direction{color:var(--yii-debug-panel-primary-strong);font-family:var(--yii-debug-font-mono);letter-spacing:.04em;font-size:.6875rem;font-weight:700;display:block}.yii-debug .yii-debug-diagnostic-ledger{margin:0}.yii-debug .yii-debug-diagnostic-row{border-top:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 58%, transparent);grid-template-columns:minmax(132px,.72fr) minmax(0,1.28fr);align-items:start;display:grid}.yii-debug .yii-debug-diagnostic-row:first-child{border-top:0}.yii-debug .yii-debug-diagnostic-row:hover{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-diagnostic-row dt,.yii-debug .yii-debug-diagnostic-row dd{min-width:0;padding:10px var(--yii-debug-space-4);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);line-height:1.5}.yii-debug .yii-debug-diagnostic-row dt{color:var(--yii-debug-panel-muted);overflow-wrap:anywhere;font-weight:600;overflow:hidden}.yii-debug .yii-debug-diagnostic-row dd{border-inline-start:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 45%, transparent);color:var(--yii-debug-panel-text);overflow-wrap:anywhere;white-space:pre-wrap;margin:0}.yii-debug .yii-debug-header-ledger .yii-debug-diagnostic-row:nth-child(2n),.yii-debug .yii-debug-server-ledger .yii-debug-diagnostic-row:nth-child(2n){background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 72%)}:is(.yii-debug .yii-debug-header-ledger .yii-debug-diagnostic-row:nth-child(2n),.yii-debug .yii-debug-server-ledger .yii-debug-diagnostic-row:nth-child(2n)):hover{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-diagnostic-values{gap:6px;display:grid}.yii-debug .yii-debug-diagnostic-value-count{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:.6875rem}.yii-debug .yii-debug-diagnostic-value-list{gap:var(--yii-debug-space-1);margin:0;padding:0;list-style:none;display:grid}.yii-debug .yii-debug-diagnostic-value-list li+li{padding-top:var(--yii-debug-space-1);border-top:1px dashed var(--yii-debug-panel-border)}.yii-debug .yii-debug-diagnostic-empty-value,.yii-debug .yii-debug-diagnostic-lane-empty,.yii-debug .yii-debug-diagnostic-empty-state,.yii-debug .yii-debug-diagnostic-filter-empty{color:var(--yii-debug-panel-muted);font-style:italic}.yii-debug .yii-debug-diagnostic-lane-empty,.yii-debug .yii-debug-diagnostic-empty-state,.yii-debug .yii-debug-diagnostic-filter-empty{padding:var(--yii-debug-space-4);margin:0}.yii-debug .yii-debug-diagnostic-filter-empty{border-top:var(--yii-debug-border);text-align:center}.yii-debug .yii-debug-server-group-body>.yii-debug-mini-toolbar{padding:var(--yii-debug-space-2) var(--yii-debug-space-4);border-bottom:var(--yii-debug-border);justify-content:flex-end;margin:0}.yii-debug .yii-debug-server-group-body>.yii-debug-mini-toolbar .yii-debug-filter-input{max-width:100%}.yii-debug .yii-debug-server-additional+.yii-debug-server-group,.yii-debug .yii-debug-server-group+.yii-debug-server-group{border-top:var(--yii-debug-border)}.yii-debug .yii-debug-server-group-summary{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);min-height:44px;padding:9px var(--yii-debug-space-4);border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);display:flex}.yii-debug .yii-debug-server-group-summary .yii-debug-server-group-title{font-size:var(--yii-debug-fs-sm);margin:0;font-weight:700}.yii-debug .yii-debug-server-group-summary{cursor:pointer;list-style:none}.yii-debug .yii-debug-server-group-summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-server-group-summary:hover,.yii-debug .yii-debug-server-group-summary:focus-visible{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-server-group-identity{align-items:baseline;gap:var(--yii-debug-space-2);display:inline-flex}.yii-debug .yii-debug-server-group:not([open])>.yii-debug-server-group-summary{border-bottom:0}.yii-debug .yii-debug-server-group-summary .yii-debug-disclosure-hint{margin-inline-start:auto}.yii-debug .yii-debug-diagnostic-row[hidden],.yii-debug .yii-debug-header-lane[hidden],.yii-debug .yii-debug-server-group[hidden],.yii-debug .yii-debug-diagnostic-filter-empty[hidden]{display:none}@container yii-debug-main (width<=900px){.yii-debug .yii-debug-header-lanes{grid-template-columns:minmax(0,1fr)}.yii-debug .yii-debug-header-lane+.yii-debug-header-lane{border-top:2px solid color-mix(in srgb, var(--yii-debug-panel-primary) 36%, var(--yii-debug-panel-border));border-inline-start:0}.yii-debug .yii-debug-header-lane+.yii-debug-header-lane:before{top:-5px;inset-inline-start:var(--yii-debug-space-4)}}@container yii-debug-main (width<=560px){.yii-debug .yii-debug-diagnostic-header{flex-direction:column;align-items:stretch}.yii-debug .yii-debug-diagnostic-filter{width:100%}.yii-debug .yii-debug-diagnostic-row{grid-template-columns:minmax(0,1fr)}.yii-debug .yii-debug-diagnostic-row dt{padding-bottom:var(--yii-debug-space-1)}.yii-debug .yii-debug-diagnostic-row dd{border-inline-start:0;padding-top:0}}.yii-debug .yii-debug-route-inventory-header{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-2);display:flex}.yii-debug .yii-debug-route-inventory-title{align-items:baseline;gap:var(--yii-debug-space-2);display:flex}.yii-debug .yii-debug-route-inventory-title h2{font-size:var(--yii-debug-fs-lg);margin:0}.yii-debug .yii-debug-route-inventory-count{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums}.yii-debug .yii-debug-route-inventory-context{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-3);display:flex}.yii-debug .yii-debug-route-inventory-provenance{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);margin:0}.yii-debug .yii-debug-route-inventory-badges,.yii-debug .yii-debug-route-methods{align-items:center;gap:var(--yii-debug-space-1);flex-wrap:wrap;display:inline-flex}.yii-debug .yii-debug-route-inventory-error{margin-bottom:var(--yii-debug-space-3)}.yii-debug .yii-debug-route-inventory-error p{margin:0}.yii-debug .yii-debug-route-ledger{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);overflow:hidden;container:routes/inline-size}.yii-debug .yii-debug-route-columns,.yii-debug .yii-debug-route-summary{align-items:center;gap:var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);grid-template-columns:24px 88px minmax(0,1fr) minmax(0,1fr) 132px;display:grid}.yii-debug .yii-debug-route-columns{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);font-weight:600}.yii-debug .yii-debug-route-list{margin:0;padding:0;list-style:none}.yii-debug .yii-debug-route-list>li{border-top:var(--yii-debug-border)}.yii-debug .yii-debug-route-list>[data-yii-debug-route-match=true]{box-shadow:inset 3px 0 0 var(--yii-debug-success)}.yii-debug .yii-debug-route-summary{cursor:pointer;min-height:56px;list-style:none}.yii-debug .yii-debug-route-summary:hover{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-route-summary:focus-visible{outline:2px solid var(--yii-debug-focus-ring);outline-offset:-3px}.yii-debug .yii-debug-route-summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-route-summary .yii-debug-disclosure-hint{text-align:right;font-size:.625rem}.yii-debug .yii-debug-route-order{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums}.yii-debug .yii-debug-route-method{border-radius:var(--yii-debug-radius-pill);min-height:20px;font-family:var(--yii-debug-font-mono);letter-spacing:.02em;white-space:nowrap;align-items:center;padding:1px 7px;font-size:.6875rem;font-weight:700;line-height:1;display:inline-flex}.yii-debug .yii-debug-route-methods-any{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-route-pattern,.yii-debug .yii-debug-route-identity{overflow-wrap:anywhere;white-space:normal;min-width:0;font-size:var(--yii-debug-fs-xs)}:is(.yii-debug .yii-debug-route-pattern,.yii-debug .yii-debug-route-identity) code{white-space:normal}.yii-debug .yii-debug-route-pattern,.yii-debug .yii-debug-route-identity code{background:0 0;padding:0}.yii-debug .yii-debug-route-identity{align-items:center;gap:var(--yii-debug-space-2);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-route-metadata{padding:var(--yii-debug-space-4);border-top:var(--yii-debug-border);background:var(--yii-debug-panel-surface);font-size:var(--yii-debug-fs-sm);margin:0}.yii-debug .yii-debug-route-metadata>div{gap:var(--yii-debug-space-4);grid-template-columns:120px minmax(0,1fr);display:grid}.yii-debug .yii-debug-route-metadata>div+div{margin-top:var(--yii-debug-space-3)}.yii-debug .yii-debug-route-metadata dt{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-route-metadata dd{overflow-wrap:anywhere;min-width:0;font-family:var(--yii-debug-font-mono);margin:0}@container routes (width<640px){.yii-debug .yii-debug-route-columns{display:none}.yii-debug .yii-debug-route-summary{gap:var(--yii-debug-space-2);grid-template-columns:24px 72px minmax(0,1fr)}.yii-debug .yii-debug-route-identity{grid-column:3}.yii-debug .yii-debug-route-summary .yii-debug-disclosure-hint{text-align:left;grid-column:3}.yii-debug .yii-debug-route-metadata>div{gap:var(--yii-debug-space-1);grid-template-columns:minmax(0,1fr)}}.yii-debug .yii-debug-route-resolution{margin-top:var(--yii-debug-space-4)}.yii-debug .yii-debug-route-resolution-message{margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-sm)}.yii-debug .yii-debug-route-trace{min-width:560px}.yii-debug .yii-debug-panel-heading{justify-content:space-between;align-items:flex-end;gap:var(--yii-debug-space-3);margin:var(--yii-debug-space-5) 0 var(--yii-debug-space-3);padding-bottom:var(--yii-debug-space-2);border-bottom:var(--yii-debug-border);display:flex}.yii-debug .yii-debug-panel-heading-copy{min-width:0}.yii-debug .yii-debug-panel-heading-copy h2{margin:0}.yii-debug .yii-debug-panel-heading-kind{flex:none;margin-bottom:2px}.yii-debug pre.yii-debug-panel-payload{min-height:180px;padding:var(--yii-debug-space-4);border:var(--yii-debug-border);background:linear-gradient(90deg, color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%), transparent 42%), var(--yii-debug-panel-code-bg);margin-bottom:0;line-height:1.55}@media (max-width:640px){.yii-debug .yii-debug-request-overview-header{gap:var(--yii-debug-space-3);grid-template-columns:minmax(0,1fr)}.yii-debug .yii-debug-request-overview-status{align-items:center;gap:var(--yii-debug-space-2);text-align:left;display:flex}.yii-debug .yii-debug-request-overview-status-label{margin:0}.yii-debug .yii-debug-request-overview-metrics{grid-template-columns:1fr}.yii-debug .yii-debug-request-overview-metric+.yii-debug-request-overview-metric{border-block-start:var(--yii-debug-border);border-inline-start:0}.yii-debug .yii-debug-request-overview-meta-item{max-width:100%}.yii-debug .yii-debug-panel-heading{align-items:flex-start}.yii-debug .yii-debug-route-inventory-header,.yii-debug .yii-debug-route-inventory-context{flex-direction:column;align-items:flex-start}.yii-debug .yii-debug-route-filter{width:100%;max-width:none}}.yii-debug .yii-debug-section-header{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);margin:var(--yii-debug-space-4) 0 var(--yii-debug-space-2);display:flex}.yii-debug .yii-debug-section-header h2,.yii-debug .yii-debug-section-header h3{margin:0}.yii-debug .yii-debug-filter-input{width:240px;max-width:50%;padding:4px var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font:inherit;flex:none}.yii-debug .yii-debug-filter-input::placeholder{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-filter-input:focus{border-color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-stack>*+*{margin-top:var(--yii-debug-space-4)}.yii-debug .yii-debug-grid-2{gap:var(--yii-debug-space-4);grid-template-columns:repeat(auto-fit,minmax(280px,1fr));display:grid}.yii-debug .yii-debug-card{padding:var(--yii-debug-panel-padding);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-md)}.yii-debug .yii-debug-nav{padding:var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);margin:0}.yii-debug .yii-debug-nav ul{flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-nav-group{margin-top:var(--yii-debug-space-3)}.yii-debug .yii-debug-nav-group>.yii-debug-nav{box-shadow:none;background:0 0;border:0;border-radius:0;padding:0}.yii-debug .yii-debug-nav-link{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-2);border-radius:var(--yii-debug-radius-md);color:var(--yii-debug-panel-text);padding:8px 12px;font-weight:600;text-decoration:none;transition:background .12s,color .12s;display:flex}.yii-debug .yii-debug-nav-link:after{content:"›";color:var(--yii-debug-panel-muted);font-size:1.1em;line-height:1}.yii-debug .yii-debug-nav-link:hover,.yii-debug .yii-debug-nav-link:focus{background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-text);text-decoration:none}.yii-debug .yii-debug-nav-link.is-active,.yii-debug .yii-debug-nav-link[aria-current=page]{background:var(--yii-debug-primary-bg);color:var(--yii-debug-on-primary)}:is(.yii-debug .yii-debug-nav-link.is-active,.yii-debug .yii-debug-nav-link[aria-current=page]):after{color:inherit}.yii-debug .yii-debug-btn{justify-content:center;align-items:center;gap:var(--yii-debug-space-1);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);min-height:32px;color:var(--yii-debug-panel-text);font-family:inherit;font-size:var(--yii-debug-fs-sm);letter-spacing:0;text-transform:none;cursor:pointer;padding:6px 14px;font-weight:600;line-height:1;text-decoration:none;transition:border-color .12s,background .12s,color .12s,transform .12s,box-shadow .12s;display:inline-flex}.yii-debug .yii-debug-btn:hover,.yii-debug .yii-debug-btn:focus{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 30%);background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-link-hover);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-btn:active{transform:translateY(0)}.yii-debug .yii-debug-btn[disabled],.yii-debug .yii-debug-btn.is-disabled{opacity:.55;pointer-events:none;transform:none}.yii-debug .yii-debug-btn-primary{background:var(--yii-debug-primary-bg);color:var(--yii-debug-on-primary);border-color:#0000}.yii-debug .yii-debug-btn-primary:is(:hover,:focus){background:var(--yii-debug-panel-primary-strong);color:var(--yii-debug-on-primary)}.yii-debug .yii-debug-btn-ghost{border-color:var(--yii-debug-panel-border);background:0 0}.yii-debug .yii-debug-btn-subtle{background:var(--yii-debug-panel-surface-muted);border-color:#0000}.yii-debug .yii-debug-btn-sm{min-height:28px;font-size:var(--yii-debug-fs-xs);padding:4px 10px}.yii-debug .yii-debug-btn-group{gap:var(--yii-debug-space-1);flex-wrap:wrap;display:inline-flex}.yii-debug .yii-debug-badge{border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-xs);letter-spacing:.01em;align-items:center;padding:2px 10px;font-weight:600;line-height:1.4;display:inline-flex}.yii-debug .yii-debug-badge-success{background:var(--yii-debug-success);color:var(--yii-debug-on-success)}.yii-debug .yii-debug-badge-info{background:var(--yii-debug-info);color:var(--yii-debug-on-info)}.yii-debug .yii-debug-badge-warning{background:var(--yii-debug-warning);color:var(--yii-debug-on-warning)}.yii-debug .yii-debug-badge-danger{background:var(--yii-debug-danger);color:var(--yii-debug-on-danger)}.yii-debug .yii-debug-badge-muted{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-verb-get{--yii-debug-hue:var(--yii-debug-verb-get)}.yii-debug .yii-debug-verb-post{--yii-debug-hue:var(--yii-debug-verb-post)}.yii-debug .yii-debug-verb-put{--yii-debug-hue:var(--yii-debug-verb-put)}.yii-debug .yii-debug-verb-delete{--yii-debug-hue:var(--yii-debug-verb-delete)}.yii-debug .yii-debug-verb-other{--yii-debug-hue:var(--yii-debug-verb-other)}.yii-debug .yii-debug-status-2xx{--yii-debug-hue:var(--yii-debug-status-2xx)}.yii-debug .yii-debug-status-3xx{--yii-debug-hue:var(--yii-debug-status-3xx)}.yii-debug .yii-debug-status-4xx{--yii-debug-hue:var(--yii-debug-status-4xx)}.yii-debug .yii-debug-status-5xx{--yii-debug-hue:var(--yii-debug-status-5xx)}.yii-debug .yii-debug-status-none{--yii-debug-hue:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-level-error{--yii-debug-hue:var(--yii-debug-level-error)}.yii-debug .yii-debug-level-warning{--yii-debug-hue:var(--yii-debug-level-warning)}.yii-debug .yii-debug-level-info{--yii-debug-hue:var(--yii-debug-level-info)}.yii-debug .yii-debug-level-trace{--yii-debug-hue:var(--yii-debug-level-trace)}.yii-debug .yii-debug-level-profile{--yii-debug-hue:var(--yii-debug-level-profile)}.yii-debug .yii-debug-level-other{--yii-debug-hue:var(--yii-debug-level-other)}.yii-debug .yii-debug-request-hero-method,.yii-debug .yii-debug-route-method,.yii-debug .yii-debug-snapshot-status,.yii-debug .yii-debug-level-chip,.yii-debug .yii-debug-db-type,.yii-debug .yii-debug-mail-status,.yii-debug .yii-debug-dump-type,.yii-debug .yii-debug-badge:is(.yii-debug-status-2xx,.yii-debug-status-3xx,.yii-debug-status-4xx,.yii-debug-status-5xx,.yii-debug-status-none){border:1px solid color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)), transparent 65%);background:color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)), transparent 90%);color:color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)) 80%, var(--yii-debug-panel-text))}.yii-debug .yii-debug-method{color:color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)) 80%, var(--yii-debug-panel-text));font-family:var(--yii-debug-font-mono);letter-spacing:.02em;font-weight:700}.yii-debug .yii-debug-level-chip{border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;padding:1px 8px;font-size:.7rem;font-weight:700;display:inline-block}.yii-debug .yii-debug-callout{align-items:flex-start;gap:var(--yii-debug-space-2);margin-bottom:var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:var(--yii-debug-border);border-left:4px solid var(--yii-debug-panel-border-strong);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);display:flex}.yii-debug .yii-debug-callout-info{border-left-color:var(--yii-debug-info)}.yii-debug .yii-debug-callout-success{border-left-color:var(--yii-debug-success)}.yii-debug .yii-debug-callout-warning{border-left-color:var(--yii-debug-warning)}.yii-debug .yii-debug-callout-danger{border-left-color:var(--yii-debug-danger)}.yii-debug .yii-debug-router-callout{flex-direction:column}.yii-debug .yii-debug-router-callout-message{margin:0}.yii-debug .yii-debug-router-summary{gap:4px var(--yii-debug-space-3);margin:0 0 var(--yii-debug-space-3);font-size:var(--yii-debug-fs-sm);grid-template-columns:max-content minmax(0,1fr);display:grid}.yii-debug .yii-debug-router-summary dt{color:var(--yii-debug-panel-muted);letter-spacing:.04em;text-transform:uppercase;font-weight:600;font-size:var(--yii-debug-fs-xs);align-self:center}.yii-debug .yii-debug-router-summary dd{overflow-wrap:anywhere;min-width:0;margin:0}.yii-debug .yii-debug-router-summary code{border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface);min-width:0;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);overflow-wrap:anywhere;padding:1px 8px;display:inline}.yii-debug .yii-debug-table-wrap{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);overflow-x:auto}.yii-debug .yii-debug-table{border-collapse:separate;border-spacing:0;width:100%;min-width:640px;color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);background:0 0;margin:0}.yii-debug .yii-debug-table thead th,.yii-debug .yii-debug-table thead td{border-bottom:var(--yii-debug-border);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 20%);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);letter-spacing:.03em;text-align:left;text-transform:uppercase;vertical-align:middle;padding:10px 12px;font-weight:700}.yii-debug .yii-debug-table thead th a{color:var(--yii-debug-panel-link);font-weight:700;text-decoration:none}.yii-debug .yii-debug-table thead th a:hover,.yii-debug .yii-debug-table thead th a:focus{color:var(--yii-debug-panel-link-hover);text-decoration:none}.yii-debug .yii-debug-table tbody td,.yii-debug .yii-debug-table tbody th{padding:var(--yii-debug-row-padding-block) 12px;border-top:1px solid var(--yii-debug-panel-border);vertical-align:top;overflow-wrap:anywhere}.yii-debug .yii-debug-table tbody th{white-space:nowrap}.yii-debug .yii-debug-table tbody tr:first-child td,.yii-debug .yii-debug-table tbody tr:first-child th{border-top:0}.yii-debug .yii-debug-table tbody tr:nth-child(2n) td,.yii-debug .yii-debug-table tbody tr:nth-child(2n) th{background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 50%)}.yii-debug .yii-debug-table tbody tr:hover td,.yii-debug .yii-debug-table tbody tr:hover th{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-table tfoot td{border-top:var(--yii-debug-border);color:var(--yii-debug-panel-muted);padding:10px 12px}.yii-debug .yii-debug-row-success td,.yii-debug .yii-debug-row-success th{background:color-mix(in srgb, var(--yii-debug-success), var(--yii-debug-panel-surface) 86%)!important}.yii-debug .yii-debug-row-info td,.yii-debug .yii-debug-row-info th{background:color-mix(in srgb, var(--yii-debug-info), var(--yii-debug-panel-surface) 86%)!important}.yii-debug .yii-debug-row-warning td,.yii-debug .yii-debug-row-warning th{background:color-mix(in srgb, var(--yii-debug-warning), var(--yii-debug-panel-surface) 86%)!important}.yii-debug .yii-debug-row-danger td,.yii-debug .yii-debug-row-danger th{background:color-mix(in srgb, var(--yii-debug-danger), var(--yii-debug-panel-surface) 86%)!important}.yii-debug .yii-debug-grid{margin-bottom:var(--yii-debug-space-4)}.yii-debug .yii-debug-grid>.summary{margin:0 0 var(--yii-debug-space-2);padding:0 var(--yii-debug-space-1);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-sm)}.yii-debug .yii-debug-grid>.summary b{color:var(--yii-debug-panel-text);font-weight:700}.yii-debug .yii-debug-grid>.empty{padding:var(--yii-debug-space-5);color:var(--yii-debug-panel-muted);text-align:center;font-style:italic}.yii-debug .yii-debug-grid td.yii-debug-cell-nowrap,.yii-debug .yii-debug-grid td.yii-debug-cell-numeric,.yii-debug .yii-debug-grid td.yii-debug-cell-pill,.yii-debug .yii-debug-table td.yii-debug-cell-nowrap,.yii-debug .yii-debug-table td.yii-debug-cell-numeric,.yii-debug .yii-debug-table td.yii-debug-cell-pill{white-space:nowrap}.yii-debug .yii-debug-grid td.yii-debug-cell-fqcn{overflow-wrap:normal}.yii-debug .yii-debug-disclosure{margin-bottom:var(--yii-debug-space-5);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:var(--yii-debug-panel-surface);overflow:hidden}.yii-debug .yii-debug-disclosure[open]>.yii-debug-disclosure-summary{border-bottom:var(--yii-debug-border)}.yii-debug .yii-debug-disclosure-summary{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);padding:10px var(--yii-debug-space-4);cursor:pointer;list-style:none;transition:background-color .12s;display:flex}.yii-debug .yii-debug-disclosure-summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-disclosure-summary:hover,.yii-debug .yii-debug-disclosure-summary:focus-visible{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-disclosure-title,.yii-debug .yii-debug-request-section-title{color:var(--yii-debug-panel-primary-strong);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.12em;text-transform:uppercase;font-weight:700}.yii-debug .yii-debug-disclosure-hint{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;transition:color .12s}.yii-debug .yii-debug-disclosure-hint [data-yii-debug-hint=expanded]{display:none}.yii-debug .yii-debug-disclosure-summary:hover .yii-debug-disclosure-hint,.yii-debug .yii-debug-disclosure-summary:focus-visible .yii-debug-disclosure-hint{color:var(--yii-debug-panel-link-hover)}.yii-debug details[open]>summary .yii-debug-disclosure-hint [data-yii-debug-hint=collapsed]{display:none}.yii-debug details[open]>summary .yii-debug-disclosure-hint [data-yii-debug-hint=expanded]{display:inline}.yii-debug .yii-debug-disclosure-body{padding:var(--yii-debug-space-4)}.yii-debug .yii-debug-disclosure-body>pre{overflow-wrap:anywhere;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);white-space:pre-wrap;margin:0;line-height:1.6}.yii-debug .yii-debug-cell-more-body{max-height:156px;position:relative;overflow:hidden}.yii-debug .yii-debug-cell-more-body:has(.yii-debug-table){max-height:420px}.yii-debug .yii-debug-cell-more-body:after{content:"";background:linear-gradient(to bottom, transparent, var(--yii-debug-panel-surface));pointer-events:none;height:32px;position:absolute;inset:auto 0 0}.yii-debug .yii-debug-cell-more.is-open .yii-debug-cell-more-body{max-height:none}.yii-debug .yii-debug-cell-more.is-open .yii-debug-cell-more-body:after{content:none}.yii-debug .yii-debug-cell-more-toggle{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);color:var(--yii-debug-panel-link);font-family:inherit;font-size:var(--yii-debug-fs-xs);cursor:pointer;background:0 0;align-items:center;margin-top:6px;padding:2px 10px;font-weight:600;display:inline-flex}.yii-debug .yii-debug-cell-more-toggle:before{content:"";width:1.25em;height:1.25em;margin-right:var(--yii-debug-space-1);background:linear-gradient(currentcolor, currentcolor) center / 8px 2px no-repeat, linear-gradient(currentcolor, currentcolor) center / 2px 8px no-repeat, var(--yii-debug-panel-bg-accent);border-radius:50%;display:inline-block}.yii-debug .yii-debug-cell-more.is-open .yii-debug-cell-more-toggle:before{background:linear-gradient(currentcolor, currentcolor) center / 8px 2px no-repeat, var(--yii-debug-panel-bg-accent)}.yii-debug .yii-debug-cell-more-toggle:hover,.yii-debug .yii-debug-cell-more-toggle:focus{background:var(--yii-debug-panel-bg-accent);border-color:var(--yii-debug-panel-primary);text-decoration:none}.yii-debug .yii-debug-grid td.yii-debug-cell-mono{font-family:var(--yii-debug-font-mono)}.yii-debug td.yii-debug-cell-payload{overflow-wrap:anywhere;width:100%}.yii-debug .yii-debug-grid td.yii-debug-cell-numeric{font-variant-numeric:tabular-nums;text-align:right}.yii-debug a.asc:after,.yii-debug a.desc:after,.yii-debug .yii-debug-sort-asc:after,.yii-debug .yii-debug-sort-desc:after{content:"↑";opacity:.78;margin-left:4px;font-size:.85em}.yii-debug a.desc:after,.yii-debug .yii-debug-sort-desc:after{content:"↓"}.yii-debug .yii-debug-pager{gap:var(--yii-debug-space-1);margin:var(--yii-debug-space-3) 0 0;flex-wrap:wrap;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-pager-item{display:inline-flex}.yii-debug .yii-debug-pager-link,.yii-debug .yii-debug-pager>li>:is(a,span){border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);min-width:40px;min-height:40px;color:var(--yii-debug-panel-link);font-size:var(--yii-debug-fs-sm);justify-content:center;align-items:center;padding:0 10px;font-weight:600;text-decoration:none;transition:border-color .12s,background .12s,color .12s;display:inline-flex}:is(.yii-debug .yii-debug-pager-link,.yii-debug .yii-debug-pager>li>:is(a,span)):hover,:is(.yii-debug .yii-debug-pager-link,.yii-debug .yii-debug-pager>li>:is(a,span)):focus{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 30%);background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-link-hover);text-decoration:none}.yii-debug .yii-debug-pager-item.is-active .yii-debug-pager-link{background:var(--yii-debug-primary-bg);color:var(--yii-debug-on-primary);cursor:default;border-color:#0000}.yii-debug .yii-debug-pager-item.is-disabled .yii-debug-pager-link{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);cursor:not-allowed;pointer-events:none}.yii-debug .yii-debug-field{margin-bottom:var(--yii-debug-space-3);flex-direction:column;gap:4px;display:flex}.yii-debug .yii-debug-field-grid{gap:var(--yii-debug-space-3) var(--yii-debug-space-4);margin-bottom:var(--yii-debug-space-3);grid-template-columns:repeat(auto-fit,minmax(240px,1fr));display:grid}.yii-debug .yii-debug-label{color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);font-weight:600}.yii-debug .yii-debug-input,.yii-debug .yii-debug-select,.yii-debug .yii-debug-textarea{border:1px solid var(--yii-debug-panel-border-strong);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);width:100%;min-height:32px;color:var(--yii-debug-panel-text);font-family:inherit;font-size:var(--yii-debug-fs-sm);padding:6px 10px;transition:border-color .12s,box-shadow .12s}:is(.yii-debug .yii-debug-input,.yii-debug .yii-debug-select,.yii-debug .yii-debug-textarea):hover{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), var(--yii-debug-panel-border-strong) 60%)}:is(.yii-debug .yii-debug-input,.yii-debug .yii-debug-select,.yii-debug .yii-debug-textarea):focus{border-color:var(--yii-debug-panel-primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 82%)}:is(.yii-debug .yii-debug-input,.yii-debug .yii-debug-select,.yii-debug .yii-debug-textarea)::placeholder{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-textarea{resize:vertical;min-height:96px;line-height:1.45}.yii-debug .yii-debug-grid .filters td,.yii-debug .yii-debug-grid td.yii-debug-filter-cell{border-bottom:var(--yii-debug-border);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 30%);padding:6px 12px}.yii-debug .yii-debug-grid .filters input,.yii-debug .yii-debug-grid .filters select,.yii-debug .yii-debug-grid .filters textarea,.yii-debug .yii-debug-grid td.yii-debug-filter-cell input,.yii-debug .yii-debug-grid td.yii-debug-filter-cell select,.yii-debug .yii-debug-grid td.yii-debug-filter-cell textarea{border:1px solid var(--yii-debug-panel-border-strong);border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface);width:100%;min-width:0;min-height:28px;color:var(--yii-debug-panel-text);font:inherit;font-size:var(--yii-debug-fs-xs);padding:4px 8px}:is(.yii-debug .yii-debug-grid .filters input,.yii-debug .yii-debug-grid .filters select,.yii-debug .yii-debug-grid .filters textarea,.yii-debug .yii-debug-grid td.yii-debug-filter-cell input,.yii-debug .yii-debug-grid td.yii-debug-filter-cell select,.yii-debug .yii-debug-grid td.yii-debug-filter-cell textarea):focus{border-color:var(--yii-debug-panel-primary);box-shadow:0 0 0 2px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 84%)}.yii-debug .yii-debug-tabs{gap:var(--yii-debug-space-1);margin:0 0 var(--yii-debug-space-3);border-bottom:var(--yii-debug-border);flex-wrap:wrap;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-tab{display:inline-flex}.yii-debug .yii-debug-tab-link{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-sm);border-bottom:2px solid #0000;align-items:center;padding:8px 14px;font-weight:600;text-decoration:none;transition:color .12s,border-color .12s;display:inline-flex}.yii-debug .yii-debug-tab-link:hover,.yii-debug .yii-debug-tab-link:focus{color:var(--yii-debug-panel-text);text-decoration:none}.yii-debug .yii-debug-tab.is-active .yii-debug-tab-link,.yii-debug .yii-debug-tab-link.is-active{border-bottom-color:var(--yii-debug-panel-primary);color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-tab-panel{padding:var(--yii-debug-space-3) 0 0;display:none}.yii-debug .yii-debug-tab-panel.is-active{display:block}.yii-debug .yii-debug-collapsible{margin-top:var(--yii-debug-space-3);display:none}.yii-debug .yii-debug-collapsible.is-open{display:block}.yii-debug .yii-debug-dropdown{display:inline-block;position:relative}.yii-debug .yii-debug-dropdown-menu{z-index:100;border:var(--yii-debug-border-strong);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);min-width:220px;box-shadow:var(--yii-debug-shadow-md);margin:0;padding:4px;list-style:none;display:none;position:absolute;top:calc(100% + 4px);left:0}.yii-debug .yii-debug-dropdown.is-open .yii-debug-dropdown-menu{display:block}.yii-debug .yii-debug-dropdown-item{border-radius:var(--yii-debug-radius-sm);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);padding:6px 10px;text-decoration:none;display:block}.yii-debug .yii-debug-dropdown-item:hover,.yii-debug .yii-debug-dropdown-item:focus{background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-link-hover);text-decoration:none}.yii-debug .yii-debug-since-previous{white-space:nowrap;align-items:center;gap:4px;display:inline-flex}.yii-debug .yii-debug-since-previous .yii-debug-since-previous-btn{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface);min-width:24px;height:24px;color:var(--yii-debug-panel-link);font-size:var(--yii-debug-fs-xs);justify-content:center;align-items:center;padding:0 6px;font-weight:700;text-decoration:none;display:inline-flex}.yii-debug .yii-debug-table-mono{min-width:0}.yii-debug .yii-debug-table-mono thead th{z-index:1;background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 10%);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.12em;padding:8px 16px;position:sticky;top:0}.yii-debug .yii-debug-table-mono tbody tr{transition:background-color .14s}.yii-debug .yii-debug-table-mono tbody tr:hover td,.yii-debug .yii-debug-table-mono tbody tr:hover th{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-table-mono tbody tr:hover th{box-shadow:inset 3px 0 0 var(--yii-debug-panel-primary);color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-table-mono tbody th,.yii-debug .yii-debug-table-mono tbody td{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);padding:12px 16px;line-height:1.55}.yii-debug .yii-debug-table-mono tbody th{width:1%;min-width:120px;max-width:220px;color:var(--yii-debug-panel-muted);letter-spacing:.02em;white-space:nowrap;text-overflow:ellipsis;font-weight:500;transition:color .14s,box-shadow .14s;overflow:hidden}.yii-debug .yii-debug-table-mono tbody td{color:var(--yii-debug-panel-text);word-break:normal;overflow-wrap:anywhere}.yii-debug .yii-debug-table-mono tbody tr+tr td,.yii-debug .yii-debug-table-mono tbody tr+tr th{border-top:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 55%, transparent)}.yii-debug .yii-debug-table-mono tbody tr:nth-child(2n) td,.yii-debug .yii-debug-table-mono tbody tr:nth-child(2n) th{background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 70%)}.yii-debug .yii-debug-table-vite-overview tbody th{width:200px;min-width:160px;max-width:200px}.yii-debug .yii-debug-vite-component+.yii-debug-vite-component{margin-top:var(--yii-debug-space-6)}.yii-debug .yii-debug-table-wrap:has(>.yii-debug-table-mono){border-radius:var(--yii-debug-radius-md);box-shadow:var(--yii-debug-shadow-sm), inset 0 0 0 1px color-mix(in srgb, var(--yii-debug-panel-border) 60%, transparent);overflow:clip}.yii-debug .yii-debug-table-mono{min-width:0}.yii-debug .yii-debug-table-mono tbody th{overflow-wrap:anywhere;white-space:normal}.yii-debug .yii-debug-table-userswitch{width:auto;min-width:100%}.yii-debug .yii-debug-table-userswitch thead th,.yii-debug .yii-debug-table-userswitch tbody td,.yii-debug .yii-debug-table-userswitch tbody th{overflow-wrap:normal;white-space:nowrap}.yii-debug .yii-debug-table-userswitch tbody tr.filters td input,.yii-debug .yii-debug-table-userswitch td.yii-debug-filter-cell input{width:100%;min-width:0}.yii-debug .yii-debug-col-userswitch-id{font-variant-numeric:tabular-nums;min-width:10ch}.yii-debug .yii-debug-col-userswitch-username{min-width:14ch}.yii-debug .yii-debug-col-userswitch-email{min-width:24ch}.yii-debug .yii-debug-col-userswitch-status{text-align:center;min-width:8ch}.yii-debug .yii-debug-col-userswitch-timestamp{font-variant-numeric:tabular-nums;min-width:18ch}.yii-debug .yii-debug-table-pointer tbody tr{cursor:pointer;transition:background-color .12s}.yii-debug .yii-debug-table-pointer tbody tr:hover{background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 86%)}.yii-debug .yii-debug-timeline-duration{margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);letter-spacing:.04em}.yii-debug .yii-debug-user{gap:var(--yii-debug-space-3);flex-direction:column;display:flex}.yii-debug .yii-debug-user-card{gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-4) var(--yii-debug-space-5);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-lg);background:radial-gradient(circle at top right, color-mix(in srgb, var(--yii-debug-panel-primary), transparent 80%), transparent 60%), var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);grid-template-columns:auto 1fr;align-items:center;display:grid;position:relative;overflow:hidden}.yii-debug .yii-debug-user-avatar{background:linear-gradient(135deg, var(--yii-debug-panel-primary), var(--yii-debug-info));width:64px;height:64px;box-shadow:0 4px 12px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 50%), inset 0 1px 0 #ffffff38;color:#fff;font-family:var(--yii-debug-font-mono);letter-spacing:-.02em;border-radius:50%;flex:none;justify-content:center;align-items:center;font-size:28px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-user-meta{flex-direction:column;gap:4px;min-width:0;display:flex}.yii-debug .yii-debug-user-name{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xl);letter-spacing:-.01em;margin:0;font-weight:700;line-height:1.2}.yii-debug .yii-debug-user-handle{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);margin:0}.yii-debug .yii-debug-user-tags{flex-wrap:wrap;gap:8px;margin-top:6px;display:flex}.yii-debug .yii-debug-user-status{border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;align-items:center;gap:6px;padding:4px 12px 4px 8px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-user-status:before{content:"";background:currentColor;border-radius:50%;width:7px;height:7px}.yii-debug .yii-debug-user-status-success{background:color-mix(in srgb, var(--yii-debug-success), transparent 84%);color:var(--yii-debug-success)}.yii-debug .yii-debug-user-status-success:before{box-shadow:0 0 0 0 color-mix(in srgb, var(--yii-debug-success), transparent 30%);animation:2.4s ease-in-out infinite yii-debug-user-pulse}.yii-debug .yii-debug-user-status-danger{background:color-mix(in srgb, var(--yii-debug-danger), transparent 84%);color:var(--yii-debug-danger)}.yii-debug .yii-debug-user-status-warning{background:color-mix(in srgb, var(--yii-debug-warning), transparent 84%);color:var(--yii-debug-warning)}.yii-debug .yii-debug-user-status-muted{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-user-pill{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-pill);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;align-items:center;padding:4px 10px;display:inline-flex}.yii-debug .yii-debug-user-section{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);overflow:hidden}.yii-debug .yii-debug-user-section>header{padding:10px var(--yii-debug-space-3);border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;align-items:center;gap:10px;font-weight:700;display:flex}.yii-debug .yii-debug-user-section dl{margin:0;padding:0}.yii-debug .yii-debug-user-section-icon{width:18px;height:18px;color:var(--yii-debug-panel-primary);justify-content:center;align-items:center;display:inline-flex}.yii-debug .yii-debug-user-section-icon svg{width:100%;height:100%}.yii-debug .yii-debug-user-row{gap:var(--yii-debug-space-3);padding:12px var(--yii-debug-space-3);border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 50%, transparent);grid-template-columns:minmax(140px,22%) 1fr;display:grid}.yii-debug .yii-debug-user-row:last-child{border-bottom:0}.yii-debug .yii-debug-user-row dt{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em;align-self:center}.yii-debug .yii-debug-user-row dd{min-width:0;margin:0}.yii-debug .yii-debug-user-value{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);word-break:break-all}.yii-debug .yii-debug-user-empty{color:color-mix(in srgb, var(--yii-debug-panel-muted), transparent 35%);font-family:var(--yii-debug-font-mono)}.yii-debug .yii-debug-user-time{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);align-items:baseline;gap:10px;display:inline-flex}.yii-debug .yii-debug-user-time .yii-debug-user-time-rel{color:var(--yii-debug-panel-text);font-weight:600}.yii-debug .yii-debug-user-time .yii-debug-user-time-abs{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-user-reveal{border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 80%, transparent);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);font:inherit;cursor:pointer;text-align:left;align-items:center;gap:12px;padding:6px 6px 6px 14px;transition:border-color .14s,background .14s;display:inline-flex}.yii-debug .yii-debug-user-reveal:hover,.yii-debug .yii-debug-user-reveal:focus{border-color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-user-reveal .yii-debug-user-mask,.yii-debug .yii-debug-user-reveal .yii-debug-user-real{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm)}.yii-debug .yii-debug-user-reveal .yii-debug-user-mask{letter-spacing:.1em;color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-user-reveal .yii-debug-user-real{overflow-wrap:anywhere;max-width:60ch;color:var(--yii-debug-panel-text);display:none}.yii-debug .yii-debug-user-reveal .yii-debug-user-reveal-cta{border-radius:var(--yii-debug-radius-pill);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 80%);color:var(--yii-debug-panel-primary);font-size:var(--yii-debug-fs-xs);letter-spacing:.08em;text-transform:uppercase;flex:none;padding:3px 10px;font-weight:700}.yii-debug .yii-debug-user-reveal .yii-debug-user-reveal-cta:before{content:"Reveal"}.yii-debug .yii-debug-user-reveal.is-revealed .yii-debug-user-mask{display:none}.yii-debug .yii-debug-user-reveal.is-revealed .yii-debug-user-real{display:inline}.yii-debug .yii-debug-user-reveal.is-revealed .yii-debug-user-reveal-cta{background:color-mix(in srgb, var(--yii-debug-warning), transparent 84%);color:var(--yii-debug-warning)}.yii-debug .yii-debug-user-reveal.is-revealed .yii-debug-user-reveal-cta:before{content:"Hide"}.yii-debug .yii-debug-sr-only{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.yii-debug .yii-debug-table-empty{padding:var(--yii-debug-space-4);border:1px dashed color-mix(in srgb, var(--yii-debug-panel-border) 70%, transparent);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 40%);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;justify-content:center;align-items:center;gap:10px;margin:0;display:flex}.yii-debug .yii-debug-table-empty:before{content:"";opacity:.5;border:1.5px solid;border-radius:50%;width:8px;height:8px}.yii-debug .yii-debug-grid-summary{align-items:center;gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);letter-spacing:.06em;text-transform:uppercase;flex-wrap:wrap;display:flex}.yii-debug .yii-debug-grid-summary strong{color:var(--yii-debug-panel-primary-strong);letter-spacing:0;text-transform:none;margin-right:.35em;font-weight:700}.yii-debug .yii-debug-grid-summary-sep{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-grid-summary-stat-warn{--yii-debug-hue:var(--yii-debug-level-warning)}.yii-debug .yii-debug-grid-summary-stat-danger{--yii-debug-hue:var(--yii-debug-level-error)}.yii-debug .yii-debug-grid-summary-stat-info{--yii-debug-hue:var(--yii-debug-level-info)}.yii-debug .yii-debug-grid-summary-stat-trace{--yii-debug-hue:var(--yii-debug-level-trace)}.yii-debug .yii-debug-grid-summary-stat-warn strong{color:var(--yii-debug-warning)}.yii-debug .yii-debug-grid-summary-stat-danger strong{color:var(--yii-debug-danger)}.yii-debug .yii-debug-grid-summary-stat-2xx{--yii-debug-hue:var(--yii-debug-status-2xx)}.yii-debug .yii-debug-grid-summary-stat-3xx{--yii-debug-hue:var(--yii-debug-status-3xx)}.yii-debug .yii-debug-grid-summary-stat-4xx{--yii-debug-hue:var(--yii-debug-status-4xx)}.yii-debug .yii-debug-grid-summary-stat-5xx{--yii-debug-hue:var(--yii-debug-status-5xx)}.yii-debug a:is(.yii-debug-grid-summary-stat-danger,.yii-debug-grid-summary-stat-warn,.yii-debug-grid-summary-stat-info,.yii-debug-grid-summary-stat-trace,.yii-debug-grid-summary-stat-2xx,.yii-debug-grid-summary-stat-3xx,.yii-debug-grid-summary-stat-4xx,.yii-debug-grid-summary-stat-5xx){border:1px solid color-mix(in srgb, var(--yii-debug-hue), transparent 65%);border-radius:var(--yii-debug-radius-pill);background:color-mix(in srgb, var(--yii-debug-hue), transparent 90%);color:color-mix(in srgb, var(--yii-debug-hue) 80%, var(--yii-debug-panel-text));align-items:center;gap:6px;padding:2px 10px;text-decoration:none;transition:transform .14s,background-color .14s;display:inline-flex}.yii-debug a:is(.yii-debug-grid-summary-stat-danger,.yii-debug-grid-summary-stat-warn,.yii-debug-grid-summary-stat-info,.yii-debug-grid-summary-stat-trace,.yii-debug-grid-summary-stat-2xx,.yii-debug-grid-summary-stat-3xx,.yii-debug-grid-summary-stat-4xx,.yii-debug-grid-summary-stat-5xx) strong{color:inherit;margin-right:0}.yii-debug a:is(.yii-debug-grid-summary-stat-danger,.yii-debug-grid-summary-stat-warn,.yii-debug-grid-summary-stat-info,.yii-debug-grid-summary-stat-trace,.yii-debug-grid-summary-stat-2xx,.yii-debug-grid-summary-stat-3xx,.yii-debug-grid-summary-stat-4xx,.yii-debug-grid-summary-stat-5xx):hover,.yii-debug a:is(.yii-debug-grid-summary-stat-danger,.yii-debug-grid-summary-stat-warn,.yii-debug-grid-summary-stat-info,.yii-debug-grid-summary-stat-trace,.yii-debug-grid-summary-stat-2xx,.yii-debug-grid-summary-stat-3xx,.yii-debug-grid-summary-stat-4xx,.yii-debug-grid-summary-stat-5xx):focus-visible{background:color-mix(in srgb, var(--yii-debug-hue), transparent 80%);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-grid-summary-action{color:var(--yii-debug-panel-primary-strong);letter-spacing:0;text-transform:none;margin-left:auto;font-weight:700;text-decoration:none}.yii-debug .yii-debug-grid-summary-action:hover,.yii-debug .yii-debug-grid-summary-action:focus-visible{color:var(--yii-debug-panel-link-hover);text-decoration:underline}.yii-debug .yii-debug-grid-footer{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-3);margin-top:var(--yii-debug-space-2);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-grid-count{margin:0}.yii-debug .yii-debug-grid-pagesize{align-items:center;gap:6px;margin-left:auto;display:inline-flex}.yii-debug .yii-debug-grid-pagesize-label{color:var(--yii-debug-panel-muted);letter-spacing:.06em;text-transform:uppercase;font-weight:600}.yii-debug .yii-debug-grid-pagesize-select{padding:3px var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-sm);letter-spacing:0;text-transform:none;cursor:pointer}.yii-debug .yii-debug-grid-pagesize-select:focus{border-color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-db-type{border-radius:var(--yii-debug-radius-pill);min-width:5em;font-family:var(--yii-debug-font-mono);letter-spacing:.04em;text-align:center;text-transform:uppercase;white-space:nowrap;padding:2px 8px;font-size:.7rem;font-weight:700;display:inline-block}.yii-debug .yii-debug-db-sql{font-family:var(--yii-debug-font-mono);overflow-wrap:anywhere}.yii-debug .yii-debug-grid-db .yii-debug-table th:not(:last-child),.yii-debug .yii-debug-grid-db .yii-debug-table td:not(:last-child){white-space:nowrap}.yii-debug .yii-debug-grid-db .yii-debug-table th:last-child,.yii-debug .yii-debug-grid-db .yii-debug-table td:last-child{width:100%}.yii-debug .yii-debug-grid-db .filters select,.yii-debug .yii-debug-grid-db td.yii-debug-filter-cell select{min-width:max-content}:is(.yii-debug .yii-debug-grid-db .filters select,.yii-debug .yii-debug-grid-db td.yii-debug-filter-cell select):focus{box-shadow:none}:is(.yii-debug .yii-debug-grid-db .filters select,.yii-debug .yii-debug-grid-db td.yii-debug-filter-cell select):focus-visible{outline-offset:1px;outline-width:2px}.yii-debug .yii-debug-sql-kw{color:var(--yii-debug-sql-kw);font-weight:700}.yii-debug .yii-debug-sql-str{color:var(--yii-debug-sql-str)}.yii-debug .yii-debug-sql-num{color:var(--yii-debug-sql-num)}.yii-debug .yii-debug-sql-comment{color:var(--yii-debug-sql-comment);font-style:italic}.yii-debug .yii-debug-sql-param{color:var(--yii-debug-sql-param);font-weight:600}.yii-debug .yii-debug-db-explain,.yii-debug .yii-debug-db-trace{margin-top:var(--yii-debug-space-2)}.yii-debug .yii-debug-db-explain-toggle,.yii-debug .yii-debug-db-trace-toggle{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);color:var(--yii-debug-panel-link);font-size:var(--yii-debug-fs-xs);cursor:pointer;background:0 0;align-items:center;gap:6px;padding:2px 10px 2px 6px;font-weight:600;text-decoration:none;transition:background-color .12s,border-color .12s;display:inline-flex}.yii-debug .yii-debug-db-explain-toggle:hover,.yii-debug .yii-debug-db-explain-toggle:focus,.yii-debug .yii-debug-db-trace-toggle:hover,.yii-debug .yii-debug-db-trace-toggle:focus-visible{background:var(--yii-debug-panel-bg-accent);border-color:var(--yii-debug-panel-primary);text-decoration:none}.yii-debug .yii-debug-db-trace-toggle{list-style:none}.yii-debug .yii-debug-db-trace-toggle::-webkit-details-marker{display:none}.yii-debug .yii-debug-db-explain-chevron,.yii-debug .yii-debug-db-trace-chevron{width:10px;font-size:14px;line-height:1;transition:transform .16s;display:inline-block}.yii-debug .yii-debug-db-explain.is-open .yii-debug-db-explain-chevron,.yii-debug .yii-debug-db-trace[open]>.yii-debug-db-trace-toggle .yii-debug-db-trace-chevron{transform:rotate(90deg)}.yii-debug .yii-debug-db-explain.is-loading .yii-debug-db-explain-toggle{opacity:.55;pointer-events:none}.yii-debug .yii-debug-db-explain-text{margin-top:var(--yii-debug-space-2);display:none}.yii-debug .yii-debug-db-explain.is-open .yii-debug-db-explain-text{display:block}.yii-debug .yii-debug-db-explain-text.is-error{padding:var(--yii-debug-space-2) var(--yii-debug-space-3);border-left:3px solid var(--yii-debug-danger);border-radius:var(--yii-debug-radius-sm);background:color-mix(in srgb, var(--yii-debug-danger), transparent 92%);color:var(--yii-debug-danger);font-size:var(--yii-debug-fs-sm);font-weight:600;display:block}.yii-debug .yii-debug-db-explain-all{margin-top:var(--yii-debug-space-3);justify-content:flex-end;display:flex}.yii-debug .yii-debug-db-explain-all-toggle{white-space:nowrap;justify-content:center;min-width:104px}.yii-debug .yii-debug-explain{margin-top:var(--yii-debug-space-3)}.yii-debug .yii-debug-explain-title{margin-bottom:var(--yii-debug-space-2);font-size:var(--yii-debug-fs-lg);font-weight:700}.yii-debug .yii-debug-explain-query{margin-bottom:var(--yii-debug-space-3);padding:var(--yii-debug-space-3);background:var(--yii-debug-panel-code-bg);border-radius:var(--yii-debug-radius-md);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);white-space:pre-wrap;overflow-wrap:break-word}.yii-debug .yii-debug-explain-empty{color:var(--yii-debug-panel-muted);font-style:italic}.yii-debug .yii-debug-empty-state{margin:var(--yii-debug-space-4) 0 0;padding:var(--yii-debug-space-5) var(--yii-debug-space-5);border:1px dashed color-mix(in srgb, var(--yii-debug-panel-border) 80%, transparent);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 30%)}.yii-debug .yii-debug-empty-state h2{margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-lg);font-weight:700}.yii-debug .yii-debug-empty-state p{margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);line-height:1.5}.yii-debug .yii-debug-empty-state p:last-child{margin-bottom:0}.yii-debug .yii-debug-empty-state code{border-radius:var(--yii-debug-radius-sm,4px);background:var(--yii-debug-panel-code-bg);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);padding:.1em .4em}.yii-debug .yii-debug-empty-state-code{margin:0 0 var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-code-bg);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);white-space:pre-wrap;overflow-wrap:break-word;line-height:1.5}.yii-debug .yii-debug-mail-list{gap:var(--yii-debug-space-3);flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-mail-list-item{margin:0;padding:0;list-style:none;display:block}.yii-debug .yii-debug-mail-pager{margin-top:var(--yii-debug-space-4)}.yii-debug .yii-debug-mail-card{gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-4) var(--yii-debug-space-4) var(--yii-debug-space-3);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);grid-template-columns:1fr;transition:transform .16s,border-color .16s,box-shadow .16s;display:grid;position:relative}.yii-debug .yii-debug-mail-card:hover{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 60%);box-shadow:var(--yii-debug-shadow-sm), 0 12px 32px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%);transform:translateY(-1px)}.yii-debug .yii-debug-mail-card-head{gap:var(--yii-debug-space-3);grid-template-columns:auto 1fr auto;align-items:flex-start;display:grid}.yii-debug .yii-debug-mail-avatar{--mail-hue:210;background:linear-gradient(135deg, hsl(var(--mail-hue) 72% 52%), hsl(calc(var(--mail-hue) + 28) 65% 40%));width:44px;height:44px;box-shadow:0 2px 10px hsl(var(--mail-hue) 60% 40% / 32%), inset 0 1px 0 #ffffff38;color:#fff;font-family:var(--yii-debug-font-mono);letter-spacing:-.01em;border-radius:50%;flex:none;justify-content:center;align-items:center;font-size:16px;font-weight:800;display:inline-flex}.yii-debug .yii-debug-mail-headline{flex-direction:column;gap:4px;min-width:0;display:flex}.yii-debug .yii-debug-mail-from{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em;text-overflow:ellipsis;white-space:nowrap;margin:0;overflow:hidden}.yii-debug .yii-debug-mail-subject{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-lg);letter-spacing:-.01em;margin:0;font-weight:700;line-height:1.25}.yii-debug .yii-debug-mail-preview{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-sm);-webkit-line-clamp:1;line-clamp:1;-webkit-box-orient:vertical;margin:2px 0 0;line-height:1.4;display:-webkit-box;overflow:hidden}.yii-debug .yii-debug-mail-meta{flex-direction:column;flex:none;align-items:flex-end;gap:6px;display:flex}.yii-debug .yii-debug-mail-status{border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;align-items:center;gap:6px;padding:3px 10px 3px 8px;font-weight:600;line-height:1;display:inline-flex}.yii-debug .yii-debug-mail-status-dot{background:var(--yii-debug-hue,var(--yii-debug-panel-muted));width:7px;height:7px;box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-hue,var(--yii-debug-panel-muted)), transparent 80%);border-radius:50%;flex:none}.yii-debug .yii-debug-mail-status-ok{--yii-debug-hue:var(--yii-debug-success)}.yii-debug .yii-debug-mail-status-fail{--yii-debug-hue:var(--yii-debug-danger)}.yii-debug .yii-debug-mail-time{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em;cursor:help}.yii-debug .yii-debug-mail-download{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);width:30px;height:30px;color:var(--yii-debug-panel-link);justify-content:center;align-items:center;text-decoration:none;transition:background-color .14s,border-color .14s,color .14s,transform .14s;display:inline-flex}.yii-debug .yii-debug-mail-download svg{width:16px;height:16px}.yii-debug .yii-debug-mail-download:hover,.yii-debug .yii-debug-mail-download:focus-visible{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 50%);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%);color:var(--yii-debug-panel-primary-strong);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-mail-recipients{padding:var(--yii-debug-space-3);border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 60%, transparent);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 40%);flex-direction:column;gap:8px;display:flex}.yii-debug .yii-debug-mail-recipient-group{gap:var(--yii-debug-space-3);grid-template-columns:64px 1fr;align-items:center;display:grid}.yii-debug .yii-debug-mail-recipient-label{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.14em;text-align:right;font-weight:700}.yii-debug .yii-debug-mail-recipient-label[data-role=reply]{color:var(--yii-debug-info)}.yii-debug .yii-debug-mail-recipient-label[data-role=bcc]{color:var(--yii-debug-warning)}.yii-debug .yii-debug-mail-recipient-pills{flex-wrap:wrap;gap:6px;min-width:0;display:flex}.yii-debug .yii-debug-mail-recipient-pill{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);max-width:100%;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);text-overflow:ellipsis;white-space:nowrap;align-items:center;padding:3px 10px;line-height:1.4;display:inline-flex;overflow:hidden}.yii-debug .yii-debug-mail-body{padding:var(--yii-debug-space-4);border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 60%, transparent);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);white-space:pre-wrap;overflow-wrap:break-word;line-height:1.55}.yii-debug .yii-debug-mail-body-empty{color:var(--yii-debug-panel-muted);text-align:center;font-style:italic}.yii-debug .yii-debug-mail-tech{border:1px dashed color-mix(in srgb, var(--yii-debug-panel-border) 70%, transparent);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 30%);overflow:hidden}.yii-debug .yii-debug-mail-tech>summary{padding:10px var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;gap:8px;list-style:none;display:flex}.yii-debug .yii-debug-mail-tech>summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-mail-tech>summary:hover{color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-mail-tech[open]>summary{border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 50%, transparent);color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-mail-tech[open] .yii-debug-mail-tech-chevron{transform:rotate(180deg)}.yii-debug .yii-debug-mail-tech-icon{width:14px;height:14px;color:var(--yii-debug-panel-primary);justify-content:center;align-items:center;display:inline-flex}.yii-debug .yii-debug-mail-tech-icon svg{width:100%;height:100%}.yii-debug .yii-debug-mail-tech-label{text-transform:uppercase;font-weight:700}.yii-debug .yii-debug-mail-tech-charset{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:none;margin-left:4px;padding:2px 8px;font-weight:600}.yii-debug .yii-debug-mail-tech-chevron{width:14px;height:14px;color:var(--yii-debug-panel-muted);justify-content:center;align-items:center;margin-left:auto;transition:transform .2s;display:inline-flex}.yii-debug .yii-debug-mail-tech-chevron svg{width:100%;height:100%}.yii-debug .yii-debug-mail-headers{padding:var(--yii-debug-space-3) var(--yii-debug-space-4);background:var(--yii-debug-panel-code-bg);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);white-space:pre-wrap;overflow-wrap:break-word;margin:0;line-height:1.5;overflow-x:auto}@media (max-width:640px){.yii-debug .yii-debug-mail-card-head{grid-template-columns:auto 1fr}.yii-debug .yii-debug-mail-meta{flex-direction:row;grid-column:1/-1;justify-content:flex-start;align-items:center}.yii-debug .yii-debug-mail-recipient-group{grid-template-columns:1fr;gap:4px}.yii-debug .yii-debug-mail-recipient-label{text-align:left}}.yii-debug .yii-debug-queue-grid-job{flex-direction:column;gap:2px;line-height:1.2;display:flex}.yii-debug .yii-debug-queue-grid-job-link{color:var(--yii-debug-text-strong);white-space:nowrap;font-weight:600;text-decoration:none}.yii-debug .yii-debug-queue-grid-job-link:hover{text-decoration:underline}.yii-debug .yii-debug-queue-grid-job-namespace{color:var(--yii-debug-text-muted);font-family:var(--yii-debug-font-mono);white-space:nowrap;font-size:.78rem}.yii-debug .yii-debug-grid-queue .yii-debug-queue-status,.yii-debug .yii-debug-grid-queue .yii-debug-queue-driver{white-space:nowrap}.yii-debug .yii-debug-queue-job-page{gap:var(--yii-debug-space-3);flex-direction:column;display:flex}.yii-debug .yii-debug-queue-job-head{align-items:center;gap:var(--yii-debug-space-2);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-queue-card{gap:var(--yii-debug-space-2);padding:var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);flex-direction:column;display:flex}.yii-debug .yii-debug-queue-card-head{gap:var(--yii-debug-space-3);grid-template-columns:auto 1fr auto;align-items:flex-start;display:grid}.yii-debug .yii-debug-queue-avatar{--queue-hue:210;border-radius:var(--yii-debug-radius-md);background:linear-gradient(135deg, hsl(var(--queue-hue) 72% 52%), hsl(calc(var(--queue-hue) + 28) 65% 40%));width:40px;height:40px;box-shadow:0 2px 10px hsl(var(--queue-hue) 60% 40% / 32%), inset 0 1px 0 #ffffff38;color:#fff;font-family:var(--yii-debug-font-mono);letter-spacing:-.01em;flex:none;justify-content:center;align-items:center;font-size:16px;font-weight:800;display:inline-flex}.yii-debug .yii-debug-queue-headline{flex-direction:column;gap:2px;min-width:0;display:flex}.yii-debug .yii-debug-queue-class{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-lg);letter-spacing:-.01em;word-break:break-all;margin:0;font-weight:700;line-height:1.25}.yii-debug .yii-debug-queue-namespace{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em}.yii-debug .yii-debug-queue-meta-pills{flex-direction:column;flex:none;align-items:flex-end;gap:6px;display:flex}.yii-debug .yii-debug-queue-status{border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;align-items:center;padding:3px 10px;font-weight:600;line-height:1;display:inline-flex}.yii-debug .yii-debug-queue-status-queued{color:var(--yii-debug-info);background:color-mix(in srgb, var(--yii-debug-info), transparent 86%)}.yii-debug .yii-debug-queue-status-done{color:var(--yii-debug-success);background:color-mix(in srgb, var(--yii-debug-success), transparent 86%)}.yii-debug .yii-debug-queue-status-failed{color:var(--yii-debug-danger);background:color-mix(in srgb, var(--yii-debug-danger), transparent 86%)}.yii-debug .yii-debug-queue-driver{border-radius:var(--yii-debug-radius-pill);border:var(--yii-debug-border);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.05em;text-transform:uppercase;cursor:help;align-items:center;padding:3px 10px;font-weight:600;line-height:1;display:inline-flex}.yii-debug .yii-debug-queue-driver-is-sync{color:var(--yii-debug-success);background:color-mix(in srgb, var(--yii-debug-success), transparent 92%);border-color:color-mix(in srgb, var(--yii-debug-success), transparent 70%)}.yii-debug .yii-debug-queue-driver-is-async{color:var(--yii-debug-warning);background:color-mix(in srgb, var(--yii-debug-warning), transparent 92%);border-color:color-mix(in srgb, var(--yii-debug-warning), transparent 70%)}.yii-debug .yii-debug-queue-hint{margin:var(--yii-debug-space-3) 0;padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border-radius:var(--yii-debug-radius-md);border-left:3px solid var(--yii-debug-warning);background:color-mix(in srgb, var(--yii-debug-warning), transparent 94%);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);line-height:1.5}.yii-debug .yii-debug-queue-tree{padding:var(--yii-debug-space-3);border-radius:var(--yii-debug-radius-sm);background:color-mix(in srgb, var(--yii-debug-panel-text), transparent 96%);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);flex-direction:column;gap:4px;line-height:1.6;display:flex}.yii-debug .yii-debug-queue-tree-row{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.yii-debug .yii-debug-queue-tree-key{color:var(--yii-debug-panel-text);font-weight:700}.yii-debug .yii-debug-queue-tree-key:after{content:":";color:var(--yii-debug-panel-muted);margin-left:1px;font-weight:400}.yii-debug .yii-debug-queue-tree-type{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);text-transform:uppercase;letter-spacing:.05em;background:color-mix(in srgb, var(--yii-debug-panel-text), transparent 92%);border-radius:3px;padding:1px 6px}.yii-debug .yii-debug-queue-tree-class{color:var(--yii-debug-info);cursor:help;font-weight:600}.yii-debug .yii-debug-queue-tree-meta{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-queue-tree-truncated{color:var(--yii-debug-warning);font-size:var(--yii-debug-fs-xs);text-transform:uppercase;letter-spacing:.05em;background:color-mix(in srgb, var(--yii-debug-warning), transparent 86%);border-radius:3px;padding:1px 6px}.yii-debug .yii-debug-queue-tree-value{word-break:break-all}.yii-debug .yii-debug-queue-tree-value-string{color:var(--yii-debug-success)}.yii-debug .yii-debug-queue-tree-value-number{color:var(--yii-debug-info)}.yii-debug .yii-debug-queue-tree-value-bool{color:var(--yii-debug-warning);font-weight:600}.yii-debug .yii-debug-queue-tree-value-null{color:var(--yii-debug-panel-muted);font-style:italic}.yii-debug .yii-debug-queue-tree-collapse{flex-direction:column;gap:4px;display:flex}.yii-debug .yii-debug-queue-tree-summary{cursor:pointer;list-style:revert;flex-wrap:wrap;align-items:baseline;gap:8px;padding:2px 0;display:inline-flex}.yii-debug .yii-debug-queue-tree-summary::-webkit-details-marker{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-queue-tree-children{margin-left:var(--yii-debug-space-4);padding-left:var(--yii-debug-space-3);border-left:1px dashed color-mix(in srgb, var(--yii-debug-panel-text), transparent 80%);flex-direction:column;gap:4px;display:flex}.yii-debug .yii-debug-queue-time{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em}.yii-debug .yii-debug-queue-payload{margin:0}.yii-debug .yii-debug-queue-error{padding:var(--yii-debug-space-3);border-radius:var(--yii-debug-radius-sm);border-left:3px solid var(--yii-debug-danger);background:color-mix(in srgb, var(--yii-debug-danger), transparent 92%);color:var(--yii-debug-danger);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);line-height:1.5}.yii-debug .yii-debug-queue-meta{gap:var(--yii-debug-space-2);padding-top:var(--yii-debug-space-2);border-top:var(--yii-debug-border);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-queue-meta-item{border-radius:var(--yii-debug-radius-pill);background:color-mix(in srgb, var(--yii-debug-panel-text), transparent 94%);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);align-items:center;gap:6px;padding:2px 10px;line-height:1.4;display:inline-flex}.yii-debug .yii-debug-queue-meta-label{color:var(--yii-debug-panel-muted);text-transform:uppercase;letter-spacing:.05em;font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-queue-meta-value{color:var(--yii-debug-panel-text);font-weight:600}.yii-debug .yii-debug-dump{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);overflow:hidden}.yii-debug .yii-debug-dump-card-head{align-items:center;gap:var(--yii-debug-space-2);padding:6px var(--yii-debug-space-3);border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);color:var(--yii-debug-panel-muted);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-dump-index{color:var(--yii-debug-panel-primary-strong);font-weight:700}.yii-debug .yii-debug-dump-type{border-radius:var(--yii-debug-radius-pill);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;--yii-debug-hue:var(--yii-debug-panel-primary);padding:1px 8px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-dump-type[data-type=array]{--yii-debug-hue:var(--yii-debug-info)}.yii-debug .yii-debug-dump-type[data-type=string]{--yii-debug-hue:var(--yii-debug-success)}.yii-debug .yii-debug-dump-type[data-type=number]{--yii-debug-hue:var(--yii-debug-warning)}.yii-debug .yii-debug-dump-type[data-type=bool],.yii-debug .yii-debug-dump-type[data-type=null]{--yii-debug-hue:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-dump-meta{align-items:center;gap:var(--yii-debug-space-2);margin-left:auto;display:inline-flex}.yii-debug .yii-debug-dump-time{color:var(--yii-debug-panel-muted);font-variant-numeric:tabular-nums}.yii-debug .yii-debug-dump-trace{color:var(--yii-debug-panel-link)}.yii-debug .yii-debug-dump-body{padding:var(--yii-debug-space-3);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);line-height:1.5;overflow-x:auto}.yii-debug .yii-debug-dump-body code{white-space:pre;background:0 0;padding:0;display:block}[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color: #DD0000"],[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color:#DD0000"]{color:#fca5a5!important}[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color: #0000BB"],[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color:#0000BB"]{color:#93c5fd!important}[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color: #007700"],[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color:#007700"]{color:#86efac!important}[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color: #FF8000"],[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color:#FF8000"]{color:#fdba74!important}[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color: #000000"],[data-yii-debug-theme=dark] .yii-debug .yii-debug-dump-body span[style*="color:#000000"]{color:var(--yii-debug-panel-text)!important}.yii-debug .yii-debug-asset-stats{gap:var(--yii-debug-space-3);margin:0 0 var(--yii-debug-space-4);grid-template-columns:repeat(4,minmax(0,1fr));display:grid}.yii-debug .yii-debug-asset-stat{padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);align-items:center;gap:10px;display:flex}.yii-debug .yii-debug-asset-stat[data-kind=bundles] .yii-debug-asset-stat-icon{color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-asset-stat[data-kind=css] .yii-debug-asset-stat-icon{color:var(--yii-debug-info)}.yii-debug .yii-debug-asset-stat[data-kind=js] .yii-debug-asset-stat-icon{color:var(--yii-debug-warning)}.yii-debug .yii-debug-asset-stat[data-kind=deps] .yii-debug-asset-stat-icon{color:var(--yii-debug-success)}.yii-debug .yii-debug-asset-stat-icon{flex:none;justify-content:center;align-items:center;width:26px;height:26px;display:inline-flex}.yii-debug .yii-debug-asset-stat-icon svg{width:100%;height:100%}.yii-debug .yii-debug-asset-stat-value{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xl);font-weight:800;line-height:1}.yii-debug .yii-debug-asset-stat-label{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;margin-left:auto}.yii-debug .yii-debug-asset-list{gap:var(--yii-debug-space-3);flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-asset-list-item{margin:0;padding:0;list-style:none;display:block}.yii-debug .yii-debug-asset-card{gap:var(--yii-debug-space-4);padding:var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);scroll-margin-top:var(--yii-debug-space-4);flex-direction:column;transition:transform .16s,border-color .16s,box-shadow .16s;display:flex;position:relative}.yii-debug .yii-debug-asset-card:hover{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 60%);box-shadow:var(--yii-debug-shadow-sm), 0 12px 32px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%);transform:translateY(-1px)}.yii-debug .yii-debug-asset-card:target{border-color:var(--yii-debug-panel-primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 78%), var(--yii-debug-shadow-sm)}.yii-debug .yii-debug-asset-card-head{gap:var(--yii-debug-space-3);grid-template-columns:auto 1fr auto;align-items:center;display:grid}.yii-debug .yii-debug-asset-card-icon{border-radius:var(--yii-debug-radius-md);background:linear-gradient(135deg, color-mix(in srgb, var(--yii-debug-panel-primary), transparent 78%), color-mix(in srgb, var(--yii-debug-info), transparent 78%));width:38px;height:38px;color:var(--yii-debug-panel-primary-strong);flex:none;justify-content:center;align-items:center;display:inline-flex}.yii-debug .yii-debug-asset-card-icon svg{width:22px;height:22px}.yii-debug .yii-debug-asset-card-title{flex-direction:column;gap:2px;min-width:0;display:flex}.yii-debug .yii-debug-asset-card-name{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-lg);letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap;margin:0;font-weight:700;line-height:1.2;overflow:hidden}.yii-debug .yii-debug-asset-card-fqcn{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.01em;text-overflow:ellipsis;white-space:nowrap;margin:0;overflow:hidden}.yii-debug .yii-debug-asset-card-meta{flex-wrap:nowrap;flex:none;justify-content:flex-end;align-items:center;gap:5px;display:inline-flex}.yii-debug .yii-debug-asset-chip{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;align-items:center;gap:4px;padding:2px 8px;font-weight:600;display:inline-flex}.yii-debug .yii-debug-asset-chip strong{color:var(--yii-debug-panel-text);font-weight:800}.yii-debug .yii-debug-asset-chip-css{border-color:color-mix(in srgb, var(--yii-debug-info), transparent 60%);background:color-mix(in srgb, var(--yii-debug-info), transparent 90%);color:var(--yii-debug-info)}.yii-debug .yii-debug-asset-chip-css strong{color:var(--yii-debug-info)}.yii-debug .yii-debug-asset-chip-js{border-color:color-mix(in srgb, var(--yii-debug-warning), transparent 60%);background:color-mix(in srgb, var(--yii-debug-warning), transparent 90%);color:var(--yii-debug-warning)}.yii-debug .yii-debug-asset-chip-js strong{color:var(--yii-debug-warning)}.yii-debug .yii-debug-asset-chip-deps{border-color:color-mix(in srgb, var(--yii-debug-success), transparent 60%);background:color-mix(in srgb, var(--yii-debug-success), transparent 90%);color:var(--yii-debug-success)}.yii-debug .yii-debug-asset-chip-deps strong{color:var(--yii-debug-success)}.yii-debug .yii-debug-asset-card-body{gap:var(--yii-debug-space-4);grid-template-columns:minmax(180px,2fr) minmax(0,3fr);display:grid}.yii-debug .yii-debug-asset-card-body[data-cols="1"]{grid-template-columns:1fr}.yii-debug .yii-debug-asset-section{gap:var(--yii-debug-space-3);flex-direction:column;min-width:0;display:flex}.yii-debug .yii-debug-asset-section-title{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.14em;text-transform:uppercase;margin:0;font-weight:700}.yii-debug .yii-debug-asset-files{flex-direction:column;gap:4px;display:flex}.yii-debug .yii-debug-asset-files+.yii-debug-asset-files{margin-top:4px}.yii-debug .yii-debug-asset-file{border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 60%, transparent);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);grid-template-columns:auto 1fr;align-items:center;gap:6px;min-width:0;padding:4px 8px;display:grid}.yii-debug .yii-debug-asset-file-type{border-radius:var(--yii-debug-radius-pill);min-width:30px;font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em;justify-content:center;align-items:center;padding:0 5px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-asset-file-type-css{background:color-mix(in srgb, var(--yii-debug-info), transparent 84%);color:var(--yii-debug-info)}.yii-debug .yii-debug-asset-file-type-js{background:color-mix(in srgb, var(--yii-debug-warning), transparent 84%);color:var(--yii-debug-warning)}.yii-debug .yii-debug-asset-file-name{color:var(--yii-debug-panel-text);text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.yii-debug .yii-debug-asset-wiring{flex-direction:column;gap:4px;margin:0;display:flex}.yii-debug .yii-debug-asset-wiring-row{border:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 60%, transparent);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);grid-template-columns:56px 1fr;align-items:baseline;gap:8px;padding:6px 10px;display:grid}.yii-debug .yii-debug-asset-wiring-label{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.12em;text-transform:uppercase;margin:0;font-weight:700}.yii-debug .yii-debug-asset-wiring-value{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);word-break:break-all;margin:0}.yii-debug .yii-debug-asset-depends{padding:10px var(--yii-debug-space-3);border:1px dashed color-mix(in srgb, var(--yii-debug-success), transparent 50%);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-success), transparent 92%);flex-direction:column;gap:8px;display:flex}.yii-debug .yii-debug-asset-depends-label{color:var(--yii-debug-success);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.14em;text-transform:uppercase;font-weight:700}.yii-debug .yii-debug-asset-depends-list{flex-wrap:wrap;gap:6px;display:flex}.yii-debug .yii-debug-asset-depend{border:1px solid color-mix(in srgb, var(--yii-debug-success), transparent 50%);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-success);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);align-items:center;gap:5px;padding:3px 10px 3px 8px;font-weight:600;text-decoration:none;transition:background-color .14s,color .14s,transform .14s,border-color .14s;display:inline-flex}.yii-debug .yii-debug-asset-depend:hover,.yii-debug .yii-debug-asset-depend:focus-visible{border-color:var(--yii-debug-success);background:color-mix(in srgb, var(--yii-debug-success), transparent 86%);color:var(--yii-debug-success);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-asset-depend-icon{opacity:.85;font-size:12px;line-height:1}.yii-debug .yii-debug-asset-depend-name{white-space:nowrap}@media (max-width:720px){.yii-debug .yii-debug-asset-stats{grid-template-columns:repeat(2,minmax(0,1fr))}.yii-debug .yii-debug-asset-card-body{grid-template-columns:1fr}.yii-debug .yii-debug-asset-card-head{grid-template-columns:auto 1fr}.yii-debug .yii-debug-asset-card-meta{grid-column:1/-1;justify-content:flex-start;max-width:100%}}.yii-debug .yii-debug-history-card{gap:var(--yii-debug-space-2);padding:var(--yii-debug-space-3);border:1px dashed color-mix(in srgb, var(--yii-debug-panel-border) 70%, transparent);border-radius:var(--yii-debug-radius-md);background:radial-gradient(circle at top right, color-mix(in srgb, var(--yii-debug-panel-primary), transparent 86%), transparent 60%), color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 40%);flex-direction:column;display:flex;position:relative}.yii-debug .yii-debug-nav-iconed .yii-debug-nav-link{align-items:center}.yii-debug .yii-debug-nav-iconed .yii-debug-nav-link .yii-debug-nav-link-label{margin-right:auto}.yii-debug .yii-debug-nav-link-icon{width:16px;height:16px;color:var(--yii-debug-panel-primary);flex:none;justify-content:center;align-items:center;margin-right:10px;transition:color .14s,transform .14s;display:inline-flex}.yii-debug .yii-debug-nav-link-icon svg{width:100%;height:100%}.yii-debug .yii-debug-nav-link.is-active .yii-debug-nav-link-icon{color:currentColor}.yii-debug .yii-debug-nav-link-label{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-sm);letter-spacing:.005em;flex:1;font-weight:600;overflow:hidden}.yii-debug .yii-debug-nav-link:not(.is-active){color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-nav-link:not(.is-active) .yii-debug-nav-link-icon{color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 35%)}.yii-debug .yii-debug-nav-link:not(.is-active):after{color:color-mix(in srgb, var(--yii-debug-panel-muted), transparent 40%)}.yii-debug .yii-debug-nav-link:not(.is-active):hover,.yii-debug .yii-debug-nav-link:not(.is-active):focus-visible{color:var(--yii-debug-panel-text);background:var(--yii-debug-panel-surface-hover)}:is(.yii-debug .yii-debug-nav-link:not(.is-active):hover,.yii-debug .yii-debug-nav-link:not(.is-active):focus-visible) .yii-debug-nav-link-icon{color:var(--yii-debug-panel-primary);transform:translate(1px)}:is(.yii-debug .yii-debug-nav-link:not(.is-active):hover,.yii-debug .yii-debug-nav-link:not(.is-active):focus-visible):after{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-tag-link{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);letter-spacing:-.01em;color:var(--yii-debug-panel-primary-strong);white-space:nowrap;border-bottom:1px dotted color-mix(in srgb, var(--yii-debug-panel-primary), transparent 60%);font-weight:700;text-decoration:none;display:inline-block}.yii-debug .yii-debug-tag-link:hover,.yii-debug .yii-debug-tag-link:focus-visible{color:var(--yii-debug-panel-primary);border-bottom-color:var(--yii-debug-panel-primary);text-decoration:none}.yii-debug .yii-debug-url-cell{text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;max-width:clamp(180px,26vw,460px);display:inline-block;overflow:hidden}.yii-debug .yii-debug-table tbody tr.is-cursor{background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 86%);box-shadow:inset 3px 0 0 var(--yii-debug-panel-primary)}.yii-debug .yii-debug-table tbody tr.is-cursor:hover{background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 78%)}.yii-debug .yii-debug-active-filters{margin-bottom:var(--yii-debug-space-3);padding:8px var(--yii-debug-space-3);border:1px solid color-mix(in srgb, var(--yii-debug-info), transparent 60%);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-info), transparent 92%);flex-wrap:wrap;align-items:center;gap:8px;display:flex}.yii-debug .yii-debug-active-filters-label{color:var(--yii-debug-info);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;font-weight:700}.yii-debug .yii-debug-active-filters-list{flex-wrap:wrap;gap:4px;display:inline-flex}.yii-debug .yii-debug-active-filter-pill{border:1px solid color-mix(in srgb, var(--yii-debug-info), transparent 50%);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);align-items:center;gap:4px;padding:2px 6px 2px 8px;text-decoration:none;transition:background-color .12s,border-color .12s;display:inline-flex}.yii-debug .yii-debug-active-filter-pill:hover,.yii-debug .yii-debug-active-filter-pill:focus-visible{border-color:var(--yii-debug-danger);background:color-mix(in srgb, var(--yii-debug-danger), transparent 88%);color:var(--yii-debug-danger);text-decoration:none}.yii-debug .yii-debug-active-filter-attr{color:var(--yii-debug-panel-muted);text-transform:lowercase}.yii-debug .yii-debug-active-filter-sep{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-active-filter-value{color:var(--yii-debug-panel-text);text-overflow:ellipsis;white-space:nowrap;max-width:220px;font-weight:700;overflow:hidden}.yii-debug .yii-debug-active-filter-x{color:var(--yii-debug-panel-muted);margin-left:2px;font-weight:700;line-height:1}.yii-debug .yii-debug-active-filter-pill:hover .yii-debug-active-filter-x,.yii-debug .yii-debug-active-filter-pill:focus-visible .yii-debug-active-filter-x{color:var(--yii-debug-danger)}.yii-debug .yii-debug-active-filters-clear{border:1px solid color-mix(in srgb, var(--yii-debug-danger), transparent 60%);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-danger);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;margin-left:auto;padding:3px 10px;font-weight:700;text-decoration:none;transition:background-color .12s,border-color .12s,transform .12s}.yii-debug .yii-debug-active-filters-clear:hover,.yii-debug .yii-debug-active-filters-clear:focus-visible{border-color:var(--yii-debug-danger);background:color-mix(in srgb, var(--yii-debug-danger), transparent 86%);color:var(--yii-debug-danger);text-decoration:none;transform:translateY(-1px)}.yii-debug .yii-debug-col-id,.yii-debug .yii-debug-col-queue-id{white-space:nowrap;min-width:120px}.yii-debug .yii-debug-grid .filters input.yii-debug-col-id-input,.yii-debug .yii-debug-grid .filters input.yii-debug-col-queue-id-input,.yii-debug .yii-debug-grid td.yii-debug-filter-cell input.yii-debug-col-id-input,.yii-debug .yii-debug-grid td.yii-debug-filter-cell input.yii-debug-col-queue-id-input{box-sizing:border-box;width:104px}.yii-debug .yii-debug-grid-history .yii-debug-table tbody td,.yii-debug .yii-debug-grid-history .yii-debug-table tbody th{white-space:nowrap;padding:8px 10px}@media (max-width:1366px){.yii-debug .yii-debug-grid-history .yii-debug-col-ip,.yii-debug .yii-debug-grid-history .yii-debug-col-mail{display:none}.yii-debug .yii-debug-grid-history .yii-debug-url-cell{max-width:200px}}@media (max-width:767px){.yii-debug .yii-debug-grid-event .yii-debug-table thead th,.yii-debug .yii-debug-grid-event .yii-debug-table thead td,.yii-debug .yii-debug-grid-event .yii-debug-table tbody tr:not(.yii-debug-event-detail-row) td,.yii-debug .yii-debug-grid-event .yii-debug-table tbody th,.yii-debug .yii-debug-grid-event .yii-debug-table tfoot td{padding:8px 10px}.yii-debug .yii-debug-grid-event .yii-debug-table .filters td,.yii-debug .yii-debug-grid-event .yii-debug-table td.yii-debug-filter-cell{padding:6px 10px}}.yii-debug th.yii-debug-col-num,.yii-debug td.yii-debug-col-num{text-align:center;white-space:nowrap;width:1%}.yii-debug td.yii-debug-col-num{font-variant-numeric:tabular-nums}.yii-debug td.yii-debug-col-num input,.yii-debug td.yii-debug-col-num select{text-align:center;width:100%;min-width:0}.yii-debug .yii-debug-explain-scroll{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);overflow-x:auto}.yii-debug .yii-debug-explain-table{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);white-space:nowrap;margin:0}.yii-debug .yii-debug-explain-table th,.yii-debug .yii-debug-explain-table td{padding:var(--yii-debug-space-2) var(--yii-debug-space-3)}.yii-debug ul.yii-debug-trace{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);white-space:normal;margin:2px 0 0;padding:0;list-style:none}.yii-debug .yii-debug-muted{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-nowrap{white-space:nowrap}.yii-debug .yii-debug-break{word-break:break-all}.yii-debug .yii-debug-not-set{color:var(--yii-debug-danger);font-style:italic}.yii-debug .yii-debug-gauge{vertical-align:middle;font-variant-numeric:tabular-nums;flex-direction:column;gap:3px;min-width:76px;display:inline-flex}.yii-debug .yii-debug-gauge-bar{border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-gauge-track);height:3px;display:block;overflow:hidden}.yii-debug .yii-debug-gauge-bar:after{content:"";width:var(--yii-debug-gauge,0%);border-radius:inherit;background:var(--yii-debug-gauge-fill);height:100%;display:block}.yii-debug .yii-debug-section{margin-bottom:var(--yii-debug-space-6)}.yii-debug .yii-debug-section:last-child{margin-bottom:0}.yii-debug .yii-debug-section-title{align-items:center;gap:var(--yii-debug-space-2);margin:0 0 var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.22em;text-transform:uppercase;font-size:.6875rem;font-weight:500;line-height:1;display:flex}.yii-debug .yii-debug-section-mark{color:var(--yii-debug-panel-primary);font-weight:700}.yii-debug .yii-debug-section-count{border-radius:var(--yii-debug-radius-pill);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 85%);color:var(--yii-debug-panel-primary);align-items:center;margin-left:auto;padding:2px 8px;font-weight:700;display:inline-flex}.yii-debug h1.yii-debug-hero-title{margin:0 0 var(--yii-debug-space-5);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-display);letter-spacing:-.015em;text-transform:lowercase;font-size:2rem;font-weight:400;line-height:1}.yii-debug h1.yii-debug-hero-title:before{content:"// ";color:var(--yii-debug-panel-primary);font-family:var(--yii-debug-font-mono);letter-spacing:0;vertical-align:2px;margin-right:.2em;font-size:.75em;font-weight:700}.yii-debug .yii-debug-readout{gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-6);grid-template-columns:repeat(auto-fit,minmax(210px,1fr));display:grid}.yii-debug .yii-debug-readout-card{gap:var(--yii-debug-space-2);min-height:132px;padding:var(--yii-debug-space-5) var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--yii-debug-panel-border), transparent 30%) 1px, transparent 0) 0 0 / 14px 14px, linear-gradient(150deg, color-mix(in srgb, var(--yii-debug-panel-primary), transparent 92%), transparent 55%), var(--yii-debug-panel-surface);flex-direction:column;transition:border-color .15s,transform .15s,box-shadow .15s;animation:.45s cubic-bezier(.2,.7,.3,1) both yii-debug-readout-in;display:flex;position:relative;overflow:hidden}.yii-debug .yii-debug-readout-card:hover{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 45%);box-shadow:var(--yii-debug-shadow-md);transform:translateY(-2px)}.yii-debug .yii-debug-readout-card:first-child{animation-delay:40ms}.yii-debug .yii-debug-readout-card:nth-child(2){animation-delay:80ms}.yii-debug .yii-debug-readout-card:nth-child(3){animation-delay:.12s}.yii-debug .yii-debug-readout-card:nth-child(4){animation-delay:.16s}.yii-debug .yii-debug-readout-card:nth-child(5){animation-delay:.2s}.yii-debug .yii-debug-readout-card:nth-child(6){animation-delay:.24s}.yii-debug .yii-debug-readout-corner{border:1.5px solid color-mix(in srgb, var(--yii-debug-panel-primary), transparent 40%);pointer-events:none;width:9px;height:9px;transition:border-color .15s;position:absolute}.yii-debug .yii-debug-readout-corner[data-corner=tl]{border-bottom:0;border-right:0;top:8px;left:8px}.yii-debug .yii-debug-readout-corner[data-corner=tr]{border-bottom:0;border-left:0;top:8px;right:8px}.yii-debug .yii-debug-readout-corner[data-corner=bl]{border-top:0;border-right:0;bottom:8px;left:8px}.yii-debug .yii-debug-readout-corner[data-corner=br]{border-top:0;border-left:0;bottom:8px;right:8px}.yii-debug .yii-debug-readout-card:hover .yii-debug-readout-corner{border-color:var(--yii-debug-panel-primary)}.yii-debug .yii-debug-readout-label{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.15em;text-transform:uppercase;margin:0;font-size:.6875rem;font-weight:500;line-height:1;display:block;position:relative}.yii-debug .yii-debug-readout-label:before{content:":: ";color:var(--yii-debug-panel-primary);opacity:.85;font-weight:700}.yii-debug .yii-debug-readout-value{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);letter-spacing:-.015em;overflow-wrap:break-word;margin:0;font-size:1.375rem;font-weight:700;line-height:1.15;display:block}.yii-debug .yii-debug-readout-meta{align-items:center;gap:var(--yii-debug-space-2);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.05em;text-transform:lowercase;margin-top:auto;font-size:.6875rem;line-height:1;display:inline-flex}.yii-debug .yii-debug-readout-chip{border-radius:var(--yii-debug-radius-sm);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 80%);color:var(--yii-debug-panel-primary);font-family:var(--yii-debug-font-mono);letter-spacing:.08em;text-transform:uppercase;align-items:center;padding:2px 7px;font-size:.625rem;font-weight:700;display:inline-flex}.yii-debug .yii-debug-readout-chip-muted{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-ext-strip{gap:var(--yii-debug-space-2);padding:var(--yii-debug-space-3);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:linear-gradient(0deg, color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 30%), var(--yii-debug-panel-surface));flex-wrap:wrap;display:flex}.yii-debug .yii-debug-ext-pill{align-items:center;gap:var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);letter-spacing:.1em;text-transform:uppercase;padding:6px 12px 6px 9px;font-size:.6875rem;font-weight:500;line-height:1;transition:border-color .15s,background .15s;display:inline-flex}.yii-debug .yii-debug-ext-pill-dot{background:color-mix(in srgb, var(--yii-debug-panel-muted), transparent 60%);border-radius:999px;width:7px;height:7px;display:inline-block}.yii-debug .yii-debug-ext-pill-label{color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-ext-pill-state{margin-left:var(--yii-debug-space-1);color:var(--yii-debug-panel-muted);letter-spacing:.12em;opacity:.7;font-size:.625rem;font-weight:700}.yii-debug .yii-debug-ext-pill.is-on{border-color:color-mix(in srgb, var(--yii-debug-success), transparent 55%);background:color-mix(in srgb, var(--yii-debug-success), var(--yii-debug-panel-surface) 90%)}.yii-debug .yii-debug-ext-pill.is-on .yii-debug-ext-pill-dot{background:var(--yii-debug-success);box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-success), transparent 75%);animation:2.4s ease-in-out infinite yii-debug-led-pulse}.yii-debug .yii-debug-ext-pill.is-on .yii-debug-ext-pill-state{color:var(--yii-debug-success);opacity:1}.yii-debug .yii-debug-ext-pill.is-off{opacity:.55}.yii-debug .yii-debug-dl{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);margin:0;padding:0;overflow:hidden}.yii-debug .yii-debug-dl-row{gap:var(--yii-debug-space-4);padding:10px var(--yii-debug-space-4);border-bottom:1px solid var(--yii-debug-panel-border);grid-template-columns:minmax(160px,200px) 1fr;align-items:baseline;display:grid}.yii-debug .yii-debug-dl-row:last-child{border-bottom:0}.yii-debug .yii-debug-dl-row dt{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.14em;text-transform:uppercase;margin:0;font-size:.6875rem;font-weight:500}.yii-debug .yii-debug-dl-row dd{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);overflow-wrap:break-word;margin:0;font-size:.8125rem}.yii-debug .yii-debug-package-groups{gap:var(--yii-debug-space-3);display:grid}.yii-debug .yii-debug-package-group{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);overflow:hidden}.yii-debug .yii-debug-package-group-header{justify-content:space-between;align-items:baseline;gap:var(--yii-debug-space-3);border-bottom:var(--yii-debug-border);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 18%);padding:9px 12px;display:flex}.yii-debug .yii-debug-package-vendor{color:var(--yii-debug-panel-primary);font-family:var(--yii-debug-font-mono);letter-spacing:.02em;margin:0;font-size:.75rem;font-weight:700}.yii-debug .yii-debug-package-group-count{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);letter-spacing:.08em;text-transform:uppercase;font-size:.625rem}.yii-debug .yii-debug-package-list{grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));margin:0;padding:0;display:grid}.yii-debug .yii-debug-package-row{gap:var(--yii-debug-space-2);border-right:1px solid color-mix(in srgb, var(--yii-debug-panel-border), transparent 28%);border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border), transparent 28%);grid-template-columns:minmax(0,1fr) auto;align-items:baseline;min-width:0;padding:7px 11px;display:grid}.yii-debug .yii-debug-package-row dt,.yii-debug .yii-debug-package-row dd{font-family:var(--yii-debug-font-mono);margin:0;font-size:.6875rem;line-height:1.35}.yii-debug .yii-debug-package-name{min-width:0;color:var(--yii-debug-panel-text);overflow-wrap:anywhere;font-weight:500}.yii-debug .yii-debug-package-version{color:var(--yii-debug-panel-primary);font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:700}.yii-debug .yii-debug-cta{align-items:center;gap:var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);letter-spacing:.02em;padding:10px 18px 10px 14px;font-size:.8125rem;font-weight:500;text-decoration:none;transition:border-color .15s,color .15s,transform .15s,box-shadow .15s;display:inline-flex}.yii-debug .yii-debug-cta:hover,.yii-debug .yii-debug-cta:focus{border-color:var(--yii-debug-panel-primary);color:var(--yii-debug-panel-primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 85%);text-decoration:none;transform:translate(3px)}:is(.yii-debug .yii-debug-cta:hover,.yii-debug .yii-debug-cta:focus) .yii-debug-cta-prompt{transform:translate(2px)}.yii-debug .yii-debug-cta-prompt{color:var(--yii-debug-panel-primary);font-weight:700;transition:transform .15s}.yii-debug .yii-debug-cta-external{color:var(--yii-debug-panel-muted);margin-left:auto;font-size:.875rem}.yii-debug .yii-debug-phpinfo-shell{gap:var(--yii-debug-space-4);grid-template-columns:220px minmax(0,1fr);align-items:start;display:grid}.yii-debug .yii-debug-phpinfo-toc{top:var(--yii-debug-space-3);max-height:calc(100vh - 80px);padding:var(--yii-debug-space-3);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);scrollbar-width:thin;align-self:start;position:sticky;overflow-y:auto}.yii-debug .yii-debug-phpinfo-toc-title{margin:0 0 var(--yii-debug-space-2);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);align-items:baseline;gap:6px;display:flex}.yii-debug .yii-debug-phpinfo-toc-title span:first-child{color:var(--yii-debug-panel-text);font-variant-numeric:tabular-nums;font-weight:700}.yii-debug .yii-debug-phpinfo-toc-title-note{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);margin-left:auto}.yii-debug .yii-debug-phpinfo-toc-overview,.yii-debug .yii-debug-phpinfo-toc-group-list{flex-direction:column;gap:2px;margin:0;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-phpinfo-toc-overview{margin-bottom:var(--yii-debug-space-2);padding-bottom:var(--yii-debug-space-2);border-bottom:var(--yii-debug-border)}.yii-debug .yii-debug-phpinfo-toc-groups{flex-direction:column;gap:2px;display:flex}.yii-debug .yii-debug-phpinfo-toc-group{margin:0}.yii-debug .yii-debug-phpinfo-toc-group-summary{border-radius:var(--yii-debug-radius-sm);color:var(--yii-debug-panel-muted);cursor:pointer;font-size:var(--yii-debug-fs-xs);grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:7px;padding:7px 8px;font-weight:600;list-style:none;transition:background-color .12s,color .12s;display:grid}.yii-debug .yii-debug-phpinfo-toc-group-summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-phpinfo-toc-group-summary:before{content:"›";color:var(--yii-debug-panel-muted);font-size:1.1em;line-height:1;transition:transform .12s}.yii-debug .yii-debug-phpinfo-toc-group-summary:hover,.yii-debug .yii-debug-phpinfo-toc-group-summary:focus-visible{background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-phpinfo-toc-group[open]>.yii-debug-phpinfo-toc-group-summary{color:var(--yii-debug-panel-text)}.yii-debug .yii-debug-phpinfo-toc-group[open]>.yii-debug-phpinfo-toc-group-summary:before{transform:rotate(90deg)}.yii-debug .yii-debug-phpinfo-toc-group-count{border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);min-width:22px;color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums;text-align:center;padding:1px 6px}.yii-debug .yii-debug-phpinfo-toc-group-list{margin:2px 0 var(--yii-debug-space-2);padding-left:14px}.yii-debug .yii-debug-phpinfo-toc-link{border-radius:0 var(--yii-debug-radius-sm) var(--yii-debug-radius-sm) 0;color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-sm);border-left:2px solid #0000;padding:7px 10px 7px 12px;text-decoration:none;transition:background-color .12s,color .12s,border-color .12s;display:block}.yii-debug .yii-debug-phpinfo-toc-link:hover,.yii-debug .yii-debug-phpinfo-toc-link:focus-visible{background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-text);text-decoration:none}.yii-debug .yii-debug-phpinfo-toc-link.is-active{border-left-color:var(--yii-debug-panel-primary);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 90%);color:var(--yii-debug-panel-primary-strong);font-weight:700}.yii-debug .yii-debug-phpinfo-main{min-width:0}.yii-debug .yii-debug-phpinfo-search{margin-bottom:var(--yii-debug-space-3);flex-direction:column;gap:6px;display:flex}.yii-debug .yii-debug-phpinfo-search-control{align-items:center;display:flex;position:relative}.yii-debug .yii-debug-phpinfo-search-icon{width:16px;height:16px;color:var(--yii-debug-panel-muted);pointer-events:none;justify-content:center;align-items:center;display:inline-flex;position:absolute;left:12px}.yii-debug .yii-debug-phpinfo-search-icon svg{width:100%;height:100%}.yii-debug .yii-debug-phpinfo-search-input{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);flex:1;padding:9px 12px 9px 36px;transition:border-color .12s,box-shadow .12s}.yii-debug .yii-debug-phpinfo-search-input::placeholder{color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-phpinfo-search-input:focus{border-color:var(--yii-debug-panel-primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-panel-primary), transparent 80%)}.yii-debug .yii-debug-phpinfo-search-clear{border-radius:var(--yii-debug-radius-sm);color:var(--yii-debug-panel-link);cursor:pointer;font:inherit;font-size:var(--yii-debug-fs-xs);background:0 0;border:0;padding:4px 8px;font-weight:600;position:absolute;right:8px}.yii-debug .yii-debug-phpinfo-search-clear:hover,.yii-debug .yii-debug-phpinfo-search-clear:focus-visible{background:var(--yii-debug-panel-surface-hover);color:var(--yii-debug-panel-link-hover)}.yii-debug .yii-debug-phpinfo-search-control:has(.yii-debug-phpinfo-search-clear:not([hidden])) .yii-debug-phpinfo-search-input{padding-right:58px}.yii-debug .yii-debug-phpinfo-search-feedback{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-2);min-height:18px;padding-inline:2px;display:flex}.yii-debug .yii-debug-phpinfo-search-feedback:has(.yii-debug-phpinfo-search-status:empty):has(.yii-debug-phpinfo-search-empty[hidden]){display:none}.yii-debug .yii-debug-phpinfo-search-status,.yii-debug .yii-debug-phpinfo-search-empty{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-phpinfo-search-empty{color:var(--yii-debug-danger)}.yii-debug .yii-debug-phpinfo-section{scroll-margin-top:var(--yii-debug-space-3)}.yii-debug .yii-debug-phpinfo-section+:is(.yii-debug .yii-debug-phpinfo-section){margin-top:var(--yii-debug-space-4)}.yii-debug .yii-debug-phpinfo-section .yii-debug-disclosure{margin-bottom:0}.yii-debug .yii-debug-phpinfo-toc-link[hidden],.yii-debug .yii-debug-phpinfo-toc-group[hidden],.yii-debug .yii-debug-phpinfo-section[hidden],.yii-debug .yii-debug-phpinfo-search-clear[hidden],.yii-debug .yii-debug-phpinfo-search-empty[hidden],.yii-debug .yii-debug-phpinfo-overview-hero-section[hidden],.yii-debug .yii-debug-disclosure[hidden],.yii-debug .yii-debug-phpinfo-extension-group[hidden],.yii-debug .yii-debug-ext-pill[hidden],.yii-debug .yii-debug-table-wrap[hidden],.yii-debug .yii-debug-phpinfo-module tr[hidden]{display:none}@media (max-width:900px){.yii-debug .yii-debug-phpinfo-shell{grid-template-columns:1fr}.yii-debug .yii-debug-phpinfo-toc{max-height:none;position:static;overflow:hidden}.yii-debug .yii-debug-phpinfo-toc-groups{gap:4px var(--yii-debug-space-2);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.yii-debug .yii-debug-phpinfo-toc-group[open]{grid-column:1/-1}.yii-debug .yii-debug-phpinfo-toc-group-list{grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:4px;padding-left:0;display:grid}.yii-debug .yii-debug-phpinfo-toc-link{border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);border-left:0;padding:4px 10px}.yii-debug .yii-debug-phpinfo-toc-link.is-active{background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 80%);border-left:0}}@media (max-width:560px){.yii-debug .yii-debug-phpinfo-toc-groups{grid-template-columns:1fr}}.yii-debug .yii-debug-phpinfo-overview-hero{gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-3);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}@media (max-width:720px){.yii-debug .yii-debug-phpinfo-overview-hero{grid-template-columns:1fr}}.yii-debug .yii-debug-phpinfo-overview-hero-headline{align-items:center;gap:var(--yii-debug-space-3);grid-template-columns:1fr auto;display:grid}.yii-debug .yii-debug-phpinfo-overview-hero-version{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-display,var(--yii-debug-font-mono));letter-spacing:-.02em;font-size:clamp(1.6rem,2.4vw + 1rem,2.4rem);font-weight:800;line-height:1}.yii-debug .yii-debug-phpinfo-overview-hero-mark{border-radius:var(--yii-debug-radius-pill);border:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);min-width:64px;color:var(--yii-debug-panel-primary-strong);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-lg);letter-spacing:.08em;text-transform:lowercase;justify-content:center;align-items:center;padding:6px 14px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-phpinfo-overview-hero-metrics{gap:var(--yii-debug-space-2) var(--yii-debug-space-4);grid-template-columns:repeat(auto-fit,minmax(150px,1fr));margin:0;display:grid}.yii-debug .yii-debug-phpinfo-overview-hero-section{gap:var(--yii-debug-space-3);min-width:0;padding:var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);flex-direction:column;display:flex}.yii-debug .yii-debug-phpinfo-overview-hero-section:first-child{grid-column:1/-1}.yii-debug .yii-debug-phpinfo-extensions{grid-column:1/-1}.yii-debug .yii-debug-phpinfo-extensions>.yii-debug-phpinfo-extension-groups{padding:var(--yii-debug-space-4)}.yii-debug .yii-debug-phpinfo-extension-group-count{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums;padding:1px 7px}.yii-debug .yii-debug-phpinfo-extension-groups{gap:var(--yii-debug-space-3);flex-direction:column;display:flex}.yii-debug .yii-debug-phpinfo-extension-group{gap:var(--yii-debug-space-2);flex-direction:column;display:flex}.yii-debug .yii-debug-phpinfo-extension-group-head{align-items:center;gap:var(--yii-debug-space-2);color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);font-weight:600;display:flex}.yii-debug .yii-debug-phpinfo-extension-group-head .yii-debug-phpinfo-extension-group-count{margin-left:auto}.yii-debug .yii-debug-phpinfo-extension-group .yii-debug-ext-pill{scroll-margin-top:var(--yii-debug-space-4)}.yii-debug .yii-debug-phpinfo-extension-group .yii-debug-ext-pill:target,.yii-debug .yii-debug-phpinfo-extension-group .yii-debug-ext-pill.is-search-match{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 45%);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 90%)}.yii-debug .yii-debug-phpinfo-overview-block-head{border-bottom:var(--yii-debug-border);align-items:center;gap:10px;padding-bottom:8px;display:flex}.yii-debug .yii-debug-phpinfo-overview-block-eyebrow{color:var(--yii-debug-panel-text);font-size:var(--yii-debug-fs-sm);font-weight:600}.yii-debug .yii-debug-phpinfo-overview-hero-metric{flex-direction:column;gap:3px;padding-block:2px;display:flex}.yii-debug .yii-debug-phpinfo-overview-hero-metric dt{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);margin:0;font-weight:600;line-height:1.2}.yii-debug .yii-debug-phpinfo-overview-hero-metric dd{overflow-wrap:break-word;min-width:0;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);margin:0;line-height:1.4}.yii-debug .yii-debug-phpinfo-overview-hero-metric dd code{color:inherit;background:0 0;padding:0}.yii-debug .yii-debug-phpinfo-overview-hero-metric.is-wide{grid-column:1/-1}.yii-debug .yii-debug-phpinfo-overview-pill{border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;text-transform:uppercase;border:1px solid #0000;align-items:center;gap:6px;padding:1px 10px;font-weight:700;display:inline-flex}.yii-debug .yii-debug-phpinfo-overview-pill[data-variant=success]{color:var(--yii-debug-success);background:color-mix(in srgb, var(--yii-debug-success), transparent 86%);border-color:color-mix(in srgb, var(--yii-debug-success), transparent 65%)}.yii-debug .yii-debug-phpinfo-overview-pill[data-variant=muted]{color:var(--yii-debug-panel-muted);background:var(--yii-debug-panel-surface-muted);border-color:var(--yii-debug-panel-border)}.yii-debug .yii-debug-phpinfo-overview-token{border:1px solid var(--yii-debug-panel-border);border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);padding:1px 8px}.yii-debug .yii-debug-phpinfo-overview-files{flex-wrap:wrap;gap:4px;display:flex}.yii-debug .yii-debug-phpinfo-overview-files .yii-debug-phpinfo-overview-token{cursor:help;transition:border-color .12s,background-color .12s}.yii-debug .yii-debug-phpinfo-overview-files .yii-debug-phpinfo-overview-token:hover{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 40%);background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%)}.yii-debug .yii-debug-phpinfo-module{gap:var(--yii-debug-space-3);flex-direction:column;margin-top:0;display:flex}.yii-debug .yii-debug-phpinfo-module-head{align-items:center;gap:10px;margin:0;padding:2px 2px 0;display:flex}.yii-debug .yii-debug-phpinfo-module-head h2{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xl);text-align:left;background:0 0;margin:0;padding:0;font-weight:600}.yii-debug .yii-debug-phpinfo-table-section{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-sm);overflow:hidden}.yii-debug .yii-debug-phpinfo-table-section-head{justify-content:space-between;align-items:center;gap:var(--yii-debug-space-2);padding:9px var(--yii-debug-space-3);border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-sm);font-weight:600;display:flex}.yii-debug .yii-debug-phpinfo-variable-group>summary{cursor:pointer;list-style:none;transition:background-color .12s}.yii-debug .yii-debug-phpinfo-variable-group>summary::-webkit-details-marker{display:none}.yii-debug .yii-debug-phpinfo-variable-group>summary:hover,.yii-debug .yii-debug-phpinfo-variable-group>summary:focus-visible{background:var(--yii-debug-panel-surface-hover)}.yii-debug .yii-debug-phpinfo-variable-group>summary>span:first-child{align-items:center;gap:8px;display:inline-flex}.yii-debug .yii-debug-phpinfo-variable-group>summary>span:first-child:before{content:"›";color:var(--yii-debug-panel-muted);font-size:1.1em;line-height:1;transition:transform .12s}.yii-debug .yii-debug-phpinfo-variable-group[open]>summary>span:first-child:before{transform:rotate(90deg)}.yii-debug .yii-debug-phpinfo-table-section-count{border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums;flex:none;padding:1px 7px;font-weight:500}.yii-debug .yii-debug-phpinfo-table-scroll{min-width:0;overflow-x:auto}.yii-debug .yii-debug-phpinfo-module .yii-debug-table{table-layout:fixed;min-width:560px}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr>:first-child{width:40%}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr>:nth-child(2):last-child{width:60%}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr>:nth-child(2):not(:last-child),.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr>:nth-child(3){width:30%}.yii-debug .yii-debug-phpinfo-module .yii-debug-table th,.yii-debug .yii-debug-phpinfo-module .yii-debug-table td{text-align:left}.yii-debug .yii-debug-phpinfo-module .yii-debug-table th[scope=row]{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);white-space:normal;font-weight:500}.yii-debug .yii-debug-phpinfo-module .yii-debug-table td{font-family:var(--yii-debug-font-mono)}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr.h>*{z-index:1;background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.03em;text-transform:uppercase;font-weight:700;position:sticky;top:0}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr.is-search-match>*{background:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 88%)!important}.yii-debug .yii-debug-phpinfo-module .yii-debug-table tr.has-local-override>td:nth-child(2){box-shadow:inset 2px 0 0 color-mix(in srgb, var(--yii-debug-warning), transparent 25%);color:color-mix(in srgb, var(--yii-debug-warning) 72%, var(--yii-debug-panel-text))}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-phpinfo-table-scroll{overflow:visible}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts{table-layout:auto;min-width:0;display:block}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tbody{grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact{border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border), transparent 25%);grid-template-columns:minmax(130px,.85fr) minmax(0,1.15fr);min-width:0;display:grid}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact:nth-child(odd){border-right:1px solid color-mix(in srgb, var(--yii-debug-panel-border), transparent 25%)}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact.yii-debug-phpinfo-fact-wide{border-right:0;grid-column:1/-1}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact>*{overflow-wrap:anywhere;background:0 0;border:0;width:auto;padding:9px 12px}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact>th{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);font-weight:600}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact>td{color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs)}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact-subheading{grid-column:1/-1;display:block}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact-subheading>th{border-block:var(--yii-debug-border);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 15%);width:auto;color:var(--yii-debug-panel-text);font-family:var(--yii-debug-font-sans);font-size:var(--yii-debug-fs-xs);letter-spacing:.02em;text-align:left;padding:8px 12px;font-weight:600;display:block}.yii-debug .yii-debug-phpinfo-status-pill,.yii-debug .yii-debug-phpinfo-redacted{border-radius:var(--yii-debug-radius-pill);width:-moz-fit-content;width:fit-content;font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.03em;border:1px solid #0000;align-items:center;padding:1px 8px;font-weight:600;display:inline-flex}.yii-debug .yii-debug-phpinfo-status-pill[data-variant=success]{border-color:color-mix(in srgb, var(--yii-debug-success), transparent 65%);background:color-mix(in srgb, var(--yii-debug-success), transparent 88%);color:var(--yii-debug-success)}.yii-debug .yii-debug-phpinfo-status-pill[data-variant=muted]{border-color:var(--yii-debug-panel-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted)}.yii-debug .yii-debug-phpinfo-redacted{border-color:color-mix(in srgb, var(--yii-debug-warning), transparent 65%);background:color-mix(in srgb, var(--yii-debug-warning), transparent 90%);color:color-mix(in srgb, var(--yii-debug-warning) 72%, var(--yii-debug-panel-text));text-transform:uppercase}.yii-debug .yii-debug-phpinfo-table-section.is-note .yii-debug-table.is-note{table-layout:auto;min-width:0}.yii-debug .yii-debug-phpinfo-table-section.is-note .yii-debug-table.is-note td,.yii-debug .yii-debug-phpinfo-table-section.is-note .yii-debug-table.is-note th{width:auto;padding:var(--yii-debug-space-3);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-sans);text-align:left;line-height:1.6}@media (max-width:720px){.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tbody{grid-template-columns:1fr}.yii-debug .yii-debug-phpinfo-table-section.is-facts .yii-debug-table.is-facts tr.yii-debug-phpinfo-fact{border-right:0}}@media (max-width:959px){.yii-debug .yii-debug-sidebar{min-width:0}.yii-debug .yii-debug-nav{overscroll-behavior-x:contain;scrollbar-width:thin;overflow-x:auto}.yii-debug .yii-debug-nav ul{gap:var(--yii-debug-space-1);flex-direction:row;width:max-content;min-width:100%}.yii-debug .yii-debug-nav-link{white-space:nowrap;min-height:40px}.yii-debug .yii-debug-nav-link:after{display:none}.yii-debug .yii-debug-nav-link-icon{margin-right:var(--yii-debug-space-1)}}@media (max-width:767px){.yii-debug .yii-debug-page{padding:var(--yii-debug-space-2)}.yii-debug .yii-debug-card{padding:var(--yii-debug-space-3)}}@media (prefers-reduced-motion:reduce){.yii-debug *,.yii-debug :before,.yii-debug :after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}@keyframes yii-debug-user-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.55;transform:scale(.85)}}@keyframes yii-debug-readout-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes yii-debug-led-pulse{0%,to{box-shadow:0 0 0 3px color-mix(in srgb, var(--yii-debug-success), transparent 75%)}50%{box-shadow:0 0 0 6px color-mix(in srgb, var(--yii-debug-success), transparent 90%)}}.yii-debug-tl-filter{align-items:end;gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface-muted);flex-wrap:wrap;display:flex}.yii-debug-tl-field{flex-direction:column;gap:4px;min-width:160px;display:flex}.yii-debug-tl-field label{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-xs);letter-spacing:.06em;text-transform:uppercase;font-weight:600}.yii-debug-tl-field input{padding:6px var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-sm);background:var(--yii-debug-panel-surface);color:var(--yii-debug-panel-text);font:inherit;font-family:var(--yii-debug-font-mono)}.yii-debug-tl-field input::placeholder{color:var(--yii-debug-panel-muted)}.yii-debug-tl-field input:focus{border-color:var(--yii-debug-panel-primary)}.yii-debug-tl-field-grow{flex:240px}.yii-debug-tl-hint{margin-bottom:var(--yii-debug-space-3);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:1px dashed color-mix(in srgb, var(--yii-debug-panel-border) 70%, transparent);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-panel-surface-muted), transparent 40%)}.yii-debug-tl-hint-title{color:var(--yii-debug-panel-text);margin:0 0 6px;font-weight:600}.yii-debug-tl-hint-body{color:var(--yii-debug-panel-muted);font-size:var(--yii-debug-fs-sm);margin:0;line-height:1.55}.yii-debug-tl-hint-body em{color:var(--yii-debug-panel-text);font-style:normal;font-weight:600}.yii-debug-tl{--yii-debug-tl-label-col:22%;grid-template-columns:minmax(180px, var(--yii-debug-tl-label-col)) 1fr;border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);grid-template-rows:auto 1fr auto;gap:0;display:grid;overflow:hidden}@media (max-width:1440px){.yii-debug-tl{--yii-debug-tl-label-col:32%}}.yii-debug-tl-axis{border-bottom:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);grid-column:2/3;height:32px;padding:8px 0;position:relative}.yii-debug-tl-tick{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);letter-spacing:.04em;white-space:nowrap;pointer-events:none;position:absolute;top:8px;transform:translate(-50%)}.yii-debug-tl-tick:after{content:"";background:var(--yii-debug-panel-border);width:1px;height:8px;position:absolute;top:18px;left:50%}.yii-debug-tl-rows{grid-column:1/3;grid-template-columns:minmax(180px, var(--yii-debug-tl-label-col)) 1fr;background:linear-gradient(to right, transparent calc(var(--yii-debug-tl-label-col) - 1px), var(--yii-debug-panel-border) calc(var(--yii-debug-tl-label-col) - 1px), var(--yii-debug-panel-border) var(--yii-debug-tl-label-col), transparent var(--yii-debug-tl-label-col));display:grid}.yii-debug-tl-row{display:contents}.yii-debug-tl-row:hover .yii-debug-tl-label,.yii-debug-tl-row:hover .yii-debug-tl-track{background:var(--yii-debug-panel-surface-hover)}.yii-debug-tl-row:hover .yii-debug-tl-name{color:var(--yii-debug-panel-text)}.yii-debug-tl-label{min-width:0;padding:8px var(--yii-debug-space-3) 8px calc(var(--yii-debug-space-3) + (var(--depth,0) * 14px));border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 50%, transparent);grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px;transition:background-color .12s;display:grid}.yii-debug-tl-dot{background:var(--yii-debug-cat-db);opacity:.85;border-radius:50%;flex:none;width:6px;height:6px}.yii-debug-tl-name{font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);color:var(--yii-debug-panel-muted);text-overflow:ellipsis;white-space:nowrap;transition:color .12s;overflow:hidden}.yii-debug-tl-track{border-bottom:1px solid color-mix(in srgb, var(--yii-debug-panel-border) 50%, transparent);transition:background-color .12s;position:relative}.yii-debug-tl-bar{background:var(--yii-debug-cat-db);border-radius:4px;min-width:2px;height:18px;position:absolute;top:50%;transform:translateY(-50%);box-shadow:inset 0 1px #ffffff1f,0 1px 2px #0000002e}.yii-debug-tl-bar-duration{color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums;white-space:nowrap;justify-self:end;font-weight:500}.yii-debug-tl-memory{grid-template-columns:minmax(180px, var(--yii-debug-tl-label-col)) 1fr auto;padding:var(--yii-debug-space-2) var(--yii-debug-space-3);border-top:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);grid-column:1/3;align-items:center;display:grid}.yii-debug-tl-memory-label{text-transform:uppercase;letter-spacing:.06em}.yii-debug-tl-memory-track{border-radius:var(--yii-debug-radius-sm);background:color-mix(in srgb, var(--yii-debug-panel-surface), transparent 50%);width:100%;color:var(--yii-debug-panel-primary);position:relative;overflow:hidden}.yii-debug-tl-memory-track svg{width:100%;height:100%;display:block}.yii-debug-tl-memory-peak{margin-left:var(--yii-debug-space-2);color:var(--yii-debug-panel-text);font-weight:700}@media (max-width:640px){.yii-debug-tl,.yii-debug-tl-rows{--yii-debug-tl-label-col:52%;grid-template-columns:minmax(140px, var(--yii-debug-tl-label-col)) minmax(0, 1fr)}.yii-debug-tl-label{padding:7px var(--yii-debug-space-2);gap:6px}.yii-debug-tl-memory{grid-template-columns:minmax(140px, var(--yii-debug-tl-label-col)) minmax(0, 1fr)}.yii-debug-tl-memory-peak{text-align:right;grid-column:2;margin:4px 0 0}}.yii-debug .yii-debug-skip-link{top:var(--yii-debug-space-2);left:var(--yii-debug-space-2);z-index:1000;padding:var(--yii-debug-space-2) var(--yii-debug-space-3);border:2px solid var(--yii-debug-focus-ring);border-radius:var(--yii-debug-radius-md);background:var(--yii-debug-panel-surface);box-shadow:var(--yii-debug-shadow-md);color:var(--yii-debug-panel-text);transform:translateY(calc(-100% - var(--yii-debug-space-4)));font-weight:700;position:fixed}.yii-debug .yii-debug-skip-link:focus-visible{text-decoration:none;transform:translateY(0)}.yii-debug .yii-debug-brand-chip-control{border:var(--yii-debug-border);background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted);cursor:pointer;justify-content:center}.yii-debug .yii-debug-brand-chip-control:hover,.yii-debug .yii-debug-brand-chip-control:focus-visible{border-color:color-mix(in srgb, var(--yii-debug-panel-primary), transparent 50%);color:var(--yii-debug-panel-primary-strong);transform:translateY(-1px)}.yii-debug :is(h2,h3).yii-debug-has-permalink{align-items:center;gap:var(--yii-debug-space-1);scroll-margin-block-start:var(--yii-debug-space-5);display:flex}.yii-debug .yii-debug-heading-permalink{border-radius:var(--yii-debug-radius-sm);min-width:32px;min-height:32px;color:var(--yii-debug-panel-muted);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-sm);opacity:0;transition:color var(--yii-debug-duration-fast) ease, opacity var(--yii-debug-duration-fast) ease, background var(--yii-debug-duration-fast) ease;flex:none;justify-content:center;align-items:center;line-height:1;text-decoration:none;display:inline-flex}.yii-debug .yii-debug-heading-permalink:hover,.yii-debug .yii-debug-heading-permalink:focus-visible{background:var(--yii-debug-panel-bg-accent);color:var(--yii-debug-panel-primary-strong);opacity:1;text-decoration:none}.yii-debug :is(h2,h3).yii-debug-has-permalink:hover>.yii-debug-heading-permalink,.yii-debug :is(h2,h3).yii-debug-has-permalink:focus-within>.yii-debug-heading-permalink,.yii-debug :is(h2,h3).yii-debug-deep-link-target>.yii-debug-heading-permalink{opacity:1}.yii-debug .yii-debug-deep-link-target{outline:2px solid var(--yii-debug-focus-ring);outline-offset:4px}.yii-debug .yii-debug-db-n1-summary{gap:var(--yii-debug-space-2);margin-bottom:var(--yii-debug-space-4);padding:var(--yii-debug-space-3) var(--yii-debug-space-4);border:1px solid color-mix(in srgb, var(--yii-debug-warning), transparent 55%);border-radius:var(--yii-debug-radius-md);background:color-mix(in srgb, var(--yii-debug-warning), transparent 92%);display:grid}.yii-debug .yii-debug-db-n1-heading{justify-content:space-between;align-items:baseline;gap:var(--yii-debug-space-2);flex-wrap:wrap;display:flex}.yii-debug .yii-debug-db-n1-list{gap:var(--yii-debug-space-2);flex-wrap:wrap;margin:0;padding:0;list-style:none;display:flex}.yii-debug .yii-debug-db-n1-link,.yii-debug .yii-debug-db-n1-row-link{align-items:center;gap:var(--yii-debug-space-2);min-height:var(--yii-debug-control-min-size);padding:var(--yii-debug-space-1) var(--yii-debug-space-2);border:1px solid color-mix(in srgb, var(--yii-debug-warning), transparent 60%);border-radius:var(--yii-debug-radius-pill);background:var(--yii-debug-panel-surface);color:color-mix(in srgb, var(--yii-debug-warning) 72%, var(--yii-debug-panel-text));font-size:var(--yii-debug-fs-xs);font-weight:700;text-decoration:none;display:inline-flex}:is(.yii-debug .yii-debug-db-n1-link,.yii-debug .yii-debug-db-n1-row-link):hover,:is(.yii-debug .yii-debug-db-n1-link,.yii-debug .yii-debug-db-n1-row-link):focus-visible,:is(.yii-debug .yii-debug-db-n1-link,.yii-debug .yii-debug-db-n1-row-link)[aria-current=true]{background:color-mix(in srgb, var(--yii-debug-warning), transparent 84%);color:var(--yii-debug-panel-text);text-decoration:none}.yii-debug .yii-debug-db-n1-fingerprint{max-width:min(56ch,65vw);font-family:var(--yii-debug-font-mono);text-overflow:ellipsis;white-space:nowrap;font-weight:500;display:inline-block;overflow:hidden}.yii-debug .yii-debug-db-n1-row-link{margin-top:var(--yii-debug-space-3)}.yii-debug .yii-debug-grid-db .yii-debug-trace{gap:0;display:grid}.yii-debug .yii-debug-grid-db .yii-debug-trace a{align-items:center;min-height:24px;display:inline-flex}.yii-debug .yii-debug-db-n1-clear[hidden]{display:none}.yii-debug .yii-debug-compare-form{gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-4);grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));align-items:end;display:grid}.yii-debug .yii-debug-compare-overview{gap:var(--yii-debug-space-3);margin-bottom:var(--yii-debug-space-4);grid-template-columns:repeat(auto-fit,minmax(min(100%,12rem),1fr));display:grid}.yii-debug .yii-debug-compare-grid{min-width:42rem}.yii-debug :is(.yii-debug-delta-up,.yii-debug-delta-down,.yii-debug-delta-neutral){padding:2px var(--yii-debug-space-2);border:var(--yii-debug-border);border-radius:var(--yii-debug-radius-pill);font-family:var(--yii-debug-font-mono);font-size:var(--yii-debug-fs-xs);font-variant-numeric:tabular-nums;align-items:center;font-weight:700;display:inline-flex}.yii-debug .yii-debug-delta-up{border-color:color-mix(in srgb, var(--yii-debug-danger), transparent 60%);background:color-mix(in srgb, var(--yii-debug-danger), transparent 90%);color:var(--yii-debug-danger)}.yii-debug .yii-debug-delta-down{border-color:color-mix(in srgb, var(--yii-debug-success), transparent 60%);background:color-mix(in srgb, var(--yii-debug-success), transparent 90%);color:var(--yii-debug-success)}.yii-debug .yii-debug-delta-neutral{background:var(--yii-debug-panel-surface-muted);color:var(--yii-debug-panel-muted)}@container yii-debug-main (width<=560px){.yii-debug .yii-debug-section-header,.yii-debug .yii-debug-panel-heading{flex-direction:column;align-items:stretch}.yii-debug .yii-debug-filter-input{width:100%;max-width:none}}@media (max-width:767px){.yii-debug .yii-debug-brand-chip-copy .yii-debug-brand-label{text-overflow:ellipsis;white-space:nowrap;max-width:5.5rem;overflow:hidden}}@media (pointer:coarse){.yii-debug .yii-debug-grid-db .yii-debug-trace a{min-width:44px;min-height:44px}.yii-debug :is(.yii-debug-btn,.yii-debug-brand-chip,.yii-debug-cell-more-toggle,.yii-debug-dropdown-item,.yii-debug-filter-input,.yii-debug-heading-permalink,.yii-debug-server-group-summary,.yii-debug-tab-link){min-height:44px}}@media (forced-colors:active){.yii-debug .yii-debug-card,.yii-debug .yii-debug-brand-bar,.yii-debug .yii-debug-brand-chip,.yii-debug .yii-debug-nav,.yii-debug .yii-debug-table-wrap{box-shadow:none;border-color:canvastext}.yii-debug .yii-debug-nav-link.is-active,.yii-debug .yii-debug-tab-link.is-active{color:highlighttext;background:highlight;border-color:highlight}.yii-debug :focus-visible{outline-color:highlight}} diff --git a/resources/assets/dist/js/db.min.js b/resources/assets/dist/js/db.min.js index d7d85e3..829f281 100644 --- a/resources/assets/dist/js/db.min.js +++ b/resources/assets/dist/js/db.min.js @@ -1 +1 @@ -import{n as e,t}from"./dom.min.js";var n=`.yii-debug-db-explain-all-toggle, .yii-debug-db-explain-all a`,r=`.yii-debug-db-explain.is-open, .yii-debug-db-explain.is-loading`,i=`Unable to load the EXPLAIN output. Try again.`,a=3;function o(e,t){return`Explaining `+e+`/`+t}function s(e,t,n){if(n&&n.completed0,c=0;c=o.total&&(o=null))}function g(e,t,n){e.finished||(e.finished=!0,d.delete(e.toggle),a.delete(e.toggle),e.container.classList.remove(`is-loading`,`is-requesting`),e.target.removeAttribute(`aria-busy`),t===`success`?(e.target.innerHTML=n.responseText,e.target.dataset.loaded=`1`,e.container.classList.add(`is-open`),e.toggle.setAttribute(`aria-expanded`,`true`)):t===`error`?(e.target.classList.add(`is-error`),e.target.setAttribute(`role`,`alert`),e.target.textContent=i,e.toggle.setAttribute(`aria-expanded`,`false`)):(e.container.classList.remove(`is-open`),e.toggle.setAttribute(`aria-expanded`,`false`)),h(e),v(),m())}function _(e){e.container.classList.add(`is-requesting`),a.set(e.toggle,e),e.request=t(e.toggle.href,{success:function(t){g(e,`success`,t)},error:function(t){g(e,`error`,t)},abort:function(t){g(e,`abort`,t)}})}function v(){for(;a.size<3&&u.length>0;){var e=u.shift();e&&!e.finished&&!e.cancelled&&_(e)}}function y(e,t){var n=d.get(e);if(n)return n;var r=f(e),i=p(r),a={batchId:t,cancelled:!1,container:r,finished:!1,request:null,target:i,toggle:e};return d.set(e,a),u.push(a),r.classList.add(`is-loading`),i.classList.remove(`is-error`),i.removeAttribute(`role`),i.setAttribute(`aria-busy`,`true`),i.textContent=``,v(),a}function b(e){e.cancelled=!0,e.request&&typeof e.request.abort==`function`&&e.request.abort(),g(e,`abort`,e.request)}function x(){o=null,u=[],Array.from(d.values()).forEach(b),document.querySelectorAll(`.yii-debug-db-explain-toggle`).forEach(function(e){var t=f(e),n=p(t);t&&t.classList.remove(`is-open`,`is-loading`,`is-requesting`),n&&n.removeAttribute(`aria-busy`),e.setAttribute(`aria-expanded`,`false`)}),m(!1)}function S(){var e=Array.from(document.querySelectorAll(`.yii-debug-db-explain-toggle`));o={completed:0,id:++l,total:e.length},e.forEach(function(e){var t=f(e),n=p(t);if(!t||!n){o.completed+=1;return}if(n.dataset.loaded===`1`){t.classList.add(`is-open`),e.setAttribute(`aria-expanded`,`true`),o.completed+=1;return}y(e,o.id)}),o&&o.completed>=o.total&&(o=null),m(!0)}e(document.querySelectorAll(`.yii-debug-db-explain-toggle`),`click`,function(e){e.preventDefault();var t=f(this),n=p(t);if(!(!t||!n)){if(t.classList.contains(`is-open`)){t.classList.remove(`is-open`),this.setAttribute(`aria-expanded`,`false`),m();return}if(n.dataset.loaded===`1`){t.classList.add(`is-open`),this.setAttribute(`aria-expanded`,`true`),m();return}y(this,null),m()}}),e(document.querySelectorAll(n),`click`,function(e){e.preventDefault(),o!==null||document.querySelectorAll(r).length>0?x():S()}),m(),e(document.querySelectorAll(`[data-yii-debug-n1-filter]`),`click`,function(e){var t=this.getAttribute(`data-yii-debug-n1-filter`);if(e.preventDefault(),c(document,t),window.history&&t){var n=new URL(window.location.href);n.hash=t,window.history.pushState(null,``,n.href)}var r=t?document.getElementById(t):null;r&&typeof r.scrollIntoView==`function`&&r.scrollIntoView({block:`center`})}),e(document.querySelectorAll(`[data-yii-debug-n1-clear]`),`click`,function(e){e.preventDefault(),c(document,null)})})();export{a as EXPLAIN_CONCURRENCY,c as applyNPlusOneFilter,o as formatExplainProgress,s as updateExplainAllControl}; \ No newline at end of file +import{n as e,t}from"./dom.min.js";var n=`.yii-debug-db-explain-all-toggle, .yii-debug-db-explain-all a`,r=`.yii-debug-db-explain.is-open, .yii-debug-db-explain.is-loading`,i=`Unable to load the EXPLAIN output. Try again.`,a=3;function o(e,t,n){var r=e.querySelector(`.yii-debug-active-filters`);if(!r&&t.activeGroup){var i=e.querySelector(`.yii-debug-db-n1-summary`)||e.querySelector(`.yii-debug-grid-db`);if(!i)return;r=e.createElement(`div`),r.className=`yii-debug-active-filters`,r.setAttribute(`role`,`group`),r.setAttribute(`aria-label`,`Active filters`),r.innerHTML=`Clear all`,r.querySelector(`.yii-debug-active-filters-clear`).addEventListener(`click`,n),i.before(r)}if(r){var a=r.querySelector(`[data-yii-debug-n1-pill]`);if(t.activeGroup){a||(a=e.createElement(`a`),a.className=`yii-debug-active-filter-pill`,a.setAttribute(`data-yii-debug-n1-pill`,`true`),a.setAttribute(`href`,`#`),a.setAttribute(`title`,`Remove this filter`),a.innerHTML=`N+1:`,a.addEventListener(`click`,n),r.querySelector(`.yii-debug-active-filters-list`).appendChild(a));var o=t.visible+` similar queries`;a.querySelector(`.yii-debug-active-filter-value`).textContent=o,a.setAttribute(`aria-label`,`Remove N+1: `+o+` filter`)}else a&&a.remove();var s=r.querySelectorAll(`.yii-debug-active-filter-pill`).length;s===0?r.remove():r.querySelector(`.yii-debug-active-filters-label`).textContent=s+` filter`+(s===1?``:`s`)+` active`}}function s(e,t){return`Explaining `+e+`/`+t}function c(e,t,n){if(n&&n.completed0,o=0;o=s.total&&(s=null))}function _(e,t,n){e.finished||(e.finished=!0,f.delete(e.toggle),a.delete(e.toggle),e.container.classList.remove(`is-loading`,`is-requesting`),e.target.removeAttribute(`aria-busy`),t===`success`?(e.target.innerHTML=n.responseText,e.target.dataset.loaded=`1`,e.container.classList.add(`is-open`),e.toggle.setAttribute(`aria-expanded`,`true`)):t===`error`?(e.target.classList.add(`is-error`),e.target.setAttribute(`role`,`alert`),e.target.textContent=i,e.toggle.setAttribute(`aria-expanded`,`false`)):(e.container.classList.remove(`is-open`),e.toggle.setAttribute(`aria-expanded`,`false`)),g(e),y(),h())}function v(e){e.container.classList.add(`is-requesting`),a.set(e.toggle,e),e.request=t(e.toggle.href,{success:function(t){_(e,`success`,t)},error:function(t){_(e,`error`,t)},abort:function(t){_(e,`abort`,t)}})}function y(){for(;a.size<3&&d.length>0;){var e=d.shift();e&&!e.finished&&!e.cancelled&&v(e)}}function b(e,t){var n=f.get(e);if(n)return n;var r=p(e),i=m(r),a={batchId:t,cancelled:!1,container:r,finished:!1,request:null,target:i,toggle:e};return f.set(e,a),d.push(a),r.classList.add(`is-loading`),i.classList.remove(`is-error`),i.removeAttribute(`role`),i.setAttribute(`aria-busy`,`true`),i.textContent=``,y(),a}function x(e){e.cancelled=!0,e.request&&typeof e.request.abort==`function`&&e.request.abort(),_(e,`abort`,e.request)}function S(){s=null,d=[],Array.from(f.values()).forEach(x),document.querySelectorAll(`.yii-debug-db-explain-toggle`).forEach(function(e){var t=p(e),n=m(t);t&&t.classList.remove(`is-open`,`is-loading`,`is-requesting`),n&&n.removeAttribute(`aria-busy`),e.setAttribute(`aria-expanded`,`false`)}),h(!1)}function C(){var e=Array.from(document.querySelectorAll(`.yii-debug-db-explain-toggle`));s={completed:0,id:++u,total:e.length},e.forEach(function(e){var t=p(e),n=m(t);if(!t||!n){s.completed+=1;return}if(n.dataset.loaded===`1`){t.classList.add(`is-open`),e.setAttribute(`aria-expanded`,`true`),s.completed+=1;return}b(e,s.id)}),s&&s.completed>=s.total&&(s=null),h(!0)}e(document.querySelectorAll(`.yii-debug-db-explain-toggle`),`click`,function(e){e.preventDefault();var t=p(this),n=m(t);if(!(!t||!n)){if(t.classList.contains(`is-open`)){t.classList.remove(`is-open`),this.setAttribute(`aria-expanded`,`false`),h();return}if(n.dataset.loaded===`1`){t.classList.add(`is-open`),this.setAttribute(`aria-expanded`,`true`),h();return}b(this,null),h()}}),e(document.querySelectorAll(n),`click`,function(e){e.preventDefault(),s!==null||document.querySelectorAll(r).length>0?S():C()}),h();function w(e){if(window.history){var t=new URL(window.location.href),n=Array.from(document.querySelectorAll(`[data-yii-debug-n1-group]`)).some(function(e){return t.hash===`#`+e.getAttribute(`data-yii-debug-n1-group`)});(e||n)&&(t.hash=e||``,t.href!==window.location.href&&window.history.pushState(null,``,t.href))}}function T(e){e.preventDefault();var t=l(document,null);w(null),o(document,t,T)}e(document.querySelectorAll(`[data-yii-debug-n1-filter]`),`click`,function(e){var t=this.getAttribute(`aria-current`)===`true`?null:this.getAttribute(`data-yii-debug-n1-filter`);e.preventDefault();var n=l(document,t);w(n.activeGroup),o(document,n,T);var r=n.activeGroup?document.getElementById(n.activeGroup):null;r&&typeof r.scrollIntoView==`function`&&r.scrollIntoView({block:`center`})}),e(document.querySelectorAll(`[data-yii-debug-n1-clear]`),`click`,T)})();export{a as EXPLAIN_CONCURRENCY,l as applyNPlusOneFilter,s as formatExplainProgress,c as updateExplainAllControl,o as updateNPlusOneBanner}; \ No newline at end of file diff --git a/resources/src/panels/db.js b/resources/src/panels/db.js index 1a73f5b..e6e302a 100644 --- a/resources/src/panels/db.js +++ b/resources/src/panels/db.js @@ -8,6 +8,72 @@ const EXPLAIN_ERROR_MESSAGE = "Unable to load the EXPLAIN output. Try again."; export const EXPLAIN_CONCURRENCY = 3; +export function updateNPlusOneBanner(root, result, clearFilter) { + var banner = root.querySelector(".yii-debug-active-filters"); + + if (!banner && result.activeGroup) { + var anchor = + root.querySelector(".yii-debug-db-n1-summary") || + root.querySelector(".yii-debug-grid-db"); + + if (!anchor) { + return; + } + + banner = root.createElement("div"); + banner.className = "yii-debug-active-filters"; + banner.setAttribute("role", "group"); + banner.setAttribute("aria-label", "Active filters"); + banner.innerHTML = + '' + + '' + + 'Clear all'; + banner + .querySelector(".yii-debug-active-filters-clear") + .addEventListener("click", clearFilter); + anchor.before(banner); + } + + if (!banner) { + return; + } + + var pill = banner.querySelector("[data-yii-debug-n1-pill]"); + + if (result.activeGroup) { + if (!pill) { + pill = root.createElement("a"); + pill.className = "yii-debug-active-filter-pill"; + pill.setAttribute("data-yii-debug-n1-pill", "true"); + pill.setAttribute("href", "#"); + pill.setAttribute("title", "Remove this filter"); + pill.innerHTML = + 'N+1' + + ':' + + '' + + ''; + pill.addEventListener("click", clearFilter); + banner.querySelector(".yii-debug-active-filters-list").appendChild(pill); + } + + var label = result.visible + " similar queries"; + pill.querySelector(".yii-debug-active-filter-value").textContent = label; + pill.setAttribute("aria-label", "Remove N+1: " + label + " filter"); + } else if (pill) { + pill.remove(); + } + + var count = banner.querySelectorAll(".yii-debug-active-filter-pill").length; + + if (count === 0) { + banner.remove(); + } else { + banner.querySelector(".yii-debug-active-filters-label").textContent = + count + " filter" + (count === 1 ? "" : "s") + " active"; + } +} + export function formatExplainProgress(completed, total) { return "Explaining " + completed + "/" + total; } @@ -58,6 +124,12 @@ export function applyNPlusOneFilter(root, groupId) { var active = valid ? groupId : null; var visible = 0; + markers.forEach(function (marker) { + if (marker.getAttribute("data-yii-debug-n1-group") !== active) { + marker.classList.remove("yii-debug-deep-link-target"); + } + }); + rows.forEach(function (row) { var matched = active === null || groupByRow.get(row) === active; @@ -352,22 +424,51 @@ export function applyNPlusOneFilter(root, groupId) { syncExplainAllControls(); + function updateNPlusOneLocation(groupId) { + if (!window.history) { + return; + } + + var url = new URL(window.location.href); + var isGroupHash = Array.from( + document.querySelectorAll("[data-yii-debug-n1-group]"), + ).some(function (marker) { + return url.hash === "#" + marker.getAttribute("data-yii-debug-n1-group"); + }); + + if (groupId || isGroupHash) { + url.hash = groupId || ""; + + if (url.href !== window.location.href) { + window.history.pushState(null, "", url.href); + } + } + } + + function clearNPlusOneFilter(event) { + event.preventDefault(); + var result = applyNPlusOneFilter(document, null); + updateNPlusOneLocation(null); + updateNPlusOneBanner(document, result, clearNPlusOneFilter); + } + on( document.querySelectorAll("[data-yii-debug-n1-filter]"), "click", function (event) { - var groupId = this.getAttribute("data-yii-debug-n1-filter"); + var groupId = + this.getAttribute("aria-current") === "true" + ? null + : this.getAttribute("data-yii-debug-n1-filter"); event.preventDefault(); - applyNPlusOneFilter(document, groupId); - - if (window.history && groupId) { - var url = new URL(window.location.href); - url.hash = groupId; - window.history.pushState(null, "", url.href); - } + var result = applyNPlusOneFilter(document, groupId); + updateNPlusOneLocation(result.activeGroup); + updateNPlusOneBanner(document, result, clearNPlusOneFilter); - var target = groupId ? document.getElementById(groupId) : null; + var target = result.activeGroup + ? document.getElementById(result.activeGroup) + : null; if (target && typeof target.scrollIntoView === "function") { target.scrollIntoView({ block: "center" }); } @@ -377,9 +478,6 @@ export function applyNPlusOneFilter(root, groupId) { on( document.querySelectorAll("[data-yii-debug-n1-clear]"), "click", - function (event) { - event.preventDefault(); - applyNPlusOneFilter(document, null); - }, + clearNPlusOneFilter, ); })(); diff --git a/resources/src/styles/main.css b/resources/src/styles/main.css index 98e6c76..0bdedfe 100644 --- a/resources/src/styles/main.css +++ b/resources/src/styles/main.css @@ -3502,6 +3502,21 @@ width: 100%; } + /* Keep the native content width without clipping the verb or adding fixed empty space. */ + .yii-debug-grid-db .filters select, + .yii-debug-grid-db td.yii-debug-filter-cell select { + min-width: max-content; + + &:focus { + box-shadow: none; + } + + &:focus-visible { + outline-width: 2px; + outline-offset: 1px; + } + } + /* SQL token spans emitted by SqlHighlighter — fixed semantic hues. */ .yii-debug-sql-kw { color: var(--yii-debug-sql-kw); diff --git a/resources/tests/db.test.js b/resources/tests/db.test.js index a6b7d3f..2d3731b 100644 --- a/resources/tests/db.test.js +++ b/resources/tests/db.test.js @@ -101,6 +101,7 @@ var historyPushes = []; var historyStub = { pushState(_state, _title, url) { historyPushes.push(url); + window.location.href = url; }, }; @@ -197,7 +198,134 @@ globalThis.XMLHttpRequest.prototype.respond = function respond( this.onreadystatechange(); }; -const { applyNPlusOneFilter } = await import("../src/panels/db.js"); +const { applyNPlusOneFilter, updateNPlusOneBanner } = + await import("../src/panels/db.js"); + +function bannerFixture(existing = false, withSummary = false) { + var pills = existing ? [new Element()] : []; + var label = new Element(); + var clear = new Element(); + clear.setAttribute("href", "/debug?panel=db&sort=-duration"); + var list = { appendChild: (pill) => pills.push(pill) }; + var banner = new Element(); + var attached = existing; + banner.querySelector = (selector) => { + if (selector === ".yii-debug-active-filters-label") return label; + if (selector === ".yii-debug-active-filters-list") return list; + if (selector === ".yii-debug-active-filters-clear") return clear; + return ( + pills.find((pill) => pill.getAttribute("data-yii-debug-n1-pill")) || null + ); + }; + banner.querySelectorAll = () => pills; + banner.remove = () => { + attached = false; + }; + var grid = { + before: () => { + attached = true; + }, + }; + var root = { + querySelector(selector) { + if (selector === ".yii-debug-db-n1-summary") + return withSummary ? grid : null; + return selector === ".yii-debug-grid-db" + ? grid + : attached + ? banner + : null; + }, + createElement(tag) { + if (tag === "div") return banner; + var pill = new Element(); + var value = new Element(); + pill.querySelector = () => value; + pill.remove = () => { + pills = pills.filter((item) => item !== pill); + }; + return pill; + }, + }; + return { root, banner, label, clear }; +} + +test("N+1 creates the standard active-filter banner and clears it without navigation", () => { + var { root, banner, label, clear } = bannerFixture(); + var cleared = 0; + var clearFilter = () => { + cleared += 1; + }; + var inactive = { activeGroup: null, visible: 4 }; + updateNPlusOneBanner(root, inactive, clearFilter); + assert.equal(root.querySelector(".yii-debug-active-filters"), null); + updateNPlusOneBanner( + { querySelector: () => null }, + { activeGroup: "a" }, + clearFilter, + ); + + updateNPlusOneBanner(root, { activeGroup: "a", visible: 3 }, clearFilter); + assert.equal(root.querySelector(".yii-debug-active-filters"), banner); + assert.equal(banner.getAttribute("aria-label"), "Active filters"); + assert.equal(label.textContent, "1 filter active"); + var pill = banner.querySelector("[data-yii-debug-n1-pill]"); + assert.equal( + pill.getAttribute("aria-label"), + "Remove N+1: 3 similar queries filter", + ); + assert.equal( + pill.querySelector(".yii-debug-active-filter-value").textContent, + "3 similar queries", + ); + + updateNPlusOneBanner(root, { activeGroup: "b", visible: 5 }, clearFilter); + assert.equal( + banner.querySelectorAll(".yii-debug-active-filter-pill").length, + 1, + ); + assert.equal( + pill.querySelector(".yii-debug-active-filter-value").textContent, + "5 similar queries", + ); + pill.dispatchEvent(new Event("click")); + clear.dispatchEvent(new Event("click")); + assert.equal(cleared, 2); + + updateNPlusOneBanner(root, inactive, clearFilter); + assert.equal(root.querySelector(".yii-debug-active-filters"), null); +}); + +test("N+1 inserts its banner before the summary instead of the grid", () => { + var { root, banner } = bannerFixture(false, true); + var querySelector = root.querySelector; + root.querySelector = (selector) => { + assert.notEqual(selector, ".yii-debug-grid-db"); + return querySelector(selector); + }; + updateNPlusOneBanner(root, { activeGroup: "a", visible: 3 }, () => {}); + assert.equal(root.querySelector(".yii-debug-active-filters"), banner); +}); + +test("N+1 shares the existing banner while preserving server filters and Clear all URL", () => { + var { root, banner, label, clear } = bannerFixture(true); + var inactive = { activeGroup: null, visible: 4 }; + updateNPlusOneBanner(root, inactive, () => {}); + assert.equal(label.textContent, "1 filter active"); + + updateNPlusOneBanner(root, { activeGroup: "a", visible: 3 }, () => {}); + assert.equal(label.textContent, "2 filters active"); + assert.equal(clear.getAttribute("href"), "/debug?panel=db&sort=-duration"); + assert.equal(clear.listeners.size, 0); + + updateNPlusOneBanner(root, inactive, () => {}); + assert.equal(root.querySelector(".yii-debug-active-filters"), banner); + assert.equal( + banner.querySelectorAll(".yii-debug-active-filter-pill").length, + 1, + ); + assert.equal(label.textContent, "1 filter active"); +}); test("N+1 group links filter query rows and expose live progress", () => { var rows = [new Element(), new Element(), new Element(), new Element()]; @@ -243,7 +371,19 @@ test("N+1 group links filter query rows and expose live progress", () => { assert.equal(clear.hidden, false); assert.equal(status.textContent, "Showing 2 potential N+1 queries."); + markers[0].classList.add("yii-debug-deep-link-target"); + applyNPlusOneFilter(root, "group-b"); + assert.equal( + markers[0].classList.contains("yii-debug-deep-link-target"), + false, + ); + + markers[2].classList.add("yii-debug-deep-link-target"); applyNPlusOneFilter(root, null); + assert.equal( + markers[2].classList.contains("yii-debug-deep-link-target"), + false, + ); rows.forEach((row) => assert.equal(row.hidden, false)); assert.equal(clear.hidden, true); }); @@ -456,8 +596,9 @@ test("explain toggles reuse loaded output and explain-all short-circuits loaded }); }); -test("N+1 filter links navigate, scroll, and clear through the document handlers", () => { +test("N+1 filter links navigate, toggle off, and clear through the document handlers", () => { historyPushes.length = 0; + window.location.href = "https://example.test/debug?tab=db&sort=-duration"; filterLinks[0].dispatchEvent(new Event("click")); @@ -468,22 +609,52 @@ test("N+1 filter links navigate, scroll, and clear through the document handlers assert.equal(clearLink.hidden, false); assert.equal(filterLinks[0].getAttribute("aria-current"), "true"); + docMarker.classList.add("yii-debug-deep-link-target"); + delete anchorTarget.scrolled; + filterLinks[0].dispatchEvent(new Event("click")); + assert.equal(historyPushes.length, 2); + assert.equal( + window.location.href, + "https://example.test/debug?tab=db&sort=-duration", + ); + assert.equal(anchorTarget.scrolled, undefined); + assert.equal(filterLinks[0].getAttribute("aria-current"), null); + assert.equal( + docMarker.classList.contains("yii-debug-deep-link-target"), + false, + ); + assert.equal(docStatus.textContent, "Showing all database queries."); + assert.equal(clearLink.hidden, true); + + filterLinks[0].dispatchEvent(new Event("click")); + docMarker.classList.add("yii-debug-deep-link-target"); clearLink.dispatchEvent(new Event("click")); + assert.equal(historyPushes.length, 4); + assert.equal(new URL(window.location.href).hash, ""); + assert.equal( + docMarker.classList.contains("yii-debug-deep-link-target"), + false, + ); assert.equal(docStatus.textContent, "Showing all database queries."); assert.equal(clearLink.hidden, true); + window.location.href += "#unrelated-section"; filterLinks[1].dispatchEvent(new Event("click")); - assert.equal(historyPushes.length, 1); + assert.equal(historyPushes.length, 4); + assert.equal(new URL(window.location.href).hash, "#unrelated-section"); delete anchorTarget.scrollIntoView; - delete anchorTarget.scrolled; filterLinks[0].dispatchEvent(new Event("click")); - assert.equal(historyPushes.length, 2); - assert.equal(anchorTarget.scrolled, undefined); + assert.equal(historyPushes.length, 5); + + // Applying an already linked group does not add a duplicate history entry. + filterLinks[0].removeAttribute("aria-current"); + filterLinks[0].dispatchEvent(new Event("click")); + assert.equal(historyPushes.length, 5); delete window.history; filterLinks[0].dispatchEvent(new Event("click")); - assert.equal(historyPushes.length, 2); + assert.equal(historyPushes.length, 5); window.history = historyStub; }); diff --git a/src/Capture/CapturePolicy.php b/src/Capture/CapturePolicy.php index 85a9259..b6760b5 100644 --- a/src/Capture/CapturePolicy.php +++ b/src/Capture/CapturePolicy.php @@ -10,7 +10,6 @@ use SensitiveParameter; use function array_reverse; -use function array_shift; use function get_object_vars; use function http_build_query; use function is_array; @@ -32,6 +31,13 @@ final readonly class CapturePolicy { /** + * Raw body bytes retained by persistent capture when no explicit limit is configured. + */ + public const int DEFAULT_MAX_BODY_BYTES = 65536; + + /** + * PCRE patterns applied to complete original keys. + * * @var list */ private array $sensitiveKeyPatterns; @@ -45,7 +51,7 @@ */ public function __construct( private array $sensitiveKeys = SensitiveDataRedactor::DEFAULT_KEYS, - private int $maxBodyBytes = 65536, + private int $maxBodyBytes = self::DEFAULT_MAX_BODY_BYTES, private array $sensitiveKeyPrefixes = [], array|null $sensitiveKeyPatterns = null, ) { @@ -57,16 +63,16 @@ public function __construct( $this->sensitiveKeyPatterns = SensitiveDataRedactor::patterns($this->sensitiveKeys, $sensitiveKeyPatterns); - SensitiveDataRedactor::isSensitiveKey( - '', - [], - $this->sensitiveKeyPrefixes, - $this->sensitiveKeyPatterns, - ); + // One probe rejects an empty prefix or an invalid pattern at configuration time instead of on first capture. + $this->isSensitiveKey(''); } /** * Returns whether a key is denied by this policy. + * + * @param string $key Original key to check case-insensitively. + * + * @return bool `true` when the key matches an exact name, a prefix, or a pattern rule; `false` otherwise. */ public function isSensitiveKey(string $key): bool { @@ -80,6 +86,8 @@ public function isSensitiveKey(string $key): bool /** * Returns the maximum number of body bytes that may reach persistent capture. + * + * @return int Configured body byte limit. */ public function maxBodyBytes(): int { @@ -106,9 +114,13 @@ public function redact(#[SensitiveParameter] array $value): array } /** - * Redacts a decoded body and suppresses its raw representation whenever redaction was required. + * Redacts a decoded body, suppressing its raw representation whenever redaction was required and truncating it at + * the configured byte boundary otherwise. * - * @return array{decoded: mixed, raw: string} + * @param string $raw Raw body exactly as received. + * @param mixed $decoded Decoded body, or `null` when the body could not be decoded. + * + * @return array{decoded: mixed, raw: string} Sanitized decoded body and its bounded raw representation. */ public function redactBody(#[SensitiveParameter] string $raw, #[SensitiveParameter] mixed $decoded): array { @@ -118,16 +130,26 @@ public function redactBody(#[SensitiveParameter] string $raw, #[SensitiveParamet default => $decoded, }; + if ($sanitized !== $decoded) { + return ['decoded' => $sanitized, 'raw' => SensitiveDataRedactor::PLACEHOLDER]; + } + + $body = $this->redactText($raw); + return [ 'decoded' => $sanitized, - 'raw' => $sanitized !== $decoded - ? SensitiveDataRedactor::PLACEHOLDER - : $this->truncateBody($raw), + 'raw' => strlen($body) > $this->maxBodyBytes + ? substr($body, 0, $this->maxBodyBytes) . SensitiveDataRedactor::TRUNCATED + : $body, ]; } /** * Redacts common `key=value` and `key: value` secret fragments in diagnostic text. + * + * @param string $text Diagnostic text to sanitize. + * + * @return string Text with every sensitive assignment value replaced. */ public function redactText(#[SensitiveParameter] string $text): string { @@ -140,16 +162,20 @@ public function redactText(#[SensitiveParameter] string $text): string PREG_SET_ORDER | PREG_OFFSET_CAPTURE, ); - foreach (array_reverse($matches) as $match) { - [$assignment, $assignmentOffset] = array_shift($match); - [$key] = array_pop($match); + // Later assignments are rewritten first so earlier match offsets stay valid. + $assignments = array_reverse($matches); + + foreach ($assignments as $match) { + [$assignment, $assignmentOffset] = $match[0]; + [$key] = $match[2]; - if (!$this->isSensitiveKey($key)) { + if ($this->isSensitiveKey($key) === false) { continue; } $valueStart = $assignmentOffset + strlen($assignment); $valueLength = strcspn($text, ",;&\r\n", $valueStart); + $text = substr_replace( $text, SensitiveDataRedactor::PLACEHOLDER, @@ -163,12 +189,18 @@ public function redactText(#[SensitiveParameter] string $text): string /** * Redacts sensitive values in a URL query string without changing the URL outside its query component. + * + * @param string $url URL to sanitize. + * + * @return string URL with every sensitive query value replaced. */ public function redactUrl(#[SensitiveParameter] string $url): string { $fragmentPosition = strpos($url, '#'); + $fragment = $fragmentPosition === false ? '' : substr($url, $fragmentPosition); $withoutFragment = $fragmentPosition === false ? $url : substr($url, 0, $fragmentPosition); + $queryPosition = strpos($withoutFragment, '?'); if ($queryPosition === false) { @@ -185,14 +217,22 @@ public function redactUrl(#[SensitiveParameter] string $url): string } /** - * Truncates an opaque body at the configured byte boundary. + * Returns a new instance whose exact-key list also covers the given keys. + * + * The already resolved pattern rules are carried over, so extending the default key list keeps the segment-aware + * defaults active instead of silently dropping them. + * + * @param list $sensitiveKeys Extra exact, case-insensitive keys to redact recursively. + * + * @return self New instance denying both the configured and the additional keys. */ - private function truncateBody(#[SensitiveParameter] string $body): string + public function withAdditionalSensitiveKeys(array $sensitiveKeys): self { - $body = $this->redactText($body); - - return strlen($body) > $this->maxBodyBytes - ? substr($body, 0, $this->maxBodyBytes) . SensitiveDataRedactor::TRUNCATED - : $body; + return new self( + [...$this->sensitiveKeys, ...$sensitiveKeys], + $this->maxBodyBytes, + $this->sensitiveKeyPrefixes, + $this->sensitiveKeyPatterns, + ); } } diff --git a/src/Collector/CollectorCoordinator.php b/src/Collector/CollectorCoordinator.php index 5d29239..4212d61 100644 --- a/src/Collector/CollectorCoordinator.php +++ b/src/Collector/CollectorCoordinator.php @@ -6,6 +6,7 @@ use InvalidArgumentException; use PHPForge\Debug\Exception\Message; +use PHPForge\Debug\Instrumentation\InstrumentationGuard; use PHPForge\Debug\Storage\{DebugSnapshot, PanelFailure, RequestSummary}; use Throwable; @@ -20,8 +21,10 @@ final class CollectorCoordinator * @var array Collectors indexed by their stable ID. */ private array $collectors = []; + /** + * @var bool Whether every registered collector completed startup in the active cycle. + */ private bool $started = false; - /** * @var array Collectors that still require cleanup in the active or failed cycle. */ @@ -73,7 +76,10 @@ public function capture(RequestSummary $summary): DebugSnapshot $panels[$id] = $snapshot->jsonSerialize(); } } catch (Throwable $throwable) { - $failures[$id] = PanelFailure::fromThrowable(PanelFailure::CAPTURE, $throwable); + $failures[$id] = PanelFailure::fromThrowable( + PanelFailure::CAPTURE, + $throwable, + ); } } @@ -134,7 +140,7 @@ public function run(callable $operation, callable|null $cleanupFailureHandler = try { $this->shutdown(); } catch (Throwable $cleanupFailure) { - self::reportCleanupFailure($cleanupFailure, $cleanupFailureHandler); + (new InstrumentationGuard($cleanupFailureHandler))->report($cleanupFailure); } throw $primaryFailure; @@ -193,14 +199,10 @@ public function startup(): void $collector->startup(); } } catch (Throwable $startupFailure) { - foreach ($this->startedCollectors as $affectedId => $affectedCollector) { - try { - $affectedCollector->shutdown(); - - unset($this->startedCollectors[$affectedId]); - } catch (Throwable) { - // Preserve the startup failure while continuing rollback. - } + try { + $this->shutdown(); + } catch (Throwable) { + // A rollback cleanup failure must not replace the primary startup failure. } throw $startupFailure; @@ -209,21 +211,4 @@ public function startup(): void $this->started = true; } - /** - * Reports a secondary cleanup failure without allowing the observer to replace the primary failure. - * - * @param (callable(Throwable): void)|null $cleanupFailureHandler Secondary-failure observer. - */ - private static function reportCleanupFailure( - Throwable $cleanupFailure, - callable|null $cleanupFailureHandler, - ): void { - if ($cleanupFailureHandler !== null) { - try { - $cleanupFailureHandler($cleanupFailure); - } catch (Throwable) { - // Diagnostic observers must not replace the primary application failure. - } - } - } } diff --git a/src/Comparison/PanelComparison.php b/src/Comparison/PanelComparison.php index baf2651..6bbaf3c 100644 --- a/src/Comparison/PanelComparison.php +++ b/src/Comparison/PanelComparison.php @@ -20,6 +20,29 @@ */ final readonly class PanelComparison { + /** + * Capture state of a panel whose payload reached the snapshot. + */ + public const string STATE_CAPTURED = 'Captured'; + /** + * Capture state of a panel whose capture failed. + */ + public const string STATE_FAILED = 'Failed'; + /** + * Capture state of a panel absent from the snapshot. + */ + public const string STATE_NOT_CAPTURED = 'Not captured'; + + /** + * @param string $id Stable panel ID. + * @param string $label Display name, falling back to the panel ID. + * @param string $baselineState Capture state in the baseline snapshot. + * @param string $targetState Capture state in the target snapshot. + * @param int $added Leaves present only in the target payload. + * @param int $removed Leaves present only in the baseline payload. + * @param int $changed Leaves that differ, or `1` for a state-only transition. + * @param int $unchanged Leaves present in both payloads under the same value. + */ private function __construct( public string $id, public string $label, @@ -37,9 +60,11 @@ private function __construct( * Failures take precedence over payloads. A state transition adds one change only when there are no structural * additions, removals, or changes; unchanged leaves remain counted even for that state-only transition. * + * @param DebugSnapshot $baseline Baseline snapshot. + * @param DebugSnapshot $target Target snapshot. * @param array $panelLabels Display names indexed by stable panel ID, in display order. * - * @return list + * @return list Panel comparisons in configured display order. */ public static function between(DebugSnapshot $baseline, DebugSnapshot $target, array $panelLabels = []): array { @@ -52,9 +77,10 @@ public static function between(DebugSnapshot $baseline, DebugSnapshot $target, a ], ); + $configuredIds = array_keys($panelLabels); $orderedIds = []; - foreach ($panelLabels as $id => $_label) { + foreach ($configuredIds as $id) { if (in_array($id, $observedIds, true)) { $orderedIds[] = $id; } @@ -80,12 +106,9 @@ public static function between(DebugSnapshot $baseline, DebugSnapshot $target, a self::panelPayload($target, $id), ); - $added = $difference->added; - $removed = $difference->removed; $changed = $difference->changed; - $unchanged = $difference->unchanged; - if ($added + $removed + $changed === 0 && $baselineState !== $targetState) { + if ($difference->added + $difference->removed + $changed === 0 && $baselineState !== $targetState) { $changed = 1; } @@ -94,10 +117,10 @@ public static function between(DebugSnapshot $baseline, DebugSnapshot $target, a label: $panelLabels[$id] ?? $id, baselineState: $baselineState, targetState: $targetState, - added: $added, - removed: $removed, + added: $difference->added, + removed: $difference->removed, changed: $changed, - unchanged: $unchanged, + unchanged: $difference->unchanged, ); } @@ -106,6 +129,8 @@ public static function between(DebugSnapshot $baseline, DebugSnapshot $target, a /** * Returns the total number of structural differences detected for the panel. + * + * @return int Sum of added, removed, and changed leaves. */ public function differenceCount(): int { @@ -115,7 +140,10 @@ public function differenceCount(): int /** * Returns the captured payload or failure envelope, preserving the distinction between absent and empty. * - * @return array|null + * @param DebugSnapshot $snapshot Snapshot to inspect. + * @param string $id Stable panel ID. + * + * @return array|null Captured payload, failure envelope, or `null` when the panel is absent. */ private static function panelPayload(DebugSnapshot $snapshot, string $id): array|null { @@ -126,12 +154,20 @@ private static function panelPayload(DebugSnapshot $snapshot, string $id): array return $snapshot->panels[$id] ?? null; } + /** + * Returns the capture state of the panel within the snapshot. + * + * @param DebugSnapshot $snapshot Snapshot to inspect. + * @param string $id Stable panel ID. + * + * @return string One of {@see STATE_CAPTURED}, {@see STATE_FAILED}, or {@see STATE_NOT_CAPTURED}. + */ private static function panelState(DebugSnapshot $snapshot, string $id): string { if (array_key_exists($id, $snapshot->failures)) { - return 'Failed'; + return self::STATE_FAILED; } - return array_key_exists($id, $snapshot->panels) ? 'Captured' : 'Not captured'; + return array_key_exists($id, $snapshot->panels) ? self::STATE_CAPTURED : self::STATE_NOT_CAPTURED; } } diff --git a/src/Comparison/PayloadDifference.php b/src/Comparison/PayloadDifference.php index 0c0d071..f8c2354 100644 --- a/src/Comparison/PayloadDifference.php +++ b/src/Comparison/PayloadDifference.php @@ -17,6 +17,12 @@ */ final readonly class PayloadDifference { + /** + * @param int $added Leaves present only in the target payload. + * @param int $removed Leaves present only in the baseline payload. + * @param int $changed Leaves present in both payloads under a different value. + * @param int $unchanged Leaves present in both payloads under the same value. + */ private function __construct( public int $added, public int $removed, @@ -29,6 +35,8 @@ private function __construct( * * @param array|null $baseline Baseline payload, or `null` when not captured. * @param array|null $target Target payload, or `null` when not captured. + * + * @return self Structural difference counts for the two payloads. */ public static function between(array|null $baseline, array|null $target): self { @@ -51,17 +59,19 @@ public static function between(array|null $baseline, array|null $target): self } return new self( - count(array_diff_key($targetLeaves, $baselineLeaves)), - count(array_diff_key($baselineLeaves, $targetLeaves)), - $changed, - $unchanged, + added: count(array_diff_key($targetLeaves, $baselineLeaves)), + removed: count(array_diff_key($baselineLeaves, $targetLeaves)), + changed: $changed, + unchanged: $unchanged, ); } /** - * @param array|null $payload + * Reduces a payload to its typed leaves indexed by escaped path. * - * @return array + * @param array|null $payload Payload to flatten, or `null` when not captured. + * + * @return array Leaf fingerprints indexed by escaped path. */ private static function flatten(array|null $payload): array { @@ -75,7 +85,11 @@ private static function flatten(array|null $payload): array } /** - * @param array $leaves + * Records the typed fingerprint of every leaf reachable from the value. + * + * @param mixed $value Value to traverse. + * @param string $path Escaped path of the value within the payload. + * @param array $leaves Accumulator receiving leaf fingerprints indexed by escaped path. */ private static function flattenValue(mixed $value, string $path, array &$leaves): void { diff --git a/src/Comparison/SnapshotComparison.php b/src/Comparison/SnapshotComparison.php index 44f5368..0b7fd6a 100644 --- a/src/Comparison/SnapshotComparison.php +++ b/src/Comparison/SnapshotComparison.php @@ -8,10 +8,6 @@ /** * Pairs request-summary metrics with per-panel structural differences for two captured snapshots. - * - * The comparison keeps references to both source snapshots so adapter facades can expose them, while the metric and - * panel results retain only labels, states, and counts. An overview therefore cannot surface values that the individual - * panels keep behind their own presentation and redaction rules unless it reads the snapshots directly. */ final readonly class SnapshotComparison { @@ -31,7 +27,11 @@ private function __construct( /** * Compares two snapshots, combining summary metrics with panel structural differences. * + * @param DebugSnapshot $baseline Baseline snapshot. + * @param DebugSnapshot $target Target snapshot. * @param array $panelLabels Display names indexed by stable panel ID, in display order. + * + * @return self Comparison pairing summary metrics with per-panel structural differences. */ public static function between(DebugSnapshot $baseline, DebugSnapshot $target, array $panelLabels = []): self { @@ -45,6 +45,8 @@ public static function between(DebugSnapshot $baseline, DebugSnapshot $target, a /** * Returns whether any summary metric or panel payload differs. + * + * @return bool `true` when at least one metric or panel differs; `false` otherwise. */ public function hasDifferences(): bool { diff --git a/src/Comparison/SummaryMetricComparison.php b/src/Comparison/SummaryMetricComparison.php index 9e71473..e7080d0 100644 --- a/src/Comparison/SummaryMetricComparison.php +++ b/src/Comparison/SummaryMetricComparison.php @@ -4,6 +4,7 @@ namespace PHPForge\Debug\Comparison; +use PHPForge\Debug\Helper\Format; use PHPForge\Debug\Storage\RequestSummary; use function number_format; @@ -15,6 +16,14 @@ */ final readonly class SummaryMetricComparison { + /** + * @param string $label History metric label. + * @param string $baseline Formatted baseline value. + * @param string $target Formatted target value. + * @param string $delta Formatted difference, or `No change` when both values match. + * @param string $trend Trend direction: `up`, `down`, or `neutral`. + * @param string|null $panelId Related panel ID, or `null` when the metric has no panel. + */ private function __construct( public string $label, public string $baseline, @@ -27,7 +36,10 @@ private function __construct( /** * Compares summaries in the canonical history metric order. * - * @return list + * @param RequestSummary $baseline Baseline request metadata. + * @param RequestSummary $target Target request metadata. + * + * @return list Metric comparisons in canonical history order. */ public static function between(RequestSummary $baseline, RequestSummary $target): array { @@ -51,7 +63,7 @@ public static function between(RequestSummary $baseline, RequestSummary $target) 'Duration', $baseline->processingTime, $target->processingTime, - 1000, + Format::MILLISECONDS_PER_SECOND, 'ms', 'profiling', ), @@ -59,7 +71,7 @@ public static function between(RequestSummary $baseline, RequestSummary $target) 'Peak memory', $baseline->peakMemory, $target->peakMemory, - 1 / 1_048_576, + 1 / Format::BYTES_PER_MB, 'MB', 'profiling', ), @@ -86,12 +98,23 @@ public static function between(RequestSummary $baseline, RequestSummary $target) /** * Returns whether the metric changed between the baseline and the target. + * + * @return bool `true` when the formatted delta reports a change; `false` otherwise. */ public function hasDifference(): bool { return $this->delta !== 'No change'; } + /** + * Formats a scaled value with its unit. + * + * @param float|int $value Scaled value to format. + * @param string $unit Unit suffix, or an empty string to omit it. + * @param int $precision Decimal places to keep. + * + * @return string Formatted value, with the unit appended when one is given. + */ private static function formatNumber(float|int $value, string $unit, int $precision): string { $formatted = number_format($value, $precision, '.', ','); @@ -99,6 +122,16 @@ private static function formatNumber(float|int $value, string $unit, int $precis return $unit === '' ? $formatted : "{$formatted} {$unit}"; } + /** + * Compares two counters reported without a unit. + * + * @param string $label History metric label. + * @param int $baseline Baseline counter. + * @param int $target Target counter. + * @param string|null $panelId Related panel ID, or `null` when the metric has no panel. + * + * @return self Metric comparison for the two counters. + */ private static function integerMetric( string $label, int $baseline, @@ -116,6 +149,18 @@ private static function integerMetric( ); } + /** + * Compares two optional measurements, reporting `Not comparable` when either side is missing. + * + * @param string $label History metric label. + * @param float|int|null $baseline Baseline measurement, or `null` when not captured. + * @param float|int|null $target Target measurement, or `null` when not captured. + * @param float $scale Factor converting the raw value to its display unit. + * @param string $unit Unit suffix. + * @param string|null $panelId Related panel ID, or `null` when the metric has no panel. + * + * @return self Metric comparison for the two measurements. + */ private static function nullableFloatMetric( string $label, float|int|null $baseline, @@ -146,6 +191,19 @@ private static function nullableFloatMetric( ); } + /** + * Compares two measurements, formatting the absolute delta and its percentage. + * + * @param string $label History metric label. + * @param float|int $baseline Baseline measurement. + * @param float|int $target Target measurement. + * @param float $scale Factor converting the raw value to its display unit. + * @param string $unit Unit suffix, or an empty string to omit it. + * @param string|null $panelId Related panel ID, or `null` when the metric has no panel. + * @param int $precision Decimal places to keep. + * + * @return self Metric comparison for the two measurements. + */ private static function numericMetric( string $label, float|int $baseline, @@ -158,15 +216,25 @@ private static function numericMetric( $scaledBaseline = $baseline * $scale; $scaledTarget = $target * $scale; $scaledDelta = $scaledTarget - $scaledBaseline; - $trend = $scaledDelta > 0 ? 'up' : ($scaledDelta < 0 ? 'down' : 'neutral'); + + $trend = match (true) { + $scaledDelta > 0 => 'up', + $scaledDelta < 0 => 'down', + default => 'neutral', + }; $delta = 'No change'; if ($scaledDelta !== 0.0) { $sign = $trend === 'up' ? '+' : ''; - $percentage = (float) $baseline !== 0.0 - ? " ({$sign}" . number_format((($target - $baseline) / $baseline) * 100, 1) . '%)' - : ''; + $percentage = ''; + + if ((float) $baseline !== 0.0) { + $ratio = number_format((($target - $baseline) / $baseline) * 100, 1); + + $percentage = " ({$sign}{$ratio}%)"; + } + $delta = $sign . self::formatNumber($scaledDelta, $unit, $precision) . $percentage; } @@ -180,11 +248,27 @@ private static function numericMetric( ); } + /** + * Formats an HTTP status code, reporting an uncaptured status as text. + * + * @param int $statusCode Captured status code, or `0` when not captured. + * + * @return string Status code, or `Not captured` when the code is `0`. + */ private static function status(int $statusCode): string { return $statusCode === 0 ? 'Not captured' : (string) $statusCode; } + /** + * Compares two textual values, reporting only whether they differ. + * + * @param string $label History metric label. + * @param string $baseline Baseline value. + * @param string $target Target value. + * + * @return self Metric comparison for the two values. + */ private static function textMetric(string $label, string $baseline, string $target): self { return new self( @@ -196,6 +280,13 @@ private static function textMetric(string $label, string $baseline, string $targ ); } + /** + * Formats a flag for display. + * + * @param bool $value Flag to format. + * + * @return string `Yes` when the flag is `true`; `No` otherwise. + */ private static function yesNo(bool $value): string { return $value ? 'Yes' : 'No'; diff --git a/src/Data/FilterEngine.php b/src/Data/FilterEngine.php index 6c05307..eb6600d 100644 --- a/src/Data/FilterEngine.php +++ b/src/Data/FilterEngine.php @@ -5,6 +5,7 @@ namespace PHPForge\Debug\Data; use PHPForge\Debug\Helper\Dump; +use PHPForge\Debug\Storage\PanelRow; use function array_filter; use function array_key_exists; @@ -24,13 +25,18 @@ */ final class FilterEngine { + /** + * Charset used by every case-insensitive comparison. + */ private const string CHARSET = 'UTF-8'; /** + * Conditions are evaluated in the order they are added, and all must match for a row to be included. + * * @var list< * array{attribute: string, operator: '>'|'<'|'>=', value: float} * |array{attribute: string, operator: 'contains'|'same', value: string} - * > + * > Conditions registered for the next {@see filter()} run. */ private array $conditions = []; @@ -39,7 +45,7 @@ final class FilterEngine * * Empty and non-scalar raw values register nothing, so unfiltered attributes can be passed through unconditionally. * - * @param string $attribute Row attribute (public property or array key) the condition applies to. + * @param string $attribute Row field (persisted panel field, public property, or array key) to compare. * @param mixed $rawValue Raw filter value as read from the request; scalars are compared as strings. * @param bool $partial Whether a non-numeric value matches as a case-insensitive substring instead of whole-value. */ @@ -71,7 +77,7 @@ public function addCondition(string $attribute, mixed $rawValue, bool $partial = /** * Registers a numeric greater-than-or-equal condition. * - * @param string $attribute Row attribute (public property or array key) the condition applies to. + * @param string $attribute Row field (persisted panel field, public property, or array key) to compare. * @param float $value Inclusive lower bound the attribute value must reach. */ public function addMinimumCondition(string $attribute, float $value): void @@ -98,25 +104,32 @@ public function filter(array $rows): array } /** + * Returns whether the row satisfies every registered condition. + * * @param array|object $row Typed row object or string-keyed array. + * + * @return bool `true` when every condition matches; `false` on the first failing condition. */ private function matches(array|object $row): bool { + $values = match (true) { + $row instanceof PanelRow => $row->jsonSerialize(), + is_object($row) => get_object_vars($row), + default => $row, + }; + foreach ($this->conditions as $condition) { $attribute = $condition['attribute']; - $values = is_object($row) ? get_object_vars($row) : $row; - if (!array_key_exists($attribute, $values)) { return false; } $candidate = $values[$attribute]; + $expected = $condition['value']; $operator = $condition['operator']; if ($operator === '>' || $operator === '<' || $operator === '>=') { - $expected = $condition['value']; - if (!is_float($expected) || !is_numeric($candidate)) { return false; } @@ -127,18 +140,14 @@ private function matches(array|object $row): bool default => $candidate >= $expected, }; - if (!$matched) { + if ($matched === false) { return false; } continue; } - $candidate = is_scalar($candidate) - ? (string) $candidate - : Dump::export($candidate); - - $expected = $condition['value']; + $candidate = is_scalar($candidate) ? (string) $candidate : Dump::export($candidate); if (!is_string($expected)) { return false; diff --git a/src/Data/PageSize.php b/src/Data/PageSize.php index 1cd2caf..99fa70e 100644 --- a/src/Data/PageSize.php +++ b/src/Data/PageSize.php @@ -16,6 +16,10 @@ */ final class PageSize { + /** + * Selector value that disables pagination. + */ + public const string ALL = 'all'; /** * Default page size applied when no `per-page` parameter is supplied or the value is invalid. */ @@ -32,7 +36,7 @@ final class PageSize '25', '50', '100', - 'all', + self::ALL, ]; /** @@ -40,6 +44,8 @@ final class PageSize * * @param string|null $raw Raw `per-page` query-parameter value, or `null` when absent. * @param int $default Page size used when no value is supplied. + * + * @return string Canonical selector value: {@see ALL}, the raw value, or the default as a string. */ public static function current(string|null $raw, int $default = self::DEFAULT): string { @@ -47,7 +53,7 @@ public static function current(string|null $raw, int $default = self::DEFAULT): return (string) $default; } - return strcasecmp($raw, 'all') === 0 ? 'all' : $raw; + return strcasecmp($raw, self::ALL) === 0 ? self::ALL : $raw; } /** @@ -60,7 +66,7 @@ public static function current(string|null $raw, int $default = self::DEFAULT): */ public static function resolve(string|null $raw, int $default = self::DEFAULT): int|null { - if ($raw !== null && strcasecmp($raw, 'all') === 0) { + if ($raw !== null && strcasecmp($raw, self::ALL) === 0) { return null; } @@ -77,6 +83,8 @@ public static function resolve(string|null $raw, int $default = self::DEFAULT): * Renders the page-size selector for the `per-page` value found in the query parameters. * * @param array $queryParams Query parameters already normalized by the panel. + * + * @return string Rendered selector markup. */ public static function selectorFor(array $queryParams): string { @@ -87,6 +95,8 @@ public static function selectorFor(array $queryParams): string * Renders the inline page-size selector shown in the grid summary header. * * @param string $current Currently selected raw value (one of {@see OPTIONS} for a highlighted option). + * + * @return string Rendered selector markup. */ private static function selectorHtml(string $current): string { @@ -99,7 +109,7 @@ private static function selectorHtml(string $current): string $select = $select->option( Option::tag() ->value($row) - ->content($row === 'all' ? 'All' : $row) + ->content($row === self::ALL ? 'All' : $row) ->selected($row === $current), ); } diff --git a/src/Data/QueryInput.php b/src/Data/QueryInput.php index 91d62bd..edef106 100644 --- a/src/Data/QueryInput.php +++ b/src/Data/QueryInput.php @@ -58,6 +58,8 @@ public static function group(array $query, string $prefix): array * whenever `null` comes back. * * @param string $value Raw submitted bound. + * + * @return float|null Finite, non-negative bound, or `null` when the value is unusable. */ public static function minimumBound(string $value): float|null { @@ -75,12 +77,21 @@ public static function minimumBound(string $value): float|null * * @param array $query Parsed query parameters. * @param string $name Parameter name to read. + * + * @return string|null Parameter value as a string, or `null` when absent or non-scalar. */ public static function scalar(array $query, string $name): string|null { return self::stringValue($query[$name] ?? null); } + /** + * Normalizes a scalar query value to its string form. + * + * @param mixed $value Raw query value. + * + * @return string|null String form of a string, `int`, or `float` value; `null` for any other type. + */ private static function stringValue(mixed $value): string|null { if (is_string($value)) { diff --git a/src/Helper/Coerce.php b/src/Helper/Coerce.php index 5561cea..e608b65 100644 --- a/src/Helper/Coerce.php +++ b/src/Helper/Coerce.php @@ -55,7 +55,7 @@ public static function int(mixed $value, int $default = 0): int } /** - * Returns the value as an int when it is an integer or numeric, `null` otherwise. + * Returns the value as an int when it is numeric, `null` otherwise. * * @param mixed $value Value to narrow. * diff --git a/src/Helper/Dump.php b/src/Helper/Dump.php index 1f4d5d7..396d27a 100644 --- a/src/Helper/Dump.php +++ b/src/Helper/Dump.php @@ -5,13 +5,11 @@ namespace PHPForge\Debug\Helper; use function addslashes; -use function array_keys; -use function count; +use function array_is_list; use function get_debug_type; use function gettype; use function is_array; use function is_scalar; -use function range; use function str_repeat; use function var_export; @@ -21,8 +19,8 @@ final class Dump { /** - * Renders a value as a display string: quoted strings, bare scalars, and 4-space-indented arrays without - * trailing commas. + * Renders a value as a display string: quoted strings, bare scalars, and 4-space-indented arrays without trailing + * commas. * * @param mixed $value JSON-safe value to render. * @param int $depth Maximum nesting level rendered before collapsing to `[...]`. @@ -48,9 +46,13 @@ public static function export(mixed $value): string } /** + * Renders an array as an indented display block, or `[...]` once the depth budget is exhausted. + * * @param array $value Array to render. * @param int $depth Maximum nesting level. * @param int $level Current nesting level. + * + * @return string Display block for the array. */ private static function dumpArray(array $value, int $depth, int $level): string { @@ -66,19 +68,24 @@ private static function dumpArray(array $value, int $depth, int $level): string $output = '['; - foreach (array_keys($value) as $key) { + foreach ($value as $key => $item) { $output .= "\n{$spaces} "; $output .= self::dumpInternal($key, $depth, $level); $output .= ' => '; - $output .= self::dumpInternal($value[$key], $depth, $level + 1); + $output .= self::dumpInternal($item, $depth, $level + 1); } return "{$output}\n{$spaces}]"; } /** + * Renders one value of any type at the current nesting level. + * + * @param mixed $value JSON-safe value to render. * @param int $depth Maximum nesting level. * @param int $level Current nesting level. + * + * @return string Display string for the value. */ private static function dumpInternal(mixed $value, int $depth, int $level): string { @@ -93,7 +100,12 @@ private static function dumpInternal(mixed $value, int $depth, int $level): stri } /** + * Renders one value as a parsable PHP expression at the current nesting level. + * + * @param mixed $value JSON-safe value to render. * @param int $level Current nesting level. + * + * @return string Parsable expression for the value. */ private static function exportInternal(mixed $value, int $level): string { @@ -106,7 +118,7 @@ private static function exportInternal(mixed $value, int $level): string return '[]'; } - $outputKeys = array_keys($value) !== range(0, count($value) - 1); + $outputKeys = array_is_list($value) === false; $spaces = str_repeat(' ', $level * 4); $output = '['; diff --git a/src/Helper/Format.php b/src/Helper/Format.php index a30932a..85883b1 100644 --- a/src/Helper/Format.php +++ b/src/Helper/Format.php @@ -26,11 +26,11 @@ final class Format /** * Bytes in one mebibyte, the unit of the `bytesToMb()` readout. */ - private const int BYTES_PER_MB = 1024 * 1024; + public const int BYTES_PER_MB = 1024 * 1024; /** * Milliseconds in one second, used to scale second-based durations. */ - private const int MILLISECONDS_PER_SECOND = 1000; + public const int MILLISECONDS_PER_SECOND = 1000; /** * Relative-time threshold: ages of one day or more render as `X d ago`. */ @@ -127,6 +127,7 @@ public static function relativeTime(int $elapsedSeconds, string $fallback): stri public static function timeOfDay(int $epochMilliseconds, string $format = 'H:i:s'): string { $seconds = intdiv($epochMilliseconds, self::MILLISECONDS_PER_SECOND); + $fraction = $epochMilliseconds % self::MILLISECONDS_PER_SECOND; if ($fraction < 0) { diff --git a/src/Helper/Fqcn.php b/src/Helper/Fqcn.php index 3799bcb..1d79692 100644 --- a/src/Helper/Fqcn.php +++ b/src/Helper/Fqcn.php @@ -26,9 +26,7 @@ public static function namespacePart(string $fqcn): string { $position = strrpos($fqcn, '\\'); - return $position === false - ? '' - : substr($fqcn, 0, $position); + return $position === false ? '' : substr($fqcn, 0, $position); } /** @@ -76,8 +74,6 @@ public static function shortName(string $fqcn): string { $position = strrpos($fqcn, '\\'); - return $position === false - ? $fqcn - : substr($fqcn, $position + 1); + return $position === false ? $fqcn : substr($fqcn, $position + 1); } } diff --git a/src/Helper/SensitiveDataRedactor.php b/src/Helper/SensitiveDataRedactor.php index 9dd2669..b05f712 100644 --- a/src/Helper/SensitiveDataRedactor.php +++ b/src/Helper/SensitiveDataRedactor.php @@ -95,10 +95,13 @@ final class SensitiveDataRedactor /** * Returns whether a key matches a configured exact name, literal prefix, or PCRE pattern. * + * @param string $key Original key to inspect case-insensitively. * @param list $sensitiveKeys Exact key names to inspect. * @param list $sensitiveKeyPrefixes Literal key prefixes to inspect case-insensitively. * @param list|null $sensitiveKeyPatterns PCRE patterns applied to the complete original key. `null` uses * defaults only with the default exact-key list; `[]` explicitly disables patterns. + * + * @return bool `true` when the key matches any configured rule; `false` otherwise. */ public static function isSensitiveKey( string $key, @@ -176,9 +179,12 @@ private static function keyMap(array $sensitiveKeys): array /** * Returns whether the key matches any normalized redaction rule. * + * @param string $key Original key to inspect. * @param array $sensitiveKeys * @param list $sensitiveKeyPrefixes * @param list $sensitiveKeyPatterns + * + * @return bool `true` when the key matches any normalized rule; `false` otherwise. */ private static function matches( string $key, @@ -268,10 +274,14 @@ private static function validatePatterns(array $patterns): void /** * @template TKey of array-key * + * Redacts one nesting level, tracking depth and the shared node budget. + * * @param array $value * @param array $sensitiveKeys * @param list $sensitiveKeyPrefixes * @param list $sensitiveKeyPatterns + * @param int $depth Current nesting level. + * @param int $nodes Entries visited so far, shared across the whole traversal. * * @return array */ diff --git a/src/Helper/Table.php b/src/Helper/Table.php index 5e88a14..dcc13c7 100644 --- a/src/Helper/Table.php +++ b/src/Helper/Table.php @@ -29,9 +29,7 @@ public static function build(array $headers, array $rows, string $class = 'yii-d $table = HtmlTable::tag()->class($class); if ($headers === []) { - return $table - ->html(Tbody::tag() - ->html(...$rows)); + return $table->html(Tbody::tag()->html(...$rows)); } $cells = []; diff --git a/src/Helper/Tabs.php b/src/Helper/Tabs.php index 55802cc..9032e69 100644 --- a/src/Helper/Tabs.php +++ b/src/Helper/Tabs.php @@ -18,10 +18,14 @@ final class Tabs { /** - * @param non-empty-string $id - * @param non-empty-string $ariaLabel - * @param list $tabs + * Renders the tab list and its panels as one markup fragment. + * + * @param non-empty-string $id Base ID every tab and panel ID derives from. + * @param non-empty-string $ariaLabel Accessible name announced for the tab list. + * @param list $tabs Tabs in display order. * @param int $activeIndex Zero-based index of the tab selected on initial render. + * + * @return string Rendered tab list followed by its panels. */ public static function render(string $id, string $ariaLabel, array $tabs, int $activeIndex = 0): string { @@ -63,14 +67,14 @@ public static function render(string $id, string $ariaLabel, array $tabs, int $a ->class($active ? 'yii-debug-tab-panel is-active' : 'yii-debug-tab-panel') ->html($tab['content']); - if (!$active) { + if ($active === false) { $panel = $panel->addAttribute('hidden', true); } $panels[] = $panel; } - $tabs = Ul::tag() + $tabList = Ul::tag() ->class('yii-debug-tabs') ->addAriaAttribute('label', $ariaLabel) ->addAttribute('role', 'tablist') @@ -81,6 +85,6 @@ public static function render(string $id, string $ariaLabel, array $tabs, int $a ->html(...$panels) ->render(); - return "{$tabs}{$content}"; + return "{$tabList}{$content}"; } } diff --git a/src/Helper/Text.php b/src/Helper/Text.php index a6fbce3..4eeacae 100644 --- a/src/Helper/Text.php +++ b/src/Helper/Text.php @@ -35,6 +35,10 @@ public static function camel2id(string $name): string * * Unparsable URLs pass through unchanged. This is a display conversion, not URL validation or HTML escaping; * callers must retain the original diagnostic URL and escape the result at the rendering boundary. + * + * @param string $url Captured URL to convert for display. + * + * @return string Path with its non-empty query and fragment, or the original URL when unparsable. */ public static function urlToPath(string $url): string { diff --git a/src/Helper/Trace.php b/src/Helper/Trace.php index b28b0da..3e47f72 100644 --- a/src/Helper/Trace.php +++ b/src/Helper/Trace.php @@ -32,6 +32,8 @@ private function __construct(private Closure|string|false $template, private arr /** * Creates a renderer emitting {@see DEFAULT_TEMPLATE} links without path rewriting. + * + * @return self Renderer using the default template and no path mappings. */ public static function create(): self { @@ -103,6 +105,8 @@ public function render(array $frame): string * single trailing slash, and only the first matching prefix is applied. * * @param array $pathMappings Remote source-path prefixes mapped to local prefixes. + * + * @return self New instance rewriting frame paths through the normalized mappings. */ public function withPathMappings(array $pathMappings): self { @@ -131,6 +135,8 @@ public function withPathMappings(array $pathMappings): self * `{line}`, and `{text}` against the escaped frame values, `false` to emit escaped plain text without a link, or a * closure receiving the frame with the normalized `file`, `line`, and `text` keys; a `string` returned by the * closure resolves the same placeholders, any other result renders as an escaped dump. + * + * @return self New instance rendering every frame with the given template. */ public function withTemplate(Closure|string|false $template): self { @@ -140,6 +146,13 @@ public function withTemplate(Closure|string|false $template): self ); } + /** + * Rewrites a captured file path through the first matching source-path mapping. + * + * @param string $file Captured source path. + * + * @return string Locally reachable path, or the captured path when no mapping matches. + */ private function mapPath(string $file): string { foreach ($this->pathMappings as $remote => $local) { @@ -151,6 +164,13 @@ private function mapPath(string $file): string return $file; } + /** + * Normalizes a path prefix to forward slashes and exactly one trailing separator. + * + * @param string $path Raw configured prefix. + * + * @return string Prefix using `/` separators and ending in a single `/`. + */ private static function normalizePrefix(string $path): string { return rtrim(str_replace('\\', '/', $path), '/') . '/'; diff --git a/src/Helper/Vocabulary.php b/src/Helper/Vocabulary.php index a6af022..0404e63 100644 --- a/src/Helper/Vocabulary.php +++ b/src/Helper/Vocabulary.php @@ -30,6 +30,7 @@ public static function logLevel(int $level): string default => 'other', }; } + /** * Returns the verb suffix for an SQL statement type. * diff --git a/src/Instrumentation/InstrumentationGuard.php b/src/Instrumentation/InstrumentationGuard.php index e3a33e0..fe6c6c2 100644 --- a/src/Instrumentation/InstrumentationGuard.php +++ b/src/Instrumentation/InstrumentationGuard.php @@ -13,7 +13,7 @@ final readonly class InstrumentationGuard { /** - * @var (Closure(Throwable): void)|null + * @var (Closure(Throwable): void)|null Observer notified of every suppressed instrumentation failure. */ private Closure|null $failureHandler; @@ -22,7 +22,7 @@ */ public function __construct(callable|null $failureHandler = null) { - $this->failureHandler = $failureHandler === null ? null : Closure::fromCallable($failureHandler); + $this->failureHandler = $failureHandler === null ? null : $failureHandler(...); } /** @@ -35,12 +35,22 @@ public function observe(callable $observer): void try { $observer(); } catch (Throwable $failure) { - if ($this->failureHandler !== null) { - try { - ($this->failureHandler)($failure); - } catch (Throwable) { - // A diagnostic failure handler is itself instrumentation and must remain fail-open. - } + $this->report($failure); + } + } + + /** + * Reports a failure that was already caught, keeping the handler itself fail-open. + * + * @param Throwable $failure Suppressed instrumentation failure. + */ + public function report(Throwable $failure): void + { + if ($this->failureHandler !== null) { + try { + ($this->failureHandler)($failure); + } catch (Throwable) { + // A diagnostic failure handler is itself instrumentation and must remain fail-open. } } } diff --git a/src/Panel/Config/ApplicationConfig.php b/src/Panel/Config/ApplicationConfig.php index ffd5669..c158733 100644 --- a/src/Panel/Config/ApplicationConfig.php +++ b/src/Panel/Config/ApplicationConfig.php @@ -11,35 +11,35 @@ { public function __construct( /** - * Yii framework version reported at request capture time. + * Framework version reported at request capture time. */ public string $yii, /** - * Configured {@see \yii\base\Application::$name}, or empty string when the application is unavailable. + * Configured application name, or an empty string when the application is unavailable. */ public string $name, /** - * Configured {@see \yii\base\Application::$version}, or empty string when none is set. + * Configured application version, or an empty string when none is set. */ public string $version, /** - * Configured {@see \yii\base\Application::$language} BCP-47 tag, or empty string. + * Configured application language as a BCP-47 tag, or an empty string. */ public string $language, /** - * Configured {@see \yii\base\Application::$sourceLanguage} BCP-47 tag, or empty string. + * Configured source language as a BCP-47 tag, or an empty string. */ public string $sourceLanguage, /** - * Configured {@see \yii\base\Application::$charset}, or empty string. + * Configured application charset, or an empty string. */ public string $charset, /** - * Active {@see YII_ENV} environment label. + * Active environment label reported by the adapter. */ public string $env, /** - * Active {@see YII_DEBUG} flag. + * Whether the application ran with debug mode enabled. */ public bool $debug, ) {} diff --git a/src/Panel/Config/ConfigCardRenderer.php b/src/Panel/Config/ConfigCardRenderer.php index 6d3ded0..08d8611 100644 --- a/src/Panel/Config/ConfigCardRenderer.php +++ b/src/Panel/Config/ConfigCardRenderer.php @@ -26,6 +26,9 @@ */ final class ConfigCardRenderer { + /** + * Decorative corner positions framing every readout card, in render order. + */ private const array CORNERS = [ 'tl', 'tr', @@ -35,33 +38,33 @@ final class ConfigCardRenderer /** * Renders the `Application details` description list (charset, current language, source language). + * + * @param ApplicationConfig $app Typed application section. + * + * @return Section Application details section. */ public static function renderApplicationDetailsSection(ApplicationConfig $app): Section { - return Section::tag() - ->class('yii-debug-section') - ->html( - H2::tag() - ->class('yii-debug-section-title') - ->html( - Span::tag() - ->class('yii-debug-section-mark') - ->content('//'), - ' Application details', - ), - Dl::tag() - ->class('yii-debug-dl') - ->html( - self::renderDlRow('Charset', $app->charset !== '' ? $app->charset : '—'), - self::renderDlRow('Current language', self::formatLanguage($app->language)), - self::renderDlRow('Source language', self::formatLanguage($app->sourceLanguage)), - ), - ); + return self::renderSection( + '//', + [' Application details'], + Dl::tag() + ->class('yii-debug-dl') + ->html( + self::renderDlRow('Charset', $app->charset !== '' ? $app->charset : '—'), + self::renderDlRow('Current language', self::formatLanguage($app->language)), + self::renderDlRow('Source language', self::formatLanguage($app->sourceLanguage)), + ), + ); } /** * Renders the Installed extensions section, or returns `null` when the roster is empty so the caller can omit the * wrapper entirely. + * + * @param ConfigSummary $summary Typed configuration summary. + * + * @return Section|null Installed extensions section, or `null` when the roster is empty. */ public static function renderInstalledExtensionsSection(ConfigSummary $summary): Section|null { @@ -85,28 +88,26 @@ public static function renderInstalledExtensionsSection(ConfigSummary $summary): $items[] = self::renderPackageGroup($vendor, $packages); } - return Section::tag() - ->class('yii-debug-section') - ->html( - H2::tag() - ->class('yii-debug-section-title') - ->html( - Span::tag() - ->class('yii-debug-section-mark') - ->content('::'), - ' Installed extensions ', - Span::tag() - ->class('yii-debug-section-count') - ->content((string) $summary->extensionCount()), - ), - Div::tag() - ->class('yii-debug-package-groups') - ->html(...$items), - ); + return self::renderSection( + '::', + [ + ' Installed extensions ', + Span::tag() + ->class('yii-debug-section-count') + ->content((string) $summary->extensionCount()), + ], + Div::tag() + ->class('yii-debug-package-groups') + ->html(...$items), + ); } /** * Renders the labeled on/off pill strip for the bundled PHP extensions (Xdebug, APCu, Memcache, Memcached). + * + * @param PhpConfig $php Typed PHP runtime section. + * + * @return Section PHP extensions section. */ public static function renderPhpExtensionsSection(PhpConfig $php): Section { @@ -117,21 +118,13 @@ public static function renderPhpExtensionsSection(PhpConfig $php): Section self::renderExtensionPill('Memcached', $php->memcached), ]; - return Section::tag() - ->class('yii-debug-section') - ->html( - H2::tag() - ->class('yii-debug-section-title') - ->html( - Span::tag() - ->class('yii-debug-section-mark') - ->content('::'), - ' PHP extensions', - ), - Div::tag() - ->class('yii-debug-ext-strip') - ->html(...$pills), - ); + return self::renderSection( + '::', + [' PHP extensions'], + Div::tag() + ->class('yii-debug-ext-strip') + ->html(...$pills), + ); } /** @@ -139,6 +132,10 @@ public static function renderPhpExtensionsSection(PhpConfig $php): Section * * The caller resolves the destination URL (typically via `Url::to(['php-info'])`) so the renderer stays free of * routing concerns and easy to test in isolation. + * + * @param string $href Destination URL of the standalone phpinfo viewer. + * + * @return A Call-to-action link element. */ public static function renderPhpInfoCta(string $href): A { @@ -152,8 +149,7 @@ public static function renderPhpInfoCta(string $href): A ->class('yii-debug-cta-prompt') ->addAriaAttribute('hidden', 'true') ->content('→'), - Span::tag() - ->content('View full phpinfo'), + Span::tag()->content('View full phpinfo'), Span::tag() ->class('yii-debug-cta-external') ->addAriaAttribute('hidden', 'true') @@ -163,6 +159,10 @@ public static function renderPhpInfoCta(string $href): A /** * Renders the four-card readout grid (`Yii`, `PHP`, `Environment`, `Application`) at the top of the detail view. + * + * @param ConfigSummary $summary Typed configuration summary. + * + * @return Div Readout grid container. */ public static function renderReadoutGrid(ConfigSummary $summary): Div { @@ -196,6 +196,10 @@ public static function renderReadoutGrid(ConfigSummary $summary): Div /** * Returns a BCP-47 tag annotated with its English display name, or the em-dash placeholder when the locale is * empty. + * + * @param string $locale BCP-47 tag to annotate. + * + * @return string Annotated tag, the bare tag when no display name resolves, or `—` for an empty locale. */ private static function formatLanguage(string $locale): string { @@ -203,17 +207,24 @@ private static function formatLanguage(string $locale): string return '—'; } + $candidates = [ + Locale::getDisplayLanguage($locale, 'en'), + Locale::getDisplayRegion($locale, 'en'), + ]; + $parts = []; - foreach ([Locale::getDisplayLanguage($locale, 'en'), Locale::getDisplayRegion($locale, 'en')] as $part) { + foreach ($candidates as $part) { if (is_string($part) && $part !== '') { $parts[] = $part; } } - $displayName = implode(', ', $parts); + if ($parts === []) { + return $locale; + } - return $parts === [] ? $locale : "{$locale} ({$displayName})"; + return "{$locale} (" . implode(', ', $parts) . ')'; } /** @@ -234,6 +245,11 @@ private static function renderCorners(): array /** * Renders one `
term
value
` row inside the application-details description list. + * + * @param string $term Row label. + * @param string $value Row value. + * + * @return Div Description-list row. */ private static function renderDlRow(string $term, string $value): Div { @@ -247,16 +263,28 @@ private static function renderDlRow(string $term, string $value): Div /** * Renders one extension pill with an on/off state and a label. + * + * @param string $name Extension name shown in the pill. + * @param bool $enabled Whether the extension is loaded. + * + * @return Span Extension pill element. */ private static function renderExtensionPill(string $name, bool $enabled): Span { - return ExtensionPill::render($name, $enabled ? 'on' : 'off', $enabled); + return ExtensionPill::render( + $name, + $enabled ? 'on' : 'off', + $enabled, + ); } /** * Renders one Composer vendor group as a compact dependency ledger. * - * @param array $packages + * @param string $vendor Composer vendor name. + * @param array $packages Package names mapped to their installed version. + * + * @return Article Vendor group element. */ private static function renderPackageGroup(string $vendor, array $packages): Article { @@ -289,6 +317,11 @@ private static function renderPackageGroup(string $vendor, array $packages): Art /** * Renders one package name and version row inside its Composer vendor group. + * + * @param string $name Package name without its vendor prefix. + * @param string $version Installed version. + * + * @return Div Package row. */ private static function renderPackageItem(string $name, string $version): Div { @@ -306,11 +339,16 @@ private static function renderPackageItem(string $name, string $version): Div /** * Builds one readout card with a label, value, and either a plain-text meta line or a chip. + * + * @param string $label Card label. + * @param string $value Card value. + * @param Span|string $meta Chip element or plain-text meta line shown under the value. + * + * @return Article Readout card element. */ private static function renderReadoutCard(string $label, string $value, Span|string $meta): Article { - $metaWrap = Span::tag() - ->class('yii-debug-readout-meta'); + $metaWrap = Span::tag()->class('yii-debug-readout-meta'); $metaWrap = $meta instanceof Stringable ? $metaWrap->html($meta) @@ -331,4 +369,30 @@ private static function renderReadoutCard(string $label, string $value, Span|str ->class('yii-debug-readout-card') ->html(...$children); } + + /** + * Builds the shared section shell: a marked title followed by the section body. + * + * @param string $mark Glyph rendered inside the section mark. + * @param list $title Title nodes rendered after the mark. + * @param string|Stringable ...$body Section body nodes. + * + * @return Section Section element. + */ + private static function renderSection(string $mark, array $title, string|Stringable ...$body): Section + { + return Section::tag() + ->class('yii-debug-section') + ->html( + H2::tag() + ->class('yii-debug-section-title') + ->html( + Span::tag() + ->class('yii-debug-section-mark') + ->content($mark), + ...$title, + ), + ...$body, + ); + } } diff --git a/src/Panel/Config/ConfigDataNormalizer.php b/src/Panel/Config/ConfigDataNormalizer.php index 701ed24..8439868 100644 --- a/src/Panel/Config/ConfigDataNormalizer.php +++ b/src/Panel/Config/ConfigDataNormalizer.php @@ -37,6 +37,8 @@ public function normalize(array $data, array $extensions): ConfigSummary * Builds the application-section view-model from the `application` slice of the raw payload. * * @param array $raw Raw `application` slice. + * + * @return ApplicationConfig Typed application section. */ private function buildApplication(array $raw): ApplicationConfig { @@ -56,6 +58,8 @@ private function buildApplication(array $raw): ApplicationConfig * Builds the PHP-runtime view-model from the `php` slice of the raw payload. * * @param array $raw Raw `php` slice. + * + * @return PhpConfig Typed PHP runtime section. */ private function buildPhp(array $raw): PhpConfig { @@ -72,6 +76,9 @@ private function buildPhp(array $raw): PhpConfig * Coerces the value at `$raw[$key]` to a boolean, falling back to `false` when missing. * * @param array $raw Slice to read from. + * @param string $key Entry to read. + * + * @return bool Coerced flag. */ private function extractBool(array $raw, string $key): bool { @@ -82,6 +89,9 @@ private function extractBool(array $raw, string $key): bool * Returns the value at `$raw[$key]` when it is a string, falling back to `''` otherwise. * * @param array $raw Slice to read from. + * @param string $key Entry to read. + * + * @return string String value, or `''` when missing or not a string. */ private function extractString(array $raw, string $key): string { diff --git a/src/Panel/Config/ConfigSnapshot.php b/src/Panel/Config/ConfigSnapshot.php index 04fe163..fd81fc2 100644 --- a/src/Panel/Config/ConfigSnapshot.php +++ b/src/Panel/Config/ConfigSnapshot.php @@ -14,6 +14,8 @@ use ArrayPayloadSnapshot; /** + * Returns the captured configuration payload. + * * @return array Captured application, PHP, and extension configuration. */ public function data(): array diff --git a/src/Panel/Config/ConfigSummary.php b/src/Panel/Config/ConfigSummary.php index df407f4..bb13cf3 100644 --- a/src/Panel/Config/ConfigSummary.php +++ b/src/Panel/Config/ConfigSummary.php @@ -30,6 +30,8 @@ public function __construct( /** * Returns the number of installed extensions in {@see $extensions}. + * + * @return int Installed extension count. */ public function extensionCount(): int { @@ -38,6 +40,8 @@ public function extensionCount(): int /** * Returns whether at least one installed extension is recorded. + * + * @return bool `true` when the roster holds at least one extension; `false` otherwise. */ public function hasExtensions(): bool { diff --git a/src/Panel/Db/DbExplainSupport.php b/src/Panel/Db/DbExplainSupport.php new file mode 100644 index 0000000..fd0dd79 --- /dev/null +++ b/src/Panel/Db/DbExplainSupport.php @@ -0,0 +1,50 @@ +duration); + return sprintf('%.1f ms', $row->getDuration()); } /** @@ -115,7 +115,7 @@ public static function renderQueryCell( ): string { $sql = Div::tag() ->class('yii-debug-db-sql') - ->html(SqlHighlighter::highlight($row->query)); + ->html(SqlHighlighter::highlight($row->getQuery())); $children = [$sql]; @@ -123,7 +123,7 @@ public static function renderQueryCell( $groupId = $nPlusOneFinding->id(); $sql = $sql->addDataAttribute('yii-debug-n1-group', $groupId); - if ($row->seq === $nPlusOneFinding->firstSequence) { + if ($row->getSequence() === $nPlusOneFinding->firstSequence) { $sql = $sql->id($groupId); } @@ -138,10 +138,10 @@ public static function renderQueryCell( ]; } - if ($row->trace !== []) { + if ($row->getTrace() !== []) { $items = array_map( static fn(array $frame): Li => Li::tag()->html(($traceLine)($frame)), - $row->trace, + $row->getTrace(), ); $children[] = Details::tag() @@ -156,12 +156,14 @@ public static function renderQueryCell( ->content('›'), Span::tag()->content(DbMessage::TRACE), ), - Ul::tag()->class('yii-debug-trace')->html(...$items), + Ul::tag() + ->class('yii-debug-trace') + ->html(...$items), ); } if ($hasExplain && $row->isExplainable()) { - $explainTargetId = "yii-debug-db-explain-{$row->seq}"; + $explainTargetId = "yii-debug-db-explain-{$row->getSequence()}"; $children[] = Div::tag() ->class('yii-debug-db-explain') @@ -171,7 +173,7 @@ public static function renderQueryCell( ->addAriaAttribute('expanded', 'false') ->addAriaAttribute('label', 'Toggle EXPLAIN output') ->class('yii-debug-db-explain-toggle') - ->href($explainUrlBuilder($row->seq)) + ->href($explainUrlBuilder($row->getSequence())) ->html( Span::tag() ->addAriaAttribute('hidden', 'true') @@ -196,11 +198,11 @@ public static function renderQueryCell( */ public static function renderRowsCell(QueryRow $row): string { - if ($row->rows === null) { + if ($row->getRows() === null) { return '–'; } - return "{$row->rows} " . ($row->rows === 1 ? 'row' : 'rows'); + return "{$row->getRows()} " . ($row->getRows() === 1 ? 'row' : 'rows'); } /** @@ -208,7 +210,7 @@ public static function renderRowsCell(QueryRow $row): string */ public static function renderTimeCell(QueryRow $row): string { - return Format::timeOfDay((int) $row->timestamp); + return Format::timeOfDay((int) $row->getTimestamp()); } /** @@ -216,11 +218,11 @@ public static function renderTimeCell(QueryRow $row): string */ public static function renderTypeCell(QueryRow $row): string { - $variant = Vocabulary::sqlVerb($row->type); + $variant = Vocabulary::sqlVerb($row->getType()); return Span::tag() ->class("yii-debug-db-type yii-debug-verb-{$variant}") - ->content($row->type) + ->content($row->getType()) ->render(); } } diff --git a/src/Panel/Db/DbSnapshot.php b/src/Panel/Db/DbSnapshot.php index d930e65..684e016 100644 --- a/src/Panel/Db/DbSnapshot.php +++ b/src/Panel/Db/DbSnapshot.php @@ -26,11 +26,13 @@ public function __construct(private array $entries) {} */ public static function capture(array $rows): self { - $occurrences = array_count_values(array_map(static fn(QueryRow $row): string => $row->query, $rows)); + $occurrences = array_count_values(array_map(static fn(QueryRow $row): string => $row->getQuery(), $rows)); return new self( array_map( - static fn(QueryRow $row): QueryRow => $row->withDuplicate($occurrences[$row->query] ?? $row->duplicate), + static fn(QueryRow $row): QueryRow => $row->withDuplicate( + $occurrences[$row->getQuery()] ?? $row->getDuplicate(), + ), $rows, ), ); diff --git a/src/Panel/Db/DbSummary.php b/src/Panel/Db/DbSummary.php index 4820dfa..bb65f4d 100644 --- a/src/Panel/Db/DbSummary.php +++ b/src/Panel/Db/DbSummary.php @@ -37,7 +37,7 @@ public array $types; /** - * @param list $rows + * @param list $rows Captured query rows the metrics are computed from. */ public function __construct(array $rows) { @@ -47,17 +47,17 @@ public function __construct(array $rows) $types = []; foreach ($rows as $row) { - if ($row->duplicate > 1) { + if ($row->getDuplicate() > 1) { $duplicates++; } - $duration += $row->duration; + $duration += $row->getDuration(); - if ($row->traceHash !== '') { - $callers[$row->traceHash] = ($callers[$row->traceHash] ?? 0) + 1; + if ($row->getTraceHash() !== '') { + $callers[$row->getTraceHash()] = ($callers[$row->getTraceHash()] ?? 0) + 1; } - $types[$row->type] = $row->type; + $types[$row->getType()] = $row->getType(); } $this->count = count($rows); @@ -69,6 +69,10 @@ public function __construct(array $rows) /** * Counts the call sites that issued at least `$threshold` statements, or `0` when the threshold is `null`. + * + * @param int|null $threshold Statements per call site that flag it, or `null` to disable the check. + * + * @return int Number of call sites at or above the threshold. */ public function excessiveCallerCount(int|null $threshold): int { @@ -92,6 +96,8 @@ public function excessiveCallerCount(int|null $threshold): int * * @param int|null $criticalQueryThreshold Query count above which the request is critical, or `null` to disable. * @param int|null $excessiveCallerThreshold Statements per call site that flag it, or `null` to disable. + * + * @return bool `true` when either threshold is exceeded; `false` otherwise. */ public function hasWarning(int|null $criticalQueryThreshold, int|null $excessiveCallerThreshold): bool { @@ -101,6 +107,10 @@ public function hasWarning(int|null $criticalQueryThreshold, int|null $excessive /** * Returns whether the query count exceeds `$threshold`, or `false` when the threshold is `null`. + * + * @param int|null $threshold Query count above which the request is critical, or `null` to disable the check. + * + * @return bool `true` when the captured query count exceeds the threshold; `false` otherwise. */ public function isCritical(int|null $threshold): bool { diff --git a/src/Panel/Db/DbSummaryRenderer.php b/src/Panel/Db/DbSummaryRenderer.php index 2cecb4b..8a91f68 100644 --- a/src/Panel/Db/DbSummaryRenderer.php +++ b/src/Panel/Db/DbSummaryRenderer.php @@ -15,6 +15,14 @@ */ final class DbSummaryRenderer { + /** + * Renders the grid summary header with the request-wide totals and the optional page-size selector. + * + * @param DbSummary $summary Request-wide Database metrics. + * @param string|null $pageSize Rendered page-size selector appended after the totals, or `null` to omit it. + * + * @return string Summary header markup. + */ public static function render(DbSummary $summary, string|null $pageSize = null): string { $separator = Span::tag() @@ -22,12 +30,16 @@ public static function render(DbSummary $summary, string|null $pageSize = null): ->content('·'); $items = [ - Span::tag()->html(Strong::tag()->content((string) $summary->count), DbMessage::QUERY_COUNT_SUFFIX->value), + Span::tag() + ->html( + Strong::tag()->content((string) $summary->count), + DbMessage::QUERY_COUNT_SUFFIX, + ), $separator, Span::tag() ->html( Strong::tag()->content(number_format($summary->duration, 3)), - DbMessage::TOTAL_SUFFIX->value, + DbMessage::TOTAL_SUFFIX, ), ]; @@ -38,7 +50,7 @@ public static function render(DbSummary $summary, string|null $pageSize = null): ->class('yii-debug-grid-summary-stat-warn') ->html( Strong::tag()->content((string) $summary->duplicates), - DbMessage::DUPLICATE_SUFFIX->value, + DbMessage::DUPLICATE_SUFFIX, ); } @@ -61,6 +73,8 @@ public static function render(DbSummary $summary, string|null $pageSize = null): * @param DbSummary $summary Request-wide Database metrics. * @param int|null $criticalQueryThreshold Query count above which the request is critical, or `null` to disable. * @param int|null $excessiveCallerThreshold Statements per call site that flag it, or `null` to disable. + * + * @return string Warning sentences when a threshold is exceeded, otherwise the executed-query count. */ public static function toolbarTitle( DbSummary $summary, diff --git a/src/Panel/Db/NPlusOneDetector.php b/src/Panel/Db/NPlusOneDetector.php index a0f9f53..33962dc 100644 --- a/src/Panel/Db/NPlusOneDetector.php +++ b/src/Panel/Db/NPlusOneDetector.php @@ -17,9 +17,33 @@ final class NPlusOneDetector { /** - * @param list $rows + * Indexes findings by every query sequence they cover, so a grid row can look up its own group. * - * @return list + * @param list $findings Findings to index. + * + * @return array Findings keyed by covered query sequence. + */ + public static function bySequence(array $findings): array + { + $bySequence = []; + + foreach ($findings as $finding) { + foreach ($finding->sequences as $sequence) { + $bySequence[$sequence] = $finding; + } + } + + return $bySequence; + } + /** + * Groups repeated `SELECT` statements by captured call site and keeps the groups above the threshold. + * + * @param list $rows Captured query rows in capture order. + * @param int $threshold Minimum occurrences a call site needs to be reported; must be at least `2`. + * + * @return list Findings ordered by count, then total duration, then first sequence. + * + * @throws InvalidArgumentException When the threshold is lower than `2`. */ public static function detect(array $rows, int $threshold = 3): array { @@ -42,30 +66,27 @@ public static function detect(array $rows, int $threshold = 3): array foreach ($rows as $row) { if ( - $row->traceHash === '' - || strtoupper($row->type) !== 'SELECT' + $row->getTraceHash() === '' + || strtoupper($row->getType()) !== 'SELECT' ) { continue; } - if (!isset($groups[$row->traceHash])) { - $groups[$row->traceHash] = [ - 'count' => 0, - 'duration' => 0.0, - 'first' => $row->seq, - 'query' => $row->query, - 'sequences' => [], - ]; - } + $group = $groups[$row->getTraceHash()] ?? [ + 'count' => 0, + 'duration' => 0.0, + 'first' => $row->getSequence(), + 'query' => $row->getQuery(), + 'sequences' => [], + ]; - $group = &$groups[$row->traceHash]; $group['count']++; - $group['duration'] += $row->duration; - $group['first'] = min($group['first'], $row->seq); - $group['sequences'][] = $row->seq; - } + $group['duration'] += $row->getDuration(); + $group['first'] = min($group['first'], $row->getSequence()); + $group['sequences'][] = $row->getSequence(); - unset($group); + $groups[$row->getTraceHash()] = $group; + } $findings = []; diff --git a/src/Panel/Db/QueryRow.php b/src/Panel/Db/QueryRow.php index 7882658..022b6be 100644 --- a/src/Panel/Db/QueryRow.php +++ b/src/Panel/Db/QueryRow.php @@ -4,6 +4,7 @@ namespace PHPForge\Debug\Panel\Db; +use PHPForge\Debug\Helper\Format; use PHPForge\Debug\Storage\{PanelRow, Payload}; use function array_values; @@ -20,65 +21,109 @@ /** * Typed view-model for a single database query row consumed by the queries grid. */ -final readonly class QueryRow implements PanelRow +final class QueryRow implements PanelRow { - public function __construct( - /** - * Uppercase SQL command verb (`SELECT`, `INSERT`, `UPDATE`, `DELETE`, ...). - */ - public string $type, + /** + * Single table-touching verbs that produce a useful EXPLAIN plan. + */ + private const array EXPLAINABLE_TYPES = [ + 'SELECT', + 'INSERT', + 'UPDATE', + 'DELETE', + 'REPLACE', + 'WITH', + ]; + + /** + * Number of times the exact same query was emitted in this request (`>= 1`). + */ + private int $duplicate = 1; + /** + * Number of rows returned/affected, or `null` when the driver did not report it. + */ + private int|null $rows = null; + /** + * Zero-based sequence index assigned by the panel. + */ + private int $seq = 0; + /** + * @var list> Backtrace frames captured for the statement. + */ + private array $trace = []; + /** + * Stable hash of the backtrace, used to count caller duplicates. + */ + private string $traceHash = ''; + /** + * SQL command verb extracted from the statement or explicitly supplied by the adapter. + */ + private string $type; + + private function __construct( /** * Full SQL statement as emitted by the profile log. */ - public string $query, + private string $query, /** * Statement execution time in milliseconds. */ - public float $duration, - /** - * @var list> Backtrace frames captured for the statement. - */ - public array $trace, - /** - * Stable hash of the backtrace, used to count caller duplicates. - */ - public string $traceHash, + private float $duration, /** * Capture timestamp in milliseconds since the Unix epoch. */ - public float $timestamp, - /** - * Zero-based sequence index assigned by the panel. - */ - public int $seq, - /** - * Number of times the exact same query was emitted in this request (`>= 1`). - */ - public int $duplicate, - /** - * Number of rows returned/affected, or `null` when the driver did not report it. - */ - public int|null $rows, - ) {} + private float $timestamp, + ) { + $this->type = self::extractType($query); + } /** * Creates a captured statement with milliseconds as the common time unit. + * + * @param string $query Full SQL statement. + * @param float $duration Execution time in milliseconds. + * @param float $timestamp Capture timestamp in milliseconds since the Unix epoch. + * + * @return self Row carrying the extracted command verb, without trace, sequence, or row count. */ public static function create(string $query, float $duration, float $timestamp): self { - preg_match('/^\\s*([a-zA-Z]+)/', $query, $matches); + return new self($query, $duration, $timestamp); + } - return new self( - strtoupper($matches[1] ?? ''), - $query, - $duration, - [], - '', - $timestamp, - 0, - 1, - null, - ); + /** + * Returns the uppercase leading command verb of a statement, or `''` when it starts with no letter. + * + * @param string $sql SQL statement to inspect. + * + * @return string Uppercase command verb, or `''`. + */ + public static function extractType(string $sql): string + { + preg_match('/^\\s*([a-zA-Z]+)/', $sql, $matches); + + return strtoupper($matches[1] ?? ''); + } + + /** + * Returns the row whose sequence matches the requested one exactly, or `null` when none does. + * + * The sequence is compared as a string, so a padded request such as `'02'` never matches sequence `2`. + * + * @param list $rows Captured rows to search. + * @param string $seq Requested sequence number. + * + * @return self|null Matching row, or `null` when the sequence was not captured. + */ + public static function findBySequence(array $rows, string $seq): self|null + { + foreach ($rows as $row) { + if ((string) $row->seq === $seq) { + return $row; + } + } + + return null; } public static function fromArray(mixed $data, string $path): self @@ -98,17 +143,30 @@ public static function fromArray(mixed $data, string $path): self ], ); - return new self( - type: $payload->string('type'), - query: $payload->string('query'), - duration: $payload->number('duration'), - trace: $payload->rows('trace'), - traceHash: $payload->string('traceHash'), - timestamp: $payload->number('timestamp'), - seq: $payload->int('seq'), - duplicate: max(1, $payload->int('duplicate')), - rows: $payload->nullableInt('rows'), + $type = $payload->string('type'); + $query = $payload->string('query'); + $duration = $payload->number('duration'); + $trace = $payload->rows('trace'); + $traceHash = $payload->string('traceHash'); + $timestamp = $payload->number('timestamp'); + $seq = $payload->int('seq'); + $duplicate = max(1, $payload->int('duplicate')); + $rows = $payload->nullableInt('rows'); + + $row = new self( + $query, + $duration, + $timestamp, ); + + $row->type = $type; + $row->trace = $trace; + $row->traceHash = $traceHash; + $row->seq = $seq; + $row->duplicate = $duplicate; + $row->rows = $rows; + + return $row; } /** @@ -116,7 +174,6 @@ public static function fromArray(mixed $data, string $path): self * * @param string $type Uppercase SQL command verb extracted from the statement. * @param int $seq Zero-based sequence index. - * @param int $duplicate Number of times the same statement was emitted in this request. * @param int|null $rows Rows reported by the driver, or `null` when it reported none. * @param array{ * info: string, @@ -129,20 +186,100 @@ public static function fromArray(mixed $data, string $path): self * memoryDiff: int, * traceHash: string * } $timing Resolved timing. + * + * The duplicate count starts at `1`; {@see DbSnapshot::capture()} resolves the real occurrences across the request. + * + * @return self Row with the timing scaled to milliseconds. */ - public static function fromTiming(array $timing, string $type, int $seq, int $duplicate, int|null $rows): self - { - return new self( - type: $type, - query: $timing['info'], - duration: $timing['duration'] * 1000, - trace: array_values($timing['trace']), - traceHash: $timing['traceHash'], - timestamp: $timing['timestamp'] * 1000, - seq: $seq, - duplicate: max(1, $duplicate), - rows: $rows, + public static function fromTiming(array $timing, string $type, int $seq, int|null $rows): self + { + $row = new self( + $timing['info'], + $timing['duration'] * Format::MILLISECONDS_PER_SECOND, + $timing['timestamp'] * Format::MILLISECONDS_PER_SECOND, ); + + $row->type = $type; + $row->trace = array_values($timing['trace']); + $row->traceHash = $timing['traceHash']; + $row->seq = $seq; + $row->rows = $rows; + + return $row; + } + + /** + * Returns the number of occurrences of this exact query. + */ + public function getDuplicate(): int + { + return $this->duplicate; + } + + /** + * Returns the execution time in milliseconds. + */ + public function getDuration(): float + { + return $this->duration; + } + + /** + * Returns the captured SQL statement. + */ + public function getQuery(): string + { + return $this->query; + } + + /** + * Returns the driver-reported row count, or `null` when unavailable. + */ + public function getRows(): int|null + { + return $this->rows; + } + + /** + * Returns the zero-based capture sequence. + */ + public function getSequence(): int + { + return $this->seq; + } + + /** + * Returns the capture timestamp in milliseconds since the Unix epoch. + */ + public function getTimestamp(): float + { + return $this->timestamp; + } + + /** + * Returns the captured source frames. + * + * @return list> Captured backtrace frames. + */ + public function getTrace(): array + { + return $this->trace; + } + + /** + * Returns the captured or derived caller hash. + */ + public function getTraceHash(): string + { + return $this->traceHash; + } + + /** + * Returns the extracted or explicitly captured command verb. + */ + public function getType(): string + { + return $this->type; } /** @@ -150,15 +287,19 @@ public static function fromTiming(array $timing, string $type, int $seq, int $du * * Only single table-touching DML verbs (`SELECT`, `INSERT`, `UPDATE`, `DELETE`, `REPLACE`, `WITH`) qualify; * multi-statement text and other verbs either error or return noise under EXPLAIN. + * + * @return bool `true` when the statement can be explained; `false` otherwise. */ public function isExplainable(): bool { - return in_array(strtoupper($this->type), ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'REPLACE', 'WITH'], true) + return in_array(strtoupper($this->type), self::EXPLAINABLE_TYPES, true) && !str_contains($this->query, ';'); } /** - * @return array + * Returns the persisted row contract. + * + * @return array Row fields in their persisted order. */ public function jsonSerialize(): array { @@ -179,75 +320,93 @@ public function jsonSerialize(): array * Returns a copy with the given duplicate count. * * @param int $duplicate Number of times the exact same statement was emitted in this request (`>= 1`). + * + * @return self Copy carrying the duplicate count. */ public function withDuplicate(int $duplicate): self { - return new self( - type: $this->type, - query: $this->query, - duration: $this->duration, - trace: $this->trace, - traceHash: $this->traceHash, - timestamp: $this->timestamp, - seq: $this->seq, - duplicate: $duplicate, - rows: $this->rows, - ); + $clone = clone $this; + $clone->duplicate = $duplicate; + + return $clone; } /** * Returns a copy with the driver-reported row count, or `null` when the driver did not report one. + * + * @param int|null $rows Rows returned or affected, or `null` when the driver reported none. + * + * @return self Copy carrying the row count. */ public function withRows(int|null $rows): self { - return new self( - type: $this->type, - query: $this->query, - duration: $this->duration, - trace: $this->trace, - traceHash: $this->traceHash, - timestamp: $this->timestamp, - seq: $this->seq, - duplicate: $this->duplicate, - rows: $rows, - ); + $clone = clone $this; + $clone->rows = $rows; + + return $clone; } /** * Returns a copy with the given sequence index. + * + * @param int $sequence Zero-based sequence index assigned by the panel. + * + * @return self Copy carrying the sequence index. */ public function withSequence(int $sequence): self { - return new self( - type: $this->type, - query: $this->query, - duration: $this->duration, - trace: $this->trace, - traceHash: $this->traceHash, - timestamp: $this->timestamp, - seq: $sequence, - duplicate: $this->duplicate, - rows: $this->rows, - ); + $clone = clone $this; + $clone->seq = $sequence; + + return $clone; } /** * Returns a copy with the given source frames and the caller hash derived from them. * * @param list> $trace Argument-free source frames. + * + * @return self Copy carrying the frames and their derived caller hash. */ public function withTrace(array $trace): self { - return new self( - type: $this->type, - query: $this->query, - duration: $this->duration, - trace: $trace, - traceHash: $trace === [] ? '' : hash('sha256', json_encode($trace, JSON_THROW_ON_ERROR)), - timestamp: $this->timestamp, - seq: $this->seq, - duplicate: $this->duplicate, - rows: $this->rows, - ); + $clone = clone $this; + $clone->trace = $trace; + $clone->traceHash = $trace === [] ? '' : hash('sha256', json_encode($trace, JSON_THROW_ON_ERROR)); + + return $clone; + } + + /** + * Returns a copy with an explicitly captured caller hash without changing the source frames. + * + * Call after {@see self::withTrace()} to preserve a logger-provided hash instead of the derived hash. + * A subsequent `withTrace()` call derives the hash again. + * + * @param string $traceHash Captured caller hash, or `''` when no caller was captured. + * + * @return self Copy carrying the hash unchanged. + */ + public function withTraceHash(string $traceHash): self + { + $clone = clone $this; + $clone->traceHash = $traceHash; + + return $clone; + } + + /** + * Returns a copy with an explicitly captured SQL command verb without changing the statement. + * + * @param string $type Captured command verb, preserved without normalization. + * + * @return self Copy carrying the verb unchanged. + */ + public function withType(string $type): self + { + $clone = clone $this; + $clone->type = $type; + + return $clone; } } diff --git a/src/Panel/Db/SqlHighlighter.php b/src/Panel/Db/SqlHighlighter.php index a9c305d..14a2bde 100644 --- a/src/Panel/Db/SqlHighlighter.php +++ b/src/Panel/Db/SqlHighlighter.php @@ -6,6 +6,7 @@ use UIAwesome\Html\Helper\Encode; +use function array_reverse; use function preg_match_all; use function strlen; use function substr; @@ -44,11 +45,16 @@ final class SqlHighlighter * Returns the SQL statement as fully escaped HTML with `yii-debug-sql-*` token spans. * * @param string $sql Raw SQL statement to highlight. + * + * @return string Escaped HTML with `yii-debug-sql-*` token spans. */ public static function highlight(string $sql): string { preg_match_all(self::PATTERN, $sql, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + // Named groups fill up to the last match, so the last present group is the one that matched. + $tokenClasses = array_reverse(self::TOKEN_CLASSES, true); + $html = ''; $offset = 0; @@ -60,7 +66,7 @@ public static function highlight(string $sql): string $token = $text; - foreach (array_reverse(self::TOKEN_CLASSES, true) as $group => $class) { + foreach ($tokenClasses as $group => $class) { if (isset($match[$group])) { $token = $class === '' ? $text : "{$text}"; diff --git a/tests/Capture/CapturePolicyTest.php b/tests/Capture/CapturePolicyTest.php index 3b88572..ba76d75 100644 --- a/tests/Capture/CapturePolicyTest.php +++ b/tests/Capture/CapturePolicyTest.php @@ -6,6 +6,7 @@ use InvalidArgumentException; use PHPForge\Debug\Capture\CapturePolicy; +use PHPForge\Debug\Exception\Message; use PHPForge\Debug\Helper\SensitiveDataRedactor; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; @@ -25,28 +26,6 @@ public function testConstructorAcceptsTheSmallestPositiveBodyLimit(): void ); } - public function testConstructorRejectsANonPositiveBodyLimit(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage( - 'greater than zero', - ); - - $policy = new CapturePolicy(maxBodyBytes: 0); - - self::fail( - 'Expected invalid policy construction to fail, got ' . $policy::class . '.', - ); - } - - public function testConstructorRejectsInvalidPatternConfigurationImmediately(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('not a valid PCRE pattern'); - - new CapturePolicy(sensitiveKeyPatterns: ['invalid']); - } - public function testMaxBodyBytesReturnsTheConfiguredPersistentLimit(): void { self::assertSame( @@ -206,7 +185,10 @@ public function testRedactBodyTruncatesOpaqueBodiesAtTheByteLimit(): void public function testRedactTextAppliesPatternRulesWhenPatternsAreTheOnlyConfiguredSource(): void { - $policy = new CapturePolicy(sensitiveKeys: [], sensitiveKeyPatterns: ['~^secret-word$~i']); + $policy = new CapturePolicy( + sensitiveKeys: [], + sensitiveKeyPatterns: ['~^secret-word$~i'], + ); self::assertSame( 'secret-word=[redacted]; plain-word=visible', @@ -217,7 +199,11 @@ public function testRedactTextAppliesPatternRulesWhenPatternsAreTheOnlyConfigure public function testRedactTextAppliesPrefixRulesWhenPrefixesAreTheOnlyConfiguredSource(): void { - $policy = new CapturePolicy(sensitiveKeys: [], sensitiveKeyPrefixes: ['private_'], sensitiveKeyPatterns: []); + $policy = new CapturePolicy( + sensitiveKeys: [], + sensitiveKeyPrefixes: ['private_'], + sensitiveKeyPatterns: [], + ); self::assertSame( 'private_key=[redacted]; public_key=visible', @@ -327,4 +313,88 @@ public function testRedactUrlSanitizesQueryValuesAndPreservesFragments(): void 'A fragment must not become part of a safe query value.', ); } + + public function testThrowInvalidArgumentExceptionForAnEmptySensitiveKeyPrefix(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + Message::SENSITIVE_KEY_PREFIX_EMPTY->getMessage(), + ); + + new CapturePolicy(sensitiveKeyPrefixes: ['']); + } + + public function testThrowInvalidArgumentExceptionForAnInvalidSensitiveKeyPattern(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + Message::SENSITIVE_KEY_PATTERN_INVALID->getMessage('invalid'), + ); + + new CapturePolicy(sensitiveKeyPatterns: ['invalid']); + } + + public function testThrowInvalidArgumentExceptionWhenMaxBodyBytesIsNotPositive(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + Message::BODY_SIZE_INVALID->getMessage(), + ); + + new CapturePolicy(maxBodyBytes: 0); + } + + public function testWithAdditionalSensitiveKeysExtendsTheDefaultListAndKeepsPatternRules(): void + { + $policy = new CapturePolicy(); + + $extended = $policy->withAdditionalSensitiveKeys(['queueSecret', 'jobPayload']); + + self::assertTrue( + $extended->isSensitiveKey('queueSecret'), + 'First additional key must be denied.', + ); + self::assertTrue( + $extended->isSensitiveKey('jobPayload'), + 'Every additional key must be denied, not only the first.', + ); + self::assertTrue( + $extended->isSensitiveKey('cookie'), + 'Configured exact keys must survive.', + ); + self::assertTrue( + $extended->isSensitiveKey('MY_APP_PASSWORD'), + 'Segment-aware defaults must survive the wider key list.', + ); + self::assertFalse( + $policy->isSensitiveKey('queueSecret'), + 'Source policy must stay unchanged.', + ); + } + + public function testWithAdditionalSensitiveKeysPreservesLimitsPrefixesAndDisabledPatterns(): void + { + $policy = new CapturePolicy( + sensitiveKeys: [], + maxBodyBytes: 3, + sensitiveKeyPrefixes: ['private_'], + sensitiveKeyPatterns: [], + ); + + $extended = $policy->withAdditionalSensitiveKeys(['queueSecret']); + + self::assertSame( + 3, + $extended->maxBodyBytes(), + 'Byte limit must carry over.', + ); + self::assertTrue( + $extended->isSensitiveKey('private_key'), + 'Prefix rules must carry over.', + ); + self::assertFalse( + $extended->isSensitiveKey('MY_APP_PASSWORD'), + 'Disabled patterns must stay disabled.', + ); + } } diff --git a/tests/Collector/CollectorCoordinatorTest.php b/tests/Collector/CollectorCoordinatorTest.php index dc4496e..9c9e178 100644 --- a/tests/Collector/CollectorCoordinatorTest.php +++ b/tests/Collector/CollectorCoordinatorTest.php @@ -6,6 +6,7 @@ use InvalidArgumentException; use PHPForge\Debug\Collector\CollectorCoordinator; +use PHPForge\Debug\Exception\Message; use PHPForge\Debug\Storage\{PanelFailure, RequestSummary}; use PHPForge\Debug\Tests\Support\{ArrayPayloadSnapshotFixture, CollectorFixture}; use PHPUnit\Framework\Attributes\Group; @@ -94,12 +95,8 @@ public function testCapturePersistsSuccessfulCollectorsAndOmitsNullSnapshots(): public function testCollectorReturnsRegisteredInstanceOrNull(): void { - $collector = new CollectorFixture( - 'app.example', - ); - $coordinator = new CollectorCoordinator( - [$collector], - ); + $collector = new CollectorFixture('app.example'); + $coordinator = new CollectorCoordinator([$collector]); self::assertSame( $collector, @@ -114,13 +111,7 @@ public function testCollectorReturnsRegisteredInstanceOrNull(): void public function testHasCollectorUsesStableId(): void { - $coordinator = new CollectorCoordinator( - [ - new CollectorFixture( - 'app.example', - ), - ], - ); + $coordinator = new CollectorCoordinator([new CollectorFixture('app.example')]); self::assertTrue( $coordinator->hasCollector('app.example'), @@ -134,12 +125,8 @@ public function testHasCollectorUsesStableId(): void public function testLifecycleCallsCollectorsOncePerCycle(): void { - $collector = new CollectorFixture( - 'app.example', - ); - $coordinator = new CollectorCoordinator( - [$collector], - ); + $collector = new CollectorFixture('app.example'); + $coordinator = new CollectorCoordinator([$collector]); $coordinator->startup(); $coordinator->startup(); @@ -281,12 +268,8 @@ public function testRunPreservesPrimaryThrowableWithoutACleanupFailureHandler(): public function testRunReturnsOperationResultAndCompletesLifecycle(): void { - $collector = new CollectorFixture( - 'app.example', - ); - $coordinator = new CollectorCoordinator( - [$collector], - ); + $collector = new CollectorFixture('app.example'); + $coordinator = new CollectorCoordinator([$collector]); self::assertSame( 'result', @@ -317,9 +300,7 @@ public function testShutdownContinuesAfterCollectorFailure(): void failShutdown: true, shutdownFailureMessage: 'Second shutdown failed.', ); - $successful = new CollectorFixture( - 'successful', - ); + $successful = new CollectorFixture('successful'); $coordinator = new CollectorCoordinator( [ $firstBroken, @@ -353,9 +334,7 @@ public function testShutdownContinuesAfterCollectorFailure(): void public function testShutdownRetriesOnlyCollectorsWhoseCleanupFailed(): void { - $successful = new CollectorFixture( - 'successful', - ); + $successful = new CollectorFixture('successful'); $flaky = new CollectorFixture( 'flaky', shutdownFailuresRemaining: 1, @@ -392,16 +371,12 @@ public function testShutdownRetriesOnlyCollectorsWhoseCleanupFailed(): void public function testStartupAllowsRetryAfterRollback(): void { - $first = new CollectorFixture( - 'first', - ); + $first = new CollectorFixture('first'); $flaky = new CollectorFixture( 'flaky', startupFailuresRemaining: 1, ); - $later = new CollectorFixture( - 'later', - ); + $later = new CollectorFixture('later'); $coordinator = new CollectorCoordinator( [ $first, @@ -527,16 +502,12 @@ public function testStartupRetriesIncompleteRollbackCleanupBeforeStartingANewCyc public function testStartupRollsBackAffectedCollectorsWhenSecondCollectorFails(): void { - $first = new CollectorFixture( - 'first', - ); + $first = new CollectorFixture('first'); $failed = new CollectorFixture( 'failed', startupFailuresRemaining: 1, ); - $later = new CollectorFixture( - 'later', - ); + $later = new CollectorFixture('later'); $coordinator = new CollectorCoordinator( [ $first, @@ -611,9 +582,7 @@ public function testStartupRollsBackAffectedCollectorsWhenSecondCollectorFails() public function testThrowInvalidArgumentExceptionForDuplicateId(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage( - 'Duplicate debug collector ID: app.example.', - ); + $this->expectExceptionMessage(Message::COLLECTOR_ID_DUPLICATE->getMessage('app.example')); new CollectorCoordinator( [ @@ -630,9 +599,7 @@ public function testThrowInvalidArgumentExceptionForDuplicateId(): void public function testThrowInvalidArgumentExceptionForEmptyId(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage( - 'Debug collector ID must not be empty.', - ); + $this->expectExceptionMessage(Message::COLLECTOR_ID_EMPTY->getMessage()); new CollectorCoordinator([new CollectorFixture(' ')]); } diff --git a/tests/Comparison/PanelComparisonTest.php b/tests/Comparison/PanelComparisonTest.php index d3e8e63..752f5eb 100644 --- a/tests/Comparison/PanelComparisonTest.php +++ b/tests/Comparison/PanelComparisonTest.php @@ -13,7 +13,10 @@ use function array_map; /** - * Locks the original adapter output before and after sharing panel comparison. + * Unit tests for {@see PanelComparison} validating panel contracts, difference detection, and immutability of baseline + * and target snapshots. + * + * {@see PanelComparisonProvider} for test case data providers. */ #[Group('history')] final class PanelComparisonTest extends TestCase @@ -33,7 +36,11 @@ public function testBetweenPreservesPanelContracts( $beforeBaseline = $baseline->jsonSerialize(); $beforeTarget = $target->jsonSerialize(); - $panels = PanelComparison::between($baseline, $target, $labels); + $panels = PanelComparison::between( + $baseline, + $target, + $labels, + ); $actual = []; $actualCounts = []; diff --git a/tests/Comparison/PayloadDifferenceTest.php b/tests/Comparison/PayloadDifferenceTest.php index 6460b0b..b0170ee 100644 --- a/tests/Comparison/PayloadDifferenceTest.php +++ b/tests/Comparison/PayloadDifferenceTest.php @@ -12,7 +12,7 @@ use function get_object_vars; /** - * Unit tests for typed structural comparisons without retaining or modifying diagnostic values. + * Unit tests for {@see PayloadDifference} validating structural comparisons and immutability of input arrays. * * {@see PayloadDifferenceProvider} for test case data providers. */ @@ -30,7 +30,10 @@ public function testBetweenPreservesTypedLeafSemantics(array|null $baseline, arr $originalBaseline = $baseline; $originalTarget = $target; - $difference = PayloadDifference::between($baseline, $target); + $difference = PayloadDifference::between( + $baseline, + $target, + ); self::assertSame( $expected, diff --git a/tests/Comparison/SnapshotComparisonTest.php b/tests/Comparison/SnapshotComparisonTest.php index ccfd324..ec00119 100644 --- a/tests/Comparison/SnapshotComparisonTest.php +++ b/tests/Comparison/SnapshotComparisonTest.php @@ -22,7 +22,11 @@ public function testBetweenRetainsSnapshotsAndSharedComparisonResults(): void $baseline = self::snapshot(['db' => ['queries' => 1]]); $target = self::snapshot(['db' => ['queries' => 2]]); - $comparison = SnapshotComparison::between($baseline, $target, ['db' => 'Database']); + $comparison = SnapshotComparison::between( + $baseline, + $target, + ['db' => 'Database'], + ); self::assertSame( $baseline, @@ -116,7 +120,10 @@ public function testHasDifferencesReturnsFalseForIdenticalCaptures(): void $panels = ['db' => ['queries' => 1]]; self::assertFalse( - SnapshotComparison::between(self::snapshot($panels), self::snapshot($panels))->hasDifferences(), + SnapshotComparison::between( + self::snapshot($panels), + self::snapshot($panels), + )->hasDifferences(), 'Equal summaries and payloads must report no difference.', ); } diff --git a/tests/Comparison/SummaryMetricComparisonTest.php b/tests/Comparison/SummaryMetricComparisonTest.php index 0130377..ef6a344 100644 --- a/tests/Comparison/SummaryMetricComparisonTest.php +++ b/tests/Comparison/SummaryMetricComparisonTest.php @@ -13,7 +13,9 @@ use function array_map; /** - * Tests summary metric compatibility with the original adapter calculations and formatting. + * Unit tests for {@see SummaryMetricComparison} validating metric contracts, boundaries, and formatted deltas. + * + * {@see SummaryMetricComparisonProvider} for test case data providers. */ #[Group('history')] final class SummaryMetricComparisonTest extends TestCase @@ -30,11 +32,12 @@ public function testBetweenPreservesAllMetricContracts( $beforeBaseline = $baseline->jsonSerialize(); $beforeTarget = $target->jsonSerialize(); - $actual = []; + $metrics = SummaryMetricComparison::between( + $baseline, + $target, + ); - foreach (SummaryMetricComparison::between($baseline, $target) as $metric) { - $actual[] = self::row($metric); - } + $actual = array_map(self::row(...), $metrics); self::assertSame( $expected, diff --git a/tests/Data/FilterEngineTest.php b/tests/Data/FilterEngineTest.php index d0165c6..36278d3 100644 --- a/tests/Data/FilterEngineTest.php +++ b/tests/Data/FilterEngineTest.php @@ -5,6 +5,7 @@ namespace PHPForge\Debug\Tests\Data; use PHPForge\Debug\Data\FilterEngine; +use PHPForge\Debug\Panel\Db\QueryRow; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; use ReflectionProperty; @@ -262,6 +263,33 @@ public function testFilterParsesLeadingComparisonOperators(): void 'A leading `<` must compare numeric strings numerically.', ); } + public function testFilterReadsPrivatePanelStateAndPreservesRowIdentity(): void + { + $engine = new FilterEngine(); + + $match = QueryRow::create('SELECT * FROM users', 2.5, 1000.0); + + $rows = [ + QueryRow::create('SELECT * FROM posts', 3.0, 1000.0), + QueryRow::create('SELECT * FROM users', 1.0, 1000.0), + $match, + ]; + + $engine->addCondition('type', 'sel', partial: true); + $engine->addCondition('query', 'USERS', partial: true); + $engine->addMinimumCondition('duration', 2.5); + + self::assertSame( + [$match], + $engine->filter($rows), + 'Panel filtering must use persisted fields while returning the original row instances.', + ); + self::assertSame( + $rows, + $engine->filter($rows), + 'Panel filtering must reset its conditions after each run.', + ); + } public function testFilterReadsPublicPropertiesFromObjectRows(): void { @@ -325,7 +353,12 @@ public function testFilterRejectsNonNumericCandidatesForNumericOperators(): void public function testFilterRequiresTheEntireComparisonGrammarToMatch(): void { - foreach (['prefix >5', '>5 suffix'] as $value) { + $values = [ + 'prefix >5', + '>5 suffix', + ]; + + foreach ($values as $value) { $engine = new FilterEngine(); $engine->addCondition('value', $value); diff --git a/tests/Panel/Config/ConfigCardRendererTest.php b/tests/Panel/Config/ConfigCardRendererTest.php index d10967a..3a4bf6c 100644 --- a/tests/Panel/Config/ConfigCardRendererTest.php +++ b/tests/Panel/Config/ConfigCardRendererTest.php @@ -163,6 +163,23 @@ public function testRenderInstalledExtensionsSectionGroupsPackagesByComposerVend ); } + public function testRenderInstalledExtensionsSectionKeepsEverySegmentAfterTheVendorInThePackageName(): void + { + $summary = self::makeSummary(extensions: ['acme/tool/plugin' => '1.0.0']); + + $section = ConfigCardRenderer::renderInstalledExtensionsSection($summary); + + self::assertNotNull( + $section, + 'Non-empty roster must produce a section.', + ); + self::assertStringContainsString( + 'tool/plugin', + $section->render(), + 'Only the first separator splits vendor from package.', + ); + } + public function testRenderInstalledExtensionsSectionReturnsNullWhenRosterIsEmpty(): void { $summary = self::makeSummary(extensions: []); diff --git a/tests/Panel/Db/DbCaptureTest.php b/tests/Panel/Db/DbCaptureTest.php index cf3f631..6ab721f 100644 --- a/tests/Panel/Db/DbCaptureTest.php +++ b/tests/Panel/Db/DbCaptureTest.php @@ -5,12 +5,12 @@ namespace PHPForge\Debug\Tests\Panel\Db; use PHPForge\Debug\Panel\Db\{DbSnapshot, DbSummary, DbSummaryRenderer, QueryRow}; -use PHPUnit\Framework\Attributes\Group; +use PHPForge\Debug\Tests\Provider\DbCaptureProvider; +use PHPUnit\Framework\Attributes\{DataProviderExternal, Group}; use PHPUnit\Framework\TestCase; /** - * Unit tests for shared Database capture factories, duplicate resolution, request-wide metrics, EXPLAIN eligibility, - * and the toolbar warning wording. + * Unit tests for {@see DbCapture} covering interleaved rows, duplicate normalization, and query factories. */ #[Group('db')] final class DbCaptureTest extends TestCase @@ -18,9 +18,21 @@ final class DbCaptureTest extends TestCase public function testCapturePreservesInterleavedRowsAndNormalizesExactDuplicates(): void { $rows = [ - new QueryRow('SELECT', 'SELECT 1', 1.25, [['file' => '/a', 'line' => 3]], 'first', 1000.0, 8, 99, 4), - new QueryRow('UPDATE', 'UPDATE t SET a=1', 2.5, [], 'second', 2000.0, 4, 99, 0), - new QueryRow('SELECT', 'SELECT 1', 3.25, [], 'first', 3000.0, 6, 99, null), + QueryRow::create('SELECT 1', 1.25, 1000.0) + ->withTrace([['file' => '/a', 'line' => 3]]) + ->withTraceHash('first') + ->withSequence(8) + ->withDuplicate(99) + ->withRows(4), + QueryRow::create('UPDATE t SET a=1', 2.5, 2000.0) + ->withTraceHash('second') + ->withSequence(4) + ->withDuplicate(99) + ->withRows(0), + QueryRow::create('SELECT 1', 3.25, 3000.0) + ->withTraceHash('first') + ->withSequence(6) + ->withDuplicate(99), ]; $snapshot = DbSnapshot::capture($rows); @@ -50,32 +62,18 @@ public function testCapturePreservesInterleavedRowsAndNormalizesExactDuplicates( ); self::assertSame( 99, - $rows[0]->duplicate, + $rows[0]->getDuplicate(), 'Capture must not mutate the source row.', ); } - public function testIsExplainableAcceptsSingleStatementDmlVerbsRegardlessOfCase(): void + #[DataProviderExternal(DbCaptureProvider::class, 'explainableStatements')] + public function testIsExplainable(string $type, string $query, bool $expected): void { - foreach (['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'REPLACE', 'WITH'] as $verb) { - foreach ([$verb, strtolower($verb)] as $type) { - self::assertTrue( - self::makeRow($type)->isExplainable(), - "Verb '{$type}' must qualify.", - ); - } - } - - foreach (['SHOW', ''] as $type) { - self::assertFalse( - self::makeRow($type)->isExplainable(), - "Verb '{$type}' must not qualify.", - ); - } - - self::assertFalse( - self::makeRow('SELECT', 'SELECT 1; SELECT 2')->isExplainable(), - 'A statement separator must disqualify the row.', + self::assertSame( + $expected, + self::makeRow($type, $query)->isExplainable(), + 'EXPLAIN eligibility must match the query type and statement count.', ); } @@ -100,11 +98,16 @@ public function testQueryFactoriesPreserveFieldsAndReturnImmutableCopies(): void ); self::assertSame( '', - QueryRow::create('123 SELECT', 0.0, 0.0)->type, + QueryRow::create('123 SELECT', 0.0, 0.0)->getType(), 'Non-word prefixes must have no SQL verb.', ); - $source = new QueryRow('UPDATE', 'UPDATE t', 9.5, [['file' => '/old']], 'old', 99.0, 5, 4, 2); + $source = QueryRow::create('UPDATE t', 9.5, 99.0) + ->withTrace([['file' => '/old']]) + ->withTraceHash('old') + ->withSequence(5) + ->withDuplicate(4) + ->withRows(2); $trace = [['file' => '/new', 'line' => 7]]; @@ -116,6 +119,7 @@ public function testQueryFactoriesPreserveFieldsAndReturnImmutableCopies(): void $expected['trace'] = $trace; $expected['traceHash'] = hash('sha256', json_encode($trace, JSON_THROW_ON_ERROR)); + $expected['seq'] = 8; self::assertSame( @@ -123,19 +127,14 @@ public function testQueryFactoriesPreserveFieldsAndReturnImmutableCopies(): void $changed->jsonSerialize(), 'Withers must preserve all unrelated fields.', ); - self::assertSame( - 'old', - $source->traceHash, - 'Withers must not mutate the original row.', - ); self::assertSame( [], - $changed->withTrace([])->trace, + $changed->withTrace([])->getTrace(), 'An empty trace must clear the frames.', ); self::assertSame( '', - $changed->withTrace([])->traceHash, + $changed->withTrace([])->getTraceHash(), 'An empty trace must not create a synthetic caller.', ); @@ -149,11 +148,6 @@ public function testQueryFactoriesPreserveFieldsAndReturnImmutableCopies(): void $duplicated->jsonSerialize(), 'The duplicate count must be the only replaced field.', ); - self::assertSame( - 4, - $source->duplicate, - 'The source duplicate count must stay untouched.', - ); $counted = $source->withRows(5); $expectedRows = $source->jsonSerialize(); @@ -166,12 +160,12 @@ public function testQueryFactoriesPreserveFieldsAndReturnImmutableCopies(): void 'The reported row count must be the only replaced field.', ); self::assertNull( - $counted->withRows(null)->rows, + $counted->withRows(null)->getRows(), 'An unreported row count must clear the field.', ); self::assertSame( 2, - $source->rows, + $source->getRows(), 'The source row count must stay untouched.', ); } @@ -179,9 +173,21 @@ public function testQueryFactoriesPreserveFieldsAndReturnImmutableCopies(): void public function testSummaryAggregatesRequestWideCountDurationDuplicatesAndCallers(): void { $rows = [ - new QueryRow('SELECT', 'SELECT 1', 1.25, [['file' => '/a', 'line' => 3]], 'first', 1000.0, 8, 99, 4), - new QueryRow('UPDATE', 'UPDATE t SET a=1', 2.5, [], 'second', 2000.0, 4, 99, 0), - new QueryRow('SELECT', 'SELECT 1', 3.25, [], 'first', 3000.0, 6, 99, null), + QueryRow::create('SELECT 1', 1.25, 1000.0) + ->withTrace([['file' => '/a', 'line' => 3]]) + ->withTraceHash('first') + ->withSequence(8) + ->withDuplicate(99) + ->withRows(4), + QueryRow::create('UPDATE t SET a=1', 2.5, 2000.0) + ->withTraceHash('second') + ->withSequence(4) + ->withDuplicate(99) + ->withRows(0), + QueryRow::create('SELECT 1', 3.25, 3000.0) + ->withTraceHash('first') + ->withSequence(6) + ->withDuplicate(99), ]; $summary = new DbSummary(DbSnapshot::capture($rows)->entries()); @@ -365,7 +371,7 @@ public function testToolbarTitleReportsExecutedCountOrTheActiveWarnings(): void private static function makeRow(string $type, string $query = 'SELECT 1'): QueryRow { - return new QueryRow($type, $query, 1.0, [], '', 0.0, 0, 1, null); + return QueryRow::create($query, 1.0, 0.0)->withType($type); } /** @@ -378,7 +384,10 @@ private static function makeSummary(array $traceHashes): DbSummary $rows = []; foreach ($traceHashes as $seq => $traceHash) { - $rows[] = new QueryRow('SELECT', "SELECT {$seq}", 1.0, [], $traceHash, 0.0, $seq, 1, null); + $rows[] = QueryRow::create("SELECT {$seq}", 1.0, 0.0) + ->withType('SELECT') + ->withTraceHash($traceHash) + ->withSequence($seq); } return new DbSummary($rows); diff --git a/tests/Panel/Db/DbExplainSupportTest.php b/tests/Panel/Db/DbExplainSupportTest.php new file mode 100644 index 0000000..3169760 --- /dev/null +++ b/tests/Panel/Db/DbExplainSupportTest.php @@ -0,0 +1,63 @@ +withType($type) + ->withTrace($trace) + ->withTraceHash($traceHash) + ->withSequence($seq) + ->withDuplicate($duplicate) + ->withRows($rows); } /** @@ -449,8 +442,7 @@ private static function makeUrlBuilder(string $tag = 'tag'): callable */ private static function traceLine(): \Closure { - return static fn(array $frame): string => Coerce::string($frame['file'] - ?? null) . ':' . Coerce::string($frame['line'] - ?? null); + return static fn(array $frame): string => Coerce::string($frame['file'] ?? null) . ':' + . Coerce::string($frame['line'] ?? null); } } diff --git a/tests/Panel/Db/NPlusOneDetectorTest.php b/tests/Panel/Db/NPlusOneDetectorTest.php index a5fae8f..142e481 100644 --- a/tests/Panel/Db/NPlusOneDetectorTest.php +++ b/tests/Panel/Db/NPlusOneDetectorTest.php @@ -5,10 +5,14 @@ namespace PHPForge\Debug\Tests\Panel\Db; use InvalidArgumentException; +use PHPForge\Debug\Exception\Message; use PHPForge\Debug\Panel\Db\{NPlusOneDetector, NPlusOneFinding, QueryRow}; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; +/** + * Unit tests for {@see NPlusOneDetector} covering read-query grouping, threshold validation, and finding order. + */ #[Group('panel')] #[Group('db')] final class NPlusOneDetectorTest extends TestCase @@ -136,6 +140,7 @@ public function testDetectOrdersFindingsByCountThenTotalDurationThenFirstSequenc public function testDuplicateCountsFromOtherCallSitesDoNotTriggerFinding(): void { $candidate = self::row(1, 'caller-b', 'SELECT 1', 1, duplicate: 5); + $rows = [ self::row(0, 'caller-a', 'SELECT 1', 1, duplicate: 5), $candidate, ]; @@ -161,11 +166,11 @@ public function testDuplicateCountsFromOtherCallSitesDoNotTriggerFinding(): void ); } - public function testRejectsUnsafeThreshold(): void + public function testThrowInvalidArgumentExceptionForAnUnsafeThreshold(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage( - 'The N+1 threshold must be at least two.', + Message::N_PLUS_ONE_THRESHOLD_INVALID->getMessage(), ); NPlusOneDetector::detect([], 1); @@ -194,16 +199,10 @@ private static function row( string $type = 'SELECT', int $duplicate = 1, ): QueryRow { - return new QueryRow( - type: $type, - query: $query, - duration: $duration, - trace: [], - traceHash: $traceHash, - timestamp: 0, - seq: $seq, - duplicate: $duplicate, - rows: null, - ); + return QueryRow::create($query, $duration, 0) + ->withType($type) + ->withTraceHash($traceHash) + ->withSequence($seq) + ->withDuplicate($duplicate); } } diff --git a/tests/Panel/Db/QueryRowTest.php b/tests/Panel/Db/QueryRowTest.php index 702f8df..3e84ad3 100644 --- a/tests/Panel/Db/QueryRowTest.php +++ b/tests/Panel/Db/QueryRowTest.php @@ -4,6 +4,7 @@ namespace PHPForge\Debug\Tests\Panel\Db; +use PHPForge\Debug\Exception\Message; use PHPForge\Debug\Panel\Db\QueryRow; use PHPForge\Debug\Storage\HydrationException; use PHPUnit\Framework\Attributes\Group; @@ -11,68 +12,102 @@ /** * Unit tests for {@see QueryRow} covering the capture-time narrowing of logger timings and the strict JSON hydration - * that restores them without coercion. + * that restores them without coercion, as well as immutable row configuration. */ #[Group('panel')] #[Group('db')] final class QueryRowTest extends TestCase { + public function testExtractTypeReturnsAnEmptyStringWhenNoLeadingVerbIsPresent(): void + { + self::assertSame( + '', + QueryRow::extractType(' (SELECT 1)'), + 'A statement not starting with a letter has no verb.', + ); + self::assertSame( + '', + QueryRow::extractType(''), + 'An empty statement has no verb.', + ); + } + public function testExtractTypeUppercasesTheLeadingVerbAndIgnoresLeadingWhitespace(): void + { + self::assertSame( + 'SELECT', + QueryRow::extractType(" \n select * from users"), + 'Leading whitespace must not hide the verb.', + ); + self::assertSame( + 'INSERT', + QueryRow::extractType('InSeRt INTO users VALUES (1)'), + 'The verb must be uppercased.', + ); + } + + public function testFindBySequenceComparesTheSequenceAsAString(): void + { + $rows = [ + QueryRow::create('SELECT 1', 1.0, 0.0)->withSequence(2), + QueryRow::create('SELECT 2', 2.0, 0.0)->withSequence(13), + ]; + + self::assertSame( + 'SELECT 2', + QueryRow::findBySequence($rows, '13')?->getQuery(), + 'An exact sequence must return its own row.', + ); + self::assertNull( + QueryRow::findBySequence($rows, '02'), + 'A padded sequence must not match.', + ); + self::assertNull( + QueryRow::findBySequence($rows, '99'), + 'An uncaptured sequence must return `null`.', + ); + } + public function testFromArrayClampsDuplicateToMinimumOfOne(): void { $row = QueryRow::fromArray(self::payload(['duplicate' => 0]), '$.panels.db.entries[0]'); self::assertSame( 1, - $row->duplicate, + $row->getDuplicate(), "A duplicate count below one must clamp to '1'.", ); } - public function testFromArrayRoundTripsEveryField(): void + public function testFromArrayPreservesExplicitTypeAndNonEncodableTrace(): void { - $row = new QueryRow( - type: 'SELECT', - query: 'SELECT * FROM t', - duration: 5.0, - trace: [['file' => '/app/index.php', 'line' => 12]], - traceHash: 'abc123', - timestamp: 1_700_000_000_000.0, - seq: 3, - duplicate: 2, - rows: 42, + $payload = self::payload( + [ + 'type' => 'select', + 'trace' => [['file' => "\xFF"]], + 'traceHash' => 'logger-hash', + ], ); - self::assertEquals( - $row, - QueryRow::fromArray($row->jsonSerialize(), '$.panels.db.entries[0]'), - 'Round-trip must preserve every field.', + self::assertSame( + $payload, + QueryRow::fromArray($payload, '$.row')->jsonSerialize(), + 'Hydration must preserve the captured type and hash without encoding the trace again.', ); } - public function testFromTimingClampsDuplicateToMinimumOfOne(): void + public function testFromArrayRoundTripsEveryField(): void { - $row = QueryRow::fromTiming( - [ - 'info' => 'SELECT 1', - 'category' => 'yii\\db\\Command::query', - 'timestamp' => 1.0, - 'trace' => [], - 'level' => 0, - 'duration' => 0.001, - 'memory' => 0, - 'memoryDiff' => 0, - 'traceHash' => 'hash', - ], - 'SELECT', - 0, - 0, - null, - ); + $row = QueryRow::create('SELECT * FROM t', 5.0, 1_700_000_000_000.0) + ->withTrace([['file' => '/app/index.php', 'line' => 12]]) + ->withTraceHash('abc123') + ->withSequence(3) + ->withDuplicate(2) + ->withRows(42); - self::assertSame( - 1, - $row->duplicate, - "A capture-time duplicate count below one must clamp to '1'.", + self::assertEquals( + $row, + QueryRow::fromArray($row->jsonSerialize(), '$.panels.db.entries[0]'), + 'Round-trip must preserve every field.', ); } @@ -95,7 +130,6 @@ public function testFromTimingReindexesKeyedTraceFrames(): void ], 'SELECT', 0, - 1, null, ); @@ -104,7 +138,7 @@ public function testFromTimingReindexesKeyedTraceFrames(): void ['file' => 'a.php'], ['file' => 'b.php'], ], - $row->trace, + $row->getTrace(), 'Trace frames must become a zero-based list.', ); } @@ -125,59 +159,201 @@ public function testFromTimingScalesToMillisecondsAndKeepsCallerValues(): void ], 'SELECT', 7, - 3, 42, ); self::assertSame( 'SELECT', - $row->type, + $row->getType(), 'The verb is supplied by the caller.', ); self::assertSame( 'SELECT 1', - $row->query, + $row->getQuery(), 'The statement comes from the timing token.', ); self::assertEqualsWithDelta( 5.0, - $row->duration, + $row->getDuration(), 1e-9, 'Duration must be scaled to milliseconds.', ); self::assertEqualsWithDelta( 2_500.0, - $row->timestamp, + $row->getTimestamp(), 1e-9, 'Timestamp must be scaled to milliseconds.', ); self::assertSame( 'hash', - $row->traceHash, + $row->getTraceHash(), 'Trace hash must round-trip.', ); self::assertSame( 7, - $row->seq, + $row->getSequence(), 'Sequence index is supplied by the caller.', ); - self::assertSame( - 3, - $row->duplicate, - 'Duplicate count is supplied by the caller.', - ); self::assertSame( 42, - $row->rows, + $row->getRows(), 'Row count is supplied by the caller.', ); } + public function testFromTimingStartsTheDuplicateCountAtOne(): void + { + $row = QueryRow::fromTiming( + [ + 'info' => 'SELECT 1', + 'category' => 'yii\\db\\Command::query', + 'timestamp' => 1.0, + 'trace' => [], + 'level' => 0, + 'duration' => 0.001, + 'memory' => 0, + 'memoryDiff' => 0, + 'traceHash' => 'hash', + ], + 'SELECT', + 0, + null, + ); + + self::assertSame( + 1, + $row->getDuplicate(), + 'A freshly captured row counts as a single occurrence.', + ); + } + + public function testGettersExposeEveryPersistedField(): void + { + $payload = self::payload( + [ + 'type' => 'update', + 'query' => 'UPDATE users SET active = 1', + 'duration' => 2.5, + 'trace' => [['file' => '/app.php', 'line' => 12]], + 'traceHash' => 'captured-hash', + 'timestamp' => 1234.0, + 'seq' => 7, + 'duplicate' => 3, + 'rows' => 42, + ], + ); + + $row = QueryRow::fromArray($payload, '$.row'); + + self::assertSame( + $payload, + [ + 'type' => $row->getType(), + 'query' => $row->getQuery(), + 'duration' => $row->getDuration(), + 'trace' => $row->getTrace(), + 'traceHash' => $row->getTraceHash(), + 'timestamp' => $row->getTimestamp(), + 'seq' => $row->getSequence(), + 'duplicate' => $row->getDuplicate(), + 'rows' => $row->getRows(), + ], + 'Typed getters must expose every persisted value without coercion.', + ); + } + + public function testGetTraceReturnsAnIndependentArray(): void + { + $row = QueryRow::create('SELECT 1', 1.0, 0.0)->withTrace([['file' => '/original.php', 'line' => 12]]); + + $trace = $row->getTrace(); + + $trace[0]['file'] = '/changed.php'; + + self::assertSame( + [['file' => '/original.php', 'line' => 12]], + $row->getTrace(), + 'Editing returned trace frames must not mutate the captured row.', + ); + } + + public function testReturnNewInstanceWhenSettingAttribute(): void + { + $row = QueryRow::create('SELECT 1', 1.0, 0.0); + + self::assertNotSame( + $row, + $row->withDuplicate(2), + 'New instance must be returned (immutability).', + ); + self::assertNotSame( + $row, + $row->withRows(5), + 'New instance must be returned (immutability).', + ); + self::assertNotSame( + $row, + $row->withSequence(3), + 'New instance must be returned (immutability).', + ); + self::assertNotSame( + $row, + $row->withTrace([['file' => '/app.php', 'line' => 7]]), + 'New instance must be returned (immutability).', + ); + self::assertNotSame( + $row, + $row->withTraceHash('captured-hash'), + 'New instance must be returned (immutability).', + ); + self::assertNotSame( + $row, + $row->withType('UPDATE'), + 'New instance must be returned (immutability).', + ); + } + + public function testReturnNewInstanceWhenSettingUnchangedAttribute(): void + { + $row = QueryRow::create('SELECT 1', 1.0, 0.0); + + self::assertNotSame( + $row, + $row->withDuplicate(1), + 'New instance must be returned even when the duplicate count is unchanged.', + ); + self::assertNotSame( + $row, + $row->withRows(null), + 'New instance must be returned even when the row count is `null`.', + ); + self::assertNotSame( + $row, + $row->withSequence(0), + 'New instance must be returned even when the sequence is unchanged.', + ); + self::assertNotSame( + $row, + $row->withTrace([]), + 'New instance must be returned even when the trace is empty.', + ); + self::assertNotSame( + $row, + $row->withTraceHash(''), + 'New instance must be returned even when the caller hash is empty.', + ); + self::assertNotSame( + $row, + $row->withType('SELECT'), + 'New instance must be returned even when the command verb is unchanged.', + ); + } + public function testThrowHydrationExceptionWhenDurationIsANumericString(): void { $this->expectException(HydrationException::class); $this->expectExceptionMessage( - "Invalid debug snapshot value at '$.panels.db.entries[0].duration': expected a number.", + Message::SNAPSHOT_VALUE_INVALID->getMessage('$.panels.db.entries[0].duration', 'a number'), ); QueryRow::fromArray(self::payload(['duration' => '5.0']), '$.panels.db.entries[0]'); @@ -187,12 +363,99 @@ public function testThrowHydrationExceptionWhenTraceIsNotAListOfObjects(): void { $this->expectException(HydrationException::class); $this->expectExceptionMessage( - "Invalid debug snapshot value at '$.panels.db.entries[0].trace[0]': expected an object.", + Message::SNAPSHOT_VALUE_INVALID->getMessage('$.panels.db.entries[0].trace[0]', 'an object'), ); QueryRow::fromArray(self::payload(['trace' => ['not-a-frame']]), '$.panels.db.entries[0]'); } + public function testWithersPreserveUnrelatedFieldsAndTheSourceRow(): void + { + $payload = self::payload( + [ + 'query' => 'SELECT * FROM users', + 'duration' => 2.5, + 'trace' => [['file' => '/original.php', 'line' => 12]], + 'timestamp' => 1234.0, + 'seq' => 7, + 'duplicate' => 3, + 'rows' => 42, + ], + ); + + $row = QueryRow::fromArray($payload, '$.row'); + + $trace = [ + [ + 'file' => '/changed.php', + 'line' => 24, + ], + ]; + + $changes = [ + [ + $row->withDuplicate(4), + ['duplicate' => 4], + ], + [ + $row->withRows(0), + ['rows' => 0], + ], + [ + $row->withRows(null), + ['rows' => null], + ], + [ + $row->withSequence(0), + ['seq' => 0], + ], + [ + $row->withTrace($trace), + [ + 'trace' => $trace, + 'traceHash' => hash('sha256', json_encode($trace, JSON_THROW_ON_ERROR)), + ], + ], + [ + $row->withTrace([]), + [ + 'trace' => [], + 'traceHash' => '', + ], + ], + [ + $row->withTraceHash('captured-hash'), + ['traceHash' => 'captured-hash'], + ], + [ + $row->withTraceHash(''), + ['traceHash' => ''], + ], + [ + $row->withType('delete'), + ['type' => 'delete'], + ], + [ + $row->withType(''), + ['type' => ''], + ], + ]; + + foreach ($changes as [$changed, $overrides]) { + self::assertSame( + [...$payload, ...$overrides], + $changed->jsonSerialize(), + 'Only the configured fields may change, without normalizing explicit values.', + ); + } + + self::assertSame( + $payload, + $row->jsonSerialize(), + 'Configuring copies must preserve every field of the source row.', + ); + } + /** * @param array $overrides * diff --git a/tests/Provider/DbCaptureProvider.php b/tests/Provider/DbCaptureProvider.php new file mode 100644 index 0000000..888e0fa --- /dev/null +++ b/tests/Provider/DbCaptureProvider.php @@ -0,0 +1,35 @@ + + */ + public static function explainableStatements(): iterable + { + yield 'empty query type' => ['', 'SELECT 1', false]; + yield 'lowercase DELETE' => ['delete', 'SELECT 1', true]; + yield 'lowercase INSERT' => ['insert', 'SELECT 1', true]; + yield 'lowercase REPLACE' => ['replace', 'SELECT 1', true]; + yield 'lowercase SELECT' => ['select', 'SELECT 1', true]; + yield 'lowercase UPDATE' => ['update', 'SELECT 1', true]; + yield 'lowercase WITH' => ['with', 'SELECT 1', true]; + yield 'multiple statements' => ['SELECT', 'SELECT 1; SELECT 2', false]; + yield 'unsupported SHOW' => ['SHOW', 'SELECT 1', false]; + yield 'uppercase DELETE' => ['DELETE', 'SELECT 1', true]; + yield 'uppercase INSERT' => ['INSERT', 'SELECT 1', true]; + yield 'uppercase REPLACE' => ['REPLACE', 'SELECT 1', true]; + yield 'uppercase SELECT' => ['SELECT', 'SELECT 1', true]; + yield 'uppercase UPDATE' => ['UPDATE', 'SELECT 1', true]; + yield 'uppercase WITH' => ['WITH', 'SELECT 1', true]; + } +} diff --git a/tests/Provider/DbQueryRendererProvider.php b/tests/Provider/DbQueryRendererProvider.php new file mode 100644 index 0000000..5db5d9a --- /dev/null +++ b/tests/Provider/DbQueryRendererProvider.php @@ -0,0 +1,41 @@ + + */ + public static function explainableVerbs(): iterable + { + yield 'lowercase DELETE' => ['delete']; + yield 'lowercase INSERT' => ['insert']; + yield 'lowercase REPLACE' => ['replace']; + yield 'lowercase SELECT' => ['select']; + yield 'lowercase UPDATE' => ['update']; + yield 'lowercase WITH' => ['with']; + yield 'uppercase DELETE' => ['DELETE']; + yield 'uppercase INSERT' => ['INSERT']; + yield 'uppercase REPLACE' => ['REPLACE']; + yield 'uppercase SELECT' => ['SELECT']; + yield 'uppercase UPDATE' => ['UPDATE']; + yield 'uppercase WITH' => ['WITH']; + } + + /** + * @return iterable + */ + public static function nonExplainableVerbs(): iterable + { + yield 'empty query type' => ['']; + yield 'unsupported PRAGMA' => ['PRAGMA']; + } +} diff --git a/tests/Provider/PanelComparisonProvider.php b/tests/Provider/PanelComparisonProvider.php index 78e8181..9e96376 100644 --- a/tests/Provider/PanelComparisonProvider.php +++ b/tests/Provider/PanelComparisonProvider.php @@ -7,7 +7,7 @@ use PHPForge\Debug\Storage\{DebugSnapshot, ExceptionSnapshot, PanelFailure, RequestSummary}; /** - * Characterization cases for panel selection, capture states, and combined difference counts. + * Data provider for {@see PanelComparisonTest} test cases. */ final class PanelComparisonProvider { @@ -54,24 +54,39 @@ public static function comparisons(): iterable ]; $counts = [ 'absent' => [ - 'absent' => [0, 0, 0, 0], 'empty' => [1, 0, 0, 0], 'failure' => [9, 0, 0, 0], - 'envelope' => [9, 0, 0, 0], 'both' => [9, 0, 0, 0], + 'absent' => [0, 0, 0, 0], + 'empty' => [1, 0, 0, 0], + 'failure' => [9, 0, 0, 0], + 'envelope' => [9, 0, 0, 0], + 'both' => [9, 0, 0, 0], ], 'empty' => [ - 'absent' => [0, 1, 0, 0], 'empty' => [0, 0, 0, 1], 'failure' => [9, 1, 0, 0], - 'envelope' => [9, 1, 0, 0], 'both' => [9, 1, 0, 0], + 'absent' => [0, 1, 0, 0], + 'empty' => [0, 0, 0, 1], + 'failure' => [9, 1, 0, 0], + 'envelope' => [9, 1, 0, 0], + 'both' => [9, 1, 0, 0], ], 'failure' => [ - 'absent' => [0, 9, 0, 0], 'empty' => [1, 9, 0, 0], 'failure' => [0, 0, 0, 9], - 'envelope' => [0, 0, 1, 9], 'both' => [0, 0, 0, 9], + 'absent' => [0, 9, 0, 0], + 'empty' => [1, 9, 0, 0], + 'failure' => [0, 0, 0, 9], + 'envelope' => [0, 0, 1, 9], + 'both' => [0, 0, 0, 9], ], 'envelope' => [ - 'absent' => [0, 9, 0, 0], 'empty' => [1, 9, 0, 0], 'failure' => [0, 0, 1, 9], - 'envelope' => [0, 0, 0, 9], 'both' => [0, 0, 1, 9], + 'absent' => [0, 9, 0, 0], + 'empty' => [1, 9, 0, 0], + 'failure' => [0, 0, 1, 9], + 'envelope' => [0, 0, 0, 9], + 'both' => [0, 0, 1, 9], ], 'both' => [ - 'absent' => [0, 9, 0, 0], 'empty' => [1, 9, 0, 0], 'failure' => [0, 0, 0, 9], - 'envelope' => [0, 0, 1, 9], 'both' => [0, 0, 0, 9], + 'absent' => [0, 9, 0, 0], + 'empty' => [1, 9, 0, 0], + 'failure' => [0, 0, 0, 9], + 'envelope' => [0, 0, 1, 9], + 'both' => [0, 0, 0, 9], ], ]; diff --git a/tests/Provider/SummaryMetricComparisonProvider.php b/tests/Provider/SummaryMetricComparisonProvider.php index 03cd6c7..98f81f9 100644 --- a/tests/Provider/SummaryMetricComparisonProvider.php +++ b/tests/Provider/SummaryMetricComparisonProvider.php @@ -9,12 +9,15 @@ use function array_replace; /** - * Regression cases recorded from the adapters before sharing summary metric calculations. + * Data provider for {@see SummaryMetricComparisonTest} test cases. */ final class SummaryMetricComparisonProvider { /** - * @return iterable + * @return iterable */ public static function metrics(): iterable { @@ -554,7 +557,10 @@ public static function metrics(): iterable } /** - * @return iterable}> + * @return iterable, + * }> */ public static function summaries(): iterable { From cf7d21a8d17d17f43bc885ec17df2171fe916dc9 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 9 Sep 2026 10:04:38 -0300 Subject: [PATCH 2/3] Raise code coverage `100%`. --- tests/Panel/Config/ConfigCardRendererTest.php | 13 ++++++++++++ tests/Panel/Db/NPlusOneDetectorTest.php | 21 +++++++++++++++++++ tools/quality/asset-size-budget.json | 6 +++--- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/tests/Panel/Config/ConfigCardRendererTest.php b/tests/Panel/Config/ConfigCardRendererTest.php index 3a4bf6c..444b014 100644 --- a/tests/Panel/Config/ConfigCardRendererTest.php +++ b/tests/Panel/Config/ConfigCardRendererTest.php @@ -16,6 +16,19 @@ #[Group('config')] final class ConfigCardRendererTest extends TestCase { + public function testRenderApplicationDetailsSectionPreservesLocaleWhenIntlCannotResolveIt(): void + { + $locale = str_repeat('a', 200); + + $summary = self::makeSummary(language: $locale); + + self::assertStringContainsString( + "
\n{$locale}\n
", + ConfigCardRenderer::renderApplicationDetailsSection($summary->application)->render(), + 'A locale exceeding the native ICU limit must remain visible without an invented display name.', + ); + } + public function testRenderApplicationDetailsSectionShowsCharsetAndLanguageRows(): void { $summary = self::makeSummary(charset: 'UTF-8', language: 'en-US', sourceLanguage: 'en'); diff --git a/tests/Panel/Db/NPlusOneDetectorTest.php b/tests/Panel/Db/NPlusOneDetectorTest.php index 142e481..3011b47 100644 --- a/tests/Panel/Db/NPlusOneDetectorTest.php +++ b/tests/Panel/Db/NPlusOneDetectorTest.php @@ -17,6 +17,27 @@ #[Group('db')] final class NPlusOneDetectorTest extends TestCase { + public function testBySequenceIndexesEveryFindingWithoutCopyingIt(): void + { + $first = new NPlusOneFinding('caller-a', 2, 3.0, 3, [3, 7], 'SELECT 1'); + $second = new NPlusOneFinding('caller-b', 2, 4.0, 9, [9, 12], 'SELECT 2'); + + self::assertSame( + [3 => $first, 7 => $first, 9 => $second, 12 => $second], + NPlusOneDetector::bySequence([$first, $second]), + 'Every sequence must reference its original finding without reindexing the keys.', + ); + } + + public function testBySequenceReturnsEmptyArrayWithoutFindings(): void + { + self::assertSame( + [], + NPlusOneDetector::bySequence([]), + 'An empty finding list must produce an empty index.', + ); + } + public function testDetectAcceptsThresholdTwoAndLowercaseSelectAfterSkippedRow(): void { $rows = [ diff --git a/tools/quality/asset-size-budget.json b/tools/quality/asset-size-budget.json index 6ee7efd..f749163 100644 --- a/tools/quality/asset-size-budget.json +++ b/tools/quality/asset-size-budget.json @@ -37,8 +37,8 @@ "maxGzipBytes": 23100 }, "js/db.min.js": { - "maxBytes": 6010, - "maxGzipBytes": 1935 + "maxBytes": 7700, + "maxGzipBytes": 2430 }, "js/debug.min.js": { "maxBytes": 24050, @@ -66,7 +66,7 @@ } }, "total": { - "maxBytes": 420700, + "maxBytes": 422400, "maxGzipBytes": 214800 } } From 4ef7b50930d21dbedd84560e5494eb7bc1450729 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 9 Sep 2026 10:11:41 -0300 Subject: [PATCH 3/3] Apply fixed Coderabbitai review. --- e2e/db-controls.spec.js | 20 ++++++++++++--- resources/assets/dist/js/db.min.js | 2 +- resources/src/panels/db.js | 9 +++++++ resources/tests/db.test.js | 37 +++++++++++++++++++++++++++- src/Helper/SensitiveDataRedactor.php | 3 ++- 5 files changed, 64 insertions(+), 7 deletions(-) diff --git a/e2e/db-controls.spec.js b/e2e/db-controls.spec.js index ae51af2..4432531 100644 --- a/e2e/db-controls.spec.js +++ b/e2e/db-controls.spec.js @@ -159,7 +159,9 @@ for (const dialect of ["yii2", "yii3"]) { ), ), ).toBe(true); - await rowTags.nth(1).click(); + await rowTags.nth(1).focus(); + await rowTags.nth(1).press("Enter"); + await expect(rowTags.nth(1)).toBeFocused(); await expect(visibleRows).toHaveCount(4); await expect(clear).toBeHidden(); await expect(banner).toHaveCount(0); @@ -176,6 +178,16 @@ for (const dialect of ["yii2", "yii3"]) { expect(page.url()).toBe("http://debug.test/?panel=db&sort=-duration"); await expect(select).toHaveValue("SELECT"); await expect(banner).toHaveCount(0); + await expect(page.locator(".yii-debug-db-n1-link")).toBeFocused(); + + await page.locator(".yii-debug-db-n1-link").press("Enter"); + const groupPill = banner.getByRole("link", { + name: "Remove N+1: 3 similar queries filter", + }); + await groupPill.focus(); + await groupPill.press("Enter"); + await expect(banner).toHaveCount(0); + await expect(page.locator(".yii-debug-db-n1-link")).toBeFocused(); await page.goto( "http://debug.test/?panel=db&sort=-duration&Db%5Bquery%5D=demo_items", @@ -192,9 +204,9 @@ for (const dialect of ["yii2", "yii3"]) { ), ), ).toBe(true); - await banner - .getByRole("link", { name: "Remove N+1: 3 similar queries filter" }) - .click(); + await groupPill.focus(); + await groupPill.press("Enter"); + await expect(page.locator(".yii-debug-db-n1-link")).toBeFocused(); await expect(banner).toContainText("1 filter active"); await expect(banner.locator("[data-yii-debug-n1-pill]")).toHaveCount(0); await expect(visibleRows).toHaveCount(4); diff --git a/resources/assets/dist/js/db.min.js b/resources/assets/dist/js/db.min.js index 829f281..766757f 100644 --- a/resources/assets/dist/js/db.min.js +++ b/resources/assets/dist/js/db.min.js @@ -1 +1 @@ -import{n as e,t}from"./dom.min.js";var n=`.yii-debug-db-explain-all-toggle, .yii-debug-db-explain-all a`,r=`.yii-debug-db-explain.is-open, .yii-debug-db-explain.is-loading`,i=`Unable to load the EXPLAIN output. Try again.`,a=3;function o(e,t,n){var r=e.querySelector(`.yii-debug-active-filters`);if(!r&&t.activeGroup){var i=e.querySelector(`.yii-debug-db-n1-summary`)||e.querySelector(`.yii-debug-grid-db`);if(!i)return;r=e.createElement(`div`),r.className=`yii-debug-active-filters`,r.setAttribute(`role`,`group`),r.setAttribute(`aria-label`,`Active filters`),r.innerHTML=`Clear all`,r.querySelector(`.yii-debug-active-filters-clear`).addEventListener(`click`,n),i.before(r)}if(r){var a=r.querySelector(`[data-yii-debug-n1-pill]`);if(t.activeGroup){a||(a=e.createElement(`a`),a.className=`yii-debug-active-filter-pill`,a.setAttribute(`data-yii-debug-n1-pill`,`true`),a.setAttribute(`href`,`#`),a.setAttribute(`title`,`Remove this filter`),a.innerHTML=`N+1:`,a.addEventListener(`click`,n),r.querySelector(`.yii-debug-active-filters-list`).appendChild(a));var o=t.visible+` similar queries`;a.querySelector(`.yii-debug-active-filter-value`).textContent=o,a.setAttribute(`aria-label`,`Remove N+1: `+o+` filter`)}else a&&a.remove();var s=r.querySelectorAll(`.yii-debug-active-filter-pill`).length;s===0?r.remove():r.querySelector(`.yii-debug-active-filters-label`).textContent=s+` filter`+(s===1?``:`s`)+` active`}}function s(e,t){return`Explaining `+e+`/`+t}function c(e,t,n){if(n&&n.completed0,o=0;o=s.total&&(s=null))}function _(e,t,n){e.finished||(e.finished=!0,f.delete(e.toggle),a.delete(e.toggle),e.container.classList.remove(`is-loading`,`is-requesting`),e.target.removeAttribute(`aria-busy`),t===`success`?(e.target.innerHTML=n.responseText,e.target.dataset.loaded=`1`,e.container.classList.add(`is-open`),e.toggle.setAttribute(`aria-expanded`,`true`)):t===`error`?(e.target.classList.add(`is-error`),e.target.setAttribute(`role`,`alert`),e.target.textContent=i,e.toggle.setAttribute(`aria-expanded`,`false`)):(e.container.classList.remove(`is-open`),e.toggle.setAttribute(`aria-expanded`,`false`)),g(e),y(),h())}function v(e){e.container.classList.add(`is-requesting`),a.set(e.toggle,e),e.request=t(e.toggle.href,{success:function(t){_(e,`success`,t)},error:function(t){_(e,`error`,t)},abort:function(t){_(e,`abort`,t)}})}function y(){for(;a.size<3&&d.length>0;){var e=d.shift();e&&!e.finished&&!e.cancelled&&v(e)}}function b(e,t){var n=f.get(e);if(n)return n;var r=p(e),i=m(r),a={batchId:t,cancelled:!1,container:r,finished:!1,request:null,target:i,toggle:e};return f.set(e,a),d.push(a),r.classList.add(`is-loading`),i.classList.remove(`is-error`),i.removeAttribute(`role`),i.setAttribute(`aria-busy`,`true`),i.textContent=``,y(),a}function x(e){e.cancelled=!0,e.request&&typeof e.request.abort==`function`&&e.request.abort(),_(e,`abort`,e.request)}function S(){s=null,d=[],Array.from(f.values()).forEach(x),document.querySelectorAll(`.yii-debug-db-explain-toggle`).forEach(function(e){var t=p(e),n=m(t);t&&t.classList.remove(`is-open`,`is-loading`,`is-requesting`),n&&n.removeAttribute(`aria-busy`),e.setAttribute(`aria-expanded`,`false`)}),h(!1)}function C(){var e=Array.from(document.querySelectorAll(`.yii-debug-db-explain-toggle`));s={completed:0,id:++u,total:e.length},e.forEach(function(e){var t=p(e),n=m(t);if(!t||!n){s.completed+=1;return}if(n.dataset.loaded===`1`){t.classList.add(`is-open`),e.setAttribute(`aria-expanded`,`true`),s.completed+=1;return}b(e,s.id)}),s&&s.completed>=s.total&&(s=null),h(!0)}e(document.querySelectorAll(`.yii-debug-db-explain-toggle`),`click`,function(e){e.preventDefault();var t=p(this),n=m(t);if(!(!t||!n)){if(t.classList.contains(`is-open`)){t.classList.remove(`is-open`),this.setAttribute(`aria-expanded`,`false`),h();return}if(n.dataset.loaded===`1`){t.classList.add(`is-open`),this.setAttribute(`aria-expanded`,`true`),h();return}b(this,null),h()}}),e(document.querySelectorAll(n),`click`,function(e){e.preventDefault(),s!==null||document.querySelectorAll(r).length>0?S():C()}),h();function w(e){if(window.history){var t=new URL(window.location.href),n=Array.from(document.querySelectorAll(`[data-yii-debug-n1-group]`)).some(function(e){return t.hash===`#`+e.getAttribute(`data-yii-debug-n1-group`)});(e||n)&&(t.hash=e||``,t.href!==window.location.href&&window.history.pushState(null,``,t.href))}}function T(e){e.preventDefault();var t=l(document,null);w(null),o(document,t,T)}e(document.querySelectorAll(`[data-yii-debug-n1-filter]`),`click`,function(e){var t=this.getAttribute(`aria-current`)===`true`?null:this.getAttribute(`data-yii-debug-n1-filter`);e.preventDefault();var n=l(document,t);w(n.activeGroup),o(document,n,T);var r=n.activeGroup?document.getElementById(n.activeGroup):null;r&&typeof r.scrollIntoView==`function`&&r.scrollIntoView({block:`center`})}),e(document.querySelectorAll(`[data-yii-debug-n1-clear]`),`click`,T)})();export{a as EXPLAIN_CONCURRENCY,l as applyNPlusOneFilter,s as formatExplainProgress,c as updateExplainAllControl,o as updateNPlusOneBanner}; \ No newline at end of file +import{n as e,t}from"./dom.min.js";var n=`.yii-debug-db-explain-all-toggle, .yii-debug-db-explain-all a`,r=`.yii-debug-db-explain.is-open, .yii-debug-db-explain.is-loading`,i=`Unable to load the EXPLAIN output. Try again.`,a=3;function o(e,t,n){var r=e.querySelector(`.yii-debug-active-filters`);if(!r&&t.activeGroup){var i=e.querySelector(`.yii-debug-db-n1-summary`)||e.querySelector(`.yii-debug-grid-db`);if(!i)return;r=e.createElement(`div`),r.className=`yii-debug-active-filters`,r.setAttribute(`role`,`group`),r.setAttribute(`aria-label`,`Active filters`),r.innerHTML=`Clear all`,r.querySelector(`.yii-debug-active-filters-clear`).addEventListener(`click`,n),i.before(r)}if(r){var a=r.querySelector(`[data-yii-debug-n1-pill]`);if(t.activeGroup){a||(a=e.createElement(`a`),a.className=`yii-debug-active-filter-pill`,a.setAttribute(`data-yii-debug-n1-pill`,`true`),a.setAttribute(`href`,`#`),a.setAttribute(`title`,`Remove this filter`),a.innerHTML=`N+1:`,a.addEventListener(`click`,n),r.querySelector(`.yii-debug-active-filters-list`).appendChild(a));var o=t.visible+` similar queries`;a.querySelector(`.yii-debug-active-filter-value`).textContent=o,a.setAttribute(`aria-label`,`Remove N+1: `+o+` filter`)}else a&&((e.activeElement===a||r.querySelectorAll(`.yii-debug-active-filter-pill`).length===1&&e.activeElement===r.querySelector(`.yii-debug-active-filters-clear`))&&e.querySelector(`[data-yii-debug-n1-filter]`).focus(),a.remove());var s=r.querySelectorAll(`.yii-debug-active-filter-pill`).length;s===0?r.remove():r.querySelector(`.yii-debug-active-filters-label`).textContent=s+` filter`+(s===1?``:`s`)+` active`}}function s(e,t){return`Explaining `+e+`/`+t}function c(e,t,n){if(n&&n.completed0,o=0;o=s.total&&(s=null))}function _(e,t,n){e.finished||(e.finished=!0,f.delete(e.toggle),a.delete(e.toggle),e.container.classList.remove(`is-loading`,`is-requesting`),e.target.removeAttribute(`aria-busy`),t===`success`?(e.target.innerHTML=n.responseText,e.target.dataset.loaded=`1`,e.container.classList.add(`is-open`),e.toggle.setAttribute(`aria-expanded`,`true`)):t===`error`?(e.target.classList.add(`is-error`),e.target.setAttribute(`role`,`alert`),e.target.textContent=i,e.toggle.setAttribute(`aria-expanded`,`false`)):(e.container.classList.remove(`is-open`),e.toggle.setAttribute(`aria-expanded`,`false`)),g(e),y(),h())}function v(e){e.container.classList.add(`is-requesting`),a.set(e.toggle,e),e.request=t(e.toggle.href,{success:function(t){_(e,`success`,t)},error:function(t){_(e,`error`,t)},abort:function(t){_(e,`abort`,t)}})}function y(){for(;a.size<3&&d.length>0;){var e=d.shift();e&&!e.finished&&!e.cancelled&&v(e)}}function b(e,t){var n=f.get(e);if(n)return n;var r=p(e),i=m(r),a={batchId:t,cancelled:!1,container:r,finished:!1,request:null,target:i,toggle:e};return f.set(e,a),d.push(a),r.classList.add(`is-loading`),i.classList.remove(`is-error`),i.removeAttribute(`role`),i.setAttribute(`aria-busy`,`true`),i.textContent=``,y(),a}function x(e){e.cancelled=!0,e.request&&typeof e.request.abort==`function`&&e.request.abort(),_(e,`abort`,e.request)}function S(){s=null,d=[],Array.from(f.values()).forEach(x),document.querySelectorAll(`.yii-debug-db-explain-toggle`).forEach(function(e){var t=p(e),n=m(t);t&&t.classList.remove(`is-open`,`is-loading`,`is-requesting`),n&&n.removeAttribute(`aria-busy`),e.setAttribute(`aria-expanded`,`false`)}),h(!1)}function C(){var e=Array.from(document.querySelectorAll(`.yii-debug-db-explain-toggle`));s={completed:0,id:++u,total:e.length},e.forEach(function(e){var t=p(e),n=m(t);if(!t||!n){s.completed+=1;return}if(n.dataset.loaded===`1`){t.classList.add(`is-open`),e.setAttribute(`aria-expanded`,`true`),s.completed+=1;return}b(e,s.id)}),s&&s.completed>=s.total&&(s=null),h(!0)}e(document.querySelectorAll(`.yii-debug-db-explain-toggle`),`click`,function(e){e.preventDefault();var t=p(this),n=m(t);if(!(!t||!n)){if(t.classList.contains(`is-open`)){t.classList.remove(`is-open`),this.setAttribute(`aria-expanded`,`false`),h();return}if(n.dataset.loaded===`1`){t.classList.add(`is-open`),this.setAttribute(`aria-expanded`,`true`),h();return}b(this,null),h()}}),e(document.querySelectorAll(n),`click`,function(e){e.preventDefault(),s!==null||document.querySelectorAll(r).length>0?S():C()}),h();function w(e){if(window.history){var t=new URL(window.location.href),n=Array.from(document.querySelectorAll(`[data-yii-debug-n1-group]`)).some(function(e){return t.hash===`#`+e.getAttribute(`data-yii-debug-n1-group`)});(e||n)&&(t.hash=e||``,t.href!==window.location.href&&window.history.pushState(null,``,t.href))}}function T(e){e.preventDefault();var t=l(document,null);w(null),o(document,t,T)}e(document.querySelectorAll(`[data-yii-debug-n1-filter]`),`click`,function(e){var t=this.getAttribute(`aria-current`)===`true`?null:this.getAttribute(`data-yii-debug-n1-filter`);e.preventDefault();var n=l(document,t);w(n.activeGroup),o(document,n,T);var r=n.activeGroup?document.getElementById(n.activeGroup):null;r&&typeof r.scrollIntoView==`function`&&r.scrollIntoView({block:`center`})}),e(document.querySelectorAll(`[data-yii-debug-n1-clear]`),`click`,T)})();export{a as EXPLAIN_CONCURRENCY,l as applyNPlusOneFilter,s as formatExplainProgress,c as updateExplainAllControl,o as updateNPlusOneBanner}; \ No newline at end of file diff --git a/resources/src/panels/db.js b/resources/src/panels/db.js index e6e302a..beba958 100644 --- a/resources/src/panels/db.js +++ b/resources/src/panels/db.js @@ -61,6 +61,15 @@ export function updateNPlusOneBanner(root, result, clearFilter) { pill.querySelector(".yii-debug-active-filter-value").textContent = label; pill.setAttribute("aria-label", "Remove N+1: " + label + " filter"); } else if (pill) { + if ( + root.activeElement === pill || + (banner.querySelectorAll(".yii-debug-active-filter-pill").length === 1 && + root.activeElement === + banner.querySelector(".yii-debug-active-filters-clear")) + ) { + root.querySelector("[data-yii-debug-n1-filter]").focus(); + } + pill.remove(); } diff --git a/resources/tests/db.test.js b/resources/tests/db.test.js index 2d3731b..646c21d 100644 --- a/resources/tests/db.test.js +++ b/resources/tests/db.test.js @@ -226,8 +226,10 @@ function bannerFixture(existing = false, withSummary = false) { attached = true; }, }; + var filterLink = new Element(); var root = { querySelector(selector) { + if (selector === "[data-yii-debug-n1-filter]") return filterLink; if (selector === ".yii-debug-db-n1-summary") return withSummary ? grid : null; return selector === ".yii-debug-grid-db" @@ -247,7 +249,10 @@ function bannerFixture(existing = false, withSummary = false) { return pill; }, }; - return { root, banner, label, clear }; + filterLink.focus = () => { + root.activeElement = filterLink; + }; + return { root, banner, label, clear, filterLink }; } test("N+1 creates the standard active-filter banner and clears it without navigation", () => { @@ -307,6 +312,36 @@ test("N+1 inserts its banner before the summary instead of the grid", () => { assert.equal(root.querySelector(".yii-debug-active-filters"), banner); }); +test("N+1 restores focus only when removing the focused control", () => { + var { root, banner, clear, filterLink } = bannerFixture(); + var active = { activeGroup: "a", visible: 3 }; + var inactive = { activeGroup: null, visible: 4 }; + var clearFilter = () => {}; + + updateNPlusOneBanner(root, active, clearFilter); + root.activeElement = banner.querySelector("[data-yii-debug-n1-pill]"); + updateNPlusOneBanner(root, inactive, clearFilter); + assert.equal(root.activeElement, filterLink); + + updateNPlusOneBanner(root, active, clearFilter); + root.activeElement = clear; + updateNPlusOneBanner(root, inactive, clearFilter); + assert.equal(root.activeElement, filterLink); + + var existing = bannerFixture(true); + updateNPlusOneBanner(existing.root, active, clearFilter); + existing.root.activeElement = existing.clear; + updateNPlusOneBanner(existing.root, inactive, clearFilter); + assert.equal(existing.root.activeElement, existing.clear); + + updateNPlusOneBanner(existing.root, active, clearFilter); + existing.root.activeElement = existing.banner.querySelector( + "[data-yii-debug-n1-pill]", + ); + updateNPlusOneBanner(existing.root, inactive, clearFilter); + assert.equal(existing.root.activeElement, existing.filterLink); +}); + test("N+1 shares the existing banner while preserving server filters and Clear all URL", () => { var { root, banner, label, clear } = bannerFixture(true); var inactive = { activeGroup: null, visible: 4 }; diff --git a/src/Helper/SensitiveDataRedactor.php b/src/Helper/SensitiveDataRedactor.php index b05f712..2e13fdf 100644 --- a/src/Helper/SensitiveDataRedactor.php +++ b/src/Helper/SensitiveDataRedactor.php @@ -95,7 +95,8 @@ final class SensitiveDataRedactor /** * Returns whether a key matches a configured exact name, literal prefix, or PCRE pattern. * - * @param string $key Original key to inspect case-insensitively. + * @param string $key Original key to inspect. Exact names and literal prefixes ignore case; PCRE patterns retain + * their own case-sensitivity modifiers. * @param list $sensitiveKeys Exact key names to inspect. * @param list $sensitiveKeyPrefixes Literal key prefixes to inspect case-insensitively. * @param list|null $sensitiveKeyPatterns PCRE patterns applied to the complete original key. `null` uses