esp-wifi-provisioning 0.1.1

Captive-portal WiFi provisioning for ESP32 targets via esp-idf-svc
* {
     box-sizing:border-box;
     margin:0;
     padding:0;
}
 body {
     font-family:system-ui, sans-serif;
     background:#0f172a;
     color:#e2e8f0;
     display:flex;
     justify-content:center;
     align-items:center;
     min-height:100vh;
     padding:1rem;
}
 .card {
     background:#1e293b;
     border-radius:1rem;
     padding:2rem;
     width:100%;
     max-width:420px;
     box-shadow:0 25px 50px rgba(0,0,0,.5);
}
 h1 {
     font-size:1.4rem;
     margin-bottom:1.5rem;
     color:#f1f5f9;
     text-align:center;
}
.last-error {
    display:flex;
    align-items:flex-start;
    gap:.5rem;
    background:rgba(248, 113, 113, 0.12);
    border:1px solid #f87171;
    border-radius:.5rem;
    color:#fca5a5;
    font-size:.85rem;
    line-height:1.4;
    padding:.6rem .8rem;
    margin-bottom:1.25rem;
}
.last-error[hidden] {
    display:none;
}
.last-error-icon {
    flex-shrink:0;
    margin-top:.05rem;
}
 label {
     display:block;
     font-size:.8rem;
     color:#94a3b8;
     margin-bottom:.3rem;
}
 input {
     width:100%;
     padding:.6rem .8rem;
     background:#0f172a;
     border:1px solid #334155;
     border-radius:.5rem;
     color:#f1f5f9;
     font-size:1rem;
     outline:none;
     margin-bottom:.5rem;
}
 input:focus {
     border-color:#6366f1;
     box-shadow:0 0 0 2px rgba(99,102,241,0.35);
}
 #pass {
     padding-right:2.2rem;
     margin-bottom:.25rem;
}
 .pass-hint {
     display:block;
     font-size:.75rem;
     color:#64748b;
     margin-bottom:1rem;
     min-height:1rem;
}
 .pass-hint--err {
     color:#f87171;
}
 .pass-hint--ok {
     color:#4ade80;
}
 .pass-wrap {
     position:relative;
}
 .eye-btn {
     position:absolute;
     right:.4rem;
     top:40%;
     transform:translateY(-50%);
     background:none;
     border:none;
     color:#94a3b8;
     cursor:pointer;
     font-size:1.1rem;
     padding:.35rem;
}
 .eye-btn:hover {
     color:#e2e8f0;
}
 .eye-icon {
     position:relative;
}
 .eye-icon.striked::after {
     content:"";
     position:absolute;
     left:-2px;
     right:-2px;
     top:50%;
     height:2px;
     background:currentColor;
     transform:rotate(-25deg);
     pointer-events:none;
}
 .networks-header {
     display:flex;
     align-items:center;
     justify-content:space-between;
     margin-bottom:.4rem;
}
 .networks-label {
     font-size:.8rem;
     color:#94a3b8;
}
 .rescan-btn {
     background:none;
     border:1px solid #334155;
     border-radius:.4rem;
     color:#94a3b8;
     font-size:.75rem;
     padding:.2rem .55rem;
     cursor:pointer;
     transition:border-color .15s, color .15s;
}
 .rescan-btn:hover:not(:disabled) {
     border-color:#6366f1;
     color:#e2e8f0;
}
 .rescan-btn:disabled {
     opacity:.45;
     cursor:not-allowed;
}
 .networks-scroll-wrap {
     max-height:220px;
     overflow-y:auto;
     margin-bottom:1rem;
}
 .networks-scroll-wrap::-webkit-scrollbar {
     width:6px;
}
 .networks-scroll-wrap::-webkit-scrollbar-thumb {
     background:#6366f1;
     border-radius:3px;
}
 .networks-scroll-wrap::-webkit-scrollbar-track {
     background:#1e293b;
}
 .networks {
     overflow:visible;
     padding-top:.5rem;
}
 .networks-err {
     color:#f87171;
     font-size:.85rem;
     padding:.5rem .8rem;
}
 .net-item {
     outline:none;
     display:flex;
     align-items:center;
     justify-content:space-between;
     padding:.55rem .8rem;
     border-radius:.5rem;
     cursor:pointer;
     border:1px solid transparent;
     transition:background .15s, transform .15s, box-shadow .15s;
     position:relative;
}
 .net-item:focus-visible, .net-item.selected {
     border-color:#6366f1;
     transform:translateY(-2px);
     box-shadow:0 4px 8px rgba(0,0,0,0.2);
}
 .net-item:not(.selected):not(:focus-within):hover {
     border-color:#6366f1;
     transform:translateY(-2px);
     box-shadow:0 4px 8px rgba(0,0,0,0.2);
}
 .net-ssid::before {
     content:attr(data-lock);
     margin-right:.25rem;
}
 .net-ssid {
     font-size:.95rem;
     max-width:150px;
     overflow:hidden;
     text-overflow:ellipsis;
     white-space:nowrap;
}
 .net-ssid:hover {
     overflow:visible;
     white-space:normal;
     position:absolute;
     left:.8rem;
     top:50%;
     transform:translateY(-50%);
     background:#1e293b;
     padding:.1rem .25rem;
     border-radius:.25rem;
     z-index:10;
     max-width:calc(100% - 1.6rem);
}
 .net-meta {
     display:flex;
     align-items:center;
}
 .net-rssi {
     font-size:.75rem;
     color:#64748b;
     white-space:nowrap;
     margin-left:.5rem;
}
 .bars {
     display:flex;
     align-items:flex-end;
     gap:2px;
     height:14px;
     margin-left:.5rem;
}
 .bar {
     width:4px;
     background:#6366f1;
     border-radius:1px;
}
 .bar.off {
     background:#334155;
}
 .bar-0 {
     height:5px;
}
 .bar-1 {
     height:8px;
}
 .bar-2 {
     height:11px;
}
 .bar-3 {
     height:14px;
}
 .btn {
     width:100%;
     padding:.7rem;
     margin-top:1rem;
     background:linear-gradient(135deg, #6366f1, #4f46e5);
     border:none;
     border-radius:.5rem;
     color:#fff;
     font-size:1rem;
     cursor:pointer;
     font-weight:600;
     transition:background .15s;
}
 .btn:hover {
     background:linear-gradient(135deg, #4f46e5, #6366f1);
}
 .btn:disabled {
     background:#334155;
     cursor:not-allowed;
}
 #status {
     margin-top:1rem;
     text-align:center;
     font-size:.85rem;
     min-height:1.2rem;
}
 .err {
     color:#f87171;
}
 .ok {
     color:#4ade80;
}
 .spinner {
     display:none;
     margin:0 auto 1rem;
     width:36px;
     height:36px;
     border:3px solid #334155;
     border-top-color:#6366f1;
     border-radius:50%;
     animation:spin .8s linear infinite;
}
 @keyframes spin {
     to {
         transform:rotate(360deg);
    }
}
 .visually-hidden {
     position:absolute;
     width:1px;
     height:1px;
     padding:0;
     margin:-1px;
     overflow:hidden;
     clip:rect(0,0,0,0);
     border:0;
}