/*
  The gallery's own chrome. NOT the mockups' styling -- each mockup carries its own copy of the app
  palette, so a mockup opened on its own looks the same as it does in the frame here.

  The tokens below are the product's, read from todos-app Theme.kt rather than picked: a review tool
  painted in some other palette makes every judgement about colour a judgement about the tool.
*/
:root {
  --bg: #f7f7f6;
  --surface: #ffffff;
  --surface-sunken: #f0efed;
  --text: #1c1b19;
  --text-muted: #57534c;
  --border: #e0ddd8;
  --border-strong: #c9c5bd;
  --accent: #b8562a;
  /* Accent as TEXT is a different value from accent as a FILL, and this project already had the
     token under two names: Theme.kt calls it TerracottaText, the web calls it
     --accent-soft-contrast. Measured against this page's rgb(247,247,246):
         #b8562a   4.45:1  — below AA, by 0.05
         #9d4722   5.83:1  — comfortably over
     The gallery used the FILL colour for its one link and so failed AA by a hair, on the one
     surface whose stated requirement is that it works on a phone. */
  --accent-text: #9d4722;
  --accent-soft: #fbeee7;
  --accent-contrast: #ffffff;
  /* APPROVED. A second hue, because approval is not the accent's job: the accent means "this wants
     you", and approval means the opposite — it is the one state on this page that takes a screen
     OUT of the queue. Using one colour for both would make the two most important states look the
     same at a glance, which is precisely what the squint test is for.
     MEASURED against every surface it lands on, not one of them -- a ratio is bound by the LIGHTEST
     backdrop the token is ever drawn on, and this project has already shipped a contrast fix computed
     against --bg while almost nothing sits on --bg. #2f6b4f: 6.29:1 on --surface, 5.87:1 on --bg,
     5.48:1 on --surface-sunken. The worst of the three is the number that matters and it clears AA.
     (The figures first written here were 5.34:1, guessed rather than computed, and understated.)
     It is never the only carrier of the meaning -- the control says "Final." in words and shows a
     check -- because colour alone is a rule this project does not bend. */
  --ok: #2f6b4f;
  --ok-soft: #eaf3ee;
  --radius: 12px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #17161a;
    --surface: #1f1e23;
    --surface-sunken: #17161a;
    --text: #eceaf0;
    --text-muted: #a29fab;
    --border: #35333c;
    --border-strong: #4a4753;
    --accent: #e8825a;
    /* In dark the accent is already a light tint on a dark surface, so one value serves both
       roles — measured at AA. Named anyway, so a component can ask for "accent as text"
       without knowing which theme it is in. */
    --accent-text: #e8825a;
    --accent-soft: #33241d;
    --accent-contrast: #1b1a1e;
    /* Lightened for a dark surface, the same move the accent makes above. In dark the DARKEST
       backdrop is the binding one, which is --surface: #7fc9a1 measures 8.49:1 there and 9.24:1 on
       --bg. Computed, not estimated -- the figure first written here was 6.8:1 and was neither. */
    --ok: #7fc9a1;
    --ok-soft: #1c2a23;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* The notch. This is opened on a phone more than anywhere else. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
}

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

/* ------------------------------------------------------------------------------------ the bar */

.bar {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 10px 14px 12px;
}

.bar__row { display: flex; align-items: center; gap: 10px; }

/* Both literals, NOT tokens, and the token version was a bug I shipped and then looked at.

   --accent-contrast flips to #1b1a1e in dark, so the mark rendered near-black on the near-black
   chip: an empty dark square where the brand should be. It is only visible in the dark screenshot,
   which is exactly the combination the project's own rules say most surviving defects live in.

   Literals are also what brand/README.md requires: it is the same mark on every surface, and a
   mark that repaints itself per theme is two different marks. The app's own ic_brand_mark.xml
   hard-codes the same #1C1B19. */
.brand {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 700;
  color: #ffffff;
  background: #1c1b19;
  border-radius: 7px;
  padding: 2px 6px;
  font-size: 15px;
}

.bar__title { font-size: 17px; margin: 0; flex: 1 1 auto; }

.bar__count {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  font-size: 13px;
  background: var(--surface-sunken);
  border-radius: 999px;
  padding: 2px 9px;
}

.picker { display: block; margin-top: 10px; }

.picker select {
  width: 100%;
  /* 44px: the floor a finger needs, and the same floor the app's own rows now use. */
  min-height: 44px;
  padding: 8px 10px;
  font: inherit;
  color: var(--text);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
}

.bar__nav { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.bar__pos { flex: 1 1 auto; text-align: center; color: var(--text-muted); font-size: 13px; font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------------- the buttons */

.btn {
  font: inherit;
  min-height: 44px;
  padding: 0 14px;
  border-radius: 10px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
}

.btn:disabled { opacity: .45; cursor: default; }

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
  font-weight: 600;
}

.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------------------------------------------------------------------------------- the screen */

main { padding: 16px 14px 56px; max-width: 760px; margin: 0 auto; }

.screen__head { margin-bottom: 12px; }
.screen__title { font-size: 20px; margin: 0; }
.screen__scenario { margin: 4px 0 0; color: var(--text-muted); font-size: 14px; }
/* DEPRECATED (#157), drawn ABOVE the scenario and louder than it, because it changes how everything
   below should be read: a reviewer who reads the argument and only then learns the screen is retired
   has spent the attention this gallery exists to protect.

   Deliberately NOT the danger colour. A retired screen is a tidy-up, not a fault, and borrowing the
   error tone would make housekeeping look like something is broken. */
.screen__deprecated {
  margin: 6px 0 0;
  padding-left: 10px;
  border-left: 3px solid var(--border-strong);
  color: var(--text-muted);
  font-size: 13px;
  font-weight: 600;
}

/* The phone frame. A fixed 390x844 viewport scaled to fit, so every mockup is judged at the same
   size regardless of the screen it is being read on -- which is the point of a gallery. */
.phone {
  width: 100%;
  max-width: 390px;
  margin: 0 auto;
  aspect-ratio: 390 / 844;
  border: 1px solid var(--border-strong);
  border-radius: 22px;
  overflow: hidden;
  background: var(--surface);
  box-shadow: 0 6px 24px rgb(0 0 0 / .10);
}

.phone iframe { width: 100%; height: 100%; border: 0; display: block; }

/* The frame KEEPS its tab stop -- it is a scrollable region, and a keyboard user needs to be able
   to scroll a 783px mockup -- but it showed nothing when focused: the UA default is
   `outline-style: none`, so the ring was 3px wide and invisible. Measured by tabbing: one control
   of eight had no focus indicator, and it was the biggest thing on the page.

   The ring goes on the FRAME rather than the iframe, because an outline drawn inside a
   border-radius: 22px box is clipped by the overflow: hidden that rounds the corners. */
/* NO FOCUS RING ON THE FRAME, and the rule that was here has been REMOVED rather than left
   hopefully in place. Measured, every path:

     focus() called programmatically  -> :focus-within matches, ring appears
     CLICKING the frame               -> does NOT match
     TAB into the frame               -> does NOT match; no focus/focusin event fires in the parent
                                         document at all, and a keyup check cannot see it either
                                         because the keyup goes to the iframe's document

   So the only path it worked on is the one no user takes. A CSS rule that matches only when JS calls
   focus() is the same thing as a tested function with no caller -- this repository has a rule about
   exactly that -- so it is gone, and the measurement is here instead.

   An earlier version of this comment claimed clicks worked. They do not; that was asserted before it
   was tested, and clicking is the first thing to test when a focus rule is in question. */

.screen__open { text-align: center; margin: 10px 0 0; font-size: 14px; }
/* 44px like every other control on this page (see .picker and .notes button above). It was a bare
   19px-high link, which is the one thing on the gallery a thumb could not reliably hit -- and the
   operator's requirement for this page is that it works on a phone. Measured at 390x844 against
   the live deployment. inline-flex rather than a block so the tap area grows without the
   underline stretching across the column. */
.screen__open a {
  color: var(--accent-text);
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 12px;
}

/* ----------------------------------------------------------------------------------- the notes */

.notes {
  margin-top: 26px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
}

.notes__title { font-size: 16px; margin: 0 0 10px; }

.notes__form textarea {
  width: 100%;
  font: inherit;
  color: var(--text);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  padding: 10px;
  resize: vertical;
}

.notes__actions { display: flex; align-items: center; gap: 12px; margin-top: 10px; }
.notes__status { color: var(--text-muted); font-size: 14px; }

.notes__list { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 10px; min-width: 0; }

.note {
  background: var(--surface-sunken);
  border-radius: 10px;
  padding: 10px 12px;
}

.note--none { color: var(--text-muted); font-size: 14px; background: none; padding: 0; }
/* pre-wrap keeps the operator's line breaks and indentation, which is the point -- but it can only
   wrap where there is WHITESPACE. A long unbroken run has none, so it cannot break, and one note
   made the whole page 169,292px wide at a 390px viewport. Found by a 20,000-character test string,
   but the realistic case is ordinary: a pasted URL, a JWT, a stack frame, a base64 blob.
   
   overflow-wrap: anywhere lets a break happen mid-run when there is no other option, and changes
   nothing for normal prose. min-width: 0 is the other half -- inside the flex column the list item
   would otherwise refuse to shrink below its content and the fix would not bite. */
.note__body {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  min-width: 0;
}
.note__meta { margin: 6px 0 0; color: var(--text-muted); font-size: 12px; }

/* --------------------------------------------------------------------------- the feedback jump */

/* Sticky, bottom, above the safe area. It exists because the phone frame is 783px tall at 390px
   wide, so the box it points at is a screen and a half below the mockup it is about. */
/* Right-aligned and compact, not centred and full-width, because it necessarily floats OVER the
   mockup: the frame is 783px tall and the button has to stay reachable while you are looking at it.
   Centred it sat squarely on top of a row -- covering the content it is asking you to comment on.
   At the right edge it overlaps the chevron column, which carries no information. */
.jump {
  position: sticky;
  bottom: calc(12px + env(safe-area-inset-bottom));
  display: flex;
  justify-content: flex-end;
  padding-right: 4px;
  pointer-events: none;
  margin-top: -52px;
  transition: opacity .18s ease, transform .18s ease;
}

.jump .btn {
  pointer-events: auto;
  box-shadow: 0 4px 16px rgb(0 0 0 / .22);
}

/* Hidden once the notes are on screen. `visibility` as well as opacity, so it cannot be tabbed to
   while invisible -- an unreachable-looking control that still takes focus is worse than one that
   is simply there. */
.jump.is-hidden {
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
}

/* ------------------------------------------------------------------------------- the skip link */

/* Off-screen until focused, then a real visible target. Hidden with a clip rather than
   display:none, because display:none removes it from the tab order and a skip link that cannot be
   tabbed to is decoration. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
  background: var(--accent);
  color: var(--accent-contrast);
  padding: 10px 14px;
  border-radius: 0 0 10px 0;
  font-weight: 600;
  text-decoration: none;
}

.skip:focus {
  left: 0;
}

/* A reply from Claude in the thread. The operator asked for answers to live here rather than only
   in a terminal they cannot see: "Please also put your answers (be concise) in the thread below the
   mockups".

   Indented and tinted rather than right-aligned. The app's own thread aligns the operator right,
   and copying that here would put THEIR notes on the left of their own review page, which is the
   wrong emphasis on a page whose whole purpose is their feedback. So the operator's notes keep the
   plain treatment and mine are visibly a response to them. */
.note--claude {
  margin-left: 18px;
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
}

.note__who {
  margin: 0 0 3px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.note--claude .note__who { color: var(--accent-text); }

/* The decision block. Above the feedback box because it is the reason the screen is in the jump at
   all, and a question below the free-text box would be answered by typing past it. */
.decision {
  margin: 18px 0 0;
  padding: 14px 16px;
  border: 1px solid var(--accent);
  border-left-width: 4px;
  border-radius: var(--radius);
  background: var(--accent-soft);
}

.decision__title {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.decision__q { margin: 0 0 12px; font-size: 17px; font-weight: 600; line-height: 1.35; }

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

/* Full width and stacked, not side by side. An option is a sentence, and two sentences on one row
   at 390px is two truncated sentences. */
.decision__opt {
  min-height: 48px;
  padding: 10px 14px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-weight: 600;
  text-align: left;
}

.decision__opt:disabled { opacity: .55; }

.decision__answered {
  margin: 10px 0 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent-text);
}

/* The version bar. Sits directly under the frame, because it describes what is IN the frame. */
/* `display: flex` on a CLASS beats the browser's own `[hidden] { display: none }`, so
   `bar.hidden = true` set the attribute and changed nothing on screen. The operator screenshotted
   the result: a "Version" control, visible, with an EMPTY dropdown inside it, on a screen that has
   only one version and had therefore taken the early return before populating it. Anything that
   toggles `hidden` from JS needs this guard whenever the element also carries a display rule. */
.versions[hidden] { display: none; }

.versions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 10px 0 0;
  font-size: 14px;
}

/* PILLS, not a select. Operator: "now it is a dropdown, I want it as pills so I can easily switch.
   There won't be so many iterations anyway." A select hides its options behind a click and says
   nothing about how many there are; with a handful of versions the whole history fits on one line
   and switching is one tap instead of three. Wrapped, so a screen that does accumulate versions
   grows downward rather than off the edge. */
.versions__pills { display: flex; flex-wrap: wrap; gap: 6px; }

.versions__pill {
  /* 44, not the 36 this shipped with. It is a real control the operator taps to switch versions,
     so it takes the same floor as every other control here rather than a smaller one because it
     happens to sit in chrome. */
  min-height: 44px;
  padding: 0 12px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.versions__pill:hover { border-color: var(--accent); }

/* Which one you are looking at, marked by TONE, WEIGHT and aria-pressed together -- never colour
   alone, which is a rule this product holds everywhere else. */
.versions__pill[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-text);
  font-weight: 650;
}

.versions__label { color: var(--text-muted); font-weight: 600; }

.versions__pick {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  padding: 0 10px;
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
}

.versions__note { flex: 1 1 100%; color: var(--text-muted); font-size: 13px; }

/* A note written while the version being viewed was the current one. An accent EDGE rather than a
   fill: the note may also be Claude's, which already carries a fill, and two fills on one row is a
   colour fight in which neither wins. */
.note--era {
  border-left: 3px solid var(--accent);
  padding-left: 10px;
}

/* ------------------------------------------------------------------- approving, and what is left

   Operator, 2026-08-09: "I also want a button to approve a mockup screen. Approved screens are
   final, and do not need further feedback. And I want an overview of screens for me to give
   feedback on."

   ONE PRIMARY ACTION PER SCREEN is a rule here, and this page now has two candidates: Send, in the
   feedback form, and Approve. They are resolved by tone rather than by weight — Send keeps the
   accent fill, Approve is an OUTLINE in the approved hue. So the eye still lands on one filled
   control, and the second reads as the alternative it is: the thing you press instead of typing. */

.approve {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 26px;
}

.btn--approve {
  border-color: var(--ok);
  color: var(--ok);
  font-weight: 600;
}

.btn--approve:hover { background: var(--ok-soft); }

.approve__state {
  color: var(--text-muted);
  font-size: 14px;
  /* min-width so a one-word state does not sit orphaned against the button on a wide screen, and
     flex: 1 so it wraps under the button on a phone rather than squeezing it below 44px. */
  flex: 1 1 12ch;
}

/* THE OVERVIEW. A panel rather than a jump: the operator asked to SEE what is waiting, and a
   control that walks you through them one at a time answers a different question. */
.review {
  margin-bottom: 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
}

.review__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.review__title { margin: 0; font-size: 17px; }

.review__why {
  margin: 6px 0 12px;
  color: var(--text-muted);
  font-size: 14px;
}

.review__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* CAPPED IN HEIGHT, NOT IN COUNT. Sixty screens is a plausible queue here and a list that long
     pushes the mockup off the page; truncating it would be the silent-truncation defect this
     project names outright, so it scrolls instead and every row stays reachable. */
  max-height: 46vh;
  overflow-y: auto;
}

.review__item {
  font: inherit;
  width: 100%;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  text-align: left;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-sunken);
  color: var(--text);
  cursor: pointer;
}

.review__item:hover { border-color: var(--accent); }
.review__item:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.review__name { font-weight: 600; }

/* WHY the row is in the list, in words. It is the second thing on the row and deliberately quiet:
   it distinguishes "you have not looked at this" from "I answered and you have not replied", which
   is the only choice the operator makes from this list. */
.review__why-row {
  color: var(--text-muted);
  font-size: 13px;
  white-space: nowrap;
}

/* WHAT CHANGED, beside the version pills. Full width under them, because it is a sentence and a
   sentence squeezed into a flex row wraps into a column of two-word lines. */
.versions__changed {
  flex-basis: 100%;
  margin: 6px 0 0;
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1.4;
}

/* The approved STATE, not the action. `--ok` is on the outline button as an affordance; here it is
   the fact. Never colour alone: the check glyph and the word "Final." carry it too, which is why
   this rule is only a colour. */
.approve__state--ok { color: var(--ok); font-weight: 600; }

/* The panel takes focus when it opens (so a screen reader announces it) and must not then draw a
   focus ring around a whole section -- that reads as an error state. The ring belongs on the
   controls inside it, which have their own. */
.review:focus { outline: none; }

/* THE GROUP HEADING in the review overview. A list item rather than a real heading element: it sits
   inside an <ol> of screens, and an <h3> between <li>s is invalid. It carries its own count, so the
   group cannot silently understate what it holds. */
.review__group {
  list-style: none;
  margin: 10px 0 2px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.review__group:first-child { margin-top: 0; }
