/* Mirrors RoyalTheme.java, via windows/UI-SPEC.md §1. Exact values, not approximations.
 *
 * The rule that governs every colour choice: gold is anything that IS the frame - borders, focus,
 * ornaments, the brand, selection. Lapis is anything that is AN ACTION - buttons, links, progress.
 * And a control takes the system's colour while an indicator keeps its meaning: urgent stays red,
 * status dots stay green. Do not flatten those into the duotone.
 */

:root {
  --bg: #090c11;              /* ground, never pure black: #000 clips and smears on the OLED walls */
  --surface: rgba(31, 37, 48, 0.804);
  --surface-raised: rgba(44, 52, 66, 0.922);
  --text: #f7f4ec;            /* warm ivory, never pure white */
  --muted: #b7b5af;
  --on-glass: rgba(255, 255, 255, 0.6);
  --gold: #c8a55b;
  --gold-dim: rgba(200, 165, 91, 0.47);
  --gold-wash: rgba(200, 165, 91, 0.102);
  --action: #3a5cb2;
  --action-dim: rgba(58, 92, 178, 0.47);
  --action-edge: #789beb;     /* lit top edge, so a button is not a flat slab */
  --danger: #d05a52;
  --ok: #5cb27a;

  /* The bottom bar, measured off buildCompactRemoteTabBar() rather than guessed.
   * The bar sits one step above the page in the SAME blue rather than beside it in a different
   * one, and its top edge is gold because "gold is what this product says an edge is made of". */
  --tabbar: #0c1017;                             /* Color.rgb(12, 16, 23) */
  --tabbar-edge: rgba(200, 165, 91, 0.18);       /* argb(46, 200, 165, 91) */
  --tab-ink: rgba(236, 238, 244, 0.59);          /* argb(150, 236, 238, 244) */
  --tab-seat: rgba(200, 165, 91, 0.13);          /* argb(33, 200, 165, 91) */
  --tab-seat-edge: rgba(200, 165, 91, 0.42);     /* argb(107, 200, 165, 91) */

  --step: 240ms;
  --settle: cubic-bezier(0.2, 0, 0, 1);
  --radius: 18px;

  /* iOS reports these only because the viewport meta says viewport-fit=cover. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
}

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

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
  /* The body never scrolls. One scrolling element inside the shell does, which is what keeps the
   * tab bar on screen under a long playlist and stops iOS rubber-banding the whole app away from
   * the status bar. */
  overflow: hidden;
  /* iOS paints a grey flash on every tap without this. The :active states below replace it. */
  -webkit-tap-highlight-color: transparent;
}

/* Cinzel for Latin display, Frank Ruhl Libre for Hebrew display; body text takes the platform UI
 * font. Both faces have a real fallback, so a blocked font request degrades rather than breaks. */
h1, h2, .display, button, .wordmark {
  font-family: Cinzel, "Frank Ruhl Libre", Georgia, "Times New Roman", serif;
  letter-spacing: 0.01em;
}
:root[dir="rtl"] h1,
:root[dir="rtl"] h2,
:root[dir="rtl"] .display,
:root[dir="rtl"] button,
:root[dir="rtl"] .wordmark {
  font-family: "Frank Ruhl Libre", Georgia, serif;
}

.hidden { display: none !important; }

/* ---- sign in and pair: one thing to do, centred, no tab bar --------------------------------- */

/* Centred, not stacked at the top. There is one thing to do on these screens and it was sitting in
 * the top third with the rest of the phone empty under it - which is what the first screenshot of
 * the sign-in screen showed, on the first screen anybody ever sees. */
.entry {
  height: 100%;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: calc(var(--safe-top) + 24px) calc(var(--safe-right) + 20px)
           calc(var(--safe-bottom) + 40px) calc(var(--safe-left) + 20px);
}
.entry > * { width: min(100%, 560px); margin-inline: auto; flex: 0 0 auto; }

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 18px 0 26px;
}

.brand-mark {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--gold-dim);
  border-radius: 12px;
  background: var(--gold-wash);
  color: var(--gold);
  font-size: 22px;
  flex: 0 0 auto;
}
.brand-mark.small { width: 30px; height: 30px; border-radius: 9px; font-size: 15px; }

.brand-name { font-size: 20px; margin: 0; }
.brand-sub { color: var(--muted); font-size: 13px; }

/* ---- the app shell ---------------------------------------------------------------------------
 *
 * Three grid rows: a bar that stays, the one thing that scrolls, and a bar that stays. No fixed
 * positioning and no z-index games, so the scroller can never run underneath either bar and a
 * focused input can never end up behind the keyboard-side one.
 */

.app {
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  height: 100dvh;
}

/* What the wall is showing, above whatever tab is open. MainActivity adds addDashboardPreview()
 * before the section body, so it is on every compact-remote screen rather than inside one of them -
 * and it is the top third of the owner's mockup. */
.wall-strip {
  padding: 10px calc(var(--safe-right) + 14px) 4px calc(var(--safe-left) + 14px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(0, 0, 0, 0.22);
}
.wall-strip .preview {
  max-height: 210px;
  width: 100%;
  object-fit: contain;
  margin-bottom: 6px;
}
/* With a picture on screen the caption is a caption; without one it is the whole strip. */
.wall-strip:has(.preview:not(.hidden)) .notice { font-size: 12px; padding: 2px 0; }
.wall-strip .notice { text-align: center; }

.appbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: calc(var(--safe-top) + 12px) calc(var(--safe-right) + 16px) 12px calc(var(--safe-left) + 16px);
  background: var(--tabbar);
  border-bottom: 1px solid var(--tabbar-edge);
}

.appbar-brand { display: flex; align-items: center; gap: 9px; flex: 0 0 auto; }
/* The wordmark is Latin in every language - it is the product's name, not a translated string. */
.wordmark { font-size: 15px; letter-spacing: 0.06em; direction: ltr; }

.appbar-home {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  overflow: hidden;
}
:root[dir="rtl"] .appbar-home { justify-content: flex-start; }

.pill {
  padding: 3px 9px;
  border: 1px solid rgba(255, 255, 255, 0.13);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  color: var(--on-glass);
  font-size: 10.5px;
  white-space: nowrap;
}

.avatar-button {
  width: 42px;
  height: 42px;
  /* The base button rule sets min-height: 44px for the tap target. On a round control that stretches
   * the circle into an egg, so it is overridden here and the 42px square carries the target. */
  min-height: 0;
  flex: 0 0 auto;
  padding: 0;
  border: 1px solid var(--gold-dim);
  border-top-color: var(--gold-dim);
  border-radius: 50%;
  background: var(--gold-wash);
  color: var(--gold);
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
}
.avatar-button[aria-expanded="true"] { background: var(--gold); color: #17130a; }

.scroller {
  overflow-y: auto;
  overflow-x: hidden;
  /* Stops a scroll that reaches the end of this element from turning into a page-level bounce. */
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  padding: 16px calc(var(--safe-right) + 16px) 28px calc(var(--safe-left) + 16px);
  /* Room for a focused input to scroll clear of the bars rather than sitting against them. */
  scroll-padding: 12px 0 24px;
}
.pane { width: min(100%, 560px); margin-inline: auto; }

/* ---- the bottom bar ---------------------------------------------------------------------------
 *
 * buildCompactRemoteTabBar(), in CSS. The parts that are decisions rather than styling:
 *
 *   - the top edge is GOLD, not a neutral hairline
 *   - the active tab gets a framed SEAT, so selection has a shape and not only a hue. Android's
 *     comment says why: "Colour alone was carrying it, which fails for anyone who cannot separate
 *     gold from ivory at 22dp, and reads as 'slightly brighter' rather than 'this one' to everybody
 *     else."
 *   - every label is the SAME size. Six labels at four sizes under six identical icons was the
 *     fault the owner pointed at, and it is invisible in a mock-up because a mock-up types one
 *     word per tab.
 *   - the bottom inset is claimed. Android had to add the same thing: without it "the labels sit
 *     under the system bar and the bottom of every tap target is gone." On iPhone that bar is the
 *     home indicator.
 */

.tabbar {
  display: flex;
  background: var(--tabbar);
  border-top: 1px solid var(--tabbar-edge);
  padding: 0 var(--safe-right) max(6px, var(--safe-bottom)) var(--safe-left);
}

.tab {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 9px 1px 7px;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--tab-ink);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.2;
  cursor: pointer;
  transition: color var(--step) var(--settle), transform 120ms var(--settle);
}
/* applyTactile(tab, 0.94f) */
.tab:active { transform: scale(0.94); }

.tab-seat {
  width: 30px;
  height: 26px;
  display: grid;
  place-items: center;
  border: 1px solid transparent;
  border-radius: 8px;
  transition: background var(--step) var(--settle), border-color var(--step) var(--settle);
}
.tab-seat svg { width: 21px; height: 21px; display: block; }

/* Two lines, and a size app.js shrinks to fit - because this label is one word in 52 languages.
 *
 * "Message" and "הודעה" are 26px. "Λίστα αναπαραγωγής" is 120px in a 73px column, and Spanish,
 * Ukrainian, German, Polish and Portuguese are not far behind: 30 of the 260 label-by-language
 * pairs clipped before this. Android hit the same wall and answered it with per-label autosize
 * pinned to the smallest; the web can wrap as well as shrink, so it does both.
 *
 * overflow-wrap: anywhere is for the single unbreakable word - "Wiedergabeliste" has nowhere to
 * break - which would otherwise ignore the second line entirely and clip on the first. */
.tab-label {
  max-width: 100%;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
  line-height: 1.15;
  text-align: center;
}

.tab[aria-selected="true"] { color: var(--gold); font-weight: 700; }
.tab[aria-selected="true"] .tab-seat {
  background: var(--tab-seat);
  border-color: var(--tab-seat-edge);
}

/* ---- surfaces --------------------------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 14px;
}

.card h2 { margin: 0 0 6px; font-size: 17px; }
.card p { margin: 0 0 16px; color: var(--muted); font-size: 14px; }

/* ---- controls ---------------------------------------------------------------------------------
 *
 * 44px is not a round number: it is the smallest target iOS asks for, and every button here clears
 * it because this is a wall remote used one-handed.
 */

button {
  width: 100%;
  min-height: 44px;
  padding: 13px 18px;
  border: 0;
  border-radius: 999px;
  background: var(--action);
  border-top: 1px solid var(--action-edge);
  color: var(--text);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: transform 120ms var(--settle), background var(--step) var(--settle);
}
button:hover:not(:disabled) { background: #4569c8; }
button:active:not(:disabled) { transform: scale(0.97); }
button:disabled { opacity: 0.45; cursor: default; }

button.quiet {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-top-color: rgba(255, 255, 255, 0.14);
  color: var(--muted);
  font-weight: 500;
}

/* The send button is the one thing this screen is for. */
button.primary { margin-top: 4px; box-shadow: 0 6px 20px rgba(58, 92, 178, 0.28); }

/* 16px MINIMUM on every field, and this is a functional requirement rather than a taste.
 * Mobile Safari zooms the page in when a field smaller than 16px takes focus, and it does not zoom
 * back out - so one tap on the message box leaves the app permanently magnified and scrolled
 * sideways. This is the single most common way a web app feels broken on an iPhone. */
/* The exclusions are load-bearing. This block is for TEXT fields; `appearance: none` applied to a
 * native control removes the control. A checkbox lost its tick and became a dark box that looked
 * the same on and off; a range lost its track and became a lone thumb floating in space. Both were
 * invisible to every measurement and obvious in the first screenshot of them. */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), select, textarea {
  width: 100%;
  min-height: 44px;
  padding: 12px 14px;
  margin-bottom: 12px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.28);
  color: var(--text);
  font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* iOS restyles these into rounded grey chrome otherwise. */
  -webkit-appearance: none;
  appearance: none;
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--gold); outline-offset: 1px; }

/* A checkbox and a range keep their native drawing and take the product's gold. */
input[type="range"] { accent-color: var(--gold); }

input[type="checkbox"] {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  margin: 0;
  accent-color: var(--gold);
}
input[type="radio"] {
  width: 20px;
  height: 20px;
  min-height: 0;
  flex: 0 0 auto;
  margin: 0;
  accent-color: var(--gold);
}

textarea { resize: vertical; min-height: 104px; }

/* The message is what the sender is looking at while they type, so it gets the parchment gold
 * rather than the ordinary field border. */
#msg-text { border-color: var(--gold-dim); font-size: 16px; }

/* The pairing code alone. It is hex, it is read off a wall a character at a time, and it stays
 * left-to-right and monospaced in every language - including the RTL ones this product is built
 * for. A title typed in Hebrew must NOT inherit any of that. */
#pair-code {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  letter-spacing: 0.12em;
  text-align: center;
  direction: ltr;
  border-color: var(--gold-dim);
}
/* The rule above is for a hex code read off a wall a character at a time. The PLACEHOLDER is a
 * Hebrew sentence, and it inherited all of it - stretched letters, running the wrong way. The
 * comment above already said a Hebrew title must not inherit this; the hint is the same case, and
 * it took a screenshot to notice. */
#pair-code::placeholder {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  letter-spacing: normal;
  direction: rtl;
  unicode-bidi: plaintext;
}
:root[dir="ltr"] #pair-code::placeholder { direction: ltr; }

.field-label {
  display: block;
  margin-bottom: 6px;
  color: var(--muted);
  font-size: 12px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.toggles {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 16px;
}
.toggles label {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 6px 13px 6px 11px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.04);
  color: var(--muted);
  font-size: 14px;
  cursor: pointer;
  transition: border-color var(--step) var(--settle), color var(--step) var(--settle);
}
.toggles input {
  width: auto;
  min-height: 0;
  margin: 0;
  accent-color: var(--gold);
  flex: 0 0 auto;
}
/* Gold is the frame: a chosen option is framed, not recoloured like an action. */
.toggles label:has(input:checked) { border-color: var(--gold-dim); color: var(--text); }

/* ---- the rail and the scroll --------------------------------------------------------------------
 *
 * The owner's drawing: circles down the left for the screens, a master circle at the top that marks
 * all of them, a gold ring on every circle when all are marked, and the message written on the
 * parchment beside them.
 *
 * The rail is on the LEFT in every language, and it is the only thing in this app that does not
 * mirror. That is deliberate and it is the owner's instruction, not an oversight - so the grid is
 * pinned with `direction: ltr` on the container and the text inside is handed its own direction
 * back. Anybody tempted to "fix" this should read the commit that added it first.
 */

.scroller.chat-open { overflow: hidden; padding: 0; }
.scroller.chat-open .pane { width: 100%; max-width: none; height: 100%; margin: 0; }

.composer-screen {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  direction: ltr;                 /* pins the rail to the left; see the note above */
  height: 100%;
  min-height: 0;
}

.rail {
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  /* The last circle has to clear the tab bar, not sit half-eaten by it - which is exactly how it
   * looked in the first screenshot anybody took of this screen. */
  padding: 14px 10px calc(var(--safe-bottom) + 28px) calc(var(--safe-left) + 10px);
  background: rgba(0, 0, 0, 0.26);
  border-right: 1px solid rgba(255, 255, 255, 0.07);
  scrollbar-width: none;
}
.rail::-webkit-scrollbar { display: none; }

.rail-circles { display: flex; flex-direction: column; align-items: center; gap: 12px; }

.circle {
  position: relative;
  width: 54px;
  height: 54px;
  min-height: 0;
  flex: 0 0 auto;
  padding: 0;
  border-radius: 50%;
  /* Two rings live here: this transparent one keeps the geometry identical whether or not a circle
   * is marked, so nothing shifts when the rail lights up. */
  border: 2px solid transparent;
  border-top-color: transparent;
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
  font-size: 19px;
  font-weight: 600;
  line-height: 1;
  overflow: visible;
}
.circle:hover:not(:disabled) { background: rgba(255, 255, 255, 0.1); }
.circle:active:not(:disabled) { transform: scale(0.94); }

.circle-face {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  overflow: hidden;
}
.circle-face svg { width: 25px; height: 25px; display: block; }

/* THE gold ring. "יש סביב כל העיגולים של הצ'טים עיגול זהוב שמסמן למשתמש שהוא סימן את כולם."
 * Gold is the frame in this product, and a marked circle is a thing in a frame. */
.circle[aria-pressed="true"] {
  border-color: var(--gold);
  background: var(--gold-wash);
  box-shadow: 0 0 0 3px rgba(200, 165, 91, 0.16);
}

/* The master circle carries the mark of the whole house, so it wears the gold solid once it is on. */
.circle.master .circle-face { color: var(--gold); }
.circle.master[aria-pressed="true"] .circle-face { background: var(--gold); color: #17130a; }

/* A screen's own state, on the circle, the way a chat list shows presence. */
.circle-dot {
  position: absolute;
  right: -1px;
  bottom: -1px;
  width: 13px;
  height: 13px;
  border: 2px solid var(--bg);
  border-radius: 50%;
  background: var(--muted);
}
.circle-dot.online { background: var(--ok); }

.circle-name {
  max-width: 62px;
  margin-top: 3px;
  color: var(--muted);
  font-size: 10px;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-weight: 400;
  line-height: 1.15;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.circle-slot { display: flex; flex-direction: column; align-items: center; }
.circle-slot[data-marked="true"] .circle-name { color: var(--gold); }

/* ---- the composing side ---- */

.compose {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  direction: rtl;                 /* handed back; overridden to ltr for Latin below */
  padding: 12px calc(var(--safe-right) + 14px) calc(var(--safe-bottom) + 14px) 14px;
}
:root[dir="ltr"] .compose { direction: ltr; }

/* The scroll itself. The parchment is a real background image - the same one the wall unrolls -
 * so what somebody writes looks like what lands on the screen. */
.scroll {
  position: relative;
  display: flex;
  flex-direction: column;
  /* The parchment's own curl occupies the top ~13% of the artwork. Writing started inside it and
   * the title was unreadable against the roll - visible the moment somebody looked at a
   * screenshot, and invisible to every measurement taken before that. */
  padding: 78px 34px 44px;
  margin-bottom: 14px;
  background-image: url("../icons/composer/message_scroll_open.webp");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  /* Grows into the space rather than leaving a void under the action row, with a floor so it never
   * squashes on a short screen and a ceiling so the artwork never stretches into a banner. */
  flex: 1 1 auto;
  min-height: 280px;
  max-height: 54vh;
}
/* With the options panel open the scroll gives up its floor, so both fit. */
.compose:has(.composer-options:not(.hidden)) .scroll,
.compose:has(.composer-extra:not(.hidden)) .scroll { min-height: 190px; max-height: 34vh; }

/* Ink on parchment: dark, and NOT the app's ivory-on-blue. An indicator keeps its meaning and so
 * does a writing surface. */
.scroll-title,
.scroll-body {
  border: 0;
  border-radius: 0;
  background: transparent;
  color: #3a2f21;
  margin: 0;
  padding: 0;
  min-height: 0;
}
.scroll-title {
  font-family: Cinzel, "Frank Ruhl Libre", Georgia, serif;
  font-size: 17px;
  font-weight: 700;
  text-align: center;
  margin-bottom: 8px;
}
:root[dir="rtl"] .scroll-title { font-family: "Frank Ruhl Libre", Georgia, serif; }
.scroll-body {
  flex: 1;
  font-size: 16px;
  line-height: 1.5;
  text-align: center;
  resize: none;
}
.scroll-title::placeholder,
.scroll-body::placeholder { color: rgba(58, 47, 33, 0.5); }
.scroll-title:focus,
.scroll-body:focus { outline: 2px solid rgba(58, 47, 33, 0.35); outline-offset: 4px; }

/* ---- RemoteScrollComposerView's action row ---- */

.actions {
  display: flex;
  flex: 0 0 auto;
  gap: 8px;
  margin-bottom: 10px;
}

.action {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  flex: 1 1 0;
  min-width: 0;
  min-height: 62px;
  padding: 9px 4px 8px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-top-color: rgba(255, 255, 255, 0.12);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.05);
  color: var(--muted);
  font-size: 11px;
  font-weight: 500;
}
.action img { width: 24px; height: 24px; object-fit: contain; display: block; }
.action span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.action:hover:not(:disabled) { background: rgba(255, 255, 255, 0.09); color: var(--text); }
/* A held state is a frame, so it takes the gold - the same rule the rail's rings follow. */
.action[aria-pressed="true"] {
  border-color: var(--gold);
  background: var(--gold-wash);
  color: var(--gold);
}
.action.alert-once[aria-pressed="true"] {
  border-color: var(--gold);
  color: var(--gold);
}
.action.alert-urgent[aria-pressed="true"] {
  border-color: var(--danger);
  background: rgba(208, 90, 82, 0.14);
  color: var(--danger);
}
/* Send is the action, so it is the one lapis thing in the row. */
.action.send {
  background: var(--action);
  border: 0;
  border-top: 1px solid var(--action-edge);
  color: var(--text);
  font-weight: 600;
}
.action.send:hover:not(:disabled) { background: #4569c8; }

.composer-extra {
  /* Opened below the fold this was simply unreachable: the parchment above it takes the height and
   * the tab bar covers what is left. It now has its own scroll and a ceiling, and app.js scrolls it
   * into view when it opens. */
  flex: 0 0 auto;
  max-height: 42vh;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  padding: 12px 14px 2px;
  margin-bottom: 10px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 14px;
  background: rgba(0, 0, 0, 0.22);
}
.composer-option-panel { margin: 0; }
.composer-choices { display: grid; gap: 8px; }
.composer-choices label {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 9px 12px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  color: var(--muted);
}
.composer-choices label:has(input:checked) {
  border-color: var(--gold);
  background: var(--gold-wash);
  color: var(--text);
}
#msg-schedule { min-height: 48px; font-size: 18px; }
/* Full width and directly under the time, so nobody has to guess that the time needs confirming. */
.schedule-confirm { display: block; width: 100%; min-height: 48px; margin-top: 10px; }

/* ---- message calendar ----------------------------------------------------------------------- */
.schedule-journal-toggle {
  width: 100%;
  min-height: 48px;
  margin: 0 0 10px;
  border-color: rgba(196, 156, 76, 0.55);
  background: linear-gradient(135deg, rgba(196, 156, 76, 0.15), rgba(68, 95, 164, 0.13));
  color: var(--text);
  font-weight: 700;
}
.scheduled-manager {
  display: grid;
  gap: 14px;
  width: 100%;
  padding: 16px;
  margin: 0 0 12px;
  border: 1px solid rgba(196, 156, 76, 0.34);
  border-radius: 20px;
  background:
    radial-gradient(circle at 10% 0%, rgba(196, 156, 76, 0.12), transparent 34%),
    linear-gradient(160deg, rgba(28, 36, 54, 0.96), rgba(13, 18, 29, 0.96));
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.28);
}
.scheduled-manager.hidden { display: none; }
.scheduled-header,
.scheduled-calendar-head,
.scheduled-list-head,
.scheduled-header-actions,
.scheduled-editor-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}
.scheduled-header { align-items: flex-start; justify-content: space-between; }
.scheduled-header h2,
.scheduled-header p,
.scheduled-list-head h3,
.scheduled-editor h3 { margin: 0; }
.scheduled-header p { margin-top: 4px; color: var(--muted); font-size: 13px; }
.scheduled-header-actions { flex-wrap: wrap; justify-content: flex-end; }
.scheduled-header-actions button { min-height: 42px; }
.scheduled-editor,
.scheduled-calendar-card,
.scheduled-card {
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.045);
  backdrop-filter: blur(12px);
}
.scheduled-editor { padding: 14px; }
.scheduled-editor > p { color: var(--muted); font-size: 13px; }
.scheduled-form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
.scheduled-form-grid label,
.scheduled-weekdays {
  display: grid;
  gap: 6px;
  color: var(--muted);
  font-size: 12px;
}
.scheduled-form-grid input,
.scheduled-form-grid select,
#scheduled-filter { min-height: 44px; }
.scheduled-target-all {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 44px;
  margin-top: 8px;
  color: var(--text);
}
.weekday-row { display: flex; flex-wrap: wrap; gap: 6px; }
.weekday-row button {
  width: 36px;
  min-height: 36px;
  padding: 0;
  border-radius: 50%;
  color: var(--muted);
}
.weekday-row button[aria-pressed="true"] {
  color: var(--gold);
  border-color: var(--gold);
  background: var(--gold-wash);
}
.scheduled-calendar-card { padding: 12px; }
.scheduled-calendar-head { justify-content: space-between; }
.scheduled-calendar-head button { min-width: 42px; min-height: 40px; }
.scheduled-calendar {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 5px;
  margin-top: 10px;
}
.calendar-weekday,
.calendar-day {
  display: grid;
  place-items: center;
  min-width: 0;
  min-height: 38px;
  padding: 3px;
  border-radius: 10px;
  font-size: 12px;
}
.calendar-weekday { color: var(--muted); font-weight: 700; }
.calendar-day { position: relative; background: rgba(255, 255, 255, 0.035); }
.calendar-day.outside { opacity: 0.38; }
.calendar-day.today { outline: 1px solid var(--gold); }
.calendar-day.selected { background: var(--gold-wash); color: var(--gold); }
.calendar-day.has-schedule::after {
  content: "";
  position: absolute;
  bottom: 4px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--gold);
}
.scheduled-list-head { justify-content: space-between; }
.scheduled-list-tools {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 8px;
}
.scheduled-list-tools button { min-height: 38px; }
.scheduled-list { display: grid; gap: 9px; }
.scheduled-card { padding: 12px; }
.scheduled-card-title { font-weight: 700; color: var(--text); }
.scheduled-card-meta { margin-top: 5px; color: var(--muted); font-size: 12px; line-height: 1.45; }
.scheduled-card-status { color: var(--gold); font-size: 12px; font-weight: 700; }
.scheduled-card-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.scheduled-card-actions button { min-height: 36px; padding: 7px 10px; font-size: 12px; }
.scheduled-empty { padding: 18px; text-align: center; color: var(--muted); }

@media (max-width: 560px) {
  .scheduled-header { display: grid; }
  .scheduled-header-actions { justify-content: stretch; }
  .scheduled-header-actions button { flex: 1 1 150px; }
  .scheduled-form-grid { grid-template-columns: 1fr; }
  .scheduled-calendar { gap: 3px; }
  .calendar-weekday, .calendar-day { min-height: 34px; font-size: 11px; }
}

/* Wider screens give the parchment room rather than stretching it across the whole window. */
@media (min-width: 720px) {
  .rail { padding-inline: 16px; }
  .circle { width: 60px; height: 60px; }
  .compose { align-items: center; }
  .compose > * { width: min(100%, 560px); }
}

/* ---- one setting on a wall ---------------------------------------------------------------------
 *
 * Label, then the control, on one row. Only the keys sanitizePatchKeys() lets through ever appear
 * here - WALL_SETTINGS is the list and NOT_OFFERED says why the rest are absent.
 */

.setting {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 52px;
  padding: 10px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.setting:last-child { border-bottom: 0; }

.setting-label {
  flex: 1;
  min-width: 0;
  font-size: 14.5px;
  color: var(--text);
  cursor: pointer;
}

.setting-value {
  flex: 0 0 auto;
  color: var(--gold);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  /* "1080 px" is a number and a unit, and bidi reordering rendered it "px 1080" inside the Hebrew
   * row. isolate keeps the pair together and ltr keeps them in that order. */
  direction: ltr;
  unicode-bidi: isolate;
}

.setting-control { flex: 0 0 auto; margin: 0; }
.setting-control.choice { width: auto; max-width: 58%; min-height: 40px; padding: 8px 12px; }
/* The slider gets its own row under the label, because a 4000px range and a word do not share 375px. */
.setting:has(.setting-control.range) { flex-wrap: wrap; }
.setting-control.range {
  width: 100%;
  min-height: 0;
  margin-top: 10px;
  padding: 0;
}

/* ---- device list -------------------------------------------------------------------------- */

.device {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.device:last-child { border-bottom: 0; }

.dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--muted);
  flex: 0 0 auto;
}
/* An indicator keeps its meaning. These are not the duotone. */
.dot.online { background: var(--ok); box-shadow: 0 0 0 3px rgba(92, 178, 122, 0.16); }
.dot.revoked { background: var(--danger); }

.device > div { min-width: 0; }
.device-name { font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.device-meta { color: var(--muted); font-size: 12.5px; }
.device-role {
  margin-inline-start: auto;
  color: var(--gold);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}

.this-device { color: var(--gold); }

/* ---- status ------------------------------------------------------------------------------- */

/* pre-line, because a partial send reports the count and then the screens that refused it, one to
 * a line. Collapsed into a paragraph they read as one confusing sentence. */
.notice { font-size: 14px; padding: 10px 0; color: var(--muted); white-space: pre-line; }
.notice.tight { padding: 4px 0; font-size: 13px; }
.notice.bad { color: var(--danger); }
.notice.good { color: var(--ok); }
.notice:empty { padding: 0; }

/* ---- the install walkthrough ----------------------------------------------------------------- */

.steps {
  margin: 0 0 18px;
  padding-inline-start: 22px;
  color: var(--text);
  font-size: 14.5px;
}
.steps li { margin-bottom: 9px; }
.steps li::marker { color: var(--gold); font-weight: 700; }

/* The Home Screen guide, drawn. Each step shows what to look for on the phone before saying it.
 * Direction-neutral on purpose: where Safari puts its buttons differs between layouts and iOS
 * versions, so the art shows which button, never which corner. */
.card p.install-first { color: var(--gold); }

/* "Remember this choice" under the rail's summary: a quiet action, not a second Send. */
.tight-button { min-height: 36px; padding: 6px 12px; font-size: 13px; margin: 4px 0 8px; }
.install-steps li { margin-bottom: 14px; }
.install-art {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 4px 0 6px;
}
.install-art svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; }
.install-key {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: 1px solid var(--gold-dim);
  border-radius: 12px;
  background: var(--gold-wash);
  color: var(--gold);
  font-size: 22px;
  line-height: 1;
}
.install-arrow { color: var(--muted); }
:root[dir="rtl"] .install-arrow { transform: scaleX(-1); }
.install-row {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border: 1px solid var(--gold-dim);
  border-radius: 12px;
  background: var(--surface-raised);
  color: var(--text);
  font-size: 14px;
}
.install-switch {
  width: 42px;
  height: 25px;
  border-radius: 999px;
  background: #34c759;
  position: relative;
}
.install-switch::after {
  content: "";
  position: absolute;
  top: 2px;
  inset-inline-end: 2px;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: #fff;
}
.install-icon { width: 44px; height: 44px; border-radius: 11px; }
.install-icon-name { font-size: 13px; color: var(--muted); }

/* ---- the wall pane --------------------------------------------------------------------------- */

.preview {
  display: block;
  width: 100%;
  border: 1px solid var(--gold-dim);
  border-radius: 12px;
  margin-bottom: 12px;
  background: #000;
}

.transport {
  display: flex;
  gap: 10px;
  margin-bottom: 16px;
}
.transport button { flex: 1; }

.scanner {
  width: 100%;
  max-height: 300px;
  margin-bottom: 14px;
  border: 1px solid var(--gold-dim);
  border-radius: 12px;
  background: #000;
  object-fit: cover;
}

/* ---- the playlist ---------------------------------------------------------------------------- */

.item {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 48px;
  padding: 8px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.item:last-of-type { border-bottom: 0; }

.item-name {
  flex: 1;
  min-width: 0;
  font-size: 14.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A hidden item is still listed, because putting it back has to be possible. */
.item.hidden-item .item-name { color: var(--muted); text-decoration: line-through; }

.item button {
  width: 40px;
  min-height: 40px;
  flex: 0 0 auto;
  padding: 0;
  border-radius: 11px;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-top-color: rgba(255, 255, 255, 0.14);
  color: var(--muted);
  font-size: 15px;
  font-weight: 400;
}
.item button:hover:not(:disabled) { background: rgba(255, 255, 255, 0.06); color: var(--text); }

/* ---- management ------------------------------------------------------------------------------ */

.row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 4px 0 10px;
}
.row-actions button {
  width: auto;
  min-height: 36px;
  flex: 0 0 auto;
  padding: 7px 13px;
  border-radius: 999px;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-top-color: rgba(255, 255, 255, 0.16);
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 500;
}
.row-actions button:hover:not(:disabled) { color: var(--text); background: rgba(255, 255, 255, 0.06); }
/* Removing access is the one control here that cannot be undone from this screen. */
.row-actions button.danger { color: var(--danger); border-color: rgba(208, 90, 82, 0.42); }

.avatars {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 18px;
}
.avatars button {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  min-height: 52px;
  flex: 0 0 auto;
  padding: 0;
  border-radius: 50%;
  border: 2px solid transparent;
  border-top-color: transparent;
  background: rgba(255, 255, 255, 0.06);
  line-height: 1;
}
.avatars svg { width: 26px; height: 26px; display: block; }
/* Gold is the frame: selection is a frame around a thing, not an action. */
.avatars button[aria-pressed="true"] { border-color: var(--gold); background: var(--gold-wash); }

/* ---- an app, not a page ------------------------------------------------------------------------
 *
 * The owner asked on 2026-11-03 for the iPhone remote to feel like an app. Each rule removes one
 * thing that gives a web page away there: a long press on a button or icon opened Safari's callout
 * and selected its label, a double tap zoomed, and a tab swapped its content with no motion.
 *
 * And glass, the look the owner chose from quick-liquid. On Safari that library draws exactly this
 * CSS fallback - blur, saturation, tint, a lit edge and a shadow - so it is written here directly
 * instead of shipping its engine to a phone. The bars stay grid rows (see the app shell above);
 * what they frost is the ambient light behind the whole app.
 */
html { touch-action: manipulation; }
button, .tab, .action, img {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

.app {
  background:
    radial-gradient(120% 55% at 50% -12%, rgba(200, 165, 91, 0.11), transparent 62%),
    radial-gradient(90% 45% at 100% 108%, rgba(69, 105, 200, 0.12), transparent 60%),
    var(--bg);
}
.appbar, .tabbar {
  background: rgba(12, 16, 23, 0.7);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  backdrop-filter: blur(18px) saturate(150%);
}
.card {
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  backdrop-filter: blur(14px) saturate(140%);
  border-color: rgba(255, 255, 255, 0.09);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07), 0 10px 28px rgba(0, 0, 0, 0.26);
}

/* A tab's content arrives rather than appears. Restarts on every switch, because a pane leaves
 * display: none each time it is shown. */
.pane:not(.hidden) { animation: pane-in var(--step) var(--settle); }
@keyframes pane-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 1ms !important; animation-duration: 1ms !important; }
}
