/**
 * Cast — the phone's throw-it-on-the-big-screen section.
 *
 * A second stylesheet rather than more of `phone.css`, for one reason that has
 * nothing to do with taste: several agents edit this repository at once, and a
 * text diff has to be the whole story. Everything here is scoped under `.cast`
 * and every value is one of `phone.css`'s own custom properties, so this file
 * cannot restyle anything that already existed and cannot invent a second
 * palette. If it fails to load, the section still works — an unstyled input and
 * an unstyled button are still an input and a button, which is the same
 * degradation rule the boot stylesheet's fallback list is built on.
 *
 * There is not one word of content in here. State arrives as `data-state` on
 * the container and is spent in opacity, colour and a ring, exactly as the
 * transport row's states are.
 */

.cast {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 14px;
  border-radius: 16px;
  background: var(--bg-2);
  border: 1px solid var(--line);
}

/* The field and the mark on one row, so the section reads as an input rather
   than as a form somebody has to fill in. Same shape as `.create`. */
.cast__row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 1 100%;
}

/*
 * The mark a guest arrives to, and the only thing this section draws at rest.
 *
 * It is the whole affordance: a panel with waves leaving it, which every phone
 * in the room has met. The field it opens is not in the document until it is
 * asked for — see `phone-cast.js` for the law that makes that structural
 * rather than cosmetic.
 */
/* THE SAME MATERIAL AS EVERY OTHER KEY IN THE CURTAIN.

   It was a 1 px outline round `transparent`, so with the transport row below it
   rebuilt as glass this was the one control left drawn in the browser's default
   vocabulary — a lone hollow rectangle above six solid keys, MEASURED in
   `.director/eye/r3-b-tap.png` as the only object in the panel with no
   thickness. Radius raised to 16 to match `.remote__key`; a curtain in which
   two neighbouring keys round differently reads as two panels. */
/* A BAR, NOT A SQUARE — because the word has to fit on it.

   MEASURED at 390x844 with the curtain open: a 64x64 square alone on a 346 pt
   row, so 282 pt of the panel's width were spent on nothing at all, and the one
   control here whose purpose is genuinely unguessable carried zero visible
   words. Full width fixes both in one move: the dead space becomes the caption,
   and `cast-open`'s own sentence — which is a good accessible name and far too
   long for an 11 px caption under a glyph — fits beside the mark at full size
   without being shortened into a second, different string.

   Same left alignment and same 16 pt radius as `.now__hands` and
   `.remote__key`, so the curtain is still one object made of one material. */
.cast__open {
  /* Now lane, 2026-09-14: a list row on an elevated fill (D.7), not a
     bordered slab. */
  flex: 1 1 100%;
  min-height: 52px;
  min-width: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  padding: 0 16px;
  text-align: left;
  border-radius: var(--r-md, 16px);
  border: 0;
  background: var(--bg-elev1, #15181d);
  box-shadow: none;
  color: var(--label-1, #f5f2ec);
  transition: background-color 250ms;
}
/* The same press as every other key. On an iPhone there are no haptics to have
   — the interface does not exist — so the depression IS the receipt, and 0.97
   on a full-width bar is a movement a person cannot see happen. */
.cast__open:active {
  background: var(--bg-elev3, #2a2f36);
  transition-duration: 100ms;
}
@media (prefers-reduced-motion: reduce) { .cast__open:active { transform: none; } }
.cast__open .mk { width: 26px; height: 26px; flex: 0 0 auto; }
/* Set at the size the rest of this surface uses for a thing you press. It is
   the point of the control, not a caption on it. `:empty` because a house whose
   box has sent no word for this code gets the mark alone — the same floor every
   other word on this page keeps — and an empty span must not eat the gap. */
.cast__open-word {
  font: 600 var(--fs-body, 17px)/var(--lh-body, 22px) var(--font-text, system-ui);
  letter-spacing: -0.4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cast__open-word:empty { display: none; }
/* Open, the field takes the row and the opener goes back to being a mark. A
   full-width bar above a full-width field would be two slabs where the section
   is meant to read as one input. */
.cast[data-state="cast-open"] .cast__open,
.cast[data-state="cast-working"] .cast__open { flex: none; padding: 0; justify-content: center; }
.cast[data-state="cast-open"] .cast__open-word,
.cast[data-state="cast-working"] .cast__open-word { display: none; }
/* Open is the guest's own colour on the ring, not a border swap — the ring is an
   inset shadow now, so it has to be restated here or the open state would lose
   the material along with the outline it used to change. */
.cast__open[data-state="cast-open"] {
  color: var(--me, var(--go));
  box-shadow:
    inset 0 1px 0 rgba(232, 240, 255, 0.18),
    inset 0 0 0 1.25px rgba(59, 232, 199, 0.45),
    0 6px 16px rgba(0, 0, 0, 0.34);
  box-shadow:
    inset 0 1px 0 rgba(232, 240, 255, 0.18),
    inset 0 0 0 1.25px color-mix(in srgb, var(--me, var(--go)) 45%, transparent),
    0 6px 16px rgba(0, 0, 0, 0.34);
}
.cast__open:disabled { opacity: 0.42; }

/* The queue already has its own complete, persistent door. This is only the
   discoverable way into it from the share surface, shown after the cast mark is
   opened so resting glass remains one mark and not a shelf. */
.cast__queue {
  flex: 1 1 100%;
  min-height: 48px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 16px;
  border-radius: 14px;
  color: var(--ink);
  text-decoration: none;
  background: linear-gradient(180deg, rgba(24, 30, 44, 0.56), rgba(8, 11, 19, 0.66));
  box-shadow: inset 0 0 0 1px rgba(150, 170, 205, 0.16);
}
.cast__queue[hidden] { display: none; }
.cast__queue .mk { width: 24px; height: 24px; flex: 0 0 auto; }
.cast__queue-word { font-size: 16px; font-weight: 600; }
.cast__queue:active { transform: scale(0.97); filter: brightness(1.22); }
@media (prefers-reduced-motion: reduce) { .cast__queue:active { transform: none; } }

/* Closed, the section is one mark and nothing else — no card around a button. */
.cast[data-state="cast-closed"] { padding: 0; border: 0; background: none; }

.cast__field {
  flex: 1;
  min-width: 0;
  min-height: 48px;
  padding: 12px 13px;
  border: 1px solid var(--line-2);
  border-radius: 12px;
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  line-height: 1.4;
  outline: none;
  /* The one place on this page a guest is expected to paste. `touch-action` and
     `user-select` are `none` on html/body to stop double-tap zoom; text input
     needs both back or the paste menu never appears under a thumb. */
  -webkit-user-select: text;
  user-select: text;
  touch-action: auto;
}
.cast__field:focus { border-color: var(--me, var(--go)); }
.cast__field:disabled { opacity: 0.62; }
/* The writing rule, drawn rather than written — same as the name field. */
.cast__field:placeholder-shown { border-style: dashed; }

.cast__go {
  flex: none;
  min-height: 48px;
  min-width: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  border: 0;
  background: var(--me, var(--go));
  color: #090a0c;
}
.cast__go .mk { width: 24px; height: 24px; }
.cast__go:disabled { opacity: 0.42; }
/* Working is the mark itself, pulsing. Not a second label on the button. */
.cast__go[data-state="cast-working"] { animation: joinpulse 1.1s ease-in-out infinite; }

/* -- what the house found ------------------------------------------------- */

/*
 * Rows exist only while a question is unanswered. They are removed the instant
 * something lands on the glass, because a list describing a question that has
 * been answered is the shelf this shell already deleted once.
 */
.cast__found {
  flex: 1 1 100%;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 46vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.cast__found[hidden] { display: none; }

.cast__hit {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: var(--tap);
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg-3);
  color: var(--ink);
  text-align: start;
}
.cast__hit:disabled { opacity: 0.5; }

.cast__art {
  flex: none;
  width: 64px;
  height: 40px;
  object-fit: cover;
  border-radius: 6px;
  background: var(--bg);
}
.cast__art[hidden] { display: none; }

.cast__lines { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cast__title {
  font-weight: 640;
  line-height: 1.25;
  /* Two lines, then it stops. A title is package data of unbounded length and a
     row that grows to fit one pushes every other row off a small screen. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.cast__by, .cast__date { font-size: 13px; color: var(--ink-2); }

/* -- the state line ------------------------------------------------------- */

.cast__state {
  flex: 1 1 100%;
  min-height: 0;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink-2);
}
.cast__state:empty { display: none; }
.cast__on { color: var(--ink); font-weight: 650; }

/*
 * A refusal is drawn, whether or not anybody wrote a word for it. Every state
 * this box can produce that is not "it worked" reddens the line, so a code with
 * no model sentence behind it is still visibly different from silence — the
 * whole point of shape carrying the condition.
 */
.cast__state[data-state^="cast-"]:not([data-state="cast-idle"]):not([data-state="cast-on"]):not([data-state="cast-working"]) {
  color: var(--stop);
}
.cast__said { color: var(--ink-2); }
