/* ==========================================================================
   PipeSense Internal Suite — sign in

   Deliberately not part of the suite design system: this page renders before
   anyone is authenticated, outside the shell, and is the only screen in the
   product that gets to be atmospheric. It is self-contained — one stylesheet,
   no framework, no CDN — so it cannot be slowed down or broken by anything the
   signed-in suite depends on.

   The background is five stacked layers. All of it is CSS and no canvas; the
   only script on the page is the four lines that tell the spotlight where the
   cursor is:

     1. .bg__aurora   two large colour fields drifting past each other. The
                      Aceternity "aurora"/gradient-blob look: heavily blurred
                      radial gradients on long, offset animations, so the two
                      never repeat the same overlap.
     2. .bg__grid     the Vercel grid — two tiled linear-gradient pairs, a 58px
                      cell over a finer one, drifting exactly one cell so the
                      loop is seamless.
     3. .bg__glow     the same grid at full strength, revealed only inside a
                      disc that follows the cursor, with a soft blue light
                      behind it. The centre is --mx / --my, which the page's one
                      small script writes on pointermove.
     4. .bg__beam     a light sweep crossing the grid, which is the thing that
                      makes a still image feel alive.
     5. .bg__noise    a fine dither over everything. Large flat gradients band
                      badly on 8-bit panels; grain hides it.

   Everything animated is transform/opacity only, and the spotlight moves by
   writing two custom properties rather than by laying out anything, so the whole
   thing composites on the GPU and costs nothing to idle on. Under
   prefers-reduced-motion every animation stops, the cursor listener is never
   attached, the spotlight is removed and the rest holds a composed still frame —
   the page keeps its design and loses only the movement.
   ========================================================================== */

:root {
    --ink:            #f4f7fb;
    --ink-2:          #aab6c8;
    --ink-3:          #71809a;
    --void:           #05070c;
    --void-2:         #090d16;
    --line:           rgba(255, 255, 255, 0.09);
    --line-strong:    rgba(255, 255, 255, 0.16);
    /* The grid: its main cell, the finer one under it, and what both become
       where the cursor lights them up. */
    --grid-line:      rgba(255, 255, 255, 0.14);
    --grid-line-fine: rgba(255, 255, 255, 0.05);
    --grid-line-hot:  rgba(150, 195, 255, 0.55);
    --glass:          rgba(16, 21, 33, 0.66);
    --brand:          #2f6dff;
    --brand-2:        #00c6d7;
    --brand-3:        #7a5cff;
    --danger:         #ff6b5e;
    --r:              14px;
    --ease:           cubic-bezier(0.4, 0, 0.2, 1);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { -webkit-text-size-adjust: 100%; }

body {
    min-height: 100vh;
    min-height: 100dvh;
    background: var(--void);
    color: var(--ink);
    font: 15px/1.6 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
          'Helvetica Neue', Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    display: flex;
    flex-direction: column;
    /* The layers below are fixed and full-bleed; nothing should scroll them. */
    overflow-x: hidden;
}

/* ==========================================================================
   The background
   ========================================================================== */

.bg {
    /* Where the spotlight sits before the pointer has said otherwise. */
    --mx: 50%;
    --my: 42%;
    position: fixed;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    /* Purely decorative: it must never be announced or focusable. */
    pointer-events: none;
}

/* -- 1. aurora ----------------------------------------------------------- */

.bg__aurora {
    position: absolute;
    inset: -30%;
    filter: blur(90px) saturate(140%);
    /* Held back from what it was: with the grid this legible the aurora's job is
       to tint the page, not to be the background on its own. */
    opacity: 0.5;
}

.bg__aurora::before,
.bg__aurora::after {
    content: '';
    position: absolute;
    inset: 0;
    background-repeat: no-repeat;
}

/* Two fields, opposite directions and different periods, so their overlap
   never lands in the same place twice. */
.bg__aurora::before {
    background-image:
        radial-gradient(38% 44% at 22% 28%, rgba(47, 109, 255, 0.85), transparent 70%),
        radial-gradient(32% 38% at 78% 22%, rgba(122, 92, 255, 0.62), transparent 70%);
    animation: drift-a 26s var(--ease) infinite alternate;
}

.bg__aurora::after {
    background-image:
        radial-gradient(40% 46% at 72% 74%, rgba(0, 198, 215, 0.58), transparent 70%),
        radial-gradient(30% 34% at 30% 82%, rgba(47, 109, 255, 0.48), transparent 72%);
    animation: drift-b 34s var(--ease) infinite alternate;
}

@keyframes drift-a {
    from { transform: translate3d(-4%, -2%, 0) scale(1);    }
    to   { transform: translate3d(6%, 5%, 0)   scale(1.18); }
}

@keyframes drift-b {
    from { transform: translate3d(5%, 3%, 0)   scale(1.12) rotate(0deg);   }
    to   { transform: translate3d(-6%, -4%, 0) scale(1)     rotate(-8deg); }
}

/* -- 2. perspective grid -------------------------------------------------- */

.bg__grid {
    position: absolute;
    inset: 0;
    background-image:
        linear-gradient(to right,  var(--grid-line) 1px, transparent 1px),
        linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px),
        linear-gradient(to right,  var(--grid-line-fine) 1px, transparent 1px),
        linear-gradient(to bottom, var(--grid-line-fine) 1px, transparent 1px);
    /* A 58px cell with four fine divisions inside it. */
    background-size: 58px 58px, 58px 58px, 14.5px 14.5px, 14.5px 14.5px;
    /* Held back only near the edges. It used to fade out across most of the
       viewport, which read as a hint of a grid at the bottom of the page rather
       than as a grid. */
    -webkit-mask-image: radial-gradient(135% 115% at 50% 88%, #000 45%, transparent 94%);
    mask-image: radial-gradient(135% 115% at 50% 88%, #000 45%, transparent 94%);
    animation: grid-drift 24s linear infinite;
}

/* -- 3. cursor spotlight -------------------------------------------------- */

.bg__glow {
    position: absolute;
    inset: 0;
    /* Two things in one layer: the grid at full strength, and a light behind it.
       They have to share the layer so a single mask cuts both. */
    background-image:
        linear-gradient(to right,  var(--grid-line-hot) 1px, transparent 1px),
        linear-gradient(to bottom, var(--grid-line-hot) 1px, transparent 1px),
        radial-gradient(circle at var(--mx) var(--my),
                        rgba(90, 150, 255, 0.22), transparent 68%);
    /* Aligned with .bg__grid's main cell so the bright lines land exactly on the
       dim ones instead of beside them. */
    background-size: 58px 58px, 58px 58px, auto;
    /* Only the disc around the cursor survives, which is what makes the brighter
       grid read as light falling across it. */
    -webkit-mask-image: radial-gradient(340px circle at var(--mx) var(--my),
                                        #000 0%, rgba(0, 0, 0, 0.45) 42%, transparent 72%);
    mask-image: radial-gradient(340px circle at var(--mx) var(--my),
                                #000 0%, rgba(0, 0, 0, 0.45) 42%, transparent 72%);
    animation: grid-drift 24s linear infinite;
    transition: opacity 500ms var(--ease);
}

/* Before the pointer has moved, the spotlight would sit at its default centre
   looking like an unexplained bright patch — so the script fades it in on the
   first movement by adding .is-tracking. */
.bg:not(.is-tracking) .bg__glow { opacity: 0; }

/* One cell of travel, so the loop is seamless. */
@keyframes grid-drift {
    from { background-position: 0 0; }
    to   { background-position: 0 58px; }
}

/* -- 3. beam -------------------------------------------------------------- */

.bg__beam {
    position: absolute;
    top: -60%;
    left: -40%;
    width: 60%;
    height: 220%;
    background: linear-gradient(90deg, transparent, rgba(140, 190, 255, 0.14), transparent);
    transform: rotate(18deg);
    animation: sweep 11s var(--ease) infinite;
}

@keyframes sweep {
    /* Held off-screen for most of the cycle: a sweep that never rests reads as
       a flicker, one that arrives every few seconds reads as light. */
    0%, 62%  { transform: translateX(0) rotate(18deg);     opacity: 0; }
    68%      { opacity: 1; }
    100%     { transform: translateX(260%) rotate(18deg);  opacity: 0; }
}

/* -- 4. grain and vignette ------------------------------------------------ */

.bg__noise {
    position: absolute;
    inset: 0;
    opacity: 0.038;
    /* Inline SVG turbulence: a real dither pattern with no image request. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

.bg__vignette {
    position: absolute;
    inset: 0;
    background:
        radial-gradient(90% 70% at 50% 40%, transparent 30%, rgba(5, 7, 12, 0.72) 100%),
        linear-gradient(to bottom, rgba(5, 7, 12, 0.55), transparent 30%, rgba(5, 7, 12, 0.8));
}

/* ==========================================================================
   Layout
   ========================================================================== */

.page {
    position: relative;
    z-index: 1;
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
}

.masthead {
    padding: 26px 32px;
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
}

/* The dark-background variant of the lockup — see the header of
   static/brand/pipe-sense-logo-dark.svg for why there are two. Height drives it;
   the width attribute in the markup is only there to reserve the right space
   before the SVG has loaded. */
.logo {
    display: block;
    height: 33px;
    width: auto;
    max-width: 62vw;
}

/* "Internal Suite" no longer belongs inside the mark, so it sits beside it,
   divided off. */
.masthead__sub {
    padding-left: 14px;
    border-left: 1px solid var(--line-strong);
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.middle {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

/* ==========================================================================
   The card
   ========================================================================== */

.card {
    position: relative;
    width: 100%;
    max-width: 424px;
    border-radius: var(--r);
    background: var(--glass);
    /* The frosting is what ties the card to the moving background behind it. */
    -webkit-backdrop-filter: blur(22px) saturate(150%);
    backdrop-filter: blur(22px) saturate(150%);
    border: 1px solid var(--line-strong);
    box-shadow:
        0 30px 70px -20px rgba(0, 0, 0, 0.75),
        inset 0 1px 0 rgba(255, 255, 255, 0.07);
    padding: 34px 32px 28px;
    animation: rise 620ms var(--ease) both;
}

@keyframes rise {
    from { opacity: 0; transform: translateY(14px) scale(0.985); }
    to   { opacity: 1; transform: none; }
}

/* The travelling border highlight — Aceternity's "border beam". A conic
   gradient rotating behind the card, clipped to a 1px ring by the inset
   backdrop of .card itself sitting on top of it. */
.card::before {
    content: '';
    position: absolute;
    inset: -1px;
    border-radius: inherit;
    padding: 1px;
    background: conic-gradient(from var(--beam-angle, 0deg),
        transparent 0deg, transparent 300deg,
        rgba(0, 198, 215, 0.9) 340deg,
        rgba(122, 92, 255, 0.9) 352deg,
        transparent 360deg);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
    animation: beam-spin 6s linear infinite;
    pointer-events: none;
}

/* A registered custom property is what makes the conic gradient's angle
   animatable at all — without @property the browser cannot interpolate it and
   the ring simply sits still. */
@property --beam-angle {
    syntax: '<angle>';
    initial-value: 0deg;
    inherits: false;
}

@keyframes beam-spin {
    to { --beam-angle: 360deg; }
}

.card__head { margin-bottom: 24px; }

h1 {
    font-size: 21px;
    font-weight: 700;
    letter-spacing: -0.02em;
    margin-bottom: 6px;
}

.sub { font-size: 13.5px; color: var(--ink-3); }

/* ==========================================================================
   Form
   ========================================================================== */

.field { margin-bottom: 16px; }

label {
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--ink-2);
    margin-bottom: 7px;
    letter-spacing: 0.01em;
}

.control { position: relative; display: flex; align-items: center; }

.control svg {
    position: absolute;
    left: 13px;
    width: 16px;
    height: 16px;
    color: var(--ink-3);
    transition: color 160ms var(--ease);
    pointer-events: none;
}

input[type="text"],
input[type="password"] {
    width: 100%;
    padding: 12px 14px 12px 39px;
    font: inherit;
    font-size: 14.5px;
    color: var(--ink);
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid var(--line-strong);
    border-radius: 10px;
    transition: border-color 160ms var(--ease), background 160ms var(--ease),
                box-shadow 160ms var(--ease);
}

input::placeholder { color: #5c6880; }

input[type="text"]:focus,
input[type="password"]:focus {
    outline: none;
    border-color: rgba(47, 109, 255, 0.85);
    background: rgba(255, 255, 255, 0.07);
    box-shadow: 0 0 0 3.5px rgba(47, 109, 255, 0.18);
}

.control:focus-within svg { color: var(--brand-2); }

.row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: 18px 0 22px;
    font-size: 13px;
}

.remember {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--ink-2);
    cursor: pointer;
    user-select: none;
}

.remember input {
    width: 15px;
    height: 15px;
    accent-color: var(--brand);
    cursor: pointer;
}

.quiet { color: var(--ink-3); font-size: 12.5px; }

button[type="submit"] {
    position: relative;
    width: 100%;
    padding: 12px 16px;
    font: inherit;
    font-size: 14.5px;
    font-weight: 650;
    color: #fff;
    background: linear-gradient(180deg, #3b78ff, #2059e6);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 10px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    overflow: hidden;
    transition: transform 140ms var(--ease), box-shadow 200ms var(--ease),
                filter 160ms var(--ease);
    box-shadow: 0 10px 26px -8px rgba(47, 109, 255, 0.7);
}

button[type="submit"]:hover { filter: brightness(1.07); box-shadow: 0 14px 32px -8px rgba(47, 109, 255, 0.85); }
button[type="submit"]:active { transform: translateY(1px); }
button[type="submit"]:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }

/* Shine passing over the button on hover — the same idea as the background
   beam, scoped to the one control that matters. */
button[type="submit"]::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(105deg, transparent 35%, rgba(255, 255, 255, 0.3) 50%, transparent 65%);
    transform: translateX(-120%);
    transition: transform 620ms var(--ease);
}
button[type="submit"]:hover::after { transform: translateX(120%); }

/* ==========================================================================
   Messages and footer
   ========================================================================== */

.error {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin-top: 16px;
    padding: 11px 13px;
    font-size: 13px;
    color: #ffd9d4;
    background: rgba(255, 107, 94, 0.12);
    border: 1px solid rgba(255, 107, 94, 0.34);
    border-radius: 10px;
    animation: shake 420ms var(--ease) both;
}
.error svg { flex-shrink: 0; width: 16px; height: 16px; color: var(--danger); margin-top: 1px; }

@keyframes shake {
    0%, 100% { transform: translateX(0); }
    25%      { transform: translateX(-5px); }
    60%      { transform: translateX(4px); }
}

.card__foot {
    margin-top: 24px;
    padding-top: 18px;
    border-top: 1px solid var(--line);
    font-size: 12.5px;
    color: var(--ink-3);
    text-align: center;
}

.pagefoot {
    padding: 20px 32px 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px 16px;
    flex-wrap: wrap;
    text-align: center;
    font-size: 12px;
    color: #4d5a72;
}

.pagefoot__built {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

/* The one place on the page where a beat of animation is purely for fun, so it
   is slow and small enough not to pull the eye off the form. */
.pagefoot__heart {
    display: inline-block;
    font-size: 11px;
    animation: beat 2.6s ease-in-out infinite;
}

@keyframes beat {
    0%, 82%, 100% { transform: scale(1); }
    88%           { transform: scale(1.32); }
    94%           { transform: scale(1.1); }
}

/* Present for the same reason it is in the suite stylesheet: text that has to
   reach a screen reader without being drawn. */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 560px) {
    .masthead { padding: 20px; gap: 10px; }
    .logo { height: 27px; }
    /* Beside a 27px lockup on a narrow screen the divider is clutter. */
    .masthead__sub { display: none; }
    .middle { padding: 8px 18px 28px; align-items: flex-start; }
    .card { padding: 26px 20px 22px; }
    h1 { font-size: 19px; }
    /* Blur is the most expensive layer and the least visible on a small screen. */
    .bg__aurora { filter: blur(64px) saturate(140%); }
    .bg__grid { background-size: 44px 44px, 44px 44px, 11px 11px, 11px 11px; }
    /* No pointer to follow, and the most expensive layer on the page. */
    .bg__glow { display: none; }
    @keyframes grid-drift {
        from { background-position: 0 0; }
        to   { background-position: 0 44px; }
    }
}

/* Backdrop-filter is what the card's legibility rests on; without it the glass
   is nearly transparent over a moving background. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .card { background: rgba(10, 14, 22, 0.94); }
}

/* ==========================================================================
   Reduced motion — the composition stays, the movement goes
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    .bg__aurora::before,
    .bg__aurora::after,
    .bg__grid,
    .bg__glow,
    .card,
    .card::before,
    .error {
        animation: none !important;
    }
    /* The script never attaches its listener in this mode, so this would be a
       bright disc parked wherever the default centre happens to be. */
    .bg__glow { display: none; }
    /* The beam only exists as movement, so it is removed rather than frozen
       mid-sweep where it would read as an unexplained bright band. */
    .bg__beam { display: none; }
    /* Held at the mid-point of their drift, which is the composition the
       animation was designed around. */
    .bg__aurora::before { transform: translate3d(1%, 1.5%, 0) scale(1.09); }
    .bg__aurora::after  { transform: translate3d(-0.5%, -0.5%, 0) scale(1.06); }
    button[type="submit"]::after { display: none; }
    .pagefoot__heart { animation: none; }
}
