/* =====================================================================
   AGENTS LENS STUDIO -- guided setup.
   The three steps an agent walks before the editor opens: the photos, the
   listing, the look. Tokens, buttons and form controls all come from
   base.css; nothing here invents a colour, a radius or a typeface.
   ===================================================================== */

/* ----------------------------------------------------------------- shell */

/* min-height and sticky bars rather than a fixed full-screen box. Step 2 puts an agent's
   thumb in a text field, and on iOS Safari a fixed shell with the keyboard up gets
   scrolled clean out of the visual viewport: the bars survive, the fields do not. The
   Settings sheet gets away with fixed because it is short and dismissible. A three step
   form is not. This looks the same and stays on screen. */
.gs-shell {
  min-height: 100dvh;
  display: flex; flex-direction: column;
  width: 100%;

  /* What the action bar at the foot of this shell measures, before the safe area it adds
     on top at run time: 14px of padding, a 52px button, 14px more, and the 1px rule along
     its top. Two rules need this number, the bar itself as a floor and .gs-body as the
     room it has to leave underneath the last thing on the step, and a padding cannot read
     an element's height in CSS. So it is written once here rather than guessed twice. Put
     a second action in the bar and this moves with it, the same way .gs-progress carries
     .gs-top's 56px. */
  --acts-h: 81px;
}

/* ------------------------------------------------------------------- top */

/* Whose screen this is and which style she picked, in reach the whole way down. The
   editor's own header does not stick and four styles down a phone that is fine, because
   nothing there is needed mid-scroll. Here the way back is. */
.gs-top {
  position: sticky; top: 0;
  z-index: 30;
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  min-height: 56px;
  padding: 6px calc(env(safe-area-inset-right, 0px) + 16px)
           6px calc(env(safe-area-inset-left, 0px) + 16px);
  padding-top: calc(env(safe-area-inset-top, 0px) + 6px);
  background: rgba(10, 10, 10, 0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.gs-top-l { display: flex; align-items: center; gap: 10px; min-width: 0; }
.gs-top-r { display: flex; align-items: center; gap: 8px; min-width: 0; }

/* The style she picked, said back to her. An uppercase letterspaced micro label is the
   one thing in this portal that is never a control, which is what keeps a gold word in a
   header from reading as a button she should press. */
.gs-style {
  max-width: 42vw;
  color: var(--gold);
  font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.13em; text-transform: uppercase;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* -------------------------------------------------------------- progress */

/* Three segments because there are three steps, edge to edge because a phone reads a
   full bleed rule as the app's own chrome and an inset one as content. It sticks under
   the bar rather than scrolling away, so how far along she is never stops being true.

   The 56px offset is .gs-top's own height. If the markup ever puts something between the
   two, this number moves with it. */
.gs-progress {
  position: sticky;
  top: calc(env(safe-area-inset-top, 0px) + 56px);
  z-index: 29;
  display: flex;
  gap: 3px;
  background: rgba(10, 10, 10, 0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.gs-seg {
  flex: 1;
  height: 4px;
  background: rgba(255, 255, 255, 0.09);
  transition: background 0.2s ease;
}
.gs-seg.on { background: var(--gold-grad); }

/* ------------------------------------------------------------------ body */

/* The well between the two bars. The document is the scroller, not this box: giving it
   its own overflow would rebuild the fixed shell the sticky bars exist to avoid, and the
   keyboard would push the fields out of it again. */
.gs-body {
  flex: 1;
  width: 100%; max-width: 560px;
  margin: 0 auto;
  padding: 22px calc(env(safe-area-inset-right, 0px) + 20px)
           0 calc(env(safe-area-inset-left, 0px) + 20px);

  /* The bar's whole height, the safe area it sits on, and a breather. It was a flat 28px
     under an 81px bar. The bar reaches its place in the flow at the very bottom of the
     scroll, so 28px did clear it, but only by 28px, and a thumb aimed at a photo well
     sitting 28px above a full width gold NEXT is a thumb that presses NEXT. The last
     control on a step has to be a target of its own, which means room under it that is
     not the primary action. */
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + var(--acts-h) + 24px);
}

.gs-h1 {
  font-size: 26px; font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

/* Narrow on purpose, the same reason .gal-note is: a measure this short is read and a
   full width one is skipped. */
.gs-lede {
  margin-top: 8px;
  max-width: 42ch;
  color: var(--ink-3);
  font-size: 13.5px;
  line-height: 1.6;
}

/* --------------------------------------------------------------- actions */

/* Next is pinned where a thumb already is and never has to be gone looking for. It is a
   flex column so a second, quieter action can sit under it without either one shrinking
   to half a button.

   Solid, not the 0.92 wash with a blur behind it that this was. A sticky bar covers
   whatever scrolls under it, and on step one at 375 by 667 what that was, before anything
   had been scrolled at all, was the bottom 59px of the second photo well: its "+" and
   most of its box. Eight per cent of a dashed gold invitation came through the wash
   clearly enough to still read as a control, and elementFromPoint in the middle of that
   well answered with this bar. She could see a plus, press it, and have nothing happen,
   which is the exact report that came back from her: clicking the + to add photos did
   nothing. A control you can see and cannot press is worse than one you cannot see, so
   the bar now hides what is behind it outright and the well reads as scrolled away rather
   than as broken.

   The blur left with the wash. A backdrop filter under an opaque fill is a compositing
   layer that paints nothing, and this one repainted on every scroll frame.

   min-height is the same measurement .gs-body reserves below itself, so if the bar ever
   grows past what --acts-h claims, it visibly outgrows its own floor here rather than
   quietly going back to sitting on the control above it. */
.gs-acts {
  position: sticky; bottom: 0;
  z-index: 30;
  display: flex; flex-direction: column;
  gap: 10px;
  min-height: var(--acts-h);
  padding: 14px calc(env(safe-area-inset-right, 0px) + 20px)
           calc(env(safe-area-inset-bottom, 0px) + 14px)
           calc(env(safe-area-inset-left, 0px) + 20px);
  border-top: 1px solid var(--line);
  background: var(--bg);
}

.gs-next { width: 100%; min-height: 52px; }

/* ----------------------------------------------------------- photo wells */

/* Step one. A well is a label wrapping a file input, so the whole box is the target and
   nothing on this screen needs a file manager to find.

   Dashed gold on a gold wash, the same language the Add slide tile and the panel's photo
   picker already use, because it is the same invitation and an agent who has met one has
   met all three. */
.gs-well {
  position: relative;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 6px;
  overflow: hidden;
  padding: 16px 18px;
  border: 2px dashed var(--gold-35);
  border-radius: var(--r-md);
  background: var(--gold-10);
  color: var(--gold-hi);
  text-align: center;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.12s ease;
}

.gs-well input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }

/* The one photo every card needs, at the shape every card comes out in. Centred and
   capped rather than run to the edges: it shares the screen with the two optional wells
   under it, and a well nobody scrolls to is a well nobody fills. */
.gs-well-cover {
  width: 100%; max-width: 320px;
  margin: 0 auto;
  aspect-ratio: 4 / 5;
  padding: 20px;
}

.gs-well-cover .gs-well-plus { font-size: 34px; }

/* The extras. Short rows rather than more cards, because the shape says what they are:
   the cover is the post, these are additions to it. */
.gs-well-extra { width: 100%; height: 132px; }

/* Any well straight after any other well, which used to be any extra after any extra.
   The rooms section can be an empty well of its own now, and with the old pair selector
   an empty rooms well and the closing card under it came out sharing an edge: two dashed
   gold boxes touching read as one box with a line through it. This does not care which
   variants they are, so a step that changes shape cannot lose the gap between them. */
.gs-well + .gs-well { margin-top: 10px; }

.gs-well-plus { font-size: 26px; font-weight: 700; line-height: 1; }

.gs-well-title {
  font-size: 13.5px; font-weight: 700;
  letter-spacing: 0.01em;
}

.gs-well-sub {
  max-width: 32ch;
  color: var(--ink-3);
  font-size: 11.5px; font-weight: 500;
  line-height: 1.45;
}

/* What to put in this particular well, said under its title. Two wells carry one: a
   twilight shot on the cover, a drone shot on the closing card. It is the line that
   stops a midday photo going on the cover, which is the single choice that most changes
   what the finished card looks like, so it cannot be the quietest thing on the screen.

   It carries the well's gold and .gs-well-sub keeps the grey, which is the whole of how
   it gets read. Left at the caption's colour and weight it is the second of two grey
   lines in one box and nobody separates the two. A step brighter and half a point bigger
   than the caption under it puts it above the small print and below the title, which is
   exactly where a recommendation sits.

   This was drawn as an outlined chip first and that was wrong in a way worth writing
   down. A rounded box with a border, centred inside a dashed well, is a button. On a
   step whose entire job is pressing things it read as the control rather than as advice
   about the control, and the well it sat in is itself the target. Advice cannot look
   more pressable than the thing it is advising about.

   Gold and not --err, and sentence case and not the uppercase 700 the labels here use,
   because both registers are already spoken for. Red is what .gs-error says when a photo
   would not open, and uppercase letterspaced is what .gs-optional and .gs-well-title say
   when something is being named. This is neither a fault nor a rule. It is a
   recommendation, and it has to be legible as one at a glance or the copy gets read as a
   requirement she has already failed.

   align-self keeps it its own width rather than the box's. .gs-well centres its children,
   and a block level flex child would run the full width of the well, which on a line of
   gold text is a banner across a photo. */
.gs-tip {
  align-self: center;
  max-width: 34ch;
  margin-top: 8px;
  color: var(--gold-hi);
  font-size: 12px; font-weight: 600;
  letter-spacing: 0.005em;
  line-height: 1.45;
  text-align: left;
}

/* Inside a well it centres with everything else in the box, and the flex gap has already
   spaced it, so the margin comes off or this line sits half again further from the title
   it belongs to than the caption under it does.

   Left aligned is the default rather than the exception because the other place this line
   lands is under a section heading rather than inside a box, and a centred line under a
   left aligned heading reads as belonging to neither. */
.gs-well > .gs-tip { margin-top: 0; text-align: center; }

/* A filled well stops centring and starts left aligning, and its words move on top of a
   scrim over the photo. The tip has to move with them or it sits centred over a picture
   while the title under it is flush left, and sits under the scrim rather than on it. */
.gs-well.is-filled .gs-tip {
  position: relative; z-index: 1;
  align-self: flex-start;
  text-align: left;
}

.gs-well-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
  object-fit: cover;
}

/* Filled. The dashed invitation becomes a solid frame, because the question it was
   asking has been answered and a box still asking reads as a box that failed. The words
   drop to the foot of the picture on a scrim, so what the well is and how to change it
   both survive a bright photo. */
.gs-well.is-filled {
  align-items: stretch; justify-content: flex-end;
  gap: 2px;
  padding: 14px;
  border-style: solid;
  border-color: var(--line-2);
  background: #0d0d0d;
  color: var(--ink);
  text-align: left;
}
.gs-well.is-filled::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 62%;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.88));
  pointer-events: none;
}
.gs-well.is-filled .gs-well-plus { display: none; }
.gs-well.is-filled .gs-well-title,
.gs-well.is-filled .gs-well-sub { position: relative; z-index: 1; }
.gs-well.is-filled .gs-well-sub { color: var(--ink-2); }

/* The rule that separates the one photo she has to give from the two she does not. Same
   shape as the panel's own shared group, so the two screens divide a form the same way.

   Read at the ink-4 those group headings use and it went grey enough to skip, which on
   this line is not a small loss: it is the only thing on the screen that says a post can
   be more than one card. A step brighter, so it is quiet without being scenery. */
.gs-optional {
  margin-top: 26px;
  margin-bottom: 14px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
  font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--ink-3);
}

/* ------------------------------------------------------ the inside photos */

/* One cover, up to twenty rooms, one closing card. The rooms are the part that grows, so
   they are the part that had to stop being a well.

   Sideways, not a stack. Twenty wells down the page is a step nobody reaches the bottom
   of, and the two things below the rooms are the closing card and the line explaining
   that leaving them empty gives a one card post, so burying them buries the only
   explanation of what this section is. A strip is a fixed height whatever is in it: at
   one photo and at twenty this section is the same 130px tall and the closing card is in
   the same place on the screen.

   It is also the shape she meets next. The editor's rail is a sideways strip of small
   4:5 cards with an add tile on the end of it (see .rail-scroll in editor.css), and this
   is the screen immediately before that one, so the order she puts photos in here is
   drawn the same way the order of the finished post is drawn there.

   overflow-y is hidden and not left alone, because a box with one axis scrolling and the
   other visible resolves the visible one to auto, which would put a second scrollbar on
   a 130px strip. Hidden clips instead, which is why there is padding at the top of it:
   the remove control's target reaches 5px above its thumbnail and hidden would cut that
   off, exactly the way it was cutting the tops off the rail's role badges.

   overscroll-behavior is what keeps a flick that runs off the end of the strip inside the
   strip. Without it the page takes the rest of the gesture, and a phone that scrolls
   sideways at all on this form has nowhere sideways to go, so it rubber bands the whole
   step for no reason. */
.gs-shots {
  display: flex;
  gap: 10px;
  margin: 10px 0 14px;
  overflow-x: auto; overflow-y: hidden;
  overscroll-behavior: contain;
  padding-top: 5px;
  /* The scrollbar is an overlay on a phone and a real 6px rail on a desktop mouse, where
     it would otherwise be drawn across the bottom of the thumbnails. */
  padding-bottom: 8px;
}

/* 88px wide, so three whole thumbnails and 51px of a fourth are on a 375px screen at
   once, at both phone heights. A width that tiles the row exactly is the one thing this
   cannot be: a strip that ends flush with its own edge says the strip ends there, and the
   count everyone stops at is the count that would then look like the maximum. The part
   tile at the edge is the whole argument that there is more room.

   4:5 because that is the shape every card in this portal comes out in, and a strip of
   thumbnails in the wrong aspect would be showing her a crop the post will not use. */
.gs-shot {
  position: relative;
  flex-shrink: 0;
  width: 88px;
  aspect-ratio: 4 / 5;
}

/* The radius is on the picture rather than an overflow rule on the box, because the
   remove control's target hangs off the top right corner and a box that clips its own
   overflow would clip that target down to whatever fits inside the thumbnail. Same reason
   .thumb puts its radius on the canvas. */
.gs-shot img {
  width: 100%; height: 100%;
  display: block;
  object-fit: cover;
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
}

/* The one destructive control on this step, so the last one that should be easy to hit by
   accident. It is drawn at 26px and hit at 44px: the disc is what she aims at and the
   invisible box around it is what her thumb actually lands on. Drawn and hit at 26px it
   would be a target you have to look at your own finger to use, and drawn at 44px it
   would cover half the photo it is meant to be removing.

   The disc sits 4px inside the corner rather than straddling it the way .gs-check does.
   The check is 20px of decoration on a tile with nothing under it; this is a control on
   top of somebody's photograph, and it needs the scrim under it to be over the picture,
   not half over the page.

   The dark fill is doing real work. A remove control has to stay findable over a bright
   white kitchen and a dark twilight exterior with the same rule, and cream on 72% black
   clears both. */
.gs-shot-x {
  position: absolute; top: 4px; right: 4px;
  width: 26px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
  border: 1px solid var(--line-2);
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.72);
  color: var(--ink);
  font-size: 15px; font-weight: 600;
  line-height: 1;
  transition: background 0.14s ease, border-color 0.14s ease, transform 0.12s ease;
}

/* The target, invisible and centred on the disc. A generated box is hit tested and hands
   the press to the button it belongs to, so this costs no markup and elementFromPoint
   still answers with the button. */
.gs-shot-x::after {
  content: '';
  position: absolute; top: 50%; left: 50%;
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
}

/* Where more photos come from, on the end of the strip and not above it or beside the
   heading, so left to right through the strip is the order the post is in and the last
   thing in that order is where the next one goes.

   Same dashed gold on a gold wash as an empty well, at thumbnail size. It is the same
   invitation the cover well makes and the same one the editor's Add slide tile makes, so
   an agent who has met one of the three has met all of them.

   The extra margin is clearance, not rhythm. The remove control on the thumbnail before
   it reaches 5px past that thumbnail's right edge, and 10px of gap would leave 5px
   between the target that deletes a photo and the target that adds one. 12px more puts
   17px between them, which is the difference between a thumb that adds and a thumb that
   destroys. */
.gs-shot-add {
  position: relative;
  flex-shrink: 0;
  width: 88px;
  aspect-ratio: 4 / 5;
  margin-left: 12px;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 3px;
  border: 2px dashed var(--gold-35);
  border-radius: var(--r-sm);
  background: var(--gold-10);
  color: var(--gold-hi);
  text-align: center;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.12s ease;
}

.gs-shot-add input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }

/* The words a well says at full size do not fit in an 88px tile. The plate sized versions
   here are the rail's proportions, which is the same tile at the same size doing the same
   job one screen later. The caption goes entirely: there is no room for a sentence, and
   the tile is the third dashed gold box she will have met by this point. */
.gs-shot-add .gs-well-plus { font-size: 22px; }
.gs-shot-add .gs-well-title {
  padding: 0 6px;
  font-size: 9.5px;
  letter-spacing: 0.07em; text-transform: uppercase;
}
.gs-shot-add .gs-well-sub { display: none; }

/* Nothing picked yet. A lone 88px tile hard against the left margin is a loose end, not
   an invitation, and this section has to say what it is for before it has anything in it
   the way the cover well does. With no thumbnails to sit beside, the add tile stops being
   a thumbnail and becomes the row that asks the question, at the height of the wells it
   sits between.

   This is belt and braces. If the empty state is drawn as its own .gs-well instead, that
   already reads correctly and none of this matches. */
.gs-shots:not(:has(.gs-shot)) {
  overflow: visible;
  padding-top: 0; padding-bottom: 0;
}
.gs-shots:not(:has(.gs-shot)) .gs-shot-add {
  width: 100%;
  aspect-ratio: auto;
  height: 132px;
  margin-left: 0;
}
.gs-shots:not(:has(.gs-shot)) .gs-shot-add .gs-well-plus { font-size: 26px; }
.gs-shots:not(:has(.gs-shot)) .gs-shot-add .gs-well-title { font-size: 13.5px; letter-spacing: 0.01em; }
.gs-shots:not(:has(.gs-shot)) .gs-shot-add .gs-well-sub { display: block; }

/* How many are in, out of how many will fit. It exists so that twenty does not arrive as
   a surprise at the nineteenth photo, and that is the whole of its job, so it is stated
   and not announced.

   No layout of its own beyond one auto margin, because it can honestly land in two
   places: at the end of a flex heading row, where the auto margin sends it to the far
   end, or inline after a heading's words, where an auto margin is nothing and the padding
   is what keeps it off the last letter. Both read right without this rule knowing which
   one it got.

   The two resets are not decoration. It will sit next to or inside a heading in the
   .gs-optional register, which is uppercase and letterspaced, and both of those inherit:
   without them the count comes out as "3 OF 20" with gaps between the letters. */
.gs-count {
  margin-left: auto;
  padding-left: 8px;
  color: var(--ink-3);
  font-size: 11px; font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* ----------------------------------------------------------- said quietly */

.gs-hint {
  margin-top: 12px;
  padding: 10px 12px;
  border-left: 2px solid var(--gold);
  background: var(--gold-10);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  color: var(--ink-2);
  font-size: 12px;
  line-height: 1.5;
}

/* Why a photo is not kept, said where the photo is asked for. Quiet because it is there
   for the first time someone meets the form and invisible to everyone else. */
.gs-note { margin-top: 10px; color: var(--ink-4); font-size: 11.5px; line-height: 1.5; }

.gs-error { margin-top: 10px; color: var(--err); font-size: 12px; line-height: 1.5; }

/* ---------------------------------------------------------------- fields */

/* Step two. Three fields on the two styles that print three and seven on the two that
   print seven, so the stack has to read as finished at both lengths. The gap does the
   work rather than the panel's `.fld + .fld` margin, which would stack on top of it and
   space seven fields a third further apart than three. */
.gs-fields {
  margin-top: 20px;
  display: flex; flex-direction: column;
  gap: 18px;
}
.gs-fields .fld + .fld { margin-top: 0; }

/* --------------------------------------------------------------- choices */

/* Step three. It opens already ticked to match the photos she gave, so it reads as a
   receipt she can change rather than a quiz she has to sit. */

.gs-q { margin-top: 24px; }
.gs-q + .gs-q { margin-top: 30px; }

.gs-q-title {
  margin-bottom: 12px;
  font-size: 15px; font-weight: 700;
  letter-spacing: -0.01em;
}

/* Two up at every width. Four choices across a phone would put each one under a thumb's
   own width, and one up would make a pair of alternatives read as a list of steps. */
.gs-tiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}

.gs-tile {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  gap: 8px;
  padding: 12px 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.012));
  color: var(--ink);
  text-align: center;
  transition: border-color 0.16s ease, background 0.16s ease, transform 0.12s ease;
}

.gs-tile.on { border-color: var(--gold-35); background: var(--gold-10); }

/* Always in the markup, shown by the tick rather than added by it, so a tile does not
   grow by 20px the moment it is chosen and shove the one beside it out of line.

   On the corner rather than inside it. Sat at top 9 right 9 it owned the rightmost 29px
   of a 162px tile, and a row of three plates centred in that tile cannot avoid those
   29px without shrinking to about 30px each. Straddling the border costs the row
   nothing: it hangs 6px into a 10px gutter, still 4px clear of the tile beside it. */
.gs-check {
  position: absolute; top: -6px; right: -6px;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--gold-grad);
  opacity: 0;
  transform: scale(0.8);
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.gs-check::after {
  content: '\2713';
  display: block;
  color: #14100a;
  font-size: 11px; font-weight: 700;
  line-height: 20px;
  text-align: center;
}
.gs-tile.on .gs-check { opacity: 1; transform: none; }

/* --------------------------------------------------------------- plates */

/* What each choice actually makes, drawn rather than described. One card or three, with
   a closing card or without: the difference is a shape, and a shape is faster to read
   than any label anyone could write for it. */
.gs-view {
  display: flex; align-items: center; justify-content: center;
  gap: 6px;
  width: 100%;
  height: 92px;
  /* 9px across, not 6. The check disc reaches 14px into the tile at its widest, and the
     row is centred, so both sides give up the same amount or the row stops being
     centred to buy clearance on one. 9 leaves the rightmost plate 5px clear of the
     disc at 375px, which is the width where the two are closest. */
  padding: 6px 9px;
  border-radius: var(--r-sm);
  background: rgba(0, 0, 0, 0.45);
  border: 1px solid var(--line);
}

/* Every plate is the same box so the row lines up whatever it is made of.

   The basis is the size a plate takes on its own and the shrink is what happens when it
   has company, so one rule covers all three compositions: one plate, two, or three. It
   was a fixed 45% with shrink turned off, which fits two and blows a row of three about
   21px out of each side of the well, over the border and into the tile beside it. Two of
   the four tiles carry three plates, and the count is the whole answer on this step, so
   the row is what has to give.

   min-width matters as much as the shrink. A flex item will not shrink below its own
   min-content, and .gs-plate holds a canvas whose backing store is 130px wide, so
   without this the row would refuse to fit no matter what the basis said. */
.gs-plate,
.gs-plate-ghost,
.gs-plate-open,
.gs-plate-sign {
  flex: 0 1 64px;
  min-width: 0;
  aspect-ratio: 4 / 5;
  border-radius: 4px;
  overflow: hidden;
}

/* The real render, at pixelScale 0.12. The box is reserved by aspect-ratio and the
   waiting look is a background on this element rather than a fill on the context:
   renderInto resizes the backing store, and a resize wipes anything drawn before it, so
   a pre-filled canvas comes back empty every time. */
.gs-plate {
  position: relative;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(212, 175, 55, 0.13), transparent 64%),
    var(--surface);
}
.gs-plate canvas {
  width: 100%; height: auto;
  display: block;
  border-radius: 4px;
}

/* The card behind the card. Dimmed and unlabelled, standing for "and the rest of them"
   rather than pretending to be a second render nobody would read at this size. */
.gs-plate-ghost {
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  opacity: 0.5;
}

/* The slot left empty. Dashed, the same way an unfilled well is, because it means the
   same thing in both places: nothing here, and that is allowed. */
.gs-plate-open {
  background: rgba(0, 0, 0, 0.3);
  border: 1px dashed var(--line-2);
}

/* The closing card. A gold rule where its wordmark sits on the real thing, which at
   72px is the only part of it anyone could recognise anyway. */
.gs-plate-sign {
  position: relative;
  background: var(--surface-2);
  border: 1px solid var(--gold-35);
}
.gs-plate-sign::after {
  content: '';
  position: absolute; left: 50%; top: 50%;
  width: 46%; height: 2px;
  transform: translate(-50%, -50%);
  border-radius: 1px;
  background: var(--gold-grad);
}

.gs-tile-title { font-size: 13px; font-weight: 700; letter-spacing: -0.005em; }
.gs-tile-sub { color: var(--ink-3); font-size: 11.5px; line-height: 1.4; }

/* --------------------------------------------------------------- narrow */

/* Both questions on one screen at 375 by 667, which is what makes step three read as a
   receipt rather than as a third form. It is 32px of spacing tighter than the full size
   version and every pixel of it was needed: with the real sub lines under the tiles, two
   of which run to three lines, the second question ended 23px under the action bar. Far
   enough down to look like the step ended at the first question, and near enough to the
   edge to look like nothing was there.

   The plates were not touched to win it. The number of plates is the answer this step
   gives, so shrinking them to save a scroll would be paying in the one currency this
   screen has. */
@media (max-width: 560px) {
  .gs-h1 { font-size: 23px; }
  .gs-lede { margin-top: 6px; }
  .gs-q { margin-top: 16px; }
  .gs-q + .gs-q { margin-top: 18px; }
  .gs-q-title { margin-bottom: 8px; }
  .gs-tiles { gap: 10px; }
  .gs-tile { gap: 6px; padding: 10px 10px 12px; }
  .gs-view { height: 80px; }
  /* The basis comes down with the well. A plate is 4:5, so a 64px one is 80 tall and a
     well with 68px of room inside it would let the solo plate hang out of the top and
     bottom. A row of three shrinks well below this anyway. */
  .gs-plate,
  .gs-plate-ghost,
  .gs-plate-open,
  .gs-plate-sign { flex-basis: 54px; }
}

/* A full width 4:5 well is taller than a short phone has left once the two bars and the
   heading are paid for, which buries the optional pair and the line explaining them
   under the fold. The well keeps its shape and gives up width instead, so what she can
   add is on the same screen as what she must. */
@media (max-height: 700px) {
  .gs-well-cover { max-width: 252px; }
  .gs-well-extra { height: 116px; }
  .gs-shots:not(:has(.gs-shot)) .gs-shot-add { width: 100%; height: 116px; }
}

/* The thumbnails are deliberately NOT in the block above, and the reason is worth keeping.
   They were, at 76px, and 76 plus a 10px gap tiles a 335px row into exactly four with
   nothing left over: at twenty photos the strip showed four whole thumbnails flush to
   both edges and looked like a section holding four. The counter beside it said 20 of 20
   and the strip disagreed with it.

   Shrinking them was not buying anything either. That block exists so the optional wells
   land on the same screen as the cover on a 667px phone, and with a rooms section on the
   step that is already lost by about 400px however wide the thumbnails are. Four pixels
   off a thumbnail cannot win back a screen, so the thumbnails keep the width that leaves
   half of a fourth one showing and the strip goes on saying there is more. */

/* ------------------------------------------------------------- pointers */

/* Hover is a mouse's idea. On a phone it latches on tap and stays latched, which after
   a step forward and a step back leaves a tile looking chosen when it is not. */
@media (hover: hover) and (pointer: fine) {
  .gs-well:hover { border-color: var(--gold); background: rgba(212, 175, 55, 0.18); }
  .gs-well.is-filled:hover { border-color: var(--gold-35); background: #0d0d0d; }
  .gs-shot-add:hover { border-color: var(--gold); background: rgba(212, 175, 55, 0.18); }
  .gs-shot-x:hover { border-color: var(--line-2); background: rgba(0, 0, 0, 0.88); }
  .gs-tile:hover { border-color: var(--gold-35); }
  .gs-tile.on:hover { border-color: var(--gold); }
}

/* Pressed. A phone gives no other answer to a tap, and without one the half second
   before the next step paints reads as a tap that missed. After the hover block, because
   both write transform and the press has to be the one that lands. */
.gs-well:active { transform: scale(0.98); }
.gs-tile:active { transform: scale(0.98); }
.gs-shot-add:active { transform: scale(0.98); }
/* The disc is centred by its offsets rather than by a transform, so a scale here is the
   whole of it and does not fight the translate on the target box around it. */
.gs-shot-x:active { transform: scale(0.9); }
