Glow Input — Free HTML CSS JS Spotlight Border Snippet

Glow Input · Forms · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Cursor-tracking glow
--x/--y variables move the radial light.
Border-only via mask
mask-composite confines the glow to 1px.
Hover and focus reveal
Glow doubles as the focus indicator.
Cheap shared handler
One pointermove writes CSS variables.
Borderless inputs
The frame comes entirely from the glow shell.
Inline field icons
Icon plus input inside one label shell.
Generic data-glow
Apply the effect to any element.
Submit state
Button swaps to a loading then success label.

About this UI Snippet

Glow Input — Cursor-Tracking Radial Border Glow on Fields

Screenshot of the Glow Input snippet rendered live

The glow input is the polished form treatment where each field's border lights up with a soft radial glow that follows your cursor as it moves across the control — the spotlight-border effect popularized by developer-tool sign-in screens. This snippet implements it for inputs and a button using plain HTML, CSS, and a tiny vanilla JavaScript handler, with the glow drawn purely as a border via a mask trick.

Tracking the cursor with custom properties

The position of the glow is stored in two CSS custom properties, --x and --y, on each field. A single shared pointermove handler reads the cursor's position relative to the hovered element with getBoundingClientRect and writes those pixel values into the variables. CSS does the rest — the glow's radial gradient is centered at var(--x) var(--y), so updating the variables moves the light. Passing the position through CSS variables instead of restyling the gradient in JS keeps the handler trivially cheap.

Glow on the border only, via mask-composite

The clever part is confining the glow to the 1px border rather than filling the whole field. Each control's ::before is inset to cover the element, painted with the radial gradient, and given padding: 1px. Two masks are then composited with mask-composite: exclude (and the -webkit-mask-composite: xor equivalent): one mask covers the content box, the other the full box, and excluding one from the other leaves only the 1px padding ring visible. The result is a gradient that shows only as a glowing border outline — the standard CSS technique for gradient borders without an extra wrapper element.

Reveal on hover and focus

The glow's ::before sits at opacity: 0 and transitions to 1 on :hover, on :focus-within for the fields, and on :hover for the button. So the border lights up when you point at or tab into a field and fades out when you leave — giving keyboard users the same clear focus affordance as mouse users, which matters because the glow doubles as the focus indicator here.

Clean, borderless inputs

The actual <input> elements are transparent with no border or outline of their own; the visual frame comes entirely from the field shell and its glow. An icon sits inline before each input, and the placeholder uses a muted color. This keeps the markup simple — a <label> wrapping an icon and an input — while the glow lives on the label shell so the whole field reacts as one.

The submit flow

The button shares the exact same glow treatment via the data-glow attribute, so the spotlight follows the cursor across it too. On submit, JavaScript prevents navigation and swaps the button label to a "Signing in…" then "Welcome ✓" state — a minimal stand-in for a real auth call you'd drop a fetch into.

Customizing it

Change the gradient color to your accent, widen the 180px circle radius for a larger pool of light, increase the padding for a thicker glowing border, or adjust the reveal transition. Add the data-glow attribute to any element to give it the same effect — it's fully generic. Pair it with a glassmorphism login panel or an animated gradient CTA for a cohesive sign-in screen.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA sign-in form renders with dark, borderless fields.
  2. 2
    Move over a fieldA soft radial glow lights its border and follows the cursor.
  3. 3
    Tab into a fieldThe glow appears on focus too, acting as the focus ring.
  4. 4
    Hover the buttonThe same spotlight border tracks across the submit button.
  5. 5
    Submit the formThe button shows Signing in, then a Welcome confirmation.
  6. 6
    Reuse the effectAdd data-glow to any element to light its border.

Real-world uses

Common Use Cases

Sign-in screens
Pair with a glassmorphism login panel.
Waitlist forms
Light up fields in an animated gradient CTA.
Search bars
Apply the glow to a search box.
Settings panels
Highlight inputs in a settings panel.
Contact forms
Upgrade a plain contact form.
Gradient border demos
A reference for the mask-composite border trick.

Got questions?

Frequently Asked Questions

Each field stores the pointer position in two CSS custom properties, --x and --y. A shared pointermove handler reads the cursor's position relative to the element with getBoundingClientRect and writes those pixel values into the variables. The glow's radial gradient is centered at var(--x) var(--y), so CSS moves the light automatically — the JS only updates two numbers.

Each control's ::before is painted with the radial gradient and given padding: 1px, then masked with mask-composite: exclude (xor on WebKit): one mask covers the content box, another the full box, and excluding one from the other leaves only the 1px padding ring visible. That confines the gradient to a border outline without any extra wrapper element.

Yes. The glow's reveal is tied to :focus-within on the fields as well as :hover, so tabbing into a field lights its border. The native input outline is removed, so the glow intentionally serves as the focus indicator, giving keyboard users the same clear affordance as mouse users.

Yes — it's generic. Any element with the data-glow attribute gets the pointermove listener, and any element carrying the ::before glow CSS will show the border light. The demo uses it on both the input shells and the submit button, but you can apply it to cards, search bars, or buttons the same way.

Render the fields and attach a pointermove handler that sets the --x and --y inline style values via a ref, or use event delegation on the form. Keep the mask-composite CSS as-is. Handle submit with a framework event that calls your auth API and toggles the button label. In Tailwind, express the gradient and masks with arbitrary values and drive the variables through an inline style.

Glow Input — Export as HTML, React, Vue, Angular & Tailwind

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Glow Input</title>
  <style>
    *{box-sizing:border-box;margin:0;padding:0}
    body{font-family:system-ui,-apple-system,sans-serif;background:#08080f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px}
    
    .gi-form{width:100%;max-width:320px;display:flex;flex-direction:column;gap:14px}
    
    /* Each field is a relatively-positioned shell that reveals a radial glow
       tracking the cursor via two CSS custom properties --x and --y. */
    .gi-field,.gi-btn{position:relative;border-radius:13px;border:1px solid #24243a;background:#101019;overflow:hidden}
    .gi-field::before,.gi-btn::before{content:'';position:absolute;inset:0;border-radius:inherit;padding:1px;background:radial-gradient(180px circle at var(--x,-100px) var(--y,-100px),#6366f1,transparent 40%);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:0;transition:opacity .25s}
    .gi-field:hover::before,.gi-btn:hover::before,.gi-field:focus-within::before{opacity:1}
    
    .gi-field{display:flex;align-items:center;gap:10px;padding:0 14px}
    .gi-icon{font-size:15px;opacity:.7;flex-shrink:0}
    .gi-input{flex:1;background:none;border:none;outline:none;color:#fff;font-family:inherit;font-size:14.5px;padding:14px 0}
    .gi-input::placeholder{color:#6b6b85}
    
    .gi-btn{padding:14px;color:#fff;font-family:inherit;font-size:14.5px;font-weight:700;cursor:pointer;background:linear-gradient(#1a1a2e,#101019)}
    .gi-btn:hover{color:#c7d2fe}
  </style>
</head>
<body>
  <div class="gi-stage">
    <form class="gi-form" id="giForm">
      <label class="gi-field" data-glow>
        <span class="gi-icon">✉</span>
        <input type="email" class="gi-input" placeholder="Email address" required>
      </label>
      <label class="gi-field" data-glow>
        <span class="gi-icon">🔒</span>
        <input type="password" class="gi-input" placeholder="Password" required>
      </label>
      <button type="submit" class="gi-btn" data-glow>Sign in</button>
    </form>
  </div>
  <script>
    // One shared pointermove updates the --x/--y of whichever glowing element the
    // cursor is over, so the border light follows the mouse precisely.
    var glows = document.querySelectorAll('[data-glow]');
    glows.forEach(function (el) {
      el.addEventListener('pointermove', function (e) {
        var r = el.getBoundingClientRect();
        el.style.setProperty('--x', (e.clientX - r.left) + 'px');
        el.style.setProperty('--y', (e.clientY - r.top) + 'px');
      });
    });
    
    var form = document.getElementById('giForm');
    form.addEventListener('submit', function (e) {
      e.preventDefault();
      var btn = form.querySelector('.gi-btn');
      btn.textContent = 'Signing in…';
      setTimeout(function () { btn.textContent = 'Welcome ✓'; }, 900);
    });
  </script>
</body>
</html>
Tailwind
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Glow Input</title>
  <!-- Tailwind CSS v3+ — arbitrary value classes (e.g. bg-[#0f172a]) are valid -->
  <script src="https://cdn.tailwindcss.com"></script>
  <style>
    * {
      box-sizing:border-box;margin:0;padding:0
    }

    body {
      font-family:system-ui,-apple-system,sans-serif;background:#08080f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px
    }

    .gi-field,.gi-btn {
      position:relative;border-radius:13px;border:1px solid #24243a;background:#101019;overflow:hidden
    }

    .gi-field::before,.gi-btn::before {
      content:'';position:absolute;inset:0;border-radius:inherit;padding:1px;background:radial-gradient(180px circle at var(--x,-100px) var(--y,-100px),#6366f1,transparent 40%);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:0;transition:opacity .25s
    }

    .gi-field:hover::before,.gi-btn:hover::before,.gi-field:focus-within::before {
      opacity:1
    }

    .gi-input::placeholder {
      color:#6b6b85
    }
  </style>
</head>
<body>
  <div class="gi-stage">
    <form class="w-full max-w-xs flex flex-col gap-3.5" id="giForm">
      <label class="gi-field flex items-center gap-2.5 py-0 px-3.5" data-glow>
        <span class="text-[15px] opacity-70 shrink-0">✉</span>
        <input type="email" class="gi-input flex-1 bg-transparent border-0 outline-none text-[#fff] font-[inherit] text-[14.5px] py-3.5 px-0" placeholder="Email address" required>
      </label>
      <label class="gi-field flex items-center gap-2.5 py-0 px-3.5" data-glow>
        <span class="text-[15px] opacity-70 shrink-0">🔒</span>
        <input type="password" class="gi-input flex-1 bg-transparent border-0 outline-none text-[#fff] font-[inherit] text-[14.5px] py-3.5 px-0" placeholder="Password" required>
      </label>
      <button type="submit" class="gi-btn p-3.5 text-[#fff] font-[inherit] text-[14.5px] font-bold cursor-pointer [background:linear-gradient(#1a1a2e,#101019)] hover:text-[#c7d2fe]" data-glow>Sign in</button>
    </form>
  </div>
  <script>
    // One shared pointermove updates the --x/--y of whichever glowing element the
    // cursor is over, so the border light follows the mouse precisely.
    var glows = document.querySelectorAll('[data-glow]');
    glows.forEach(function (el) {
      el.addEventListener('pointermove', function (e) {
        var r = el.getBoundingClientRect();
        el.style.setProperty('--x', (e.clientX - r.left) + 'px');
        el.style.setProperty('--y', (e.clientY - r.top) + 'px');
      });
    });
    
    var form = document.getElementById('giForm');
    form.addEventListener('submit', function (e) {
      e.preventDefault();
      var btn = form.querySelector('.gi-btn');
      btn.textContent = 'Signing in…';
      setTimeout(function () { btn.textContent = 'Welcome ✓'; }, 900);
    });
  </script>
</body>
</html>
React
import React, { useEffect } from 'react';

// CSS — optionally move to GlowInput.module.css
const css = `
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#08080f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px}

.gi-form{width:100%;max-width:320px;display:flex;flex-direction:column;gap:14px}

/* Each field is a relatively-positioned shell that reveals a radial glow
   tracking the cursor via two CSS custom properties --x and --y. */
.gi-field,.gi-btn{position:relative;border-radius:13px;border:1px solid #24243a;background:#101019;overflow:hidden}
.gi-field::before,.gi-btn::before{content:'';position:absolute;inset:0;border-radius:inherit;padding:1px;background:radial-gradient(180px circle at var(--x,-100px) var(--y,-100px),#6366f1,transparent 40%);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:0;transition:opacity .25s}
.gi-field:hover::before,.gi-btn:hover::before,.gi-field:focus-within::before{opacity:1}

.gi-field{display:flex;align-items:center;gap:10px;padding:0 14px}
.gi-icon{font-size:15px;opacity:.7;flex-shrink:0}
.gi-input{flex:1;background:none;border:none;outline:none;color:#fff;font-family:inherit;font-size:14.5px;padding:14px 0}
.gi-input::placeholder{color:#6b6b85}

.gi-btn{padding:14px;color:#fff;font-family:inherit;font-size:14.5px;font-weight:700;cursor:pointer;background:linear-gradient(#1a1a2e,#101019)}
.gi-btn:hover{color:#c7d2fe}
`;

export default function GlowInput() {
  // Auto-generated escape hatch: the original snippet's vanilla JS runs once
  // after mount and queries the rendered DOM. For idiomatic React, lift this
  // into state + handlers.
  useEffect(() => {
    const _listeners = [];
    const _originalAddEventListener = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function(type, listener, options) {
      _listeners.push({ target: this, type, listener, options });
      return _originalAddEventListener.call(this, type, listener, options);
    };
    // One shared pointermove updates the --x/--y of whichever glowing element the
    // cursor is over, so the border light follows the mouse precisely.
    var glows = document.querySelectorAll('[data-glow]');
    glows.forEach(function (el) {
      el.addEventListener('pointermove', function (e) {
        var r = el.getBoundingClientRect();
        el.style.setProperty('--x', (e.clientX - r.left) + 'px');
        el.style.setProperty('--y', (e.clientY - r.top) + 'px');
      });
    });
    
    var form = document.getElementById('giForm');
    form.addEventListener('submit', function (e) {
      e.preventDefault();
      var btn = form.querySelector('.gi-btn');
      btn.textContent = 'Signing in…';
      setTimeout(function () { btn.textContent = 'Welcome ✓'; }, 900);
    });
    // Cleanup: restore addEventListener and remove all listeners
    return () => {
      EventTarget.prototype.addEventListener = _originalAddEventListener;
      for (const { target, type, listener, options } of _listeners) {
        target.removeEventListener(type, listener, options);
      }
    };
  }, []);

  return (
    <>
      <style>{css}</style>
      <div className="gi-stage">
        <form className="gi-form" id="giForm">
          <label className="gi-field" data-glow>
            <span className="gi-icon">✉</span>
            <input type="email" className="gi-input" placeholder="Email address" required />
          </label>
          <label className="gi-field" data-glow>
            <span className="gi-icon">🔒</span>
            <input type="password" className="gi-input" placeholder="Password" required />
          </label>
          <button type="submit" className="gi-btn" data-glow>Sign in</button>
        </form>
      </div>
    </>
  );
}
React + Tailwind
import React, { useEffect } from 'react';
// Requires Tailwind CSS v3+ — https://tailwindcss.com/docs/installation
// Arbitrary value classes (e.g. bg-[#0f172a]) are valid Tailwind v3+

export default function GlowInput() {
  // Auto-generated escape hatch: the original snippet's vanilla JS runs once
  // after mount and queries the rendered DOM. For idiomatic React, lift this
  // into state + handlers.
  useEffect(() => {
    const _listeners = [];
    const _originalAddEventListener = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function(type, listener, options) {
      _listeners.push({ target: this, type, listener, options });
      return _originalAddEventListener.call(this, type, listener, options);
    };
    // One shared pointermove updates the --x/--y of whichever glowing element the
    // cursor is over, so the border light follows the mouse precisely.
    var glows = document.querySelectorAll('[data-glow]');
    glows.forEach(function (el) {
      el.addEventListener('pointermove', function (e) {
        var r = el.getBoundingClientRect();
        el.style.setProperty('--x', (e.clientX - r.left) + 'px');
        el.style.setProperty('--y', (e.clientY - r.top) + 'px');
      });
    });
    
    var form = document.getElementById('giForm');
    form.addEventListener('submit', function (e) {
      e.preventDefault();
      var btn = form.querySelector('.gi-btn');
      btn.textContent = 'Signing in…';
      setTimeout(function () { btn.textContent = 'Welcome ✓'; }, 900);
    });
    // Cleanup: restore addEventListener and remove all listeners
    return () => {
      EventTarget.prototype.addEventListener = _originalAddEventListener;
      for (const { target, type, listener, options } of _listeners) {
        target.removeEventListener(type, listener, options);
      }
    };
  }, []);

  return (
    <>
      <style>{`
* {
  box-sizing:border-box;margin:0;padding:0
}

body {
  font-family:system-ui,-apple-system,sans-serif;background:#08080f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px
}

.gi-field,.gi-btn {
  position:relative;border-radius:13px;border:1px solid #24243a;background:#101019;overflow:hidden
}

.gi-field::before,.gi-btn::before {
  content:'';position:absolute;inset:0;border-radius:inherit;padding:1px;background:radial-gradient(180px circle at var(--x,-100px) var(--y,-100px),#6366f1,transparent 40%);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:0;transition:opacity .25s
}

.gi-field:hover::before,.gi-btn:hover::before,.gi-field:focus-within::before {
  opacity:1
}

.gi-input::placeholder {
  color:#6b6b85
}
      `}</style>
      <div className="gi-stage">
        <form className="w-full max-w-xs flex flex-col gap-3.5" id="giForm">
          <label className="gi-field flex items-center gap-2.5 py-0 px-3.5" data-glow>
            <span className="text-[15px] opacity-70 shrink-0">✉</span>
            <input type="email" className="gi-input flex-1 bg-transparent border-0 outline-none text-[#fff] font-[inherit] text-[14.5px] py-3.5 px-0" placeholder="Email address" required />
          </label>
          <label className="gi-field flex items-center gap-2.5 py-0 px-3.5" data-glow>
            <span className="text-[15px] opacity-70 shrink-0">🔒</span>
            <input type="password" className="gi-input flex-1 bg-transparent border-0 outline-none text-[#fff] font-[inherit] text-[14.5px] py-3.5 px-0" placeholder="Password" required />
          </label>
          <button type="submit" className="gi-btn p-3.5 text-[#fff] font-[inherit] text-[14.5px] font-bold cursor-pointer [background:linear-gradient(#1a1a2e,#101019)] hover:text-[#c7d2fe]" data-glow>Sign in</button>
        </form>
      </div>
    </>
  );
}
Vue
<template>
  <div class="gi-stage">
    <form class="gi-form" id="giForm">
      <label class="gi-field" data-glow>
        <span class="gi-icon">✉</span>
        <input type="email" class="gi-input" placeholder="Email address" required>
      </label>
      <label class="gi-field" data-glow>
        <span class="gi-icon">🔒</span>
        <input type="password" class="gi-input" placeholder="Password" required>
      </label>
      <button type="submit" class="gi-btn" data-glow>Sign in</button>
    </form>
  </div>
</template>

<script setup>
import { onMounted } from 'vue';

let glows;
let form;

onMounted(() => {
  glows = document.querySelectorAll('[data-glow]');
  glows.forEach(function (el) {
    el.addEventListener('pointermove', function (e) {
      var r = el.getBoundingClientRect();
      el.style.setProperty('--x', (e.clientX - r.left) + 'px');
      el.style.setProperty('--y', (e.clientY - r.top) + 'px');
    });
  });
  form = document.getElementById('giForm');
  form.addEventListener('submit', function (e) {
    e.preventDefault();
    var btn = form.querySelector('.gi-btn');
    btn.textContent = 'Signing in…';
    setTimeout(function () { btn.textContent = 'Welcome ✓'; }, 900);
  });
});
</script>

<style scoped>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#08080f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px}

.gi-form{width:100%;max-width:320px;display:flex;flex-direction:column;gap:14px}

/* Each field is a relatively-positioned shell that reveals a radial glow
   tracking the cursor via two CSS custom properties --x and --y. */
.gi-field,.gi-btn{position:relative;border-radius:13px;border:1px solid #24243a;background:#101019;overflow:hidden}
.gi-field::before,.gi-btn::before{content:'';position:absolute;inset:0;border-radius:inherit;padding:1px;background:radial-gradient(180px circle at var(--x,-100px) var(--y,-100px),#6366f1,transparent 40%);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:0;transition:opacity .25s}
.gi-field:hover::before,.gi-btn:hover::before,.gi-field:focus-within::before{opacity:1}

.gi-field{display:flex;align-items:center;gap:10px;padding:0 14px}
.gi-icon{font-size:15px;opacity:.7;flex-shrink:0}
.gi-input{flex:1;background:none;border:none;outline:none;color:#fff;font-family:inherit;font-size:14.5px;padding:14px 0}
.gi-input::placeholder{color:#6b6b85}

.gi-btn{padding:14px;color:#fff;font-family:inherit;font-size:14.5px;font-weight:700;cursor:pointer;background:linear-gradient(#1a1a2e,#101019)}
.gi-btn:hover{color:#c7d2fe}
</style>
Angular
// @ts-nocheck
// Note: vanilla JS DOM manipulation is preserved as-is inside ngAfterViewInit().
// For idiomatic Angular, replace document.getElementById() with @ViewChild() refs
// and move state into component properties with two-way binding.
import { Component, AfterViewInit, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-glow-input',
  standalone: true,
  imports: [CommonModule],
  encapsulation: ViewEncapsulation.None,
  template: `
    <div class="gi-stage">
      <form class="gi-form" id="giForm">
        <label class="gi-field" data-glow>
          <span class="gi-icon">✉</span>
          <input type="email" class="gi-input" placeholder="Email address" required>
        </label>
        <label class="gi-field" data-glow>
          <span class="gi-icon">🔒</span>
          <input type="password" class="gi-input" placeholder="Password" required>
        </label>
        <button type="submit" class="gi-btn" data-glow>Sign in</button>
      </form>
    </div>
  `,
  styles: [`
    *{box-sizing:border-box;margin:0;padding:0}
    body{font-family:system-ui,-apple-system,sans-serif;background:#08080f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px}
    
    .gi-form{width:100%;max-width:320px;display:flex;flex-direction:column;gap:14px}
    
    /* Each field is a relatively-positioned shell that reveals a radial glow
       tracking the cursor via two CSS custom properties --x and --y. */
    .gi-field,.gi-btn{position:relative;border-radius:13px;border:1px solid #24243a;background:#101019;overflow:hidden}
    .gi-field::before,.gi-btn::before{content:'';position:absolute;inset:0;border-radius:inherit;padding:1px;background:radial-gradient(180px circle at var(--x,-100px) var(--y,-100px),#6366f1,transparent 40%);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:0;transition:opacity .25s}
    .gi-field:hover::before,.gi-btn:hover::before,.gi-field:focus-within::before{opacity:1}
    
    .gi-field{display:flex;align-items:center;gap:10px;padding:0 14px}
    .gi-icon{font-size:15px;opacity:.7;flex-shrink:0}
    .gi-input{flex:1;background:none;border:none;outline:none;color:#fff;font-family:inherit;font-size:14.5px;padding:14px 0}
    .gi-input::placeholder{color:#6b6b85}
    
    .gi-btn{padding:14px;color:#fff;font-family:inherit;font-size:14.5px;font-weight:700;cursor:pointer;background:linear-gradient(#1a1a2e,#101019)}
    .gi-btn:hover{color:#c7d2fe}
  `]
})
export class GlowInputComponent implements AfterViewInit {
  ngAfterViewInit(): void {
    // One shared pointermove updates the --x/--y of whichever glowing element the
    // cursor is over, so the border light follows the mouse precisely.
    var glows = document.querySelectorAll('[data-glow]');
    glows.forEach(function (el) {
      el.addEventListener('pointermove', function (e) {
        var r = el.getBoundingClientRect();
        el.style.setProperty('--x', (e.clientX - r.left) + 'px');
        el.style.setProperty('--y', (e.clientY - r.top) + 'px');
      });
    });
    
    var form = document.getElementById('giForm');
    form.addEventListener('submit', function (e) {
      e.preventDefault();
      var btn = form.querySelector('.gi-btn');
      btn.textContent = 'Signing in…';
      setTimeout(function () { btn.textContent = 'Welcome ✓'; }, 900);
    });
  }
}