/*! For license information please see index.js.LICENSE.txt */ (()=>{"use strict";var e,t={4689:(e,t,n)=>{var r,o,a,i,c,l,s,u,d,f,p,h,m,g,y=n(5072),v=n(7665),b=n(2564),w=n(8069),E=n(7097),x=n(8696),S=n(7039),k=n(4536),R=n(5263),T=n(6913),N=n(2665),O=n(4547),P=n(3676),I=n(9296),L=n(4461),A=n(7213),C=n(6639),j=window.mwcode_snippet_vault.prefix,M=window.mwcode_snippet_vault.domain,D=window.mwcode_snippet_vault.rest_url.replace(/\/+$/,""),G=window.mwcode_snippet_vault.api_url.replace(/\/+$/,""),F=window.mwcode_snippet_vault.plugin_url.replace(/\/+$/,""),z="1"===window.mwcode_snippet_vault.is_pro,U=z&&"1"===window.mwcode_snippet_vault.is_registered,B=window.mwcode_snippet_vault.rest_nonce,H=window.mwcode_snippet_vault.options,W=n(2109),Y=n(197),V=n(1686);function q(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}var K=(0,V.i7)(r||(r=q(["\n to { transform: rotate(360deg); }\n"]))),J=V.Ay.div(o||(o=q(['\n position: relative;\n /* No rule above it. The note is quiet enough to sit at the end on its own, and\n a line across the page announced it as another section — so the gap has to\n do that work instead, and has to be big enough to read as one.\n\n Note that StyledViewArea above carries margin: -18px, which bleeds the board\n out to the panel edges and takes 18px off whatever this asks for — so what\n lands on screen is 18px less than the number here. */\n margin: 35px 2px 0;\n padding: 0 34px 2px 0;\n\n p {\n margin: 0;\n /* Long enough to hold the note in three lines at full width, short enough\n that the eye still finds the next line on a wide screen. */\n max-width: 104ch;\n font-size: 12.5px;\n line-height: 1.65;\n color: rgba(255, 255, 255, 0.62);\n }\n\n a {\n color: #7dedff;\n text-decoration: none;\n }\n\n a:hover { text-decoration: underline; }\n\n /* NekoIntro appended this to external links; the plain paragraph that replaced\n it keeps the habit rather than dropping the cue. */\n a[target="_blank"]::after {\n content: \' ↗\';\n font-size: 0.85em;\n opacity: 0.75;\n }\n\n .intro-fold {\n position: absolute;\n top: -4px;\n right: 0;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 17px;\n line-height: 1;\n border-radius: 7px;\n cursor: pointer;\n color: rgba(255, 255, 255, 0.35);\n background: transparent;\n border: 1px solid transparent;\n transition: background 0.15s ease, color 0.15s ease;\n }\n\n .intro-fold:hover {\n color: white;\n background: rgba(255, 255, 255, 0.12);\n }\n\n .intro-fold:focus-visible {\n outline: 2px solid rgba(255, 255, 255, 0.92);\n outline-offset: 2px;\n }\n\n &.is-folded {\n padding-right: 0;\n padding-bottom: 0;\n }\n\n /* Folded, all that is left is the way back in — a full-width strip with a\n single "?" on it. A pill carrying the whole sentence was still a sentence,\n which is what folding it was meant to get rid of; a bar that spans the board\n reads as a lid on something rather than as another line to read. */\n .intro-peek {\n display: block;\n width: 100%;\n font-family: inherit;\n font-size: 13px;\n font-weight: 600;\n line-height: 1;\n padding: 9px 0;\n border-radius: 10px;\n cursor: pointer;\n color: rgba(255, 255, 255, 0.42);\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(255, 255, 255, 0.11);\n transition: background 0.15s ease, color 0.15s ease;\n }\n\n .intro-peek:hover { color: white; background: rgba(255, 255, 255, 0.12); }\n\n /* Same ring as focusRing below, written out because that const is declared\n after this one and a template literal is evaluated where it is written. */\n .intro-peek:focus-visible {\n outline: 2px solid rgba(255, 255, 255, 0.92);\n outline-offset: 2px;\n box-shadow: none;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .intro-fold, .intro-peek { transition: none; }\n }\n']))),$=V.Ay.div(a||(a=q(['\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n\n /* Named, not a bare "span". NekoButton puts its label in a span of its own, so\n a descendant selector reached inside the button and greyed the label — a red\n button with unreadable text on it. */\n .action-note {\n font-size: 12px;\n line-height: 1.5;\n color: var(--neko-gray-60, rgba(0, 0, 0, 0.55));\n }\n']))),X="\n &:focus-visible {\n outline: 2px solid rgba(255, 255, 255, 0.92);\n outline-offset: 2px;\n box-shadow: none;\n }\n",Q="\n display: flex;\n align-items: baseline;\n gap: 10px;\n flex-wrap: wrap;\n padding: 0 2px 7px;\n margin-bottom: 11px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.12);\n\n h3 {\n display: flex;\n align-items: center;\n gap: 7px;\n margin: 0;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: rgba(255, 255, 255, 0.82);\n }\n\n .head-note {\n font-size: 11.5px;\n color: rgba(255, 255, 255, 0.55);\n }\n",Z=V.Ay.div(i||(i=q(["\n display: grid;\n /* 340px is what a card needs for its capabilities to sit two-by-two without\n truncating. At 300px a wide screen squeezed in a fourth column and made\n every card narrower instead of wider — eleven chips ellipsised rather than\n three. The floor keeps the columns honest; 1fr still shares out the slack. */\n grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));\n gap: 12px;\n /* The 2px matches the group heading above, so cards, heading and the header\n strip at the top of the tab all share one left edge. */\n padding: 0 2px;\n"]))),ee=V.Ay.div(c||(c=q(["\n margin-top: 18px;\n\n .plugin-group + .plugin-group { margin-top: 22px; }\n\n .group-head { "," }\n\n .group-head b {\n font-size: 10.5px;\n font-weight: 700;\n letter-spacing: 0;\n padding: 1px 7px;\n border-radius: 999px;\n color: rgba(255, 255, 255, 0.9);\n background: rgba(255, 255, 255, 0.12);\n font-variant-numeric: tabular-nums;\n }\n\n /* The running group is the one that's working; the offer at the bottom gets a\n warmer rule so it reads as an invitation rather than a leftovers bin. */\n .plugin-group.is-active .group-head { border-bottom-color: rgba(43, 212, 125, 0.4); }\n .plugin-group.is-active .group-head b {\n color: #2bd47d;\n background: rgba(43, 212, 125, 0.16);\n }\n\n .plugin-group.is-none .group-head { border-bottom-color: rgba(255, 200, 87, 0.34); }\n .plugin-group.is-none .group-head b {\n color: #ffc857;\n background: rgba(255, 200, 87, 0.16);\n }\n"])),Q),te=V.Ay.div(l||(l=q(["\n ","\n position: relative;\n display: flex;\n flex-direction: column;\n border-radius: 12px;\n background: rgba(6, 18, 42, 0.30);\n border: 1px solid rgba(255, 255, 255, 0.14);\n overflow: hidden;\n transition: background 0.18s ease, border-color 0.18s ease, transform 0.18s ease;\n\n &::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 2px;\n background: var(--accent, transparent);\n opacity: 0.75;\n }\n\n &:hover {\n background: rgba(6, 18, 42, 0.44);\n border-color: rgba(255, 255, 255, 0.3);\n transform: translateY(-1px);\n }\n\n &.is-dormant {\n background: rgba(6, 18, 42, 0.14);\n border-style: dashed;\n border-color: rgba(255, 255, 255, 0.16);\n }\n\n /* The board already goes still when less motion is asked for; everything built\n since owes the same. Colour and border still respond to hover — it's the\n movement that's the problem, not the feedback. */\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n &:hover { transform: none; }\n .card-main img { transition: none; }\n }\n\n &.is-dormant::before { opacity: 0.3; }\n &.is-dormant img { filter: grayscale(1); opacity: 0.5; }\n &.is-dormant:hover img { filter: none; opacity: 1; }\n\n &.is-inactive img { filter: grayscale(0.4); opacity: 0.82; }\n\n .card-main {\n display: flex;\n flex-direction: column;\n padding: 15px 16px 13px 17px;\n text-decoration: none;\n flex: 1 1 auto;\n ","\n }\n\n .card-foot a { "," }\n\n .card-head {\n display: flex;\n align-items: flex-start;\n gap: 13px;\n min-width: 0;\n }\n\n /* Same tinted tile as the board: the icons are fetched from ps.w.org and a\n firewalled site would otherwise show a blank square beside each name. */\n .card-main img {\n flex: 0 0 auto;\n width: 42px;\n height: 42px;\n border-radius: 11px;\n display: block;\n background: color-mix(in srgb, var(--accent, #8fb3d9) 20%, rgba(255, 255, 255, 0.07));\n transition: filter 0.2s ease, opacity 0.2s ease;\n }\n\n .card-body {\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .card-title {\n display: flex;\n align-items: center;\n gap: 7px;\n font-size: 13.5px;\n font-weight: 650;\n line-height: 1.2;\n color: white;\n }\n\n .card-title .dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex: 0 0 auto;\n }\n\n .card-title .dot.on { background: #2bd47d; box-shadow: 0 0 6px rgba(43, 212, 125, 0.9); }\n .card-title .dot.off { background: rgba(255, 255, 255, 0.4); }\n\n .card-role {\n font-size: 11.5px;\n line-height: 1.45;\n color: rgba(255, 255, 255, 0.66);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* Two fixed columns, so four capabilities are always a 2x2 and every card in\n the grid ends up exactly the same height. Wrapping freely gave two, three\n or four rows depending on how long the words happened to be. */\n .card-features {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 5px;\n margin-top: 11px;\n }\n\n .card-features > span {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n min-width: 0;\n font-size: 10.5px;\n line-height: 1.2;\n padding: 4px 8px;\n border-radius: 6px;\n white-space: nowrap;\n /* the whole card is a link, so the chips have to opt out of link colour */\n color: rgba(255, 255, 255, 0.82);\n background: rgba(255, 255, 255, 0.07);\n border: 1px solid rgba(255, 255, 255, 0.09);\n }\n\n .card-features svg { flex: 0 0 auto; opacity: 0.7; }\n\n /* The three longest labels don't fit half a card; they ellipsis and the chip\n carries the full text as a tooltip. */\n .card-features em {\n font-style: normal;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &.is-dormant .card-features span {\n color: rgba(255, 255, 255, 0.55);\n background: rgba(255, 255, 255, 0.04);\n }\n\n .card-foot {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0 16px 13px 17px;\n }\n\n .card-family {\n flex: 1 1 auto;\n font-size: 9px;\n font-weight: 700;\n letter-spacing: 0.13em;\n text-transform: uppercase;\n color: rgba(255, 255, 255, 0.32);\n }\n\n .card-foot a {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n text-decoration: none;\n padding: 4px 11px;\n border-radius: 7px;\n color: rgba(255, 255, 255, 0.72);\n border: 1px solid rgba(255, 255, 255, 0.16);\n transition: background 0.16s ease, color 0.16s ease, border-color 0.16s ease;\n }\n\n .card-foot a:hover {\n background: rgba(255, 255, 255, 0.14);\n color: white;\n }\n\n .card-foot a.pro {\n color: #ffd48a;\n border-color: rgba(255, 186, 66, 0.4);\n }\n\n .card-foot a.pro:hover {\n background: rgba(255, 186, 66, 0.22);\n color: white;\n }\n"])),"\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n",X,X),ne=V.Ay.div(s||(s=q(["\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));\n gap: 12px;\n /* No bottom margin of its own — the section it sits in sets the space under\n its content, the same for all five. */\n margin: 0 2px;\n"]))),re=V.Ay.div(u||(u=q(["\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n padding: 16px 16px 15px;\n border-radius: 12px;\n background: rgba(6, 18, 42, 0.30);\n border: 1px solid rgba(255, 255, 255, 0.14);\n transition: border-color 0.2s ease, background 0.2s ease;\n\n &.is-running {\n border-color: rgba(255, 255, 255, 0.34);\n background: rgba(6, 18, 42, 0.44);\n }\n\n h3 {\n margin: 0 0 10px;\n font-size: 13px;\n font-weight: 650;\n letter-spacing: 0;\n color: white;\n }\n\n .gauge-value {\n display: flex;\n align-items: baseline;\n gap: 3px;\n font-size: 26px;\n font-weight: 600;\n line-height: 1;\n font-variant-numeric: tabular-nums;\n color: white;\n }\n\n .gauge-value i {\n font-style: normal;\n font-size: 12px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.6);\n }\n\n .gauge-sub {\n margin-top: 5px;\n font-size: 11px;\n color: rgba(255, 255, 255, 0.6);\n }\n\n .verdict {\n margin-top: 10px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n padding: 3px 10px;\n border-radius: 999px;\n /* Before a run this slot states the target instead of sitting empty, so the\n number you're about to get already has something to be measured against. */\n color: rgba(255, 255, 255, 0.6);\n background: rgba(255, 255, 255, 0.07);\n border: 1px solid rgba(255, 255, 255, 0.12);\n }\n\n .verdict.is-fast {\n color: #2bd47d;\n background: rgba(43, 212, 125, 0.14);\n border-color: rgba(43, 212, 125, 0.4);\n }\n\n .verdict.is-fair {\n color: #ffc857;\n background: rgba(255, 200, 87, 0.14);\n border-color: rgba(255, 200, 87, 0.4);\n }\n\n .verdict.is-slow {\n color: #ff8080;\n background: rgba(255, 107, 107, 0.16);\n border-color: rgba(255, 107, 107, 0.45);\n }\n\n /* Distinct from is-slow: a slow answer is a result, no answer is a fault. */\n .verdict.is-failed {\n color: #ffd0a0;\n background: rgba(255, 143, 90, 0.16);\n border-color: rgba(255, 143, 90, 0.5);\n }\n\n .hint {\n margin: 10px 0 12px;\n font-size: 11.5px;\n line-height: 1.5;\n color: rgba(255, 255, 255, 0.62);\n }\n\n /* Pushes the button to the bottom so all three line up even when one hint\n wraps to an extra line. */\n .hint + button { margin-top: auto; }\n\n button { width: 100%; }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n /* The gauge sweeps its arc on every one of the ten samples. NekoGauge sets\n that transition inline, so !important is the only thing that reaches it. */\n .neko-gauge path { transition: none !important; }\n }\n"]))),oe=V.Ay.div(d||(d=q(["\n /* No side margin of its own. Everything it wraps — the speed bar, the gauges,\n the phpinfo panel, the log console — already carries margin: 0 2px, and the\n section headings get the same 2px from sectionHead's padding. Adding 2px\n here as well double-inset the lot to 4px, and the override that was meant to\n cancel it named .php-body and .logs-bar, which have no side margin to\n cancel. One inset, set in one place, by the pieces themselves. */\n /* Folded, the tab is a list of rows, so it is built like one. Every heading is\n the same height whether or not it carries a button, with the same space above\n and below its text, and the rule sits at the bottom of that box — which puts\n each line exactly halfway between the title above it and the title below.\n Before this the heading kept 7px under it and 26px over the next one, so the\n rules looked stuck to the wrong titles. */\n .section-head {\n ","\n align-items: center;\n min-height: 30px;\n padding: 11px 2px;\n margin-bottom: 0;\n }\n\n .health-section + .health-section {\n margin-top: 0;\n padding-top: 0;\n }\n\n /* Opened, the section needs its content off the rule above it, and off the\n next heading below — the same 11px the headings use, so the rhythm carries\n on rather than restarting. */\n .section-head.is-open { margin-bottom: 14px; }\n .health-section.is-open { padding-bottom: 11px; }\n\n /* The overview: three ratings over six PHP values, one surface, no heading of\n its own. It is the top of the tab and it is the answer — labelling it would\n be labelling the page.\n\n A plain translucent black rather than the tinted gradient it had: it sinks\n into whatever blue is behind it instead of carrying a navy of its own, and\n the cards keep the only colour on the panel. */\n .health-top {\n display: flex;\n flex-direction: column;\n gap: 12px;\n background: #00000029;\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-top-color: rgba(255, 255, 255, 0.18);\n border-radius: 14px;\n padding: 14px;\n margin: 0 2px 12px;\n }\n\n /* The six values are supporting evidence for the three ratings above them, so\n they sit quieter — a hairline separates the two rather than a gap alone,\n which left the panel reading as two unrelated rows of boxes. */\n .health-top .php-facts {\n padding-top: 12px;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n }\n\n /* The offer keeps a surface of its own — it is a pitch, not a section. The\n analysis body went flat once the overview took the top: two panels stacked,\n one inside the other's shadow, read as a box in a box. */\n .analysis-body { margin: 0 2px; }\n\n .analysis-offer {\n background: rgba(0, 24, 40, 0.42);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 12px;\n padding: 16px 18px;\n margin: 0 2px;\n }\n\n .analysis-offer { display: flex; align-items: center; gap: 16px; }\n .analysis-offer svg { flex: 0 0 auto; color: #ffc857; }\n .analysis-offer > div { display: flex; flex-direction: column; gap: 4px; min-width: 0; }\n .analysis-offer strong { font-size: 13.5px; font-weight: 650; color: white; }\n .analysis-offer span { font-size: 12px; line-height: 1.55; color: rgba(255, 255, 255, 0.66); }\n\n .analysis-cta {\n flex: 0 0 auto;\n margin-left: auto;\n font-size: 10.5px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n text-decoration: none;\n padding: 8px 14px;\n border-radius: 9px;\n color: white;\n background: linear-gradient(135deg, #3f7bec, #2f5fbd);\n ","\n }\n\n /* Button first, explanation beside it — the same shape as Speed's and Errors'\n bars, and always visible for the same reason. The disclosure used to sit\n above the button as a bulleted list, which made a two-second action look\n like a form to be agreed to. */\n /* What the section says about itself when it is folded: what was found, and\n when. The count is lit the way the error summary lights its own, so the two\n folded sections read as the same kind of line. */\n .analysis-note {\n margin: 0;\n font-size: 12px;\n line-height: 1.55;\n color: rgba(255, 255, 255, 0.62);\n }\n\n .analysis-note b { color: white; font-variant-numeric: tabular-nums; }\n\n .analysis-steps {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .analysis-steps li {\n display: flex;\n align-items: center;\n gap: 11px;\n font-size: 13px;\n color: rgba(255, 255, 255, 0.36);\n }\n\n /* Each step carries its own state in the marker: waiting is a hollow ring,\n working spins, finished is a tick. Without it the list just sat there and\n the long last step looked like nothing was happening. */\n .analysis-steps i {\n position: relative;\n flex: 0 0 auto;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n border: 2px solid rgba(255, 255, 255, 0.18);\n }\n\n .analysis-steps .is-done { color: rgba(255, 255, 255, 0.72); }\n .analysis-steps .is-done i {\n border-color: #2bd47d;\n background: #2bd47d;\n }\n\n /* The tick, drawn rather than fonted so it lands the same everywhere.\n Centred off the middle rather than nudged in from the corner: the glyph is\n an L rotated 45°, so its own box has to be centred first and turned second,\n or it sits low and left the way hardcoded offsets left it. The -55% lifts it\n by the half-stroke the rotation adds below the join. */\n .analysis-steps .is-done i::after {\n content: '';\n position: absolute;\n left: 50%;\n top: 50%;\n width: 4px;\n height: 8px;\n border: solid #ffffff;\n border-width: 0 2px 2px 0;\n transform: translate(-50%, -55%) rotate(45deg);\n }\n\n .analysis-steps .is-now { color: white; }\n .analysis-steps .is-now i {\n border-color: rgba(255, 200, 87, 0.25);\n border-top-color: #ffc857;\n animation: ",' 0.7s linear infinite;\n }\n\n /* Asked for less motion: the ring stops and simply shows which step is live. */\n @media (prefers-reduced-motion: reduce) {\n .analysis-steps .is-now i {\n animation: none;\n border-color: #ffc857;\n background: rgba(255, 200, 87, 0.35);\n }\n }\n\n .analysis-steps .step-note {\n margin-left: auto;\n font-size: 11px;\n color: rgba(255, 255, 255, 0.42);\n }\n\n .analysis-error {\n font-size: 12.5px;\n line-height: 1.55;\n color: #ffd0a0;\n }\n\n .analysis-error span {\n display: block;\n margin-top: 3px;\n font-size: 11.5px;\n color: rgba(255, 255, 255, 0.5);\n }\n\n .analysis-areas {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 10px;\n }\n\n /* The rating was five 7px dots and nothing else — the one thing the eye should\n catch from across the room, hidden in 35 pixels. The card now carries its own\n verdict: a tinted wash, a tinted edge, and the icon in the same colour. Read\n at a glance it is green, amber or red; read properly it still says why. */\n .analysis-area {\n display: flex;\n flex-direction: column;\n gap: 3px;\n padding: 11px 13px;\n border-radius: 10px;\n background:\n linear-gradient(155deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03)),\n var(--tone-wash, transparent);\n border: 1px solid var(--tone-edge, rgba(255, 255, 255, 0.1));\n }\n\n .analysis-area.is-good {\n --tone-wash: linear-gradient(155deg, rgba(43, 212, 125, 0.16), rgba(43, 212, 125, 0.03));\n --tone-edge: rgba(43, 212, 125, 0.32);\n }\n\n .analysis-area.is-fair {\n --tone-wash: linear-gradient(155deg, rgba(255, 200, 87, 0.16), rgba(255, 200, 87, 0.03));\n --tone-edge: rgba(255, 200, 87, 0.32);\n }\n\n .analysis-area.is-poor {\n --tone-wash: linear-gradient(155deg, rgba(255, 128, 128, 0.18), rgba(255, 128, 128, 0.03));\n --tone-edge: rgba(255, 128, 128, 0.34);\n }\n\n .analysis-area.is-good .area-name svg { color: #2bd47d; }\n .analysis-area.is-fair .area-name svg { color: #ffc857; }\n .analysis-area.is-poor .area-name svg { color: #ff8080; }\n\n /* Unrated, the card still shows its shape — name, five empty dots, and what it\n is waiting for. Dimmed enough to read as "not yet" rather than "nothing". */\n .analysis-area.is-blank {\n background: rgba(255, 255, 255, 0.025);\n border-style: dashed;\n border-color: rgba(255, 255, 255, 0.12);\n }\n .analysis-area.is-blank .area-name { color: rgba(255, 255, 255, 0.34); }\n .analysis-area.is-blank .area-label { color: rgba(255, 255, 255, 0.44); font-weight: 600; }\n .analysis-area.is-blank .area-summary { color: rgba(255, 255, 255, 0.32); }\n .analysis-area.is-blank .area-dots i { background: rgba(255, 255, 255, 0.09); }\n\n /* The same icon the section below wears, so the card that rates Speed and the\n section that measures it are visibly the same subject. */\n .area-name {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 9.5px;\n font-weight: 700;\n letter-spacing: 0.09em;\n text-transform: uppercase;\n color: rgba(255, 255, 255, 0.5);\n }\n\n .area-name svg { flex: 0 0 auto; opacity: 0.85; }\n\n .area-dots { display: flex; gap: 3px; margin: 2px 0 1px; }\n .area-dots i { width: 7px; height: 7px; border-radius: 50%; background: rgba(255,255,255,0.16); }\n .analysis-area.is-good .area-dots i.on { background: #2bd47d; }\n .analysis-area.is-fair .area-dots i.on { background: #ffc857; }\n .analysis-area.is-poor .area-dots i.on { background: #ff8080; }\n\n /* The six values a support conversation turns on, lifted out of a 21,000px\n dump so they can be read rather than hunted for. They live in the overview\n rather than in the Environment section, next to the ratings that judge\n them — the numbers and the verdict on the numbers belong together. */\n .php-facts {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));\n gap: 8px;\n }\n\n .php-fact {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 8px 11px;\n border-radius: 9px;\n background: rgba(255, 255, 255, 0.04);\n border: 1px solid rgba(255, 255, 255, 0.07);\n }\n\n /* Hosts that disable phpinfo() get the labels and a dash: the shape of the\n answer is still worth showing when the answer isn\'t available. */\n .php-fact.is-blank { background: rgba(255, 255, 255, 0.02); }\n .php-fact.is-blank .fact-value { color: rgba(255, 255, 255, 0.3); }\n .php-fact.is-blank .fact-label { color: rgba(255, 255, 255, 0.34); }\n\n .fact-value {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 14px;\n font-weight: 600;\n line-height: 1.1;\n color: white;\n overflow-wrap: anywhere;\n }\n\n .fact-label {\n font-size: 9.5px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: rgba(255, 255, 255, 0.5);\n }\n\n .area-label { font-size: 13px; font-weight: 650; color: white; }\n /* Dimmer than the verdict above it — the rating and its label are the answer,\n this is the footnote. */\n .area-summary { font-size: 11.5px; line-height: 1.5; color: rgba(255, 255, 255, 0.5); }\n\n /* Given a block of its own rather than left as a loose paragraph with two\n thirds of the panel empty beside it. The measure stays readable; the block\n fills the width, so it reads as the summary it is. */\n /* The headline sentence, so it is the brightest thing in the panel and it\n fills its box. It used to cap the text at 88ch inside a full-width block,\n which left a third of the block empty and read as a layout fault rather\n than as a measure. */\n .analysis-conclusion {\n margin: 0 0 14px;\n padding: 14px 17px;\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.06);\n border-left: 3px solid rgba(255, 255, 255, 0.34);\n font-size: 14px;\n line-height: 1.55;\n color: #ffffff;\n }\n\n .analysis-conclusion span { display: block; }\n\n /* One column of folded rows. Two columns made sense when every finding was a\n block of prose; folded they are single lines, and a list of lines reads\n better stacked — and the reasoning, when opened, gets the full width. */\n .analysis-findings {\n list-style: none; margin: 0; padding: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n .analysis-findings li {\n border-radius: 9px;\n background: rgba(255, 255, 255, 0.04);\n border-left: 3px solid rgba(255, 255, 255, 0.2);\n overflow: hidden;\n }\n\n .analysis-findings .is-high { border-left-color: #ff6b6b; }\n .analysis-findings .is-medium { border-left-color: #ffc857; }\n .analysis-findings .is-low { border-left-color: #74c0fc; }\n\n /* The whole row is the control, so the target is the line rather than a\n chevron somebody has to aim at. */\n .finding-head {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 10px 13px;\n font-family: inherit;\n text-align: left;\n background: none;\n border: 0;\n cursor: pointer;\n transition: background 0.15s ease;\n ',"\n }\n\n .finding-head:hover { background: rgba(255, 255, 255, 0.05); }\n\n .finding-head b { flex: 1 1 auto; font-size: 13px; font-weight: 650; color: white; }\n .finding-head em {\n flex: 0 0 auto;\n font-style: normal; font-size: 9.5px; font-weight: 700; letter-spacing: 0.08em;\n text-transform: uppercase; color: rgba(255, 255, 255, 0.42);\n }\n\n .finding-chevron {\n flex: 0 0 auto;\n width: 7px;\n height: 7px;\n border: solid rgba(255, 255, 255, 0.5);\n border-width: 0 1.5px 1.5px 0;\n transform: translateY(-2px) rotate(45deg);\n transition: transform 0.18s ease;\n }\n\n /* Anchored to the finding's own li. Left as a bare .is-open it also matched\n the section wrapper once Analysis became foldable, so opening the section\n turned every chevron upward while its finding was still shut. */\n .analysis-findings li.is-open .finding-chevron { transform: translateY(1px) rotate(-135deg); }\n\n .finding-more {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 13px 12px;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .finding-head, .finding-chevron { transition: none; }\n }\n\n /* Three levels, not one. Everything used to be the same near-white, so the\n reasoning, the instruction and the title all carried equal weight and the\n eye had nowhere to land. The why recedes; the what-to-do steps forward\n behind an arrow in the finding's own severity colour. */\n .finding-detail {\n font-size: 12px;\n line-height: 1.55;\n color: rgba(255, 255, 255, 0.52);\n }\n\n .finding-action {\n position: relative;\n padding-left: 22px;\n font-size: 12.5px;\n line-height: 1.55;\n font-weight: 500;\n color: #ffffff;\n }\n\n .finding-action::before {\n content: '→';\n position: absolute;\n left: 2px;\n top: 0;\n font-size: 13px;\n color: rgba(255, 255, 255, 0.45);\n }\n\n .is-high .finding-action::before { color: #ff6b6b; }\n .is-medium .finding-action::before { color: #ffc857; }\n .is-low .finding-action::before { color: #74c0fc; }\n\n /* Marked as ours, deliberately. A recommendation from the people who wrote the\n analysis should say so rather than read as neutral advice. */\n .finding-plugin {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n align-self: flex-start;\n margin-top: 4px;\n padding: 5px 11px 5px 6px;\n border-radius: 8px;\n font-size: 12px;\n font-weight: 600;\n text-decoration: none;\n color: white;\n background: rgba(255, 255, 255, 0.08);\n border: 1px solid rgba(255, 255, 255, 0.16);\n ",'\n }\n\n .finding-plugin img { width: 20px; height: 20px; border-radius: 5px; }\n .finding-plugin i {\n font-style: normal; font-size: 9.5px; font-weight: 700; letter-spacing: 0.06em;\n text-transform: uppercase; color: rgba(255, 255, 255, 0.45);\n }\n\n .analysis-clean { margin: 0; font-size: 12.5px; color: rgba(255, 255, 255, 0.62); }\n\n /* Whichever of the disclosure, the steps, the error and the result are on\n screen, they space themselves the same way — and the first of them carries\n no top margin, so it can\'t collapse out through .analysis-body and push the\n whole section away from the rule above it.\n\n That is what made Analysis sit 16px under its rule while every other section\n sat at 14px: a margin on a first child, escaping its parent. */\n .analysis-body > * { margin-top: 0; }\n .analysis-body > * + * { margin-top: 14px; }\n\n /* Four headings each carrying a "Show tests" / "Show all values" / "Show log"\n pill was four different labels for one idea, and they read as actions rather\n than as state. A caret says foldable, says which way it is folded, and needs\n no words. */\n .section-head.is-toggle {\n position: relative;\n cursor: pointer;\n }\n\n /* The action and the caret travel together at the right end, and sit above the\n stretched hit area below — otherwise the row\'s own click handler would\n swallow the button and fold the section instead of running it. */\n .head-right {\n position: relative;\n z-index: 1;\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n /* NekoButton carries a 5px sibling margin for loose layouts; here the flex gap\n already spaces it, and the margin only pushes it off the right edge. */\n .head-right .neko-button { margin: 0; }\n\n .section-head.is-toggle h3 button {\n font: inherit;\n letter-spacing: inherit;\n text-transform: inherit;\n color: inherit;\n background: none;\n border: 0;\n padding: 0;\n margin: 0;\n cursor: pointer;\n transition: color 0.15s ease;\n ',"\n }\n\n /* The whole heading row is the target, without putting an

inside a\n