:root{ --gr:{{C_GROUND}};--sa:{{C_SURFACE_ALT}}; --ik:{{C_INK}};--im:{{C_INK_MUTED}};--if:{{C_INK_FAINT}}; --ac:{{C_ACCENT}};--ai:{{C_ACCENT_INK}};--at:{{C_ACCENT_TEXT}};--aw:{{C_ACCENT_WASH}}; --ct:{{C_CRIT}};--ctt:{{C_CRIT_TEXT}};--cw:{{C_CRIT_WASH}}; --dot:.55} @media(prefers-color-scheme:dark){:root{ --gr:{{C_D_GROUND}};--sa:{{C_D_SURFACE_ALT}}; --ik:{{C_D_INK}};--im:{{C_D_INK_MUTED}};--if:{{C_D_INK_FAINT}}; --ac:{{C_D_ACCENT}};--ai:{{C_D_ACCENT_INK}};--at:{{C_D_ACCENT_TEXT}};--aw:{{C_D_ACCENT_WASH}}; --ct:{{C_D_CRIT}};--ctt:{{C_D_CRIT_TEXT}};--cw:{{C_D_CRIT_WASH}};--dot:.40}} html[data-force-scheme=light]{ --gr:{{C_GROUND}};--sa:{{C_SURFACE_ALT}}; --ik:{{C_INK}};--im:{{C_INK_MUTED}};--if:{{C_INK_FAINT}}; --ac:{{C_ACCENT}};--ai:{{C_ACCENT_INK}};--at:{{C_ACCENT_TEXT}};--aw:{{C_ACCENT_WASH}}; --ct:{{C_CRIT}};--ctt:{{C_CRIT_TEXT}};--cw:{{C_CRIT_WASH}};--dot:.55} html[data-force-scheme=dark]{ --gr:{{C_D_GROUND}};--sa:{{C_D_SURFACE_ALT}}; --ik:{{C_D_INK}};--im:{{C_D_INK_MUTED}};--if:{{C_D_INK_FAINT}}; --ac:{{C_D_ACCENT}};--ai:{{C_D_ACCENT_INK}};--at:{{C_D_ACCENT_TEXT}};--aw:{{C_D_ACCENT_WASH}}; --ct:{{C_D_CRIT}};--ctt:{{C_D_CRIT_TEXT}};--cw:{{C_D_CRIT_WASH}};--dot:.40} *{box-sizing:border-box} html{font-size:16px} @media(min-width:600px){html{font-size:20px}} /* Only the horizontal axis is clamped. The seal's rings scale past their own box and would otherwise give the page a horizontal scrollbar -- but the vertical axis has to stay scrollable, because the change-password state adds two more fields and a message box and grows taller than the viewport. Clamping both axes silently cuts that state off. */ html,body{overflow-x:hidden!important} body{margin:0;min-height:100vh;background:var(--gr);color:var(--ik); font:1rem/1.7 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; -webkit-font-smoothing:antialiased} /* A 1.4px dot at 24px spacing is texture, not a brand surface. Fixed, so the drifting field never adds scroll height. */ .field{position:fixed;inset:0;pointer-events:none;opacity:var(--dot); background-image:radial-gradient(circle,var(--aw) 1.4px,transparent 1.5px); background-size:1.2rem 1.2rem;animation:dr 24s linear infinite} @keyframes dr{0%{background-position:0 0}100%{background-position:6rem 6rem}} /* Kept deliberately shallow. A ring doubling in size reads as an alarm, which is not what a seal beside a sign-in form is for. */ @keyframes pu{0%{transform:scale(.96);opacity:.32}70%,100%{transform:scale(1.3);opacity:0}} /* .loginscreen_logo is the wrapper the stock file uses. Keeping the class name means the markup below still looks like the page PAN-OS shipped. */ .loginscreen_logo{position:relative;min-height:100vh;width:100%;max-width:40rem; margin:0 auto;padding:clamp(1.8rem,7vh,3.4rem) 1.5rem;display:grid; grid-template-columns:1fr 11rem;gap:2.4rem;align-items:center} .col{min-width:0} /* The rings do not carry a tone here: a portal login page has no verdict to report, so the seal is brand, not severity. */ .seal{position:relative;display:grid;place-items:center;height:11rem;overflow:hidden} .seal .r{position:absolute;width:7.2rem;height:7.2rem;border-radius:50%; border:1px solid var(--at);animation:pu 7.5s ease-out infinite} .seal .r:nth-child(2){animation-delay:2.5s} .seal .r:nth-child(3){animation-delay:5s} .seal .d{position:relative;width:6.6rem;height:6.6rem;border-radius:50%;background:var(--sa); border:1px solid var(--aw);color:var(--at);display:grid;place-items:center} .seal .d svg{width:3.2rem;height:3.2rem} /* The mark, twice. An SVG behind url() renders as an isolated document -- currentColor is dead in there and these custom properties are out of scope -- so changing scheme means changing the whole asset. Doing that here rather than inside the SVG is what lets data-force-scheme reach it, which is how one machine previews both schemes. Each copy is written once and selected through --lg; naming them in all four rule sets would carry the artwork four times. */ :root{--lgl:url("data:image/svg+xml,{{PORTAL_LOGO_LIGHT}}"); --lgd:url("data:image/svg+xml,{{PORTAL_LOGO_DARK}}");--lg:var(--lgl)} @media(prefers-color-scheme:dark){:root{--lg:var(--lgd)}} html[data-force-scheme=light]{--lg:var(--lgl)} html[data-force-scheme=dark]{--lg:var(--lgd)} /* The lockup: the mark painted by the stylesheet, the name beside it as live text. Splitting them is what makes a rename in config reach this page -- an SVG cannot measure text, so a name drawn into fixed artwork is a name that gets clipped or shrunk. It also means only the mark needs a copy per scheme; the text takes its colour from the cascade, inherited rather than stated, because the one theme that stands this on the accent band has already set it. Painting from CSS also puts it on screen at first paint: the body ships no img and the logo variable is empty, so nothing here waits on jQuery. */ #logo{height:2.2rem;margin:0 0 1.4rem;display:flex;align-items:center;gap:.55rem; font-size:1.02rem;font-weight:700;letter-spacing:-.01em;color:inherit} #logo .mk{width:1.9rem;height:1.9rem;flex:none; background:var(--lg) center/contain no-repeat} #heading{margin:0 0 .4rem;font-size:1.75rem;line-height:1.08;letter-spacing:-.04em; font-weight:700;color:var(--ik);text-align:left;font-family:inherit} /* The portal name, carried in markup because gp_portal_name is applied with .html() and would replace the switch spans beside it. */ #heading .pn{display:block;margin:0 0 .25rem;font-size:.6rem;font-weight:700; letter-spacing:.14em;text-transform:uppercase;color:var(--at)} .gloss{margin:0 0 1.5rem;color:var(--im);font-size:.9rem;line-height:1.65} /* Below: the markup the form token expands into. The ids are PAN-OS', not ours -- loadPage() and submitClicked() look them up by id, so they are restyled in place. The token is named in prose, never written literally outside the body: PAN-OS substitutes the first occurrence in the file and does not care that it is inside a comment. */ #formdiv{margin:0} #taLogin{margin:0;padding:0;width:auto;min-height:0;background:none;border:0; box-shadow:none;text-align:left} .login_fields>div{width:auto;margin:0 0 .8rem} #formdiv input[type=text],#formdiv input[type=password]{ display:block;width:100%;min-height:2.8rem;padding:.6rem .85rem; font:inherit;font-size:.88rem;color:var(--ik);background:var(--sa); border:1px solid var(--sa);border-radius:.45rem;-webkit-appearance:none;appearance:none; transition:border-color .15s ease,box-shadow .15s ease} #formdiv input[type=text]:focus,#formdiv input[type=password]:focus{ outline:0;border-color:var(--at);box-shadow:0 0 0 3px var(--aw)} #formdiv input::placeholder{color:var(--if);opacity:1} #submitbutton{margin:1.2rem 0 0} #formdiv .buttonFixed{display:block;width:100%;min-height:2.8rem;margin:0; padding:.7rem 1.5rem;font:inherit;font-size:.88rem;font-weight:650; background:var(--ac);color:var(--ai);border:1px solid transparent;border-radius:.45rem; cursor:pointer;-webkit-appearance:none;appearance:none; box-shadow:0 .4rem 1.1rem rgba(0,0,0,.18)} #formdiv .buttonFixed:hover{filter:brightness(1.08)} #dError,.error_msg{margin:1.1rem 0 0;padding:.75rem .85rem;border-radius:.5rem; font:inherit;font-size:.78rem;line-height:1.55;color:var(--ik);background:var(--cw); border-left:3px solid var(--ct);list-style:none} /* loadPage() writes the failure text as an
  • with no list around it. */ #dError li{display:block;list-style:none} #dInputStr{margin:0 0 .8rem;font-size:.84rem;color:var(--im)} /* The two wrappers, listed by id. PAN-OS puts class="msg" on the inner div as well, so a `#dChangePasswordMsgArea .msg` reset matches the message box too -- and at one id plus one class it outranks the `#dChangePasswordMsg` rule below, stripping the padding and the background off the box that actually shows the text. Only the change-password state renders any of this, so nothing catches it until a password expires. */ #dChangePasswordMsgArea,#dChangePasswordMsgBorder{max-width:none;margin:1.1rem 0 0;padding:0;background:none} #dChangePasswordMsg{padding:.75rem .85rem;border-radius:.5rem;font-size:.78rem; background:var(--sa);border-left:3px solid var(--at)} /* --- getsoftwarepage.esp ------------------------------------------------ The portal serves this same imported file for the agent-download page and swaps the form token for a different form. One file, two contexts, so the copy switches with them: .pl shows on the login page, .ps on the download page. Default is login -- if the detection script never runs, the page degrades to the login wording rather than to nothing. */ .ps{display:none} html[data-page=sw] .pl{display:none} html[data-page=sw] .ps{display:inline} /* The injected markup is 2005-era table layout with align/valign/width attributes and an inline padding-top on the outer table. Inline styles outrank a stylesheet, which is why the width, height and padding resets carry !important -- the rest is beating presentational attributes, which do not. */ html[data-page=sw] table{width:100%!important;height:auto!important; padding-top:0!important;border-collapse:collapse} html[data-page=sw] td{padding:0;text-align:left;vertical-align:top} #taGetSofewarePage p{margin:0 0 .5rem} /* PAN-OS' own anchors, styled where they stand. All three weighted equally on purpose: this rule set is the fallback, shown whenever the script below has not replaced it, and a primary chosen here would have to be chosen by DOM position -- PAN-OS lists Windows 32-bit first, so the accent would steer people onto a 32-bit build. The anchors carry no class; p > a is the only anchor shape in this form. */ #taGetSofewarePage p a{display:block;min-height:2.8rem;padding:.7rem 1rem; font-size:.88rem;font-weight:650;text-align:center;text-decoration:none; border-radius:.45rem;background:var(--sa);color:var(--at);border:1px solid var(--sa)} #taGetSofewarePage p a:hover{filter:brightness(.97)} /* --- the download button --- Everything above is the page PAN-OS builds: three equal links over three rows explaining which one to take, i.e. a decision the browser has already made. The script at the end of the body reads the platform out of the user agent, MOVES those anchors into the menu below -- moved, never rebuilt, so no href is ever retyped and no markup is ever built in JS -- and puts the matching one on this button. None of this is visible until that script sets data-dl on the document, which it does last. #dl ships hidden, so a thrown exception, a blocked script or a platform nobody recognised leaves the working stock links in place rather than an empty card. */ html[data-dl] #taGetSofewarePage p, html[data-dl] #taGetSofewarePage table{display:none} .dl[hidden]{display:none} /* One-pixel gap rather than a border between the halves: they share a colour, so a border would need its own contrasting value in every palette. */ .dlrow{display:flex;gap:1px} .dlmain{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:.5rem; min-height:2.8rem;padding:.7rem 1.1rem;font-size:.88rem;font-weight:650;text-decoration:none; cursor:pointer;background:var(--ac);color:var(--ai);border:1px solid transparent; border-radius:.45rem 0 0 .45rem;box-shadow:0 .4rem 1.1rem rgba(0,0,0,.18);} .dlmain svg{width:1.05rem;height:1.05rem;flex:none} .dlcar{display:inline-flex;align-items:center;justify-content:center;width:2.6rem; min-height:2.8rem;padding:0;font:inherit;cursor:pointer; background:var(--ac);color:var(--ai);border:1px solid transparent; border-radius:0 .45rem .45rem 0;box-shadow:0 .4rem 1.1rem rgba(0,0,0,.18);-webkit-appearance:none;appearance:none} .dlcar svg{width:1.1rem;height:1.1rem} .dlmain:hover,.dlcar:hover{filter:brightness(1.08)} .dlcar[aria-expanded=true] svg{transform:rotate(180deg)} /* The focus rule at the foot of this file covers a and input. The caret is a button, so it needs its own. */ .dlcar:focus-visible{outline:3px solid var(--at);outline-offset:3px} .dlmenu{margin:.4rem 0 0;padding:.3rem;border-radius:.45rem;border:1px solid var(--sa); background:var(--gr)} .dlmenu[hidden]{display:none} .dlitem{display:block;padding:.55rem .7rem;border-radius:.35rem;font-size:.82rem; font-weight:650;color:var(--ik);text-decoration:none} .dlitem:hover{background:var(--sa)} /* The description rows are styled for that same fallback: three sentences restating which build suits which OS, which is exactly what the button below answers. Hidden with the links once the button is up. #dFormat is empty in every capture. */ #taGetSofewarePage table{margin:1rem 0 0} #taGetSofewarePage table td{padding:.25rem 0;font-size:.74rem;line-height:1.5;color:var(--if)} #dFormat{display:none} .note{margin:1.4rem 0 0;font-size:.76rem;color:var(--if);line-height:1.6} .note a{color:var(--at)} a:focus-visible,input:focus-visible{outline:3px solid var(--at);outline-offset:3px} @media(max-width:600px){ .loginscreen_logo{grid-template-columns:1fr;gap:1.6rem;padding:1.75rem 1.15rem 2.5rem; align-items:start} #heading{font-size:1.55rem} /* justify-self on the seal, NOT justify-items inside it. The rings are absolutely positioned, so justify-items moved their static position to the left edge while the disc kept its own width -- the two shared a left edge and so had different centres. */ .seal{height:6.6rem;width:6.6rem;order:-1;justify-self:start} .seal .r{width:4.8rem;height:4.8rem} .seal .d{width:4.2rem;height:4.2rem} .seal .d svg{width:2.1rem;height:2.1rem}} @media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}} html[data-gp=logout]{ --gr:{{C_GROUND}};--sa:{{C_SURFACE_ALT}}; --ik:{{C_INK}};--im:{{C_INK_MUTED}};--if:{{C_INK_FAINT}}; --ac:{{C_ACCENT}};--ai:{{C_ACCENT_INK}};--at:{{C_ACCENT_TEXT}};--aw:{{C_ACCENT_WASH}}; --dot:.55;font-size:16px;overflow-x:hidden} @media(prefers-color-scheme:dark){html[data-gp=logout]{ --gr:{{C_D_GROUND}};--sa:{{C_D_SURFACE_ALT}}; --ik:{{C_D_INK}};--im:{{C_D_INK_MUTED}};--if:{{C_D_INK_FAINT}}; --ac:{{C_D_ACCENT}};--ai:{{C_D_ACCENT_INK}};--at:{{C_D_ACCENT_TEXT}};--aw:{{C_D_ACCENT_WASH}}; --dot:.40}} html[data-gp=logout][data-force-scheme=light]{ --gr:{{C_GROUND}};--sa:{{C_SURFACE_ALT}}; --ik:{{C_INK}};--im:{{C_INK_MUTED}};--if:{{C_INK_FAINT}}; --ac:{{C_ACCENT}};--ai:{{C_ACCENT_INK}};--at:{{C_ACCENT_TEXT}};--aw:{{C_ACCENT_WASH}};--dot:.55} html[data-gp=logout][data-force-scheme=dark]{ --gr:{{C_D_GROUND}};--sa:{{C_D_SURFACE_ALT}}; --ik:{{C_D_INK}};--im:{{C_D_INK_MUTED}};--if:{{C_D_INK_FAINT}}; --ac:{{C_D_ACCENT}};--ai:{{C_D_ACCENT_INK}};--at:{{C_D_ACCENT_TEXT}};--aw:{{C_D_ACCENT_WASH}};--dot:.40} html[data-gp=logout] *{box-sizing:border-box} @media(min-width:600px){html[data-gp=logout]{font-size:20px}} /* The body is the centring container here. On the login page that job belonged to .loginscreen_logo, but this page has no wrapper to spare -- that element is needed for the card itself. */ html[data-gp=logout] body{margin:0;min-height:100vh;display:flex;align-items:center; justify-content:center;padding:clamp(1.8rem,7vh,3.4rem) 1.5rem; background:var(--gr);color:var(--ik); font:1rem/1.7 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; -webkit-font-smoothing:antialiased} /* The dot field as a pseudo-element. The login page uses a real .field div, but no markup can be added to this page and body::before paints the same texture. position:fixed keeps it out of the scroll box. */ html[data-gp=logout] body::before{content:"";position:fixed;inset:0;pointer-events:none; opacity:var(--dot);background-image:radial-gradient(circle,var(--aw) 1.4px,transparent 1.5px); background-size:1.2rem 1.2rem;animation:dr 24s linear infinite} @keyframes dr{0%{background-position:0 0}100%{background-position:6rem 6rem}} @keyframes pu{0%{transform:scale(1);opacity:.38}70%,100%{transform:scale(1.14);opacity:0}} /* .loginscreen_logo is the card, and it is also the seal: the rings the login page draws around a disc are drawn around the card instead, since there is no element left to hang a seal on. z-index:-1 puts them behind the card's own background, so only the part that has travelled past the edge is visible -- which is the whole effect, and it needs no clipping. Clipping is not available here: overflow:hidden on this element would have to be written as a html[data-gp=logout] rule, and that is the rule that cuts the login page's change-password state off. */ html[data-gp=logout] .loginscreen_logo{position:relative;width:100%;max-width:24rem; margin:0;padding:2rem 1.8rem 1.8rem;text-align:left;border-radius:.75rem; border:1px solid var(--sa);background:var(--gr); box-shadow:0 1.2rem 3rem rgba(0,0,0,.14)} html[data-gp=logout] .loginscreen_logo::before,html[data-gp=logout] .loginscreen_logo::after{ content:"";position:absolute;inset:0;z-index:-1;border-radius:.75rem; border:1px solid var(--at);pointer-events:none;animation:pu 7.5s ease-out infinite} html[data-gp=logout] .loginscreen_logo::after{animation-delay:3.75s} /* The logo is painted by CSS, not by the logo variable, and the img PAN-OS wrote is hidden outright. PAN-OS hard-codes its own mark into this body and its jQuery handler only rewrites that src at ready. The browser has already fetched and painted the Palo Alto logo by then, so the page visibly flashes the wrong one before settling on ours. On the login page that was avoidable -- we own that body and ship the img with no src -- but here the body is PAN-OS'. A stylesheet applies at first paint, so moving the artwork into CSS removes the flash entirely. The img is display:none rather than swapped, so its src never matters, whatever jQuery does to it later. The logo variable stays set even though this rule supersedes it: the same variable brands the portal home page, which this file does not restyle. That page gets the light copy and only the light copy -- it is an img src, and an img has no way to be told which scheme it is in. Its Bootstrap chrome is light either way, so that is the right copy. */ html[data-gp=logout]{--lgl:url("data:image/svg+xml,{{PORTAL_LOGO_LIGHT}}"); --lgd:url("data:image/svg+xml,{{PORTAL_LOGO_DARK}}");--lg:var(--lgl)} @media(prefers-color-scheme:dark){html[data-gp=logout]{--lg:var(--lgd)}} html[data-gp=logout][data-force-scheme=light]{--lg:var(--lgl)} html[data-gp=logout][data-force-scheme=dark]{--lg:var(--lgd)} /* PAN-OS owns this body, so the name cannot be put beside the mark as markup. ::before carries the artwork and ::after the text -- both on our own stylesheet, which is the only way in. The img PAN-OS wrote is hidden rather than swapped, so whatever its src ends up being never matters. */ html[data-gp=logout] #logo{height:2.2rem;margin:0 0 1.4rem;display:flex;align-items:center;gap:.55rem; font-size:1.02rem;font-weight:700;letter-spacing:-.01em} html[data-gp=logout] #logo::before{content:"";width:1.9rem;height:1.9rem;flex:none; background:var(--lg) center/contain no-repeat} html[data-gp=logout] #logo::after{content:"{{COMPANY_CSS}}"} html[data-gp=logout] #logo img{display:none} html[data-gp=logout] #heading{margin:0 0 1.1rem;font-size:1.75rem;line-height:1.08; letter-spacing:-.04em;font-weight:700;color:var(--ik);text-align:left;font-family:inherit} html[data-gp=logout] #formdiv{margin:0} /* The message div. PAN-OS fills it from logout_text_array at ready, so it is empty at parse time -- min-height stops the card resizing when text lands. */ html[data-gp=logout] #logout{min-height:2.6rem;margin:0 0 1.4rem;padding:.75rem .85rem; width:auto;font-family:inherit;font-size:.88rem;font-weight:400;line-height:1.6; text-align:left;color:var(--ik);background:var(--sa);border-left:3px solid var(--at)} html[data-gp=logout] #taLogout{margin:0;padding:0;width:auto;min-height:0; background:none;border:0;box-shadow:none;text-align:left} html[data-gp=logout] .buttonFixed-logout{display:block;width:100%;min-height:2.8rem; margin:0;padding:.7rem 1.5rem;font:inherit;font-size:.88rem;font-weight:650; background:var(--ac);color:var(--ai);border:1px solid transparent;border-radius:.45rem; cursor:pointer;-webkit-appearance:none;appearance:none;text-transform:none; box-shadow:0 .4rem 1.1rem rgba(0,0,0,.18)} html[data-gp=logout] .buttonFixed-logout:hover{filter:brightness(1.08)} html[data-gp=logout] .buttonFixed-logout:focus-visible{outline:3px solid var(--at); outline-offset:3px} @media(max-width:600px){ html[data-gp=logout] body{padding:1.75rem 1.15rem;align-items:flex-start} html[data-gp=logout] .loginscreen_logo{padding:1.6rem 1.3rem 1.5rem} html[data-gp=logout] #heading{font-size:1.55rem}} @media(prefers-reduced-motion:reduce){ html[data-gp=logout] *{animation:none!important;transition:none!important}}