PageSourceSearch

https://aamoney.netlify.app/.netlify/scripts/hud?variant=public

js aamoney.netlify.app collected 2026-10-03 10:23:48 UTC 34,481 bytes, 553 lines download raw bytes

1/* Netlify HUD — the badge and owner toolbar Netlify injects on published sites.
2   Built inside a srcdoc iframe from the data-* attributes on the injected tag. */
3(() => {
4  "use strict";
5
6  const script =
7    document.currentScript || document.querySelector("script[data-nf-variant]");
8  if (!script) return;
9
10  const ds = script.dataset;
11
12  const warn = (msg) =>
13    console.warn("[netlify-hud] " + msg + "; not rendering");
14
15  const raw = ds.nfVariant;
16  if (!raw) return;
17  const variant = raw === "public" || raw === "owner-private" ? raw : null;
18  if (!variant) return warn("unknown variant: " + raw);
19
20  const APP_HOSTS = ["app.netlify.com", "app.netlifystg.com"];
21  const APP_HOST = APP_HOSTS.indexOf(ds.appHost || "") !== -1 ? ds.appHost : "";
22  const SITE_ID =
23    /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
24      ds.netlifySiteId || ""
25    )
26      ? ds.netlifySiteId
27      : "";
28
29  if (!APP_HOST) return warn("missing or invalid data-app-host");
30  if (variant === "owner-private" && !SITE_ID)
31    return warn("missing or invalid data-netlify-site-id");
32
33  // Underscore, though the acquisition standard's per-feature table spells this
34  // netlify-badge. Its formatting rule allows underscores with a reason, and the reason
35  // is series continuity: this is the highest-volume owned-loop surface, and renaming it
36  // splits every existing report at the cutover.
37  // Notion: "Marketing Channel Classification - Review", 3cd1da88359a80aabee7f7ddbc8c490a.
38  const UTM_SOURCE = "netlify_badge";
39
40  // Baked at deploy from BRANCH (see netlify.toml): the canary branch, or blank on production/local.
41  // Reported as hud_variant. utm_campaign is the program label under the acquisition
42  // standard and stays `loops` on every deploy, canary included.
43  let HUD_VARIANT = "main";
44  if (HUD_VARIANT === "main" || !/^[a-z0-9-]+$/.test(HUD_VARIANT)) HUD_VARIANT = "";
45
46  if (
47    document.getElementById("nl-hud-frame") ||
48    document.getElementById("nl-badge-frame")
49  )
50    return;
51
52  /* ————— Dismissal, remembered per site and per device ————— */
53  const STORE_KEY = "nl-hud:" + variant + ":v1";
54  const readState = () => {
55    try {
56      return localStorage.getItem(STORE_KEY) || "";
57    } catch (e) {
58      return "";
59    }
60  };
61  const writeState = (value) => {
62    try {
63      if (value) localStorage.setItem(STORE_KEY, value);
64      else localStorage.removeItem(STORE_KEY);
65    } catch (e) {
66    }
67  };
68
69  const saved = readState();
70  if (saved === "hidden") return;
71  const startMinimized = variant === "owner-private" && saved === "mini";
72
73  const overview = (query) =>
74    `https://${APP_HOST}/projects/${SITE_ID}/overview?${query}`;
75  const hudLink = (query, action) =>
76    overview(query + "&from_hud=private" + (HUD_VARIANT ? "," + HUD_VARIANT : "") + "&hud_action=" + action);
77  const promptLink = (id) => hudLink("promptTemplate=" + encodeURIComponent(id), "ar_task");
78
79  /* ————— Shared bits ————— */
80  const LOGO = (
81    s
82  ) => `<svg width="${s}" height="${s}" viewBox="0 0 32 32" fill="none" aria-hidden="true">
83    <path fill="#fff" d="M17.9922 18.3225H14.005L13.6746 17.9921V14.005L14.005 13.6746H17.9922L18.3226 14.005V17.9921L17.9922 18.3225Z"/>
84    <g fill="#05BDBA">
85      <path d="M9.6958 24.3311H9.35705L7.6661 22.6401V22.3041L10.2511 19.7164L12.042 19.7191L12.2808 19.9552V21.7461L9.6958 24.3311Z"/>
86      <path d="M7.6661 9.69576V9.35701L9.35705 7.66606H9.6958L12.2808 10.2511V12.0392L12.042 12.2808H10.2511L7.6661 9.69576Z"/>
87      <path d="M10.0457 17.4369H0.205466L0 17.2314V14.7658L0.205466 14.5603H10.0457L10.2511 14.7658V17.2314L10.0457 17.4369Z"/>
88      <path d="M31.7945 17.4369H21.9544L21.7489 17.2314V14.7658L21.9544 14.5603H31.7945L32 14.7658V17.2314L31.7945 17.4369Z"/>
89      <path d="M14.5631 10.0456V2.66547L14.7686 2.46001H17.2343L17.4396 2.66547V10.0456L17.2343 10.2511H14.7686L14.5631 10.0456Z"/>
90      <path d="M14.5631 29.3316V21.9515L14.7686 21.746H17.2343L17.4396 21.9515V29.3316L17.2343 29.5371H14.7686L14.5631 29.3316Z"/>
91    </g></svg>`;
92  const AI_SVG = (s) =>
93    `<svg width="${s}" height="${s}" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M5.68 9.925 4.607 8.85l-.013-.379 1.1-1.1.38.012 1.075 1.075.012.379-1.1 1.1zM9.96 9.925 8.886 8.85l-.013-.379 1.1-1.1.38.012 1.075 1.075.012.379-1.1 1.1z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M9.005 2.52v1.296h2.657a3.19 3.19 0 0 1 3.191 3.192v3.356a3.19 3.19 0 0 1-3.191 3.191H4.338a3.19 3.19 0 0 1-3.191-3.19V7.007a3.19 3.19 0 0 1 3.191-3.192h2.836V2.52l.229-.245h1.373zM4.338 5.316c-.934 0-1.691.758-1.691 1.692v3.356c0 .934.758 1.691 1.691 1.691h7.324c.933 0 1.69-.757 1.691-1.69V7.007c0-.934-.757-1.692-1.691-1.692z"/></svg>`;
94  const SHARE_SVG = `<svg width="15" height="15" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.5636 8C6.94548 8.31953 6.24382 8.5 5.5 8.5C4.75618 8.5 4.05452 8.31953 3.4364 8H1.5L0.5 9V13
94L1.5 14H9.5L10.5 13V9L9.5 8H7.5636ZM8.87868 9.5H7.90075C7.16414 9.82186 6.35125 10 5.5 10C4.64875 10 3.83586 9.82186 3.09925 9.5H2.12132L2 9.62132V12.3787L2.12132 12.5H8.87868L9 12.3787V9.62132L8.87868 9.5Z"/><path d="M13.9999 10.1213V11.8463L12.2043 11.8481L12.0039 12.0357V13.1606L12.2043 13.3481H13.7955L13.7975 13.3463H14.7024L15.4999 12.5V9.5L14.5001 8.5001L12.2043 8.49976L12.0039 8.68726V9.81225L12.2043 9.99976L13.8786 10L13.9999 10.1213Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M5.5 7C6.88071 7 8 5.88071 8 4.5C8 3.11929 6.88071 2 5.5 2C4.11929 2 3 3.11929 3 4.5C3 5.88071 4.11929 7 5.5 7ZM5.5 5.5C6.05228 5.5 6.5 5.05228 6.5 4.5C6.5 3.94772 6.05228 3.5 5.5 3.5C4.94772 3.5 4.5 3.94772 4.5 4.5C4.5 5.05228 4.94772 5.5 5.5 5.5Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M13.7 5.1C13.7 6.25937 12.7594 7.2 11.6 7.2C10.4406 7.2 9.5 6.25937 9.5 5.1C9.5 3.94062 10.4406 3 11.6 3C12.7594 3 13.7 3.94062 13.7 5.1ZM11.6 5.7C11.9309 5.7 12.2 5.43095 12.2 5.1C12.2 4.76905 11.9309 4.5 11.6 4.5C11.2691 4.5 11 4.76905 11 5.1C11 5.43095 11.2691 5.7 11.6 5.7Z"/></svg>`;
95  const TOOLS_SVG = `<svg width="15" height="15" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.98407 9.625L8.58935 11.0952C8.62792 11.1889 8.71922 11.25 8.82053 11.25H9.77587C9.88268 11.25 9.95527 11.1415 9.91454 11.0428L7.3828 4.90468C7.3442 4.81107 7.25294 4.75 7.15169 4.75H6.34295C6.24163 4.75 6.15033 4.81115 6.11177 4.90484L3.58524 11.0429C3.54461 11.1416 3.61719 11.25 3.72395 11.25H4.67399C4.7753 11.25 4.86659 11.1889 4.90516 11.0952L5.51045 9.625H7.98407ZM7.46943 8.375L6.74726 6.62092L6.02508 8.375H7.46943Z"/><path d="M10.75 11.25C10.6119 11.25 10.5 11.1381 10.5 11V5C10.5 4.86193 10.6119 4.75 10.75 4.75H11.5C11.6381 4.75 11.75 4.86193 11.75 5V11C11.75 11.1381 11.6381 11.25 11.5 11.25H10.75Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M0 5.5C0 2.73858 2.23858 0.5 5 0.5H11C13.7614 0.5 16 2.73858 16 5.5V10.5C16 13.2614 13.7614 15.5 11 15.5H5C2.23858 15.5 0 13.2614 0 10.5V5.5ZM5 1.75H11C13.0711 1.75 14.75 3.42893 14.75 5.5V10.5C14.75 12.5711 13.0711 14.25 11 14.25H5C2.92893 14.25 1.25 12.5711 1.25 10.5V5.5C1.25 3.42893 2.92893 1.75 5 1.75Z"/></svg>`;
96  const LOCK_SVG = `<svg width="13" height="13" viewBox="0 0 16 16" fill="currentColor" fill-rule="evenodd" aria-hidden="true"><path d="M8 1.5A3.5 3.5 0 0 0 4.5 5v2H4A1.5 1.5 0 0 0 2.5 8.5v4A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5v-4A1.5 1.5 0 0 0 12 7h-.5V5A3.5 3.5 0 0 0 8 1.5zm2 5.5V5a2 2 0 1 0-4 0v2h4z"/></svg>`;
97  const LAUNCH_SVG = `<svg width="15" height="15" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M11.072 6C10.8557 5.99995 10.6443 5.93576 10.4644 5.81554C10.2846 5.69533 10.1444 5.52449 10.0617 5.32463C9.97895 5.12477 9.95733 4.90486 9.99956 4.69271C10.0418 4.48056 10.146 4.2857 10.299 4.13276C10.4519 3.97982 10.6468 3.87568 10.859 3.8335C11.0711 3.79132 11.291 3.81299 11.4909 3.89578C11.6907 3.97857 11.8615 4.11876 11.9817 4.29862C12.1019 4.47848 12.166 4.68994 12.166 4.90625C12.1658 5.19633 12.0505 5.47447 11.8454 5.67956C11.6402 5.88464 11.3621 5.9999 11.072 6Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M3.59279 4.5H6.77473L8.48034 2.74577L10.1619 1L14.4308 1.00686L15 1.59031V6.01763L11.5291 9.61732L11.78 12.4852L11.1608 13.7165L8.58695 15.2986L7.59043 14.7959L7.31674 11.6676L4.624 8.875H1.65598L1.06836 7.92609L2.42011 5.22418L3.59279 4.5ZM8.7673 11.037L8.96573 13.305L10.0122 12.6618L10.2486 12.1917L10.1476 11.037L8.7673 11.037ZM8.43037 10.6616L12.036 6.93056L13.5 5.41225V2.50537L10.7987 2.50103L10.0283 3.30089L5.69627 7.82603L8.43037 10.6616ZM5.27424 7.38834L5.35943 6H4.01862L3.57089 6.27649L3.02131 7.375H5.26138L5.27424 7.38834Z"/><path d="M2.05008 10.9935L3.79156 9.6874L4.81429 10.7954L3.17932 12.0216L2.95615 13.1374L4.07201 12.9143L5.28155 11.3016L6.31603 12.4222L5.10008 14.0435L4.64717 14.3289L2.14717 14.8289L1.26465 13.9464L1.76465 11.4464L2.05008 10.9935Z"/></svg>`;
98
99  const CHEVRON_SVG = (dir) => {
100    const paths =
101      dir === "left"
102        ? "M12 4.5 8.5 8 12 11.5M7.5 4.5 4 8 7.5 11.5"
103        : "M4 4.5 7.5 8 4 11.5M8.5 4.5 12 8 8.5 11.5";
104    return `<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="${paths}"/></svg>`;
105  };
106
107  const CLOSE_SVG = `<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" aria-hidden="true"><path d="M4.5 4.5 11.5 11.5M11.5 4.5 4.5 11.5"/></svg>`;
108
109  const ARROW_UP_SVG = `<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 12.75v-9.5M3.9 7.35 8 3.25l4.1 4.1"/></svg>`;
110
111  const FONT = `-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif`;
112
113  const PAD = { t: 8, r: 18, b: 18, l: 8 };
114  const INSET = PAD.l + PAD.r;
115
116  const VW = "var(--nl-vw,100vw)";
117  const VH = "var(--nl-vh,100vh)";
118
119  const NARROW = 600;
120
121  const rootClass = (extra) => {
122    const c = [];
123    if (window.innerWidth < NARROW) c.push("nl-narrow");
124    if (extra) c.push(extra);
125    return c.length ? ` class="${c.join(" ")}"` : "";
126  };
127
128  const BASE_CSS = `
129    :root{--nl-vw:${window.innerWidth}px;--nl-vh:${window.innerHeight}px}
130    *{box-sizing:border-box}
131    html,body{margin:0;padding:0;background:transparent;overflow:hidden}
132    body{display:flex;align-items:flex-end;justify-content:flex-end;height:100vh;
133      padding:${PAD.t}px ${PAD.r}px ${PAD.b}px ${PAD.l}px;
134      font-family:${FONT};font-size:14px;line-height:1.5;color:#f4f9f9;-webkit-font-smoothing:antialiased}
135    .nl-wrap{display:flex;flex-direction:column;align-items:flex-end;
135gap:10px;width:max-content;max-width:calc(${VW} - ${INSET}px)}
136    button{font-family:inherit;cursor:pointer}
137    :focus-visible{outline:2px solid #32e6e2;outline-offset:2px;border-radius:6px}
138    .nl-icon-btn{position:relative;flex:none;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;border-radius:8px;border:1px solid transparent;background:transparent;color:#d5e3e6;transition:background .15s ease,color .15s ease}
139    .nl-icon-btn:hover{background:rgba(255,255,255,0.08);color:#f4f9f9}
140    .nl-tap::after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
141      width:max(100%,44px);height:max(100%,44px)}
142    @keyframes nl-rise{from{opacity:0;transform:translateY(10px) scale(.97)}to{opacity:1;transform:none}}
143    @media (prefers-reduced-motion:reduce){*{animation-duration:.01ms!important;transition:none!important}}
144  `;
145
146  const MEASURE_JS = `
147    function nlSend(){
148      var wrap=document.querySelector('.nl-wrap'),boxes=[];
149      for(var i=0;i<wrap.children.length;i++){
150        var c=wrap.children[i];
151        if(!c.offsetWidth||!c.offsetHeight)continue;
152        boxes.push({x:c.offsetLeft-wrap.offsetLeft+${PAD.l},
153                    y:c.offsetTop-wrap.offsetTop+${PAD.t},
154                    w:c.offsetWidth,h:c.offsetHeight});
155      }
156      parent.postMessage({nlHud:'resize',
157        width:wrap.offsetWidth+${INSET},
158        height:wrap.offsetHeight+${PAD.t + PAD.b},
159        boxes:boxes},'*');
160    }
161    function nlSendSoon(){requestAnimationFrame(nlSend);}
162    window.addEventListener('load',nlSend);
163    window.addEventListener('message',function(e){
164      if(e.source!==parent||!e.data||e.data.nlHud!=='viewport')return;
165      var s=document.documentElement.style;
166      s.setProperty('--nl-vw',e.data.w+'px');
167      s.setProperty('--nl-vh',e.data.h+'px');
168      document.documentElement.classList.toggle('nl-narrow',e.data.w<${NARROW});
169      nlSendSoon();
170    });
171    var nlCoarse=window.matchMedia('(pointer: coarse)').matches;
172    function nlSubmit(form){
173      if(form.requestSubmit)return form.requestSubmit();
174      if(form.checkValidity&&!form.checkValidity())
175        return form.reportValidity&&form.reportValidity();
176      form.submit();
177    }
178  `;
179
180  /* ————— Public badge (+ "build your own" popover) ————— */
181  function publicDoc() {
182    return `<!doctype html><html${rootClass()}><head><meta charset="utf-8"><meta name="color-scheme" content="dark"><style>${BASE_CSS}
183      .nl-card{position:relative;display:none;width:min(340px,calc(${VW} - ${INSET}px));max-height:calc(${VH} - 84px);overflow-y:auto;background:linear-gradient(180deg,#132832 0%,#0e1e25 100%);border:1px solid rgba(255,255,255,0.09);border-radius:18px;padding:18px;transform-origin:bottom right;animation:nl-rise .3s cubic-bezier(.34,1.4,.4,1) both}
184      .nl-card.open{display:block}
185      .nl-card-close{position:absolute;top:10px;right:10px}
186      .nl-card h2{margin:0;padding-right:30px;font-size:16px;font-weight:750;letter-spacing:-0.015em;line-height:1.35}
187      .nl-card h2 em{font-style:normal;background:linear-gradient(90deg,#32e6e2,#4d7cfe,#9a6bff);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent}
188      .nl-card p{margin:8px 0 0;font-size:12.5px;color:#d5e3e6}
189      .nl-prompt{position:relative;margin-top:14px;background:#10222b;border:1px solid rgba(255,255,255,0.09);border-radius:14px;padding:10px 12px;transition:border-color .15s ease,box-shadow .15s ease}
190      .nl-prompt:focus-within{border-color:#05bdba;box-shadow:0 0 0 3px rgba(5,189,186,0.18)}
191      .nl-prompt textarea{display:block;width:100%;height:38px;resize:none;border:0;outline:none;background:transparent;color:#f4f9f9;font:inherit;font-size:13px;line-height:19px;padding:0 28px 0 0}
192      .nl-prompt textarea::placeholder{color:#a9bdc2}
193      .nl-send{position:absolute;right:9px;bottom:9px;width:24px;height:24px;flex:none;display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:50%;background:#05bdba;color:#02191b;transition:background .15s ease}
194      .nl-send:hover{background:#32e6e2}
195      @media (pointer: coarse){
196        .nl-prompt textarea{font-size:16px;line-height:21px;height:42px}
197      }
198      .nl-or{display:flex;align-items:center;gap:10px;margin:12px 0;font-size:10.5px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#a9bdc2}
199      .nl-or::before,.nl-or::after{content:"";flex:1;height:1px;background:rgba(255,255,255,0.09)}
200      .nl-drop{display:flex;align-items:center;justify-content:space-between;gap:6px;text-decoration:none;background:#1b3a45;border:1px solid rgba(255,255,255,0.09);border-radius:10px;padding:10px 12px;font-size:13px;font-weight:600;color:#cfe0e3;transition:background .15s ease,color .15s ease,border-color .15s ease}
201      .nl-drop:hover{background:#224753;color:#f4f9f9;border-color:rgba(50,230,226,0.3)}
202      .nl-drop .nl-arrow{transition:transform .18s cubic-bezier(.34,1.4,.4,1)}
203      .nl-drop:hover .nl-arrow{transform:translateX(3px)}
204      .nl-badge{display:flex;align-items:center;gap:9px;white-space:nowrap;border:1px solid rgba(255,255,255,0.09);background:linear-gradient(180deg,#142b34,#0e1e25);color:#f4f9f9;border-radius:999px;padding:9px 14px 9px 11px;font-size:13px;font-weight:600;letter-spacing:0.01em;transition:border-color .2s ease}
205      .nl-badge:hover{border-color:rgba(50,230,226,0.26)}
206      .nl-hide{display:block;width:100%;margin-top:10px;border:0;background:transparent;color:#a9bdc2;font-size:11.5px;font-weight:600;padding:4px;border-radius:8px;transition:color .15s ease}
207      .nl-hide:hover{color:#d5e3e6;text-decoration:underline}
208      .nl-badge-label{color:#d5e3e6;font-weight:500}
209      .nl-badge-brand{font-weight:750}
210    </style></head><body>
211      <div class="nl-wrap">
212        <section class="nl-card" id="nl-card" aria-label="Build your own site on Netlify">
213          <button type="button" class="nl-icon-btn nl-tap nl-card-close" id="nl-card-close" aria-label="Close">${CLOSE_SVG}</button>
214          <h2><em>Build your own site with Netlify</em></h2>
215          <p>Describe what you want, and an AI agent designs, builds and deploys it in minutes.</p>
216          <form class="nl-prompt" action="https://${APP_HOST}/start" method="get" target="_blank" rel="noopener">
217            <textarea name="prompt" rows="2" required maxlength="300" autocomplete="off"
218                      enterkeyhint="go"
219                      placeholder="A shop for my handmade surfboards&hellip;"
220                      aria-label="Describe the site you want to build"></textarea>
221            <button type="submit" class="nl-send nl-tap" aria-label="Build this site">${ARROW_UP_SVG}</button>
222            <input type="hidden" name="utm_source" value="${UTM_SOURCE}">
223            <input type="hidden" name="utm_campaign" value="loops">
224            <input type="hidden" name="utm_medium" value="owned">
225            <input type="hidden" name="utm_content" value="prompt">
226            ${SITE_ID ? `<input type="hidden" name="utm_id" value="${SITE_ID}">` : ""}
227            ${HUD_VARIANT ? `<input type="hidden" name="hud_variant" value="${HUD_VARIANT}">` : ""}
228          </form>
229          <div class="nl-or"><span>or</span></div>
230          <a class="nl-drop" href="https://${APP_HOST}/drop?utm_source=${UTM_SOURCE}&utm_campaign=loops&utm_medium=owned&utm_content=drop${SITE_ID ? "&utm_id=" + SITE_ID : ""}${HUD_VARIANT ? "&hud_variant=" + HUD_VARIANT : ""}" target="_blank" rel="noopener">
231            <span>Drop a site you already have</span><span class="nl-arrow" aria-hidden="true">&rarr;</span>
232          </a>
233          <button type="button" class="nl-hide" id="nl-hide">Hide this badge</button>
234        </section>
235        <button type="button" class="nl-badge" id="nl-badge" aria-expanded="false" aria-controls="nl-card"
236                aria-label="Powered by Netlify — build your own site">
237          ${LOGO(18)}
238          <span><span class="nl-badge-label">Powered by</span> <span class="nl-badge-brand">Netlify</span></span>
239        </button>
240      </div>
241      <script>
242        ${MEASURE_JS}
243        var badge=document.getElementById('nl-badge'),card=document.getElementById('nl-card'),
244            box=card.querySelector('textarea');
245        function setOpen(open){
246          card.classList.toggle('open',open);
247          badge.setAttribute('aria-expanded',open?'true':'false');
248          nlSendSoon();
249          if(open&&!nlCoarse)box.focus();
250        }
251        box.addEventListener('keydown',function(e){
252          if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();nlSubmit(box.form);}
253        });
254        badge.addEventListener('click',function(){setOpen(!card.classList.contains('open'));});
255        document.getElementById('nl-card-close').addEventListener('click',function(){setOpen(false);badge.focus();});
256        document.addEventListener('keydown',function(e){
257          if(e.key==='Escape'&&card.classList.contains('open')){setOpen(false);badge.focus();}
258        });
259        document.getElementById('nl-hide').addEventListener('click',function(){
260          parent.postMessage({nlHud:'state',value:'hidden'},'*');
261        });
262      <\/script>
263    </body></html>`;
264  }
265
266  /* ————— Owner-private HUD ————— */
267  function ownerPrivateDoc() {
268    const task = (title, desc, templateId) => `
269      <div class="nl-task">
270        <div class="nl-task-meta"><div class="nl-task-title">${title}</div><div class="nl-task-desc">${desc}</div></div>
271        <a class="nl-task-run" href="${promptLink(
272          templateId
273        )}" target="_blank" rel="noopener">${AI_SVG(12)} Run</a>
274      </div>`;
275
276    return `<!doctype html><html${rootClass(
277      startMinimized ? "nl-mini-state" : ""
278    )}><head><meta charset="utf-8"><meta name="color-scheme" content="dark"><style>${BASE_CSS}
279      .nl-popover{display:none;width:min(320px,calc(${VW} - ${INSET}px));max-height:calc(${VH} - 90px);overflow-y:auto;background:linear-gradient(180deg,#132832 0%,#0e1e25 100%);border:1px solid rgba(255,255,255,0.09);border-radius:18px;transform-origin:bottom right;animation:nl-rise .28s cubic-bezier(.34,1.4,.4,1) both}
280      .nl-popover.open{display:block}
281      .nl-popover-head{display:flex;align-items:center;gap:9px;padding:13px 12px 5px 16px}
282      .nl-popover-icon{display:inline-flex;color:#32e6e2}
283      .nl-popover-title{flex:1;font-size:13.5px;font-weight:700;letter-spacing:-0.01em;color:#f4f9f9}
284      .nl-popover-body{padding:0 16px 12px}
285      .nl-task{display:flex;align-items:center;gap:10px;padding:11px 0}
286      .nl-task+.nl-task{border-top:1px solid rgba(255,255,255,0.06)}
287      .nl-task-meta{flex:1;min-width:0}
288      .nl-task-title{font-size:13.5px;font-weight:650;color:#f4f9f9}
289      .nl-task-desc{font-size:11.5px;color:#a9bdc2}
290      .nl-task-run{flex:none;display:inline-flex;align-items:center;gap:6px;text-decoration:none;border:1px solid rgba(50,230,226,0.35);background:transparent;color:#32e6e2;font-size:12px;font-weight:700;border-radius:999px;padding:6px 13px;transition:background .15s ease,transform .15s cubic-bezier(.34,1.4,.4,1)}
291      .nl-task-run:hover{background:rgba(5,189,186,0.12);transform:translateY(-1px)}
292      .nl-bar,.nl-mini{-webkit-user-select:none;user-select:none}
293      .nl-bar{display:flex;align-items:center;gap:2px;white-space:nowrap;border:1px solid rgba(255,255,255,0.09);background:linear-gradient(180deg,#142b34,#0e1e25);border-radius:999px;padding:7px 8px 7px 14px;color:#f4f9f9}
294      .nl-logo{flex:none;margin-right:8px;display:inline-flex}
295      .nl-status{display:inline-flex;align-items:center;gap:5px;border:0;background:transparent;font-family:inherit;font-size:12.5px;font-weight:700;letter-spacing:0.01em;color:#fbbf24;padding:6px 5px;border-radius:8px;transition:background .15s ease}
296      .nl-status:hover,.nl-status[aria-expanded='true']{background:rgba(251,191,36,0.12)}
297      .nl-tip{display:none;align-self:flex-start;width:min(300px,calc(${VW} - ${INSET}px));background:linear-gradient(180deg,#132832 0%,#0e1e25 100%);border:1px solid rgba(255,255,255,0.09);border-radius:14px;padding:12px 14px;font-size:12px;line-height:1.5;color:#d5e3e6;animation:nl-rise .2s cubic-bezier(.34,1.4,.4,1) both}
298      .nl-tip.open{display:block}
299      .nl-tip a{display:flex;width:fit-content;align-items:center;gap:4px;margin-top:8px;color:#32e6e2;text-decoration:none;font-weight:650}
300      .nl-tip a:hover{text-decoration:underline}
301      .nl-sep{width:1px;height:18px;background:rgba(255,255,255,0.09);margin:0 10px 0 12px;flex:none}
302      .nl-btn{display:inline-flex;align-items:center;gap:7px;text-decoration:none;border:0;background:transparent;color:#d5e3e6;font-family:inherit;font-size:13px;font-weight:600;padding:8px 12px;border-radius:999px;transition:background .15s ease,color .15s ease}
303      .nl-btn svg{color:#32e6e2}
304      .nl-btn:hover{background:rgba(255,255,255,0.07);color:#f4f9f9}
305      .nl-btn[aria-expanded='true']{background:rgba(255,255,255,0.1);color:#f4f9f9}
306      .nl-publish{display:inline-flex;align-items:center;gap:7px;text-decoration:none;margin-left:6px;background:#05bdba;color:#02191b;font-size:13px;font-weight:700;padding:8px 15px;border-radius:999px;transition:background .15s ease,transform .15s cubic-bezier(.34,1.4,.4,1)}
307      .nl-publish:hover{background:#32e6e2;transform:translateY(-1px)}
308      .nl-min{margin-left:6px}
309
310      /* ——— Minimized ——— */
311      .nl-bar,.nl-mini{min-height:52px}
312      .nl-mini{display:none;align-items:center;gap:2px;border:1px solid rgba(255,255,255,0.09);background:linear-gradient(180deg,#142b34,#0e1e25);border-radius:999px;padding:5px 5px 5px 13px;
312cursor:pointer}
313      .nl-mini-state .nl-mini{display:flex}
314      .nl-mini-state .nl-bar,.nl-mini-state .nl-popover,.nl-mini-state .nl-tip{display:none}
315      .nl-mini-brand{display:inline-flex;align-items:center;gap:7px;padding-right:8px}
316      .nl-mini-lock{display:inline-flex;color:#fbbf24}
317
318      /* ——— Narrow layout ——— */
319      .nl-narrow .nl-shrinks{position:absolute;width:1px;height:1px;overflow:hidden;
320        clip-path:inset(50%);white-space:nowrap}
321      .nl-narrow .nl-status{gap:0}
322      .nl-narrow .nl-bar{padding:6px 6px 6px 10px}
323      .nl-narrow .nl-logo{margin-right:6px}
324      .nl-narrow .nl-sep{margin:0 6px 0 8px}
325      .nl-narrow .nl-btn{padding:8px 9px}
326      .nl-narrow .nl-publish{margin-left:4px;padding:8px 11px}
327    </style></head><body>
328      <div class="nl-wrap">
329        <section class="nl-popover" id="nl-pop" aria-label="Pre-launch tools">
330          <div class="nl-popover-head">
331            <span class="nl-popover-icon">${TOOLS_SVG}</span>
332            <span class="nl-popover-title">Pre-launch tools</span>
333            <button type="button" class="nl-icon-btn nl-tap" id="nl-pop-close" aria-label="Close">${CLOSE_SVG}</button>
334          </div>
335          <div class="nl-popover-body">
336            ${task(
337              "SEO / GEO audit",
338              "Check that Google and AI assistants can find and read your site.",
339              "d7bd9353-a6a3-442d-86f0-9b15488f68c3"
340            )}
341            ${task(
342              "Performance check",
343              "See how fast your pages load, and what is slowing them down.",
344              "f8ef80a5-be9b-494d-9fac-a4ac5d5689a4"
345            )}
346            ${task(
347              "Security audit",
348              "Look for common weak spots, like out-of-date code or open settings.",
349              "5f50f746-2ba1-4a3d-8a47-1a515f4420eb"
350            )}
351          </div>
352        </section>
353        <div class="nl-mini" id="nl-mini">
354          <span class="nl-mini-brand">${LOGO(
355            18
356          )}<span class="nl-mini-lock">${LOCK_SVG}</span></span>
357          <button type="button" class="nl-icon-btn nl-tap nl-mini-open" id="nl-mini-open"
358                  aria-label="Show Netlify site tools">${CHEVRON_SVG(
359                    "left"
360                  )}</button>
361          <button type="button" class="nl-icon-btn nl-tap nl-mini-hide" id="nl-mini-hide"
362                  aria-label="Hide Netlify tools on this site">${CLOSE_SVG}</button>
363        </div>
364        <section class="nl-tip" id="nl-tip" aria-label="Project visibility">
365          This project is private - only your team and people you invite can view it.
366          <a href="https://docs.netlify.com/manage/security/secure-access-to-sites/project-visibility/"
367             target="_blank" rel="noopener">Learn more <span aria-hidden="true">&#8599;</span></a>
368        </section>
369        <div class="nl-bar" role="toolbar" aria-label="Netlify site tools">
370          <span class="nl-logo">${LOGO(20)}</span>
371          <button type="button" class="nl-status" id="nl-status"
372                  aria-expanded="false" aria-controls="nl-tip">${LOCK_SVG}<span class="nl-shrinks">Private</span></button>
373          <span class="nl-sep" aria-hidden="true"></span>
374          <a class="nl-btn" href="${hudLink(
375            "modal=share", "share"
376          )}" target="_blank" rel="noopener">${SHARE_SVG}
377            <span class="nl-shrinks">Share</span></a>
378          <button type="button" class="nl-btn" id="nl-tools" aria-expanded="false" aria-controls="nl-pop">${TOOLS_SVG}
379            <span class="nl-shrinks">Pre-launch tools</span></button>
380          <a class="nl-publish" href="${hudLink(
381            "modal=go-live", "make_public"
382          )}" target="_blank" rel="noopener">${LAUNCH_SVG} Make public</a>
383          <button type="button" class="nl-icon-btn nl-tap nl-min" id="nl-min"
384                  aria-label="Minimize Netlify tools">${CHEVRON_SVG(
385                    "right"
386                  )}</button>
387        </div>
388      </div>
389      <script>
390        ${MEASURE_JS}
391        var btn=document.getElementById('nl-tools'),pop=document.getElementById('nl-pop');
392        function setOpen(open){
393          pop.classList.toggle('open',open);
394          btn.setAttribute('aria-expanded',open?'true':'false');
395          nlSendSoon();
396        }
397        btn.addEventListener('click',function(){setOpen(!pop.classList.contains('open'));});
398        document.getElementById('nl-pop-close').addEventListener('click',function(){setOpen(false);btn.focus();});
399
400        var statusBtn=document.getElementById('nl-status'),tip=document.getElementById('nl-tip'),tipTimer=null;
401        function setTip(open){
402          tip.classList.toggle('open',open);
403          statusBtn.setAttribute('aria-expanded',open?'true':'false');
404          nlSendSoon();
405        }
406        function openTip(){clearTimeout(tipTimer);setTip(true);}
407        function closeTipSoon(){clearTimeout(tipTimer);tipTimer=setTimeout(function(){setTip(false);},200);}
408        if(!nlCoarse){
409          statusBtn.addEventListener('mouseenter',openTip);
410          statusBtn.addEventListener('mouseleave',closeTipSoon);
411          tip.addEventListener('mouseenter',openTip);
412          tip.addEventListener('mouseleave',closeTipSoon);
413        }
414        statusBtn.addEventListener('focus',function(){
415          var keyboard;
416          try{keyboard=statusBtn.matches(':focus-visible');}catch(e){keyboard=!nlCoarse;}
417          if(keyboard)openTip();
418        });
419        statusBtn.addEventListener('blur',closeTipSoon);
420        statusBtn.addEventListener('click',function(){
421          clearTimeout(tipTimer);
422          setTip(nlCoarse ? !tip.classList.contains('open') : true);
423        });
424
425        var root=document.documentElement;
426        function setStage(value){
427          root.classList.toggle('nl-mini-state',value==='mini');
428          if(value==='mini'){setOpen(false);setTip(false);}
429          parent.postMessage({nlHud:'state',value:value},'*');
430          nlSendSoon();
431        }
432        document.getElementById('nl-min').addEventListener('click',function(){
433          setStage('mini');
434          document.getElementById('nl-mini-open').focus();
435        });
436        document.getElementById('nl-mini').addEventListener('click',function(e){
437          if(e.target.closest('#nl-mini-hide'))return;
438          setStage('');
439          document.getElementById('nl-min').focus();
440        });
441        document.getElementById('nl-mini-hide').addEventListener('click',function(){
442          parent.postMessage({nlHud:'state',value:'hidden'},'*');
443        });
444        document.addEventListener('keydown',function(e){
445          if(e.key!=='Escape')return;
446          if(tip.classList.contains('open')){setTip(false);statusBtn.focus();return;}
447          if(pop.classList.contains('open')){setOpen(false);btn.focus();}
448        });
449      <\/script>
450    </body></html>`;
451  }
452
453  /* ————— Mount ————— */
454  const frame = document.createElement("iframe");
455
456  const base =
457    "position:fixed;bottom:0;right:0;border:0;margin:0;padding:0;background:transparent;color-scheme:dark;z-index:2147483645;";
458
459  const CLOSED = 'path("M0 0Z")';
460
461  if (variant === "public") {
462    frame.id = "nl-badge-frame";
463    frame.setAttribute("title", "Powered by Netlify");
464    frame.style.cssText = base + "width:216px;height:92px;clip-path:" + CLOSED;
465    frame.srcdoc = publicDoc();
466  } else {
467    frame.id = "nl-hud-frame";
468    frame.setAttribute("title", "Netlify");
469    frame.style.cssText = base + "width:652px;height:94px;clip-path:" + CLOSED;
470    frame.srcdoc = ownerPrivateDoc();
471  }
472
473  const BLEED = 5;
474
475  function clipFor(boxes, w, h, dx, dy) {
476    if (!Array.isArray(boxes) || !boxes.length) return CLOSED;
477    return (
478      'path("' +
479      boxes
480        .map((o) => {
481          const x = o.x + dx,
482            y = o.y + dy;
483          const l = Math.max(0, x - BLEED),
484            r = Math.min(w, x + o.w + BLEED);
485          const t = Math.max(0, y - BLEED),
486            b = Math.min(h, y + o.h + BLEED);
487          return `M${l} ${t}H${r}V${b}H${l}Z`;
488        })
489        .join("") +
490      '")'
491    );
492  }
493
494  const onMessage = (e) => {
495    if (e.source !== frame.contentWindow || !e.data) return;
496
497    if (e.data.nlHud === "state") {
498      const value =
499        e.data.value === "mini" || e.data.value === "hidden"
500          ? e.data.value
501          : "";
502      writeState(value);
503      if (value === "hidden") teardown();
504      return;
505    }
506
507    if (e.data.nlHud !== "resize") return;
508    const wantW = Math.max(Number(e.data.width) || 0, 1);
509    const wantH = Math.max(Number(e.data.height) || 0, 1);
510    const w = Math.min(
511      wantW,
512      document.documentElement.clientWidth || window.innerWidth
513    );
514    const h = Math.min(wantH, window.innerHeight);
515    frame.style.width = w + "px";
516    frame.style.height = h + "px";
517    frame.style.clipPath = clipFor(e.data.boxes, w, h, w - wantW, h - wantH);
518    if (!frame.dataset.nlReady) frame.dataset.nlReady = "1";
519  };
520  window.addEventListener("message", onMessage);
521
522  const sendViewport = () => {
523    if (frame.contentWindow) {
524      frame.contentWindow.postMessage(
525        { nlHud: "viewport", w: window.innerWidth, h: window.innerHeight },
526        "*"
527      );
528    }
529  };
530  let queued = false;
531  const onViewportChange = () => {
532    if (queued) return;
533    queued = true;
534    requestAnimationFrame(() => {
535      queued = false;
536      sendViewport();
537    });
538  };
539  frame.addEventListener("load", sendViewport);
540  window.addEventListener("resize", onViewportChange);
541  window.addEventListener("orientationchange", onViewportChange);
542
543  function teardown() {
544    window.removeEventListener("message", onMessage);
545    window.removeEventListener("resize", onViewportChange);
546    window.removeEventListener("orientationchange", onViewportChange);
547    frame.remove();
548  }
549
550  const mount = () => document.body.appendChild(frame);
551  if (document.body) mount();
552  else document.addEventListener("DOMContentLoaded", mount);
553})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.