/* ==========================================================================
   Magic Login — the shared passwordless sign-in screen.

   Ported from the "TicketPulse Design system" Claude Design project, template
   templates/magic-login/MagicLogin.dc.html. A PORT, not a fork: the template's
   values are reproduced so a change there can be diffed against this file.

   NO TOKENS ARE DEFINED HERE. Every brand colour is read as
   var(--brand-primary, <literal>) exactly as the template writes it. Where a
   host ships the design system's token port — Apps.PulseTV/wwwroot/css/tokens.css
   and PulseStudios.Web/wwwroot/tokens.css both carry the [data-brand="…"] role
   blocks — the variable resolves and the door wears that brand's hue. Where a
   host has no port (Apps.TicketPulse), the fallbacks ARE the master TicketPulse
   values, so it renders correctly without one. Adding a third token source would
   be the thing that eventually drifts.

   Two deliberate deviations from the template, both stated rather than silent:

   1. The template's style-hover / style-focus attributes exist only in the
      design-tool runtime. Here they are real :hover / :focus-visible rules.

   2. The template is drawn at desktop width and has no mobile state. Below 900px
      the brand panel becomes a compact header strip instead of a full-height
      surface — a creator opening this at a venue is holding a phone, and a
      screen that spends its first viewport on decoration puts the email field
      below the fold.
   ========================================================================== */

.ml {
    /* Local, not tokens: these describe this layout, not the brand. */
    --ml-ink: var(--tp-black);
    --ml-muted: var(--tp-grey-text);
    --ml-surface: var(--tp-grey-bg);
    --ml-panel-wash: linear-gradient(165deg, rgba(68, 0, 153, 0.85) 0%, rgba(46, 0, 87, 0.94) 100%);

    /* Used only when a photograph sits behind the panel. The template's 0.85 opening stop is
       measured against its own artwork; over an arbitrary photo it is not a contrast guarantee, and
       every word on this panel is white. The heavier stops keep the text legible over a bright
       frame — a stage shot is mostly highlights — at the cost of a little of the picture. */
    --ml-panel-wash-photo: linear-gradient(165deg, rgba(68, 0, 153, 0.92) 0%, rgba(46, 0, 87, 0.97) 100%);

    /* Owns the viewport rather than sitting in the host page's flow. This is the "independent"
       part of an independent layout, and it is load-bearing: Apps.TicketPulse's global stylesheet
       puts padding-top: 90px on body to clear its fixed site header, which this layout does not
       render — in normal flow that reserved space became a 90px white band above the screen and
       pushed the bottom of it out of view. Any host with a fixed header would have done the same,
       so the layout stops depending on the host's body box entirely.

       overflow-y: auto so the panel and the form scroll together on a short viewport; z-index
       clears the host's own fixed furniture (TicketPulse's back-to-top button sits at 999). */
    position: fixed;
    inset: 0;
    z-index: 1000;
    overflow-y: auto;
    display: flex;
    flex-wrap: wrap;
    align-content: stretch;
    font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    color: var(--ml-ink);
    background: var(--tp-white);
}

/* PulseTV and Pulse Studios lead with their own hue, so the panel wash follows the brand rather
   than staying master purple on all three doors. */
.ml[data-brand="pulsetv"] {
    --ml-panel-wash: linear-gradient(165deg, rgba(106, 0, 255, 0.85) 0%, rgba(68, 0, 153, 0.94) 100%);
}

.ml[data-brand="pulse-studios"] {
    --ml-panel-wash: linear-gradient(165deg, rgba(74, 0, 168, 0.85) 0%, rgba(46, 0, 87, 0.94) 100%);
}

/* -------------------------------------------------------------------------
   Brand panel
   ------------------------------------------------------------------------- */

.ml__brand {
    flex: 1 1 460px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 48px;
    padding: 56px;
    box-sizing: border-box;
    color: var(--tp-white);
    background-image: var(--ml-panel-wash);
    background-size: cover;
    background-position: center;
}

.ml__eyebrow {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.14em;
    opacity: 0.85;
}

.ml__pitch {
    display: flex;
    flex-direction: column;
    gap: 24px;
    max-width: 440px;
}

.ml__headline {
    margin: 0;
    font-size: 42px;
    line-height: 1.1;
    letter-spacing: -0.02em;
    font-weight: 700;
}

.ml__lede {
    margin: 0;
    font-size: 16px;
    line-height: 1.6;
    opacity: 0.85;
}

.ml__products {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 15px;
}

.ml__products li {
    display: flex;
    align-items: center;
    gap: 12px;
}

.ml__products svg {
    flex-shrink: 0;
    opacity: 0.9;
}

.ml__products-verb { opacity: 0.75; }

.ml__colophon {
    font-size: 12px;
    opacity: 0.6;
}

/* -------------------------------------------------------------------------
   Form column
   ------------------------------------------------------------------------- */

.ml__form {
    flex: 1 1 420px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 48px 32px;
    box-sizing: border-box;
    background: var(--tp-white);
}

/* 440 rather than the template's 400: the three-mark row is the widest thing in this column, and at
   400 the highlighted mark pushed the third one onto a second line. */
.ml__form-inner {
    width: 100%;
    max-width: 440px;
}

/* Reserved height so the column does not jump when the marks finish loading. */
.ml__lockup {
    margin-bottom: 36px;
    min-height: 68px;
}

.ml__sub {
    margin-top: 6px;
    font-size: 13px;
    font-weight: 500;
    color: var(--ml-muted);
}

/* One row, baseline-aligned rather than centred: the three marks have different aspect ratios, and
   centring makes the shorter ones look as if they are floating.

   nowrap is deliberate. The three marks together ARE the message — one sign-in, these three doors —
   and a row that wraps reads as two unrelated groups. The sizes below are chosen to fit the column
   at every breakpoint rather than being allowed to wrap out of trouble. */
.ml__family {
    display: flex;
    align-items: flex-end;
    gap: 14px;
    flex-wrap: nowrap;
}

/* Each mark gets a BOX and is contained inside it, rather than being sized by height. The three
   lockups are shaped very differently — TicketPulse is a stacked icon-over-wordmark at about 5:3,
   the PulseTV and Pulse Studios marks are wide wordmarks — so a shared height renders TicketPulse
   at roughly 70px across and PulseTV at 90px, which made the highlighted mark look SMALLER than the
   muted one beside it. A fixed box equalises the space each occupies whatever its proportions.

   The two brands whose door this is not are held back rather than hidden: the point is that the
   same sign-in opens them, so they must read as present and secondary at once. Colour returns on
   hover for anyone checking which mark is which. */
.ml__family-logo {
    flex: 0 0 auto;
    width: 106px;
    height: 38px;
    object-fit: contain;
    object-position: left center;
    opacity: 0.38;
    filter: grayscale(1);
    transition: opacity 0.2s ease, filter 0.2s ease;
}

.ml__family-logo:hover { opacity: 0.7; }

/* This door's brand: full colour in a box half again as large, so which one it is survives being
   glanced at rather than read. Greyscale does most of the separating — on a page where every other
   accent is already the brand hue, size alone would not be enough. */
.ml__family-logo--current {
    width: 168px;
    height: 62px;
    opacity: 1;
    filter: none;
}

@media (prefers-reduced-motion: reduce) {
    .ml__family-logo { transition: none; }
}

.ml__staff {
    margin-top: 32px;
    padding: 14px 16px;
    background: var(--ml-surface);
    border-radius: 12px;
    display: flex;
    gap: 10px;
    align-items: flex-start;
    color: var(--ml-muted);
}

.ml__staff svg {
    flex-shrink: 0;
    margin-top: 1px;
}

.ml__staff p {
    margin: 0;
    font-size: 13px;
    line-height: 1.5;
}

.ml__staff b { color: var(--ml-ink); }

/* -------------------------------------------------------------------------
   Page content — the classes each sign-in page renders into @Body
   ------------------------------------------------------------------------- */

.ml-stack {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.ml-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 12px;
    border-radius: 999px;
    background: var(--brand-tint, var(--pulse-lavender));
    color: var(--brand-primary, var(--tp-purple));
    font-size: 13px;
    font-weight: 700;
}

.ml-title {
    margin: 0 0 8px;
    font-size: 30px;
    line-height: 1.1;
    letter-spacing: -0.02em;
    font-weight: 700;
}

/* The sent and verified states lead with an icon, so their heading sits lower in the hierarchy
   visually while staying the page's h1 for assistive technology. */
.ml-title--sm { font-size: 26px; }

.ml-lede {
    margin: 0;
    font-size: 15px;
    line-height: 1.6;
    color: var(--ml-muted);
}

.ml-lede b { color: var(--ml-ink); }

.ml-field {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.ml-label {
    font-size: 13px;
    font-weight: 700;
    color: var(--ml-ink);
}

.ml-hint {
    font-size: 13px;
    line-height: 1.5;
    color: var(--ml-muted);
}

.ml-input {
    width: 100%;
    box-sizing: border-box;
    padding: 13px 16px;
    font-family: inherit;
    font-size: 15px;
    color: var(--ml-ink);
    background: var(--ml-surface);
    border: 2px solid transparent;
    border-radius: 8px;
    outline: none;
    transition: all 0.2s ease;
}

.ml-input::placeholder { color: var(--ml-muted); }

/* :focus-visible alone would leave a mouse user with no visible focus on a field they clicked
   into, which on a single-field form is the whole page state. */
.ml-input:focus {
    border-color: var(--brand-primary, var(--tp-purple));
    background: var(--tp-white);
    box-shadow: 0 0 0 3px rgba(107, 63, 160, 0.35);
}

.ml-input[aria-invalid="true"] { border-color: var(--tp-accent-orange); }

.ml-input--code {
    font-family: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
    font-size: 20px;
    letter-spacing: 0.4em;
    text-align: center;
}

.ml-error {
    font-size: 13px;
    font-weight: 500;
    color: var(--tp-accent-orange);
}

/* Empty until something goes wrong; without this the live region reserves a blank line and the
   button jumps down the first time an error appears. */
.ml-error:empty { display: none; }

.ml-notice {
    font-size: 13px;
    font-weight: 500;
    color: var(--ml-muted);
}

.ml-notice:empty { display: none; }

.ml-btn {
    width: 100%;
    padding: 14px 20px;
    font-family: inherit;
    font-size: 15px;
    font-weight: 700;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.2s ease;
}

.ml-btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.ml-btn--primary {
    background: var(--brand-primary, var(--tp-purple));
    color: var(--tp-white);
}

.ml-btn--primary:hover:not(:disabled) {
    background: var(--brand-primary-dark, var(--tp-purple-dark));
    box-shadow: 0 8px 32px rgba(68, 0, 153, 0.12);
}

.ml-btn--secondary {
    padding: 12px 20px;
    background: var(--tp-white);
    border: 2px solid var(--brand-primary, var(--tp-purple));
    color: var(--brand-primary, var(--tp-purple));
}

.ml-btn--secondary:hover:not(:disabled) { background: var(--brand-tint, var(--pulse-lavender)); }

.ml-btn--link {
    width: auto;
    align-self: center;
    padding: 4px;
    background: none;
    border: none;
    color: var(--ml-muted);
    font-size: 14px;
    font-weight: 500;
    text-decoration: underline;
}

.ml-btn--link:hover:not(:disabled) { color: var(--ml-ink); }

.ml-btn:active:not(:disabled) { transform: scale(0.97); }

.ml-btn:focus-visible,
.ml-link:focus-visible {
    outline: 3px solid var(--brand-primary, var(--tp-purple));
    outline-offset: 2px;
}

.ml-btn-row {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.ml-status-icon {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--brand-tint, var(--pulse-lavender));
    color: var(--brand-primary, var(--tp-purple));
}

/* Success is teal across the design system regardless of which brand is on the door — it reads as
   "this worked", not as "this is PulseTV". */
.ml-status-icon--done {
    background: rgba(0, 201, 167, 0.12);
    color: var(--tp-accent-teal);
}

.ml-legal,
.ml-legal p {
    margin: 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--ml-muted);
}

.ml-link,
.ml-legal a {
    color: var(--brand-primary, var(--tp-purple));
    font-weight: 500;
}

.ml-checklist {
    margin: 0;
    padding-left: 20px;
    font-size: 14px;
    line-height: 1.6;
    color: var(--ml-muted);
}

.ml-checklist b,
.ml-checklist strong { color: var(--ml-ink); }

.ml-mono {
    font-family: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
    font-size: 0.9em;
    color: var(--ml-ink);
}

/* -------------------------------------------------------------------------
   Small screens — see deviation 2 at the top of this file
   ------------------------------------------------------------------------- */

@media (max-width: 900px) {
    .ml { display: block; }

    .ml__brand {
        gap: 16px;
        padding: 24px 24px 28px;
        min-height: 0;
    }

    /* The pitch and colophon are the parts that cost a viewport. The eyebrow and headline stay:
       they say which door this is, which is the one thing worth the space. */
    .ml__pitch { gap: 12px; }
    .ml__lede,
    .ml__products,
    .ml__colophon { display: none; }

    .ml__headline { font-size: 28px; }

    .ml__form { padding: 32px 24px 48px; }
    .ml__lockup { margin-bottom: 24px; }
}

/* Narrow phones. The row keeps all three marks on one line here too — scaled down rather than
   wrapped, because the three-together is the point. At 375px this comes to 300px of marks plus
   20px of gaps inside a 327px column. */
@media (max-width: 480px) {
    .ml__family { gap: 10px; }

    .ml__family-logo {
        width: 80px;
        height: 30px;
    }

    .ml__family-logo--current {
        width: 130px;
        height: 48px;
    }

    .ml__lockup { min-height: 48px; }
}

@media (prefers-reduced-motion: reduce) {
    .ml-input,
    .ml-btn { transition: none; }

    .ml-btn:active:not(:disabled) { transform: none; }
}
