/* Sign-in page for the OM Energy Media Benchmark.
 *
 * Same tokens, same paper, same type as the dashboard behind it, so signing in does not
 * feel like a different product. Three constraints run through the whole file:
 *
 *   1. The live CSP is `style-src 'self'` with no 'unsafe-inline'. There is no <style>
 *      block and no style="" attribute anywhere, including inside the SVG. All colour
 *      comes from classes here; only geometry is a presentation attribute.
 *   2. Nothing loads from another origin. No webfont, no @font-face, no data: URI.
 *   3. Spacing uses one scale - 4 8 12 16 20 24 32 40 56 72 96 - and radii are 4, 6, 8.
 *      If a new number appears in a margin or a padding, it is a mistake, not a nudge.
 */

:root {
  --ink: #0f1729;
  --muted: #4b5768;
  --subtle: #7a8798;
  --line: #e3e8ef;
  --line-strong: #cbd3df;
  --panel: #ffffff;
  --panel-soft: #f8fafc;

  --brand: #4a3aa7;
  --brand-dark: #2f2568;
  --brand-wash: #f6f5fc;

  --elec: #2a78d6;
  --gas-ink: #b8471a;
  --red: #c0362c;

  --measure: 1200px;
  --pad: clamp(20px, 4vw, 56px);
}

*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: Inter, "IBM Plex Sans", "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 16px;
  color: var(--ink);
  color-scheme: light;
  /* The same paper the dashboard is printed on. */
  background: linear-gradient(180deg, #ffffff 0, #fafbfd 420px, #f2f5f9 100%);
  background-attachment: fixed;
}

/* Violet arrives from the same corner as it does in the app header. */
.wash {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: radial-gradient(120% 160% at 96% 0%,
      rgba(74, 58, 167, 0.075) 0%, rgba(74, 58, 167, 0.03) 34%, transparent 60%);
}

/* Bands span the viewport so their rules bleed to both edges; content sits in a measure.
   At 1920 the empty margin is ruled, which is what makes the page read as a faceplate
   rather than as a centred box. */
.band { position: relative; z-index: 1; }
.measure { max-width: var(--measure); margin-inline: auto; padding-inline: var(--pad); }

p { margin: 0; }

/* --- masthead ---------------------------------------------------------------------- */

.band--head { border-bottom: 1px solid var(--line-strong); }
.band--head .measure {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-block: 20px;
  gap: 16px;
}

.brand { display: flex; align-items: center; gap: 12px; min-width: 0; }

/* The PNG is opaque and carries its own dark ground, so it is used as a tile and never
   scaled into a wordmark on light. */
.tile {
  width: 40px; height: 40px;
  border-radius: 8px;
  object-fit: cover;
  border: 1px solid rgba(15, 23, 41, 0.10);
  flex: 0 0 auto;
}

.wordmark { font-size: 14px; font-weight: 700; letter-spacing: -0.004em; }

.lang {
  display: inline-flex;
  padding: 4px;
  gap: 4px;
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  flex: 0 0 auto;
}
.lang a {
  position: relative;
  min-width: 44px; min-height: 32px;
  display: grid; place-items: center;
  border-radius: 4px;
  font-size: 11px; font-weight: 800; letter-spacing: 0.03em;
  color: var(--muted);
  text-decoration: none;
}
/* Drawn at 44x32, but the tap target is a full 44x44. */
.lang a::after { content: ""; position: absolute; inset: -6px -2px; }
.lang a:hover { color: var(--ink); }
.lang a.is-active { background: var(--brand); color: #ffffff; }

/* --- main band --------------------------------------------------------------------- */

.band--main .measure { padding-block: 48px 56px; }

.eyebrow {
  font-size: 11px; font-weight: 800; line-height: 1.2;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--brand);
}

h1 {
  margin: 16px 0 0;
  font-size: clamp(28px, 2.4vw, 38px);
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: -0.018em;
  text-wrap: balance;
  /* Two lines are reserved in both languages so switching EN/CZ shifts nothing below. */
  min-height: calc(2 * 1.12em);
}

.standfirst {
  margin-top: 20px;
  max-width: 46ch;
  font-size: 16px; line-height: 1.55;
  color: var(--muted);
}

.signals {
  border-top: 1px solid var(--line);
  padding-top: 20px;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, auto));
  column-gap: 56px;
  row-gap: 20px;
  justify-content: start;
}
.sfigure {
  font-size: 26px; font-weight: 800; line-height: 1.05;
  letter-spacing: -0.012em;
  font-variant-numeric: tabular-nums lining-nums;
}
.slabel {
  margin-top: 8px;
  max-width: 22ch;
  font-size: 12px; font-weight: 600; line-height: 1.35;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted);
}

/* --- the recess -------------------------------------------------------------------- */

.access {
  background: var(--brand-wash);
  border: 1px solid var(--line);
  border-top: 2px solid var(--brand);
  border-radius: 4px;
  padding: 24px;
  /* No box-shadow, ever. The recess reads as milled into the sheet; a drop shadow
     instantly re-casts it as a generic web card. box-shadow on this page is reserved for
     the focus halo and nothing else. */
}
.access.is-error { border-top-color: var(--red); }

.access h2 {
  margin: 0;
  font-size: 20px; font-weight: 700; line-height: 1.3;
  letter-spacing: -0.012em;
}
.access .intro {
  margin-top: 8px;
  font-size: 13px; line-height: 1.5;
  color: var(--muted);
}
.access form { margin-top: 24px; }

.field + .field { margin-top: 20px; }
.field label {
  display: block;
  margin-bottom: 8px;
  font-size: 12px; font-weight: 700; line-height: 1.2;
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--muted);
}
.field input {
  width: 100%;
  /* min-height, never height: text-spacing overrides must not clip the value. */
  min-height: 48px;
  padding: 0 14px;
  font: 400 16px/1.2 inherit;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
.field input:hover { border-color: #b9c3d2; }
.field input:focus-visible {
  border-color: var(--brand);
  outline: 2px solid var(--brand-dark);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(74, 58, 167, 0.18);
}
/* Fields never turn red. Marking both implies both were wrong; marking one is an oracle.
   The recess's top edge carries the error state instead. */
.field--pw { position: relative; }
.field--pw input { padding-right: 52px; }
.field input::-ms-reveal { display: none; }

input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ink);
  box-shadow: inset 0 0 0 100px var(--panel);
  transition: background-color 9999s ease-in-out 0s;
}

/* Progressive enhancement: without scripting there is nothing to toggle, so the control
   is not shown at all. */
.reveal { display: none; }
.js .reveal {
  position: absolute;
  right: 2px; bottom: 2px;
  width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent;
  border: 0;
  border-radius: 6px;
  color: var(--muted);
  cursor: pointer;
}
.js .reveal:hover { color: var(--ink); background: var(--panel-soft); }
.js .reveal:focus-visible {
  outline: 2px solid var(--brand-dark);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(74, 58, 167, 0.18);
}
.reveal-icon { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.5; }
.reveal-slash { display: none; }
.reveal[aria-pressed="true"] .reveal-slash { display: inline; }

.submit {
  width: 100%;
  min-height: 48px;
  margin-top: 24px;
  border: 0;
  border-radius: 4px;
  background: var(--brand);
  color: #ffffff;
  font: 700 15px/1 inherit;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: background-color 140ms ease, transform 90ms ease;
}
.submit:hover { background: var(--brand-dark); }
.submit:active { transform: translateY(1px); }
.submit:focus-visible {
  outline: 2px solid var(--brand-dark);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px #ffffff, 0 0 0 7px rgba(74, 58, 167, 0.18);
}
/* Busy is the label swap, aria-busy and a darker fill. No spinner, so nothing has to be
   hidden under reduced motion. The button is never disabled before submit: an empty form
   must reach the server, or the denial stops being uniform. */
.submit[aria-busy="true"] { background: var(--brand-dark); cursor: progress; }

.access-rule {
  margin: 20px -24px 0;
  border: 0;
  border-top: 1px solid var(--line);
}
.note {
  margin-top: 16px;
  font-size: 12px; line-height: 1.5;
  color: var(--muted);
}
.note + .note { margin-top: 8px; }

/* --- the single denial -------------------------------------------------------------- */

.denial {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  margin-top: 24px;
  padding: 12px 16px;
  background: #fdf3f2;
  border: 1px solid #f0cdc8;
  border-left: 3px solid var(--red);
  border-radius: 4px;
  font-size: 13px; line-height: 1.45;
  color: var(--ink);
}
.denial:focus-visible { outline: 2px solid var(--brand-dark); outline-offset: 2px; }
.denial-glyph {
  width: 16px; height: 16px;
  flex: 0 0 auto;
  margin-top: 4px;
  color: var(--red);
  stroke: currentColor; stroke-width: 1.5; fill: none;
}

/* --- coverage panel ----------------------------------------------------------------- */

.band--panel {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.band--panel .measure { padding-block: 32px; }

.pn-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 20px;
  margin-bottom: 12px;
}
.pn-label {
  font-size: 11px; font-weight: 800; line-height: 1.2;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--brand);
}
.pn-span {
  margin-left: 12px;
  font-weight: 600; letter-spacing: 0.04em;
  color: var(--muted);
}
.pn-legend {
  display: flex;
  gap: 20px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.04em;
  color: var(--muted);
}
/* The legend key is a specimen of the mark itself, at its exact drawn height. */
.key {
  display: inline-block;
  width: 14px; height: 3px;
  margin-right: 8px;
  vertical-align: middle;
}
.key--e { background: var(--elec); }
.key--g { background: var(--gas-ink); }

/* Rendered height equals the viewBox height, so y maps 1:1 to CSS pixels and only x
   stretches. That is what stops two rows merging on a narrow screen. */
.pn { display: block; width: 100%; height: 150px; }

.pn-grid line {
  stroke: var(--line-strong);
  stroke-width: 1;
  stroke-dasharray: 1 4;
  vector-effect: non-scaling-stroke;
}
.pn-tracks rect { fill: var(--line); }
.pn-bar--e { fill: var(--elec); }
.pn-bar--g { fill: var(--gas-ink); }
.pn-base { fill: var(--line-strong); }
.pn-axis line {
  stroke: var(--subtle);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  shape-rendering: crispEdges;
}
.pn-axis line.pn-end { stroke: var(--brand); stroke-width: 2; }

.pn-labels {
  position: relative;
  height: 14px;
  margin-top: 8px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.04em;
  color: var(--muted);
}
.pn-labels span { position: absolute; transform: translateX(-50%); white-space: nowrap; }
.pn-labels .start { left: 0; transform: none; }
.pn-labels .end { left: 100%; transform: translateX(-100%); }
.y21 { left: 15.789%; }
.y22 { left: 31.579%; }
.y23 { left: 47.368%; }
.y24 { left: 63.158%; }
.y25 { left: 78.947%; }

.pn-caption, .honesty {
  font-size: 13px; line-height: 1.55;
  color: var(--muted);
  max-width: 76ch;
}
.pn-caption { margin-top: 16px; }
.honesty { margin-top: 8px; }

/* --- footer ------------------------------------------------------------------------- */

.band--foot { margin-top: auto; }
.band--foot .measure { padding-block: 16px 20px; }
.band--foot p { font-size: 12px; line-height: 1.5; color: var(--muted); }

/* --- desktop composition ------------------------------------------------------------ */

@media (min-width: 1024px) {
  .band--main .measure {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 384px;
    grid-template-areas:
      "identity access"
      "signals  access";
    column-gap: 96px;
    row-gap: 40px;
    align-items: start;
  }
  .identity { grid-area: identity; }
  .access { grid-area: access; align-self: start; }
  .signals { grid-area: signals; }
}

@media (min-width: 1024px) and (max-width: 1279px) {
  .band--main .measure { column-gap: 56px; grid-template-columns: minmax(0, 1fr) 360px; padding-block: 40px 48px; }
}

@media (max-width: 1023px) {
  /* iOS Safari repaints a fixed background on every keyboard event. */
  body { background-attachment: scroll; }
  .band--main .measure { padding-block: 40px 48px; }
  .access { margin-top: 32px; }
  .signals { margin-top: 32px; }
}

@media (max-width: 767px) {
  .band--main .measure { padding-block: 32px 40px; }
  .tile { width: 36px; height: 36px; }
  .access { padding: 20px; }
  .access-rule { margin-inline: -20px; }
  .sfigure { font-size: 20px; }
  .signals { column-gap: 32px; }
  .pn-head { flex-wrap: wrap; row-gap: 8px; }
}

@media (max-width: 640px) {
  /* The measure is now narrow enough that "Jan 2020" runs into the 2021 label. Drop the
     odd years: the axis still reads left to right in even steps, and the two endpoints -
     the ones the caption names - stay. */
  .y21, .y23, .y25 { display: none; }
  /* Let the coverage span sit on its own line instead of wrapping mid-phrase. */
  .pn-span { display: block; margin-left: 0; margin-top: 4px; }
}

@media (max-width: 560px) {
  .signals { grid-template-columns: minmax(0, 1fr); }
}

/* --- motion ------------------------------------------------------------------------- */

/* Entrances live only inside the no-preference query, so the base stylesheet already
   describes the final state and nothing can ever be stuck invisible. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
  @keyframes drop { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
  @keyframes fade { from { opacity: 0; } to { opacity: 1; } }
  @keyframes wipe { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }

  .band--head { animation: drop 280ms cubic-bezier(.22,.61,.36,1) both; }
  .identity { animation: rise 320ms 60ms cubic-bezier(.22,.61,.36,1) both; }
  .access { animation: rise 320ms 110ms cubic-bezier(.22,.61,.36,1) both; }
  .signals { animation: fade 300ms 200ms ease-out both; }
  /* Only the bars wipe. The grid, tracks, baseline and axis are painted at frame one, so
     the panel is a frame that gains evidence rather than an empty box that fills. */
  .pn-bars { animation: wipe 800ms 260ms cubic-bezier(.16,1,.3,1) both; }
  .pn-head, .pn-labels, .pn-caption, .honesty { animation: fade 300ms 400ms ease-out both; }
  .denial { animation: drop 180ms ease-out both; }

  /* A refused sign-in re-renders the page. Without this the whole entrance replays and
     the form the reader is trying to correct is invisible for the first 400ms. Only the
     denial itself animates. */
  .is-denied .band--head,
  .is-denied .identity,
  .is-denied .access,
  .is-denied .signals,
  .is-denied .pn-bars,
  .is-denied .pn-head,
  .is-denied .pn-labels,
  .is-denied .pn-caption,
  .is-denied .honesty { animation: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition-duration: 1ms !important;
  }
  .pn-bars { clip-path: none; }
  .submit:active { transform: none; }
}

/* --- forced colors ------------------------------------------------------------------ */

@media (forced-colors: active) {
  .wash { display: none; }
  .access, .field input, .lang, .denial { border: 1px solid CanvasText; }
  .submit { forced-color-adjust: none; background: Highlight; color: HighlightText; }
  .pn-bar--e, .pn-bar--g { fill: CanvasText; }
  .pn-tracks rect { fill: GrayText; }
  .pn-grid line, .pn-axis line { stroke: GrayText; }
  .key--e, .key--g { forced-color-adjust: none; background: CanvasText; }
  :focus-visible { outline: 3px solid Highlight; outline-offset: 2px; }
}

/* --- email sign-in ------------------------------------------------------------------ */

/* Both states are server-rendered and one carries the `hidden` attribute, so the page
   works with scripting off and nothing flashes on load. */
.state-form[hidden], .state-sent[hidden] { display: none; }

.state-sent .intro { max-width: 42ch; }
.state-sent .note a { color: var(--brand); }

/* The shared password stays reachable while the email route is being proven, but it is
   not the thing being offered. A <details> keeps it available and quiet, and it opens
   without JavaScript. */
.fallback {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.fallback[hidden] { display: none; }
.fallback summary {
  font-size: 13px;
  color: var(--muted);
  cursor: pointer;
  list-style: none;
}
.fallback summary::-webkit-details-marker { display: none; }
.fallback summary::before {
  content: "+";
  display: inline-block;
  width: 16px;
  font-weight: 700;
  color: var(--brand);
}
.fallback[open] summary::before { content: "\2212"; }
.fallback summary:hover { color: var(--ink); }
.fallback summary:focus-visible {
  outline: 2px solid var(--brand-dark);
  outline-offset: 2px;
  border-radius: 4px;
}
.fallback .intro { margin-top: 12px; }
.fallback form { margin-top: 16px; }

/* Quieter than the primary action: this is the way in that is on its way out. */
.submit--quiet {
  background: var(--panel);
  color: var(--brand-dark);
  border: 1px solid var(--line-strong);
}
.submit--quiet:hover { background: var(--panel-soft); color: var(--brand-dark); }
