/* ==========================================================================
   Grem component library.

   Every rule below reads semantic tokens from tokens.css. There is not one
   literal color, spacing value, radius, or duration in this file, which is
   what stops the fifth card getting 13px of padding because it looked right
   that day.

   One Button with variants, not a PrimaryButton and a SubmitButton. One
   Field that owns its label, help text, and error together, because their
   relationship is the thing that goes wrong when they are assembled by hand.
   ========================================================================== */

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

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

/* The `hidden` attribute has to beat every display rule in this file.
   [hidden] comes from the user agent stylesheet, so an author rule as
   ordinary as `.form { display: flex }` overrides it and leaves a hidden
   element on screen. This is the one place !important is the correct tool:
   there is no state in which something marked hidden should render. */
[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--color-surface-page);
  color: var(--color-text-primary);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-base);
  -webkit-font-smoothing: antialiased;
}

/* Focus is never removed anywhere on this site. :focus-visible keeps the
   ring off mouse clicks without taking it away from the keyboard. */
:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus-ring);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-xs);
}

/* The correct reduction is not "turn off transitions". Movement becomes a
   fade, and the state feedback stays. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

::selection {
  background: var(--color-action-subtle);
  color: var(--color-on-subtle);
}

/* --------------------------------------------------------------------------
   Type
   -------------------------------------------------------------------------- */

h1, h2, h3, h4 {
  margin: 0;
  color: var(--color-text-primary);
  font-weight: var(--weight-semibold);
  letter-spacing: -0.01em;
}

h1 { font-size: var(--text-2xl); line-height: var(--leading-2xl); }
h2 { font-size: var(--text-xl);  line-height: var(--leading-xl); }
h3 { font-size: var(--text-md);  line-height: var(--leading-md); }
h4 { font-size: var(--text-base); line-height: var(--leading-base); }

@media (min-width: 720px) {
  h1 { font-size: var(--text-3xl); line-height: var(--leading-3xl); }
}

p { margin: 0; }

/* Running prose is capped so lines stay inside 45 to 75 characters. */
.prose { max-width: var(--measure); }

.lead {
  font-size: var(--text-md);
  line-height: var(--leading-md);
  color: var(--color-text-secondary);
}

.muted { color: var(--color-text-tertiary); }

.small {
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
}

.eyebrow {
  margin: 0;
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

.mono {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
}

/* Links in running prose stay underlined. Links in navigation do not, since
   position already carries the signal there. */
.link {
  color: var(--color-action);
  text-decoration: underline;
  text-underline-offset: var(--space-1);
  border-radius: var(--radius-xs);
}
.link:hover { color: var(--color-action-hover); }

/* Text has no container to overlay, so the press shifts the text color
   itself toward the primary text color. That is darker in light mode and
   lighter in dark mode, so it raises contrast in both rather than risking
   it. */
.link:active {
  color: color-mix(in srgb, var(--color-text-primary)
                   var(--state-pressed-opacity), var(--color-action-hover));
}

/* A link standing alone is an action rather than part of a sentence, so it
   takes a real target. The inline exemption in WCAG 2.5.8 does not apply to
   it, and 22px of line box is not something to aim at. */
.link--standalone {
  display: inline-flex;
  align-items: center;
  min-height: var(--size-target-min);
}

/* --------------------------------------------------------------------------
   Layout primitives
   -------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--width-page);
  margin-inline: auto;
  padding-inline: var(--space-gutter);
}

@media (min-width: 720px) {
  .container { padding-inline: var(--space-gutter-lg); }
}

/* Width caps for the content inside a container.
   These cap the measure without centering, so every block on every page
   starts at the same left edge as the wordmark. Centering each one instead
   would make sections drift left and right down the page, which is the
   single fastest way to make a layout feel unplanned. */
.measure       { max-width: var(--width-text); }
.measure--form { max-width: var(--width-form); }

.stack     { display: flex; flex-direction: column; gap: var(--space-stack); }
.stack--tight { gap: var(--space-stack-tight); }
.stack--loose { gap: var(--space-stack-loose); }
.stack--section { gap: var(--space-section); }

.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}
.row--between { justify-content: space-between; }
.row--end { justify-content: flex-end; }

.section { padding-block: var(--space-section); }

@media (min-width: 720px) {
  .section { padding-block: var(--space-9); }
}

.grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: 1fr;
}

@media (min-width: 720px) {
  .grid--2 { grid-template-columns: repeat(2, 1fr); }
  .grid--3 { grid-template-columns: repeat(3, 1fr); }
}

.divider {
  height: var(--border-width);
  border: 0;
  margin: 0;
  background: var(--color-border);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Skip link. Visible the moment it takes focus, and nowhere before.

   It carries .button as well, so its rest, hover, pressed, focus, and
   disabled states are the Button component's rather than a second set
   maintained by hand. All that belongs here is where it sits. */
.skip-link {
  position: absolute;
  left: var(--space-4);
  top: calc(-1 * var(--space-11));
  z-index: 10;
  transition: top var(--duration-fast) var(--ease-decelerate);
}
.skip-link:focus { top: var(--space-4); }

/* --------------------------------------------------------------------------
   Button

   One component. Variants are modifiers, sizes are modifiers, and the
   component owns all five states so no call site ever defines a hover.
   -------------------------------------------------------------------------- */

.button {
  /* Component-local tokens, defaulting to the secondary variant. */
  --button-background: var(--color-surface-raised);
  --button-foreground: var(--color-text-primary);
  --button-border: var(--color-border-strong);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);

  min-height: var(--size-target-min);
  padding: var(--space-3) var(--space-5);

  background: var(--button-background);
  color: var(--button-foreground);
  border: var(--border-width) solid var(--button-border);
  border-radius: var(--radius-sm);

  font-family: inherit;
  font-size: var(--text-base);
  line-height: var(--leading-base);
  font-weight: var(--weight-medium);
  text-align: center;
  text-decoration: none;
  white-space: nowrap;

  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}

.button--primary {
  --button-background: var(--color-action);
  --button-foreground: var(--color-text-on-accent);
  --button-border: transparent;
}

.button--ghost {
  --button-background: transparent;
  --button-border: transparent;
  --button-foreground: var(--color-text-secondary);
  /* A borderless control needs about twice the breathing room of a
     bordered one, because whitespace is the only container it has. */
  padding: var(--space-3) var(--space-4);
}

.button--danger {
  --button-background: var(--color-danger);
  --button-foreground: var(--color-text-on-accent);
  --button-border: transparent;
}

/* The quiet half of a destructive pair. It still reads as destructive,
   through color and a border at 4.8:1, without dominating a page that has
   no primary action on it. The filled variant is kept for the step that
   cannot be undone. */
.button--danger-outline {
  --button-background: var(--color-surface-raised);
  --button-foreground: var(--color-danger);
  --button-border: var(--color-danger);
}

.button--lg {
  min-height: var(--size-control-lg);
  font-size: var(--text-md);
  line-height: var(--leading-md);
  padding: var(--space-3) var(--space-6);
}

.button--sm {
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
}

.button--block { display: flex; width: 100%; }

/* Hover and pressed are computed overlays of the button's own content
   color, so every variant stays correct without a second palette. */
.button:hover:not(:disabled):not([aria-disabled="true"]) {
  background: color-mix(in srgb, currentColor var(--state-hover-opacity),
                        var(--button-background));
}
.button:active:not(:disabled):not([aria-disabled="true"]) {
  background: color-mix(in srgb, currentColor var(--state-pressed-opacity),
                        var(--button-background));
}
.button:disabled,
.button[aria-disabled="true"] {
  background: color-mix(in srgb, var(--color-text-primary)
                        var(--state-disabled-container), transparent);
  color: color-mix(in srgb, var(--color-text-primary)
                   var(--state-disabled-content), transparent);
  border-color: transparent;
  cursor: not-allowed;
}

/* An icon-only control still needs a 44px target and a real label. */
.button--icon {
  padding: var(--space-2);
  min-width: var(--size-target-min);
}

.button__icon {
  width: var(--space-5);
  height: var(--space-5);
  flex: none;
}

/* --------------------------------------------------------------------------
   Field

   Label, control, help text, and error as one unit. The label is always
   visible and always above the control; a placeholder standing in for a
   label vanishes the moment typing starts, fails contrast by default, and
   is not reliably announced.
   -------------------------------------------------------------------------- */

.field { display: flex; flex-direction: column; gap: var(--space-1); }

.field__label {
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text-secondary);
}

.field__input {
  width: 100%;
  min-height: var(--size-control-lg);
  padding: var(--space-3);
  background: var(--color-surface-raised);
  color: var(--color-text-primary);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: var(--text-base);
  line-height: var(--leading-base);
  transition: border-color var(--duration-fast) var(--ease-standard);
}

.field__input::placeholder { color: var(--color-text-disabled); }
.field__input:hover:not(:disabled) { border-color: var(--color-text-secondary); }
.field__input:disabled {
  background: color-mix(in srgb, var(--color-text-primary)
                        var(--state-disabled-container), transparent);
  color: color-mix(in srgb, var(--color-text-primary)
                   var(--state-disabled-content), transparent);
  border-color: transparent;
  cursor: not-allowed;
}
.field__input[aria-invalid="true"] { border-color: var(--color-danger); }

/* A code is read back off a screen digit by digit, so it gets mono figures
   and room between them. */
.field__input--code {
  font-family: var(--font-mono);
  font-size: var(--text-xl);
  line-height: var(--leading-xl);
  letter-spacing: 0.24em;
  text-align: center;
}

.field__help,
.field__error {
  margin: 0;
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
}
.field__help { color: var(--color-text-tertiary); }
.field__error {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  color: var(--color-danger);
  font-weight: var(--weight-medium);
}

/* Proximity does the grouping. 4px from a label to its control, 24px to the
   next field. If those gaps were similar the form would read as a flat list
   and people would lose track of which label owns which input. */
.form { display: flex; flex-direction: column; gap: var(--space-stack-loose); }

/* --------------------------------------------------------------------------
   Card and panel
   -------------------------------------------------------------------------- */

.card {
  background: var(--color-surface-raised);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-inset-card);
}

@media (max-width: 719px) {
  .card { padding: var(--space-inset-card-sm); }
}

.card--raised { box-shadow: var(--shadow-1); }

/* A panel carries a heading and a body, and sets the rhythm between them. */
.panel { display: flex; flex-direction: column; gap: var(--space-4); }

.panel__header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

/* --------------------------------------------------------------------------
   Inline message

   Sits next to the thing it concerns. This is the default for validation
   and for anything contextual. Nothing on this site toasts.
   -------------------------------------------------------------------------- */

.message {
  --message-background: var(--color-surface-sunken);
  --message-foreground: var(--color-text-secondary);
  --message-accent: var(--color-border-strong);

  display: flex;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--message-background);
  color: var(--message-foreground);
  border-left: var(--space-1) solid var(--message-accent);
  border-radius: var(--radius-sm);
  /* Body size, not the 14px used for labels and help text. What goes in
     here is a sentence someone has to read, usually about a consequence,
     which makes it content rather than chrome. The title separates itself
     by weight at the same size, the way HIG separates Headline from Body. */
  font-size: var(--text-base);
  line-height: var(--leading-base);
}

.message--danger {
  --message-background: var(--color-danger-subtle);
  --message-foreground: var(--color-danger-on-subtle);
  --message-accent: var(--color-danger);
}
.message--success {
  --message-background: var(--color-success-subtle);
  --message-foreground: var(--color-success-on-subtle);
  --message-accent: var(--color-success);
}
.message--warning {
  --message-background: var(--color-warning-subtle);
  --message-foreground: var(--color-warning-on-subtle);
  --message-accent: var(--color-warning);
}
.message--accent {
  --message-background: var(--color-action-subtle);
  --message-foreground: var(--color-on-subtle);
  --message-accent: var(--color-action);
}

.message__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  /* Capped here as well as by the page column, because a message can sit in
     a container wider than a comfortable line. */
  max-width: var(--measure);
}

.message__title { font-weight: var(--weight-semibold); }

/* --message-foreground is the color measured against --message-background,
   so a link on that fill takes it rather than the page's action color. */
.message .link { color: var(--message-foreground); }

.message .link:hover {
  color: color-mix(in srgb, var(--color-text-primary)
                   var(--state-hover-opacity), var(--message-foreground));
}

.message .link:active {
  color: color-mix(in srgb, var(--color-text-primary)
                   var(--state-pressed-opacity), var(--message-foreground));
}

/* --------------------------------------------------------------------------
   Badge. Real status only. Not a decorative texture.
   -------------------------------------------------------------------------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-xs);
  background: var(--color-surface-sunken);
  color: var(--color-text-secondary);
  font-size: var(--text-xs);
  line-height: var(--leading-xs);
  font-weight: var(--weight-medium);
}

.badge--accent {
  background: var(--color-action-subtle);
  color: var(--color-on-subtle);
}

.badge__dot {
  width: var(--space-2);
  height: var(--space-2);
  border-radius: var(--radius-full);
  background: currentColor;
  flex: none;
}

/* --------------------------------------------------------------------------
   List row. One row per device, one per fact.
   -------------------------------------------------------------------------- */

.rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.row-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: var(--size-target-min);
  padding-block: var(--space-4);
  border-top: var(--border-width) solid var(--color-border);
}
.row-item:first-child { border-top: 0; }

.row-item__main { display: flex; flex-direction: column; gap: var(--space-1); }
.row-item__title { font-weight: var(--weight-medium); }
.row-item__meta {
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  color: var(--color-text-tertiary);
}

/* --------------------------------------------------------------------------
   Definition list, for the account summary. Numbers use tabular figures so
   digits line up down the column.
   -------------------------------------------------------------------------- */

.facts { margin: 0; display: grid; gap: var(--space-4); }

@media (min-width: 560px) {
  .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.facts__row { display: flex; flex-direction: column; gap: var(--space-1); }

.facts__key {
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  color: var(--color-text-tertiary);
}

.facts__value {
  margin: 0;
  font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   Code block. Used for commands and for the key derivation, both of which
   are content rather than ornament.
   -------------------------------------------------------------------------- */

.code {
  margin: 0;
  padding: var(--space-4);
  background: var(--color-surface-sunken);
  color: var(--color-text-secondary);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  overflow-x: auto;
  tab-size: 2;
}

.code b {
  color: var(--color-text-primary);
  font-weight: var(--weight-semibold);
}

/* --------------------------------------------------------------------------
   Table
   -------------------------------------------------------------------------- */

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
}

.table th,
.table td {
  text-align: left;
  vertical-align: top;
  padding: var(--space-3) var(--space-4) var(--space-3) 0;
  border-top: var(--border-width) solid var(--color-border);
}

.table th {
  font-weight: var(--weight-semibold);
  color: var(--color-text-secondary);
}

.table thead th { border-top: 0; }
.table td { color: var(--color-text-secondary); }
.table td:first-child { color: var(--color-text-primary); }

/* --------------------------------------------------------------------------
   Skeleton and empty state
   -------------------------------------------------------------------------- */

.skeleton {
  background: var(--color-surface-sunken);
  border-radius: var(--radius-xs);
  animation: skeleton-pulse var(--duration-slow) var(--ease-standard)
             infinite alternate;
}

/* Opacity only. No gradient sweeping across it, which is a banned effect
   and would also be a second moving thing to ignore. */
@keyframes skeleton-pulse {
  from { opacity: 1; }
  to   { opacity: 0.4; }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; opacity: 0.6; }
}

.skeleton--line { height: var(--space-4); }
.skeleton--title { height: var(--space-5); max-width: var(--space-11); }

.empty {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-5) 0;
  color: var(--color-text-tertiary);
}

/* --------------------------------------------------------------------------
   Header and footer

   Chrome is quiet: a neutral surface, secondary text, no accent fills. The
   accent belongs to the content layer and to the one primary action.
   -------------------------------------------------------------------------- */

.site-header {
  background: var(--color-surface-raised);
  border-bottom: var(--border-width) solid var(--color-border);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: var(--size-target-min);
  padding-block: var(--space-3);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--size-target-min);
  color: var(--color-text-primary);
  font-weight: var(--weight-semibold);
  font-size: var(--text-md);
  letter-spacing: -0.01em;
  text-decoration: none;
  border-radius: var(--radius-xs);
}

.brand__mark {
  width: var(--space-5);
  height: var(--space-5);
  color: var(--color-action);
  flex: none;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.site-nav__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--size-target-min);
  padding-inline: var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  font-weight: var(--weight-medium);
  text-decoration: none;
  transition: background-color var(--duration-fast) var(--ease-standard);
}

.site-nav__link:hover {
  background: color-mix(in srgb, currentColor var(--state-hover-opacity),
                        transparent);
}
.site-nav__link:active {
  background: color-mix(in srgb, currentColor var(--state-pressed-opacity),
                        transparent);
}

/* The current page is marked by weight and an underline, never by color
   alone. */
.site-nav__link[aria-current="page"] {
  color: var(--color-text-primary);
  font-weight: var(--weight-semibold);
  box-shadow: inset 0 -2px 0 0 var(--color-action);
}

/* Below the fold on a phone the nav labels wrap, so they drop to the same
   row underneath the brand rather than squeezing. */
@media (max-width: 559px) {
  .site-header__inner { flex-wrap: wrap; }
  .site-nav { width: 100%; justify-content: flex-start; }
  .site-nav__link { padding-inline: var(--space-2); }
}

.site-footer {
  margin-top: auto;
  background: var(--color-surface-raised);
  border-top: var(--border-width) solid var(--color-border);
  padding-block: var(--space-6);
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  color: var(--color-text-tertiary);
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.site-footer a { color: var(--color-text-secondary); }

.main { flex: 1; }

/* Inline code inside prose. The block form is .code above. */
:not(pre) > code {
  padding: 0 var(--space-1);
  background: var(--color-surface-sunken);
  border-radius: var(--radius-xs);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

/* The captcha renders into a fixed slot, so the form does not jump when the
   widget finishes loading. */
.captcha {
  min-height: var(--space-9);
  display: flex;
  align-items: center;
  /* The widget is a fixed-size third-party iframe. Keeping it inside its
     own scroll container means it can never widen the page, whatever size
     it decides to be. The compact variant is chosen in site.js so this
     almost never has to do anything. */
  max-width: 100%;
  overflow-x: auto;
}

.captcha:empty::after {
  content: "Loading the check that confirms you are a person";
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  color: var(--color-text-tertiary);
}

/* A hero's actions sit on one row and go full width on a narrow screen,
   where two buttons side by side would each be too small to hit. */
@media (max-width: 479px) {
  .row--actions .button { width: 100%; }
}
