/**
 * App shell — three-column layout.
 * Measured at 1600x1000: 450px settings rail, ~1010px canvas, 140px history.
 * Source: tools/ui-extract/out/components/app-shell.json
 */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background-color: var(--nai-bg-page);
  color: var(--nai-text);
  font-family: var(--nai-font);
  font-size: var(--nai-text-base);
  line-height: var(--nai-line-base);
}

/*
 * Form controls do NOT inherit font-family, font-size, or line-height from an
 * ancestor — every browser substitutes its own UA default. Without this the
 * whole settings panel renders its inputs, selects and buttons in Arial at
 * 13.3px, which `ui:diff` reported as ~24 separate font findings with one cause.
 */
input,
select,
button,
textarea {
  font-family: inherit;
  font-size: inherit;
  line-height: var(--nai-line-base);
}

/*
 * No focus rings. NAI draws none — its controls look identical focused and
 * unfocused — and the browser's default `auto 1px` ring was showing on any
 * control that lacked an explicit rule.
 *
 * This removes a visible affordance for keyboard users, which is normally a
 * bad trade. It is deliberate here: the target is a faithful clone of NAI's
 * appearance, and this is a private tool for a small group rather than a
 * public site.
 */
input:focus,
input:focus-visible,
select:focus,
select:focus-visible,
button:focus,
button:focus-visible,
textarea:focus,
textarea:focus-visible {
  outline: none;
}

.image-gen-page {
  display: flex;
  width: 100%;
  height: 100vh;
  background-color: var(--nai-bg-page);
  overflow: hidden;
}

/* Canvas — flexes to fill whatever the two fixed rails leave. */
.canvas {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  padding: 52px 30px 30px;
  /* Anchors .canvas__progress, which spans the column edge-to-edge and so has
     to escape the 30px side padding above. */
  position: relative;
}

/* Generating indicator — a 2px strip across the top of the canvas column, with
   the accent sweeping along it until the image arrives.

   Measured at 1010x2 @450,20 in a 1600px viewport: full column width (the rails
   occupy 0-450 and 1460-1600), 20px from the top, outside the padding box. The
   `left/right: 0` here is what reproduces that — the live node carries
   `width: 100%` inline against a parent that is not padded.

   The paint is on the ::after, not on this element: the track itself is
   transparent with no children, which is why an element-only walk of the live
   page concluded nothing painted here. Everything visible is the pseudo. */
.canvas__progress {
  position: absolute;
  top: 20px;
  left: 0;
  right: 0;
  height: 2px;
}
.canvas__progress::after {
  content: "";
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 2px;
  /* rgb(25,27,49) is its own colour — deliberately NOT --nai-bg-page
     (rgb(19,21,44)) nor --nai-bg-raised (rgb(34,37,63), which IS the 60% stop).
     Writing the 40% stop as a token would shift the gradient. */
  background-image: linear-gradient(
    90deg,
    var(--nai-accent) 0%,
    rgb(25, 27, 49) 40%,
    var(--nai-bg-raised) 60%,
    var(--nai-accent) 100%
  );
  /* Double-width so the scroll below has somewhere to travel; the gradient
     repeats seamlessly because both end stops are the accent. */
  background-size: 200%;
  animation: nai-generate-sweep 2s linear infinite;
}

/* Scrolls the gradient one full background-width per 2s, forever. Negative, so
   the sweep runs left-to-right. */
@keyframes nai-generate-sweep {
  0% {
    background-position: 0% 0%;
  }
  100% {
    background-position: -200% 0%;
  }
}

/* The sweep is decorative and runs unbroken for the length of a generation —
   long enough to matter to anyone who asked for less motion. */
@media (prefers-reduced-motion: reduce) {
  .canvas__progress::after {
    animation: none;
  }
}

/* Generated image — fits the pane without cropping or overflowing it. */
.canvas__image {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: var(--nai-radius);
}

/* Measured: a 100x100 box at opacity 0.5 holding a 100x74 glyph filled with
   #22253F (the raised tone). There is NO backing tile — an earlier version drew
   a filled 96x96 rounded square with an emoji inside it. */
.canvas__placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100px;
  height: 100px;
  opacity: 0.5;
}
.canvas__placeholder-icon {
  display: block;
  width: 100px;
  height: 74px;
  background-color: var(--nai-bg-raised);
  mask: url('../icons/image-placeholder.svg') center / contain no-repeat;
  -webkit-mask: url('../icons/image-placeholder.svg') center / contain no-repeat;
}

.history-rail {
  flex: 0 0 auto;
  width: var(--nai-history-width);
  height: 100%;
  padding: var(--nai-space-4) 0;
  background-color: var(--nai-bg-page);
  border-left: var(--nai-border);
}

.history-rail {
  /* The rail scrolls on its own; the page never does (see the settings rail).
     Column layout so the title stays put and only the list below it moves. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.history-rail__title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--nai-space-1);
  font-size: var(--nai-text-base);
  opacity: 0.9;
  flex: 0 0 auto;
}

.history-rail__list {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--nai-space-2);
  padding: var(--nai-space-3) var(--nai-space-2) 0;
  /* Thin scrollbar so a 140px rail doesn't lose 15px of it to chrome. */
  scrollbar-width: thin;
}

.history-rail__empty {
  margin: var(--nai-space-3) 0 0;
  text-align: center;
  font-size: var(--nai-text-xs);
  opacity: 0.5;
}

/* One thumbnail. A button, not a div — clicking restores the image, so it has
   to be reachable by keyboard and announce itself as actionable. */
.history-item {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: var(--nai-border);
  border-radius: var(--nai-radius);
  background-color: var(--nai-bg-raised);
  cursor: pointer;
  overflow: hidden;
  line-height: 0;
}

.history-item:hover,
.history-item:focus-visible {
  border-color: var(--nai-text-muted);
  outline: none;
}

.history-item__image {
  width: 100%;
  height: auto;
  display: block;
  /* Thumbnails arrive at full generation size; the rail scales them down and
     the aspect ratio varies per resolution, so height is left to follow. */
}

/* The current selection, so the rail shows which image the canvas is holding. */
.history-item[aria-current='true'] {
  border-color: var(--nai-accent);
}

/* Sign-in gate.
 *
 * A full-viewport cover rather than a centred modal over a visible page: the
 * app behind it is inert until there's a session, so showing it dimmed would
 * advertise controls that cannot be used. The backdrop is opaque for the same
 * reason.
 *
 * Not a security boundary — see the comment on #login-gate in index.html. */
.login-gate {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--nai-space-4);
  background-color: var(--nai-bg-page);
}

/* `hidden` alone loses to `display: flex` — the attribute's UA rule is
   display:none at a lower specificity than this element's own class. */
.login-gate[hidden] {
  display: none;
}

.login-gate__card {
  width: 100%;
  max-width: 400px;
  padding: 30px;
  text-align: center;
  background-color: var(--nai-bg-panel);
  border: var(--nai-border);
  border-radius: var(--nai-radius-lg);
}

/* The brand mark, sized as on the panel header: 18x21 artwork, not a 20x20
   icon cell — scaling it square would stretch the pen tip. */
.login-gate__mark {
  display: block;
  width: 18px;
  height: 21px;
  margin: 0 auto var(--nai-space-3);
  background-color: var(--nai-text);
  mask: var(--icon) center / contain no-repeat;
  -webkit-mask: var(--icon) center / contain no-repeat;
}

.login-gate__title {
  margin: 0 0 var(--nai-space-2);
  font-size: var(--nai-text-lg);
  font-weight: 600;
  line-height: var(--nai-line-base);
}

.login-gate__body {
  margin: 0 0 var(--nai-space-4);
  font-size: var(--nai-text-sm);
  line-height: var(--nai-line-sm);
  color: var(--nai-text-muted);
}

/* Discord's own brand colour, deliberately breaking from the NAI palette —
   users identify the button by that blurple, and this is the one control on
   the page whose meaning comes from outside the app. */
.login-gate__button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--nai-space-2);
  width: 100%;
  padding: 10px var(--nai-space-4);
  background-color: rgb(88, 101, 242);
  color: rgb(255, 255, 255);
  border-radius: var(--nai-radius);
  font-size: var(--nai-text-base);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.login-gate__button:hover {
  background-color: rgb(71, 82, 196);
}

/* The wordmark is 20x16, not the 20x20 icon cell — it is wider than it is tall
   and the default square would letterbox it. */
.icon--discord {
  width: 20px;
  height: 16px;
  vertical-align: 0;
}

.icon-discord { --icon: url('../icons/discord.svg'); }

/* Failure detail (a dead /api/me, mostly). Hidden until there is something
   to say. */
.login-gate__note {
  margin: var(--nai-space-3) 0 0;
  font-size: var(--nai-text-xs);
  line-height: var(--nai-line-sm);
  color: var(--nai-text-muted);
}
