/* QAlign — dark, calm, and out of the way while people are talking. */

/* ── hidden means hidden ──────────────────────────────────────────────────
   The browser's own rule is `[hidden]{display:none}` at zero specificity, so ANY
   selector that gives an element a display — `#app{display:grid}`, `.sheet{display:
   grid}`, `#wsPane{display:flex}` — quietly out-ranks it and lays the element out
   while it is invisible. A full-screen transparent box that still takes clicks is
   indistinguishable from the app being frozen, and this codebase has paid for it
   four separate times: the QuickAlign stepper, every `.sheet`, the workspace pane
   that shipped last night and made the mode button unpressable, and the four more
   found by sweeping for it (`#app`, `#recdot`, `.wscount`, `#flipBtn`).

   The nineteen hand-written `.x[hidden]{display:none}` rules below are this same fix
   applied one id at a time, and one at a time is how the fifth gets missed. So here
   is the rule itself, once, at the top. `!important` is deliberate: an element that
   is hidden and displayed at the same time is a bug by definition, so there is
   nothing legitimate for it to override. test/board-ui.mjs asserts the property over
   every element on the page carrying the attribute, rather than over the one that
   happened to break. */
[hidden]{display:none!important}

:root{
  /* Brand — Team Meet, option 9. Electric blue through hyper violet on midnight. */
  --blue:#0794FC; --signal:#0773F9; --royal:#0755EB; --deep:#042D97;
  /* --blue is the BRAND blue and belongs on borders, fills and dark backgrounds. It is
     3.16:1 on white, so it fails AA as text in light mode — the "Follow" toggle and
     every speaker name in the transcript were unreadable there. --blue-ink is the same
     blue where the background is dark and the already-darkened accent where it is
     light, so a rule can say "blue text" without having to know the theme. */
  --blue-ink:#0794FC;
  --indigo:#1D2FC6; --violet:#5B2EF2; --midnight:#050817; --cloud:#F8FAFF;
  --grad:linear-gradient(135deg,#0794FC 0%,#0755EB 48%,#5B2EF2 100%);
  --grad-deep:linear-gradient(135deg,#042D97 0%,#1D2FC6 55%,#5B2EF2 100%);

  --bg:#050817; --panel:#0A0E24; --panel2:#0F1430; --line:#1C2350;
  --ink:#F8FAFF; --mid:#9AA6C8; --dim:#74809A;
  --acc:#0794FC;          /* interactive, the brand's own blue */
  --acc2:#5B2EF2;         /* the second half of the gradient */
  --good:#2BD9A6;         /* verified / aligned — a semantic, not a brand colour */
  --warn:#F5B851;         /* unverified, open */
  --bad:#FF6B7A;          /* block */
  --r:16px;
  --mono:ui-monospace,SFMono-Regular,Menlo,monospace;
  --display:'Sora',system-ui,sans-serif;

  /* Surfaces, named by depth rather than by colour, so a light theme is a matter of
     redefining ten values instead of hunting through the file. */
  --field:#0d1218;        /* sunken: inputs, meters, tab strips */
  --sunk:#141a24; --sunk2:#16233a;
  --hair:#1c2230;         /* dividers inside a card */
  --edge:#2b3444; --edge2:#2a3a5c;
  --deepbg:#070B1C; --panel3:#151b3d;
  --chipbg:rgba(8,11,16,.72);
  --hover:#1d2430;
  --stage:#05070f;        /* behind the video, where no tile reaches */
  --shadow:0 18px 44px -18px rgba(0,0,0,.8);
  --scrim:rgba(5,8,23,.72);
  --tint:0;               /* 1 in light mode — lets a rule ask which theme it is in */
  --offbg:#3a1f24; --offline:#5b2b31; --offink:#ffb4b4;   /* muted / camera off / leave */
  --onbg:#0d1b4a;  --online:#2440a8;                      /* a control that is engaged */
}

/* ═══════════════════════ light mode ═══════════════════════
   Not a preference. A working light.

   A laptop screen is the only lamp most people have pointed at their face, and a dark
   interface wastes it: in an unlit room a dark UI leaves you a silhouette. A light one
   turns the panel into a softbox. That is the whole reason this exists, which is also
   why the key light below sits in the same settings block — the theme lifts the room,
   the key light does the actual work.

   Semantic colours are re-picked rather than reused: --good at #2BD9A6 is legible on
   midnight and nearly invisible on white. */
:root[data-theme="light"]{
  --bg:#F4F6FC; --panel:#FFFFFF; --panel2:#F7F9FF; --panel3:#EEF2FE;
  --line:#DCE2F2;
  --ink:#0C1024; --mid:#4A5473; --dim:#5C6684;
  --acc:#0B4FD6; --acc2:#5124D8;    /* darkened for text: the brand blue is 3.2:1 on white */
  --blue-ink:#0B4FD6;
  --good:#0E7C57; --warn:#8A5600; --bad:#B81F33;
  --field:#FFFFFF; --sunk:#F1F4FD; --sunk2:#E9EFFE;
  --hair:#E4E9F6; --edge:#CED6EB; --edge2:#BFC9E6;
  --deepbg:#FFFFFF;
  --chipbg:rgba(8,11,16,.72);   /* over video — deliberately NOT flipped */
  --hover:#EDF1FC;
  --stage:#E7EBF6;
  --shadow:0 14px 34px -16px rgba(16,24,58,.28);
  --scrim:rgba(244,246,252,.78);
  --tint:1;
  --offbg:#FDE9EC; --offline:#F3C2CA; --offink:#B02236;
  --onbg:#E6EEFE;  --online:#B9CDF7;
}
/* "Match my system" is the default, so an un-stamped document follows the OS. */
@media (prefers-color-scheme: light){
  :root:not([data-theme="dark"]){
    --bg:#F4F6FC; --panel:#FFFFFF; --panel2:#F7F9FF; --panel3:#EEF2FE;
    --line:#DCE2F2;
    --ink:#0C1024; --mid:#4A5473; --dim:#5C6684;
    --acc:#0B4FD6; --acc2:#5124D8; --blue-ink:#0B4FD6;
    --good:#0E7C57; --warn:#8A5600; --bad:#B81F33;
    --field:#FFFFFF; --sunk:#F1F4FD; --sunk2:#E9EFFE;
    --hair:#E4E9F6; --edge:#CED6EB; --edge2:#BFC9E6;
    --deepbg:#FFFFFF;
    --chipbg:rgba(8,11,16,.72);   /* over video — deliberately NOT flipped */
    --hover:#EDF1FC;
    --stage:#E7EBF6;
    --shadow:0 14px 34px -16px rgba(16,24,58,.28);
    --scrim:rgba(244,246,252,.78);
    --tint:1;
    --offbg:#FDE9EC; --offline:#F3C2CA; --offink:#B02236;
    --onbg:#E6EEFE;    --online:#B9CDF7;
  }
}
*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0;background:var(--bg);color:var(--ink);
  font:15px/1.55 'Inter',-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif;
  -webkit-font-smoothing:antialiased;overflow:hidden;
}
button{font:inherit;color:inherit;cursor:pointer;border:0;background:none}
input,textarea,select{font:inherit;color:inherit}
a{color:var(--acc2)}

/* ───────── door ───────── */
.site{position:fixed;inset:0;z-index:50;overflow-y:auto;background:var(--bg)}
.site.gone{opacity:0;pointer-events:none;transition:opacity .45s}
.gate-inner{width:min(460px,92vw);text-align:center}
.mark{display:none}
.gate h1{margin:0;font-size:34px;letter-spacing:-.02em;font-weight:650}
.tag{margin:.5rem 0 1.6rem;color:var(--mid)}
#gateForm{display:grid;gap:8px}
#gateForm input{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:12px 14px;outline:none}
#gateForm input:focus{border-color:var(--acc2)}
.primary{background:var(--grad);color:#fff;font-weight:650;
  border-radius:10px;padding:12px;margin-top:4px}
.primary:disabled{opacity:.5}
.gate-toggles{display:flex;gap:16px;justify-content:center;margin:14px 0 0;color:var(--mid);font-size:13px}
.gate-toggles label{display:flex;gap:6px;align-items:center;cursor:pointer}
.arc{list-style:none;display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin:26px 0 0;padding:0;
  text-align:left;font-size:11px;color:var(--dim)}
.arc li{border-top:2px solid var(--line);padding-top:8px}
.arc li:first-child{border-color:var(--acc2)}
.arc b{display:block;color:var(--mid);font-weight:600;font-size:12px}
.gate-foot{margin-top:22px;font-size:12px;color:var(--dim)}

/* ───────── room shell ───────── */
#app{position:fixed;inset:0;display:grid;
  grid-template-columns:1fr 400px;grid-template-rows:1fr 60px;
  grid-template-areas:"stage rail" "bar rail";}
#app.norail{grid-template-columns:1fr 0;}
#app.norail #rail{display:none}
#stage{grid-area:stage;position:relative;padding:12px;overflow:hidden;min-width:0}
/* min-width:0 matters: without it a wide control row stretches the grid column and
   pushes the side panel off the right edge of the window. Adding two buttons to the
   bar did exactly that. */
/* The control bar owns its own stacking context so anything that pops UP out of it —
   the reaction tray — lands above the video strip instead of behind it. overflow is
   visible for the same reason; the bar clips horizontally via its children instead. */
#bar{grid-area:bar;position:relative;z-index:10;display:flex;align-items:center;
  justify-content:space-between;padding:0 14px;gap:10px;min-width:0;
  border-top:1px solid var(--line);background:var(--panel)}
.bar-mid{overflow:visible}
#rail{grid-area:rail;border-left:1px solid var(--line);background:var(--panel);display:flex;flex-direction:column;min-width:0}

/* ───────── video grid ───────── */
/* The grid owns the stage, until someone presents — then the screen does and the
   faces become a strip. An inline height here would fight the flex column and the
   filmstrip would silently cover the presentation. */
#grid{flex:1;min-height:0;display:flex;flex-direction:column;gap:10px;--striph:118px}
#stagearea{display:grid;gap:10px;flex:1;min-height:0;align-content:center;justify-content:center;
  grid-auto-rows:1fr}
#strip{display:flex;gap:8px;justify-content:center;flex:none;height:118px}
#strip[hidden]{display:none}
#strip .tile{width:auto;aspect-ratio:4/3;height:100%}
#app.focusmode #stagearea{grid-template-columns:1fr!important;grid-template-rows:1fr!important}
.viewbtn{display:none;position:absolute;right:16px;top:16px;z-index:5;width:38px;height:38px;border-radius:10px;
  background:rgba(8,11,16,.75);border:1px solid var(--line);color:var(--mid);font-size:15px;
  backdrop-filter:blur(6px)}
.viewbtn:hover{color:var(--ink);border-color:var(--blue)}
.viewbtn[hidden]{display:none}
.panelbtn{background:var(--panel2);border:1px solid var(--line);border-radius:10px;padding:8px 13px;
  font-size:12.5px;color:var(--mid);white-space:nowrap}
.panelbtn:hover{border-color:var(--blue);color:var(--ink)}
.panelbtn[hidden]{display:none}
.closebtn{display:flex;align-items:center;gap:5px;border:1px solid var(--line);border-radius:9px;padding:5px 10px}
.closebtn:hover{border-color:var(--blue);color:var(--ink)}
.closebtn span{font-size:12px}
.closebtn i{font-style:normal;line-height:1}
.closebtn .chev-l{display:none}
.tile{position:relative;background:#0e1218;border:1px solid var(--line);border-radius:var(--r);
  overflow:hidden;display:grid;place-items:center;min-height:0}
/* [stated] "my view versus hers it shouldnt cut that video feed into anything other
   than what it actually is (zoom is good at boxing these in)". `cover` fills the tile
   by cropping whatever does not fit — which on a portrait phone camera in a landscape
   tile means most of the person. `contain` shows the camera's real frame and lets the
   tile letterbox around it, which is what Zoom does and why its gallery never cuts a
   face in half. The ground behind it is the stage colour, so the box reads as
   deliberate rather than as a gap.

   The strip and the picture-in-picture keep `cover`: those are thumbnails a few
   centimetres wide, where letterboxing spends more than the crop costs. */
.tile video{width:100%;height:100%;object-fit:contain;background:#0e1218}
#stagearea.focused .tile.instrip video, .tile.selfpip video{object-fit:cover}
/* [stated] "on mobile the very thin border glow on whos talking is working but thats
   not working right on desktop". The ring was an OUTSET box-shadow, and
   `#stagearea .tile` carries `contain: layout paint` — which clips everything painted
   outside the element's box. On the desktop stage the glow was drawn and immediately
   clipped away; the phone layout does not go through that container, which is why it
   survived there and nowhere else. Drawn inside the box it cannot be clipped, and it
   reads the same. */
.tile.speaking{border-color:var(--good);box-shadow:inset 0 0 0 2px rgba(43,217,166,.55)}
.tile .avatar{width:76px;height:76px;border-radius:50%;display:grid;place-items:center;
  background:var(--panel2);color:var(--mid);font-size:28px;font-weight:600}
.tile .chip{position:absolute;left:10px;bottom:10px;display:flex;gap:6px;align-items:center;
  background:var(--chipbg);border-radius:8px;padding:4px 9px;font-size:12px;backdrop-filter:blur(6px)}
.tile .chip .role{color:var(--dim);font-size:10px;text-transform:uppercase;letter-spacing:.06em}
.tile .chip .muted-i{color:var(--bad)}
/* The badge used to be positioned absolutely at the chip's own coordinates and then
   pushed sideways by its OWN width — so it lined up only when the two happened to be
   the same size, and their bottoms aligned rather than their centres. It is a child of
   the chip now: one flex row, one gap, nothing to keep in sync. */
.tile .chip .badge{font-size:9.5px;line-height:1;letter-spacing:.07em;text-transform:uppercase;
  color:#fff;background:var(--grad);border-radius:5px;padding:4px 6px;font-weight:700;
  pointer-events:none;white-space:nowrap;margin-left:1px;align-self:center;
  transform:translateY(-.5px)}

/* The caption strip sits under the stage, not on top of a face. It used to overlap the
   name chip on the bottom-left tile, which is exactly where a person's name lives. */
/* Above every tile, always. Captions are the one thing on this screen that somebody
   may be depending on to follow the meeting at all. */
.captions{position:absolute;left:50%;transform:translateX(-50%);bottom:62px;
  width:min(760px,calc(100% - 40px));pointer-events:none;z-index:11;
  display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:4px}
/* bottom:62px clears the name chip in the corner at every size; the pills are
   centred and sized to their text, so they only reach the corner if a line is long
   enough to deserve the whole width — and then they are above it, not on it. */
.captions.off{display:none}

/* ───────── the caption pills ─────────
   Two, the same size. The settled one is what was just said; the live one is what is
   being said, kept to a single line so its height can never change — the settled line
   above it therefore never moves. See drawCaptions() for the width ratchet that stops
   the live pill twitching narrower on a recogniser revision.

   Sized to content and centred, so a short sentence is a short pill, not a bar. */
.cap-pill{display:block;max-width:100%;box-sizing:border-box;padding:7px 14px;
  border-radius:10px;background:rgba(4,7,20,.86);border:1px solid rgba(28,35,80,.9);
  backdrop-filter:blur(10px);box-shadow:0 10px 30px rgba(0,0,0,.4);
  font-size:15px;line-height:1.45;color:#EAF0FF;text-align:left;overflow-wrap:anywhere;
  transition:opacity .2s ease}
.cap-pill b{color:var(--blue);font-weight:600}
.cap-pill.settled{opacity:.72}
.cap-pill.live{white-space:nowrap;overflow:hidden;color:var(--mid)}
.cap-pill.live b{color:var(--dim)}
/* The live slot is always there, one line tall, whether or not anything is in it. If
   it collapsed when empty, the settled line above would drop a slot every time a
   sentence finished and climb back when the next one started — twice per sentence,
   which is the jump this whole file exists to prevent. Invisible costs nothing;
   moving costs everything. */
.cap-pill.live{min-height:calc(1.45em + 16px)}
.cap-pill.is-empty{visibility:hidden}
.cap-pill.settled.is-empty{display:none}     /* above the live slot: collapsing it moves nothing */
@media (max-width:700px){ .cap-pill{font-size:13.5px;padding:6px 10px} .captions{bottom:56px} }

/* ───────── bar ───────── */
.bar-left,.bar-right{display:flex;align-items:center;gap:10px;min-width:0;flex:0 1 auto}
.bar-right{justify-content:flex-end}
.bar-mid{display:flex;gap:7px;min-width:0;flex:0 1 auto}
.ctl{width:44px;height:44px;border-radius:50%;background:var(--panel2);border:1px solid var(--line);
  display:grid;place-items:center;font-size:19px;line-height:1}
.ctl:hover{background:var(--hover)}
.ctl.off{background:var(--offbg);border-color:var(--offline);color:var(--offink)}
.ctl.on{background:var(--onbg);border-color:var(--online);color:var(--acc)}
.ctl.leave{width:auto;padding:0 16px;border-radius:21px;background:var(--offbg);border-color:var(--offline);color:var(--offink);font-size:13px}
/* [stated] "remove the text in bottom left entirely doesnt need to show the code its
   in url", and the engine name likewise: "we don't need to say transcribing nova 2 …
   there should just be link".

   These lived under @media (max-width:900px) because the report came from a phone, and
   that was the wrong reading of "entirely" — on a desktop both were still sitting in
   the corner, which read later as a rollback of a fix that had never applied there. The
   address bar shows the code on every screen, so the argument does not depend on width.

   Two things stay, and neither is text: the share control, which is the button that
   copies an invite and the only thing in that pill anybody presses; and the listening
   dot, which says whether transcription is running. Hovering the dot still names the
   engine, for whoever actually wants to know. */
.room .rc{display:none}
.asrtag{display:none}

.room{font-family:var(--mono);font-size:12px;color:var(--mid);background:var(--panel2);
  border:1px solid var(--line);border-radius:7px;padding:4px 8px;
  display:inline-flex;align-items:center;gap:7px}
/* Now that the code is gone at every width, this pill is an icon button and nothing
   else — so it is sized like one instead of like a label that happens to have a glyph
   on the end. 13px in a 31px box was the leftover shape of the text that used to sit
   beside it. */
.room{width:34px;height:34px;padding:0;display:grid;place-items:center;border-radius:10px}
.room .shareicon{width:17px;height:17px;flex:none;opacity:.75;transition:opacity .15s}
button.room:hover .shareicon{opacity:1;color:var(--acc2)}
.dot{width:8px;height:8px;border-radius:50%;background:var(--dim)}
.dot.live{background:var(--good);box-shadow:0 0 8px var(--good)}
.muted{color:var(--dim);font-size:12px}
.ghost{color:var(--mid);font-size:13px;padding:6px 10px;border-radius:8px}
.ghost:hover{background:var(--panel2)}

/* ───────── rail ───────── */
.rail-top{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--line)}
.phases{display:flex;gap:4px;font-size:11px;text-transform:uppercase;letter-spacing:.07em}
/* Same trap as .sheet: a display rule beats the [hidden] attribute, so the QuickAlign
   stepper stayed on screen in every other mode. Any element hidden by attribute needs
   this said explicitly. */
.phases[hidden]{display:none}
.phases button{padding:5px 9px;border-radius:7px;color:var(--dim);background:transparent;
  font-size:inherit;letter-spacing:inherit;text-transform:inherit;font-family:inherit}
.phases button.done{color:var(--mid)}
.phases button.now{color:#fff;background:var(--grad);font-weight:700}
.phases button.can{cursor:pointer}
.phases button.can:hover{background:var(--panel2);color:var(--ink)}
.phases button:disabled{cursor:default}
.rail-body{flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:14px}
.rail-body::-webkit-scrollbar{width:8px}
.rail-body::-webkit-scrollbar-thumb{background:#232a35;border-radius:8px}

.card{background:var(--panel2);border:1px solid var(--line);border-radius:var(--r);padding:13px}
.card h3{margin:0 0 10px;font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--dim);font-weight:650}
.card h3 .n{color:var(--mid);font-weight:500;text-transform:none;letter-spacing:0}
.card.offer{border-color:var(--acc2);background:linear-gradient(160deg,var(--sunk2),var(--sunk))}
.card.hero{border-color:var(--edge)}
.empty{color:var(--dim);font-size:13px;line-height:1.55}

.meter{height:6px;background:var(--field);border-radius:4px;overflow:hidden;margin:8px 0 6px}
.meter i{display:block;height:100%;background:var(--grad);transition:width .6s}
.meter-row{display:flex;justify-content:space-between;font-size:11px;color:var(--dim)}

.item{padding:7px 0;border-bottom:1px solid var(--hair);font-size:13.5px;line-height:1.5}
.item:last-child{border-bottom:0}
.item .q{display:block;margin-top:4px;color:var(--mid);font-size:12px;border-left:2px solid var(--edge);padding-left:8px}
.item .q b{color:var(--dim);font-weight:500}
.item .flag{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--warn);
  border:1px solid #4a3a22;border-radius:5px;padding:1px 5px;margin-left:6px;white-space:nowrap}
.item .flag.human{color:var(--acc);border-color:var(--online)}

.who{display:flex;align-items:center;gap:8px;padding:6px 0;border-bottom:1px solid var(--hair);font-size:13px}
.who:last-child{border-bottom:0}
.who .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.who select{background:var(--field);border:1px solid var(--line);border-radius:7px;padding:4px 6px;font-size:12px}
.who .vv{font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.05em;width:64px;text-align:right}

textarea,input.f,select.f{width:100%;background:var(--field);border:1px solid var(--line);border-radius:9px;
  padding:9px 11px;outline:none;resize:vertical;font-size:13.5px;line-height:1.5}
textarea:focus,input.f:focus,select.f:focus{border-color:var(--acc2)}
label.lbl{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.07em;color:var(--dim);margin:10px 0 5px}
.btnrow{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap}
/* color is set, not inherited. Without it a .btn takes whatever colour its container
   happens to have — which is fine inside a panel and unreadable inside an overlay that
   sets a light colour for its own text. "Take down" and "Close for me" came out white
   on white in light mode for exactly this reason. A control's legibility must not
   depend on where somebody nests it. */
.btn{background:var(--panel2);color:var(--ink);border:1px solid var(--line);border-radius:9px;padding:9px 13px;font-size:13px}
.btn:hover{background:var(--hover)}
.btn.go{background:var(--grad);color:#fff;font-weight:600;border:0}
.btn.quiet{color:var(--mid)}
.btn:disabled{opacity:.45;cursor:default}
.btn.wide{flex:1}

.tabs{display:flex;gap:4px;margin-bottom:10px;flex-wrap:wrap}
.tabs button{font-size:12px;padding:6px 10px;border-radius:8px;background:var(--field);border:1px solid var(--line);color:var(--mid)}
.tabs button.on{background:var(--grad);color:#fff;border-color:transparent;font-weight:600}
.tabs button .b{opacity:.7;margin-left:4px}

.proposal{white-space:pre-wrap;font-size:14.5px;line-height:1.62}
.round{font-family:var(--mono);font-size:11px;color:var(--acc2);border:1px solid var(--edge2);border-radius:6px;padding:2px 7px}

.probe{padding:8px 0;border-bottom:1px solid var(--hair);font-size:13.5px;line-height:1.5}
.probe:last-child{border-bottom:0}
.probe .nm{color:var(--acc2);font-weight:600}
.probe .c{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--dim);margin-left:6px}

.tal{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin:10px 0}
.tal div{background:var(--field);border:1px solid var(--line);border-radius:9px;padding:8px;text-align:center}
.tal b{display:block;font-size:19px;line-height:1.2}
.tal span{font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.05em}
.verdict{border-radius:9px;padding:10px 12px;font-size:13.5px;line-height:1.5;margin-top:4px}
.verdict.yes{background:#07281f;border:1px solid #1c5c48;color:#b8f5de}
.verdict.no{background:#2c1a1e;border:1px solid #5b2b31;color:#f8cfcf}

.commit-row{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px}
.commit-row button{flex:1 1 auto;font-size:12px;padding:8px 6px;border-radius:8px;background:var(--field);
  border:1px solid var(--line);color:var(--mid);white-space:nowrap}
.commit-row button.on{border-color:var(--good);color:var(--good);background:#08251c}
.commit-row button.blockon{border-color:var(--bad);color:var(--bad);background:#26161a}

.act-row{display:flex;gap:8px;padding:8px 0;border-bottom:1px solid var(--hair);font-size:13.5px}
.act-row:last-child{border-bottom:0}
.act-row .own{color:var(--acc2);font-size:12px;white-space:nowrap}

#toasts{position:fixed;left:50%;transform:translateX(-50%);top:14px;z-index:60;display:flex;flex-direction:column;gap:8px;align-items:center}
.toast{background:var(--panel2);border:1px solid var(--line);border-radius:10px;padding:9px 14px;font-size:13px;
  box-shadow:0 8px 30px rgba(0,0,0,.5)}
.toast.warn{border-color:#5b4a2b;color:#ffdca8}
.spin{display:inline-block;width:11px;height:11px;border:2px solid var(--edge);border-top-color:var(--acc2);
  border-radius:50%;animation:sp .8s linear infinite;vertical-align:-1px;margin-right:6px}
@keyframes sp{to{transform:rotate(360deg)}}

@media (max-width:900px){
  #app{grid-template-columns:1fr;grid-template-rows:1fr 300px 60px;
    grid-template-areas:"stage" "rail" "bar"}
  #rail{border-left:0;border-top:1px solid var(--line)}
  #app.norail{grid-template-rows:1fr 0 60px}
  .arc{grid-template-columns:1fr 1fr}
}

/* ───────── the coach ─────────
   One instruction at a time, computed from what the room actually is. It is the
   first thing in the rail because "what do I do now" is the question a person in a
   new process is always asking, and answering it once at the door is not enough. */
.coach{background:linear-gradient(160deg,var(--sunk2),var(--sunk));border:1px solid var(--edge2);
  border-radius:var(--r);padding:14px}
.coach .do{font-size:15px;line-height:1.45;font-weight:600}
.coach .sub{margin-top:7px;color:var(--mid);font-size:13px;line-height:1.55}
.coach .ask{margin-top:10px;font-size:15px;line-height:1.5;color:var(--ink);
  border-left:2px solid var(--blue);padding-left:10px}
.coach .btnrow{margin-top:12px}
.rail-tools{display:flex;gap:6px;align-items:stretch}
/* One height for everything in this row — a 28px circle beside a taller Close pill
   reads as two different controls that happen to be neighbours. */
.rail-tools > button, .rail-tools .ghost{height:34px;box-sizing:border-box;
  display:inline-flex;align-items:center;justify-content:center}
#guideBtn, #themeBtn{width:34px;padding:0;border:1px solid var(--line);border-radius:9px;font-size:13px}

.cstep{display:flex;gap:4px;margin-bottom:10px}
.cstep button{flex:1;min-width:0;font-size:10px;letter-spacing:-.2px;padding:7px 2px;border-radius:8px;
  background:var(--field);border:1px solid var(--line);color:var(--dim);text-align:center;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cstep button .num{display:block;font-size:9px;opacity:.7}
.cstep button.now{background:var(--grad);color:#fff;border-color:transparent;font-weight:700}
.cstep button.done{color:var(--good);border-color:#1c5c48}

.invite{display:flex;gap:6px;margin-top:10px}
.invite input{flex:1;background:var(--field);border:1px solid var(--line);border-radius:8px;
  padding:8px 10px;font-size:12px;font-family:var(--mono);color:var(--mid);min-width:0}

/* ───────── how this works ───────── */
.sheet{position:fixed;inset:0;z-index:70;background:rgba(6,8,12,.72);backdrop-filter:blur(4px);
  display:grid;place-items:center;padding:20px}
/* `display:grid` beats the [hidden] attribute, and a full-screen invisible overlay
   that still eats clicks is indistinguishable from the app being frozen. */
.sheet[hidden]{display:none}
.sheet-card{position:relative;width:min(720px,94vw);max-height:86vh;overflow-y:auto;
  background:var(--panel);border:1px solid var(--line);border-radius:16px;padding:30px 34px}
.sheet-x{position:absolute;right:14px;top:10px;font-size:24px;color:var(--dim);line-height:1}
.sheet-x:hover{color:var(--ink)}
.sheet-body h2{margin:0 0 4px;font-size:22px;letter-spacing:-.01em}
.sheet-body h3{margin:26px 0 8px;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--acc2)}
.sheet-body p{margin:0 0 10px;color:var(--mid);font-size:14px;line-height:1.65}
.sheet-body p.lead{color:var(--ink)}
.sheet-body ol,.sheet-body ul{margin:0 0 10px;padding-left:20px;color:var(--mid);font-size:14px;line-height:1.65}
.sheet-body li{margin-bottom:8px}
.sheet-body b{color:var(--ink)}
.sheet-body .q{display:block;margin-top:3px;color:var(--ink);border-left:2px solid var(--acc);padding-left:9px}
.sheet-body .src{margin-top:26px;padding-top:14px;border-top:1px solid var(--line);font-size:12px;color:var(--dim)}

/* ───────── door: mode picker ───────── */
.gate-inner.wide{width:min(700px,94vw)}
.gate-row{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.modepick{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin:14px 0 4px;text-align:left}
.modepick button{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:12px 13px}
.modepick button:hover{border-color:#31415c}
.modepick button.on{border-color:var(--acc2);background:linear-gradient(160deg,var(--sunk2),var(--sunk))}
.modepick .mh{display:flex;align-items:center;gap:8px;font-weight:650;font-size:14px}
.modepick .mt{font-size:9px;text-transform:uppercase;letter-spacing:.08em;color:var(--dim);
  border:1px solid var(--line);border-radius:5px;padding:1px 5px}
.modepick .mb{margin-top:5px;color:var(--mid);font-size:12px;line-height:1.5}
.modepick button.on .mt{color:var(--acc2);border-color:var(--edge2)}

/* ───────── rail: mode bar ───────── */
/* The tab strip SCROLLS inside its own box; it never overflows onto its neighbour.
   `min-width:0` lets the strip shrink, but its buttons are nowrap and do not, so the
   content simply painted outside the box — and a button that paints outside its box
   still takes clicks. Adding a fifth tab put Transcript exactly on top of the rail's
   "?" and Chat on top of Close, so neither could be pressed at any width: the rail is
   a fixed 400px and five tabs measure 360 in 270. Same family as the `1fr` track that
   has cost this codebase 874px of sideways scroll and then 47px twice — a child that
   cannot shrink has to be given somewhere to go. */
.tabs-top{display:flex;gap:2px;min-width:0;flex:1 1 auto;overflow-x:auto;
  scrollbar-width:none;-ms-overflow-style:none;
  /* the last tab fades out rather than being cut, so there is visibly more to the right */
  -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 18px),transparent);
  mask-image:linear-gradient(90deg,#000 calc(100% - 18px),transparent)}
.tabs-top::-webkit-scrollbar{display:none}
.tabs-top button{position:relative;font-size:12.5px;padding:7px 10px;border-radius:9px;color:var(--dim);white-space:nowrap;flex:0 0 auto}
.tabs-top button:hover{background:var(--panel2);color:var(--mid)}
.tabs-top button.on{background:var(--panel2);color:var(--ink);font-weight:600}
.tabs-top .unread{top:4px;right:4px}
/* and the controls beside it are never squeezed to make room for tabs */
.rail-tools{flex:0 0 auto}

/* One control, showing the mode you are in — not four buttons and four tooltips. */
.modeline{position:relative;padding:10px 12px 0}
.modeBtn{display:flex;align-items:center;gap:9px;width:100%;
  background:var(--panel2);border:1px solid var(--line);border-radius:10px;padding:9px 12px;font-size:13.5px;
  transition:border-color .15s,background .15s}
.modeBtn .mk{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);
  border-right:1px solid var(--line);padding-right:9px}
.modeBtn #modeNow{flex:1;text-align:left;font-weight:600}
.modeBtn .chev{color:var(--blue);flex:none;transition:transform .18s}
.modeBtn:hover:not(:disabled){border-color:var(--blue);background:#131a3e}
.modeBtn[aria-expanded=true] .chev{transform:rotate(180deg)}
.modeBtn:disabled{opacity:.6;cursor:default}
.modeBtn:disabled .chev{display:none}
.modemenu{position:absolute;left:12px;right:12px;top:calc(100% - 2px);z-index:12;
  background:var(--panel2);border:1px solid var(--line);border-radius:12px;padding:6px;
  box-shadow:0 24px 60px rgba(3,10,50,.7)}
.modemenu[hidden]{display:none}
.modemenu button{display:block;width:100%;text-align:left;padding:9px 10px;border-radius:9px}
.modemenu button:hover{background:var(--panel3)}
.modemenu button.on{background:rgba(7,148,252,.12)}
.modemenu b{display:block;font-size:13.5px}
.modemenu span{display:block;color:var(--dim);font-size:11.5px;line-height:1.5;margin-top:2px}

#notesPane,#txPane,#chatPane{flex:1;display:flex;flex-direction:column;min-height:0}
#notesPane[hidden],#txPane[hidden],#chatPane[hidden]{display:none}
.tx-head{display:flex;align-items:center;justify-content:space-between;padding:9px 12px;
  border-bottom:1px solid var(--line);font-size:12px}
.tx-head .ghost.on{color:var(--blue-ink)}
.tx-body{gap:0}
.txline{padding:7px 0;border-bottom:1px solid rgba(28,35,80,.55);font-size:13.5px;line-height:1.6}
.txline:last-child{border-bottom:0}
.txline b{color:var(--blue-ink);font-weight:600}
.txline.ai b{color:var(--acc2)}
.txline i{color:var(--dim);font-style:normal;font-family:var(--mono);font-size:10.5px;margin-left:7px}
.txline.live{opacity:.6}
#phases{padding:8px 12px;border-bottom:1px solid var(--line)}

.rail-ask{display:flex;gap:6px;padding:10px 12px;border-top:1px solid var(--line);background:var(--panel)}
.rail-ask input{flex:1;min-width:0;background:var(--field);border:1px solid var(--line);border-radius:9px;
  padding:9px 11px;font-size:13px;outline:none}
.rail-ask input:focus{border-color:var(--acc2)}

/* ───────── recording ───────── */
/* The meeting's own red dot and the clip's stack in one corner: the meeting is
   recorded without anyone pressing anything, so its dot is the only place that
   fact is shown, and a clip taken during it sits under it rather than on it. */
.recdots{position:absolute;left:20px;top:20px;display:flex;flex-direction:column;align-items:flex-start;gap:6px;pointer-events:none}
.recdots .recdot{pointer-events:auto}
.recdot{display:flex;align-items:center;gap:8px;
  background:rgba(8,11,16,.82);border:1px solid #5b2b31;border-radius:9px;padding:6px 11px;
  font-size:12px;letter-spacing:.06em;backdrop-filter:blur(6px)}
.recdot i{width:9px;height:9px;border-radius:50%;background:#ef4444;animation:blink 1.6s infinite}
.recdot b{font-family:var(--mono);color:#AEB9D6;font-weight:500}
@keyframes blink{0%,100%{opacity:1}50%{opacity:.25}}
.ctl.rec{background:#3a1f24;border-color:#5b2b31;color:#ff9a9a}

/* ───────── mode content ───────── */
.topic{border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-bottom:8px;background:var(--field)}
.topic.now{border-color:var(--acc2)}
.topic.covered{border-color:#1c5c48}
.topic h4{margin:0;font-size:13.5px;display:flex;align-items:center;gap:8px;line-height:1.4}
.topic .st{font-size:9px;text-transform:uppercase;letter-spacing:.07em;padding:2px 6px;border-radius:5px;
  border:1px solid var(--line);color:var(--dim);white-space:nowrap}
.topic.covered .st{color:var(--good);border-color:#1c5c48}
.topic.now .st{color:var(--acc2);border-color:var(--edge2)}

.shots{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.shot{border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--field)}
.shot img{width:100%;display:block;aspect-ratio:16/9;object-fit:cover;cursor:zoom-in}
.shot .cap{padding:7px 9px;font-size:11.5px;color:var(--mid);line-height:1.45}
.shot .cap b{color:var(--ink);font-weight:600}

.ask-row{padding:9px 0;border-bottom:1px solid var(--hair);font-size:13.5px;line-height:1.55}
.ask-row:last-child{border-bottom:0}
.ask-row .who{color:var(--acc2);font-weight:600;font-size:12px}
.ask-row .ans{margin-top:4px}
.ask-row .beyond{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--warn);
  border:1px solid #4a3a22;border-radius:5px;padding:1px 5px;margin-left:6px}

.lightbox{position:fixed;inset:0;z-index:80;background:rgba(6,8,12,.9);display:grid;place-items:center;padding:24px}
.lightbox[hidden]{display:none}
.lightbox img{max-width:96vw;max-height:88vh;border-radius:12px}
.lightbox p{position:absolute;bottom:18px;left:50%;transform:translateX(-50%);color:var(--mid);font-size:13px;
  background:rgba(8,11,16,.8);padding:8px 14px;border-radius:8px;max-width:80vw;text-align:center}

@media (max-width:900px){ .modepick{grid-template-columns:1fr} .gate-row{grid-template-columns:1fr} }

.tag2{margin:-6px 0 22px;color:var(--dim);font-size:13px;line-height:1.6;max-width:520px;
  margin-left:auto;margin-right:auto}

/* ───────── door: start or join ───────── */
#gateForm{display:grid;gap:8px}
.codechip{display:flex;justify-content:center;margin:2px 0 2px}
.codechip span{font-family:var(--mono);font-size:20px;letter-spacing:.08em;color:var(--blue);
  background:rgba(7,148,252,.08);border:1px solid rgba(7,148,252,.35);border-radius:12px;padding:11px 18px}
.gerr{color:#f8cfcf;background:#2c1a1e;border:1px solid #5b2b31;border-radius:10px;
  padding:10px 12px;font-size:13px;margin:2px 0 0;line-height:1.5}
.gerr[hidden]{display:none}

/* ───────── presenting ───────── */
#stage{display:flex;flex-direction:column;gap:10px}
.spotlight{position:relative;flex:1;min-height:0;border-radius:var(--r);overflow:hidden;
  background:#05070a;border:1px solid var(--acc2)}
.spotlight[hidden]{display:none}
.spotlight video{width:100%;height:100%;object-fit:contain;background:#05070a;display:block}
.spotbar{position:absolute;z-index:3;left:12px;top:12px;display:flex;gap:10px;align-items:center;
  background:rgba(8,11,16,.82);border:1px solid var(--line);border-radius:9px;padding:6px 11px;
  font-size:12.5px;backdrop-filter:blur(6px)}
.spotbar .btn{padding:5px 10px;font-size:12px}
#app.presenting #grid{flex:none;height:132px;align-content:stretch}
#app.presenting #grid .tile{min-height:0}
#app.presenting #grid .chip{font-size:11px;padding:3px 7px}

/* ───────── chat ───────── */
.chat-body{gap:10px}
.cm{font-size:13.5px;line-height:1.55}
.cm .nm{color:var(--acc2);font-weight:600;font-size:12px;margin-right:6px}
.cm .tm{color:var(--dim);font-size:11px;font-family:var(--mono)}
.cm.sys{color:var(--mid);font-size:12.5px;font-style:italic}
.cm a{color:var(--acc);word-break:break-all}
.clip{background:var(--panel2);border:1px solid rgba(7,148,252,.4);border-radius:12px;padding:11px 12px}
.clip b{display:block;font-size:13px;margin-bottom:2px;font-style:normal}
.clip .row{display:flex;gap:6px;margin-top:9px}
.clip .row a,.clip .row button{font-size:12px;padding:6px 10px;border-radius:8px;
  background:var(--field);border:1px solid var(--line);color:var(--mid);text-decoration:none}
.clip .row a.go{background:var(--grad);color:#fff;border:0;font-weight:600}
.chat-send{display:flex;align-items:center;gap:6px;padding:10px 12px;border-top:1px solid var(--line)}
.chat-send input{flex:1;min-width:0;background:var(--field);border:1px solid var(--line);border-radius:9px;
  padding:9px 11px;font-size:13px;outline:none}
.chat-send input:focus{border-color:var(--acc2)}

.ctl.wide-ctl{display:flex;align-items:center;justify-content:center;width:auto;height:42px;
  padding:0 16px;border-radius:21px;gap:8px;white-space:nowrap;line-height:1}
.ctl.wide-ctl span{font-size:12.5px;font-weight:500}
@media (max-width:1100px){ .ctl.wide-ctl span{display:none} .ctl.wide-ctl{width:42px;padding:0} }
.ctl{position:relative}
.unread{position:absolute;top:3px;right:2px;min-width:16px;height:16px;padding:0 4px;
  border-radius:9px;background:#E5484D;color:#fff;font-family:var(--mono);font-size:10px;
  font-weight:600;line-height:16px;text-align:center;font-style:normal;
  box-shadow:0 0 0 2px var(--panel)}
/* The rail is closed on a phone, so the button that opens it wears the mark too. */
.hasunread{position:relative}
.hasunread::after{content:'';position:absolute;top:-3px;right:-3px;width:9px;height:9px;
  border-radius:50%;background:#E5484D;box-shadow:0 0 0 2px var(--panel)}

/* ───────── who is who, and where they are looking ─────────
   One hue per person, the same on every machine because it is derived from their id.
   It runs along the top of their tile rather than around it: a full border competes
   with the speaking ring, and the ring is the more urgent of the two. */
.tile::before{content:'';position:absolute;left:0;right:0;top:0;height:2px;z-index:2;
  background:var(--who, transparent);opacity:.85;border-radius:var(--r) var(--r) 0 0}
.tile.me::before{opacity:.55}
.whereat{position:absolute;top:8px;left:8px;z-index:3;display:inline-flex;align-items:center;
  gap:5px;padding:3px 8px;border-radius:999px;font-size:10.5px;letter-spacing:.01em;
  color:#EAF0FF;background:rgba(8,11,16,.72);backdrop-filter:blur(6px);
  border:1px solid rgba(255,255,255,.12)}
.whereat::before{content:'';width:5px;height:5px;border-radius:50%;background:var(--who,#8aa);
  box-shadow:0 0 6px var(--who,#8aa)}
button.room{cursor:pointer}
button.room:hover{border-color:var(--acc2);color:var(--ink)}

/* ───────── icons in the bar ─────────
   Mic and camera stay as the emoji Jack liked. Everything else is line art at a size
   we control, which is also the fix for the settings glyph rendering oversized —
   an emoji is drawn at whatever size the platform font decides. */
.ctl.icon svg{width:21px;height:21px;display:block}
.ctl.icon{font-size:0}
#setBtn svg{width:19px;height:19px}
.recdotic{width:11px;height:11px;fill:#ff6b7a;display:block}
#recBtn.rec .recdotic{animation:recpulse 1.2s ease-in-out infinite}
@keyframes recpulse{0%,100%{opacity:1}50%{opacity:.3}}

/* ───────── what a control actually does, on hover ─────────
   The native `title` takes about a second to appear, cannot be styled, and on a
   crowded bar it arrives as a grey box over somebody's face. This is the same
   information, faster, above the bar, and unable to intercept a click. */
[data-tip]{position:relative}
[data-tip]::after{
  content:attr(data-tip);position:absolute;bottom:calc(100% + 10px);left:50%;
  transform:translateX(-50%) translateY(4px);
  /* A tooltip is a dark chip in BOTH themes — same rule as anything painted over
     video. var(--ink) here was near-black in light mode: dark text on a dark chip,
     invisible exactly when somebody has just switched theme and is reading labels. */
  background:#0B0F22;color:#F1F4FF;border:1px solid rgba(255,255,255,.16);
  border-radius:9px;padding:7px 11px;font-size:12.5px;line-height:1.35;
  font-family:'Inter',system-ui,sans-serif;font-weight:450;letter-spacing:0;
  white-space:nowrap;max-width:min(74vw,340px);overflow:hidden;text-overflow:ellipsis;
  pointer-events:none;opacity:0;z-index:40;
  box-shadow:0 12px 30px -12px rgba(0,0,0,.85);
  transition:opacity .12s ease,transform .12s ease;transition-delay:.28s;
}
[data-tip]::before{
  content:'';position:absolute;bottom:calc(100% + 5px);left:50%;
  transform:translateX(-50%) translateY(4px);
  border:5px solid transparent;border-top-color:var(--line);
  pointer-events:none;opacity:0;z-index:41;
  transition:opacity .12s ease,transform .12s ease;transition-delay:.28s;
}
[data-tip]:hover::after,[data-tip]:focus-visible::after,
[data-tip]:hover::before,[data-tip]:focus-visible::before{
  opacity:1;transform:translateX(-50%) translateY(0);
}
/* The controls at the edges would push their label off-screen, so they hug the edge. */
#leaveBtn[data-tip]::after{left:auto;right:0;transform:translateX(0) translateY(4px)}
#leaveBtn[data-tip]:hover::after{transform:translateX(0) translateY(0)}
#leaveBtn[data-tip]::before,#leaveBtn[data-tip]::after{left:auto;right:14px}
.bar-left [data-tip]::after{left:0;transform:translateX(0) translateY(4px)}
.bar-left [data-tip]:hover::after{transform:translateX(0) translateY(0)}
.bar-left [data-tip]::before{left:22px}
.bar-right [data-tip]::after{left:auto;right:0;transform:translateX(0) translateY(4px)}
.bar-right [data-tip]:hover::after{transform:translateX(0) translateY(0)}
.bar-right [data-tip]::before{left:auto;right:30px}
/* A finger has no hover. Showing these on tap would just cover the thing you tapped. */
@media (hover:none){ [data-tip]::after,[data-tip]::before{display:none} }

.btn.go.wide{display:block;width:100%;text-align:center}
#shareCopy.done{background:var(--good);border-color:var(--good);color:#04241b}
#shareCopy.showing{font-family:var(--mono);font-size:12px;word-break:break-all;white-space:normal}


/* ═══════════════════════════ the landing page ═══════════════════════════
   Dark-first, the brand's own gradient used as light rather than as paint, and
   the faceted mark left to be itself instead of tiled across everything. */

.site{scroll-behavior:smooth}
.display{font-family:var(--display);font-weight:700;letter-spacing:-.035em;line-height:1.03;
  font-size:clamp(34px,5vw,68px);margin:0}
.display.sm{font-size:clamp(34px,4.4vw,58px)}
.h2{font-family:var(--display);font-weight:600;letter-spacing:-.025em;
  font-size:clamp(28px,3.4vw,44px);line-height:1.1;margin:0 0 14px}
.lead{color:var(--mid);font-size:clamp(15px,1.35vw,18px);line-height:1.7;margin:0 0 14px;max-width:62ch}
.lead em{color:var(--ink);font-style:normal}

.nav{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:space-between;
  padding:16px clamp(18px,4vw,52px);backdrop-filter:blur(14px);
  background:linear-gradient(180deg,rgba(5,8,23,.92),rgba(5,8,23,.55));border-bottom:1px solid rgba(28,35,80,.6)}
.brand-lockup{display:flex;align-items:center;gap:11px;text-decoration:none}
.brand-lockup img{height:30px;width:auto;display:block}
.brand-lockup .wm{height:13px;opacity:.95}
.nav-links{display:flex;gap:clamp(12px,2vw,26px);align-items:center}
.nav-links a{color:var(--mid);text-decoration:none;font-size:13.5px;font-weight:500}
.nav-links a:hover{color:var(--ink)}
@media (max-width:760px){ .nav-links a:not(:last-child){display:none} }

.hero{position:relative;padding:clamp(30px,5vh,64px) clamp(18px,4vw,52px) clamp(34px,5vh,64px);
  overflow:hidden;min-height:calc(100svh - 64px);display:grid;align-items:center}
.ambient{position:absolute;inset:-20% -10% auto -10%;height:150%;pointer-events:none;
  background:radial-gradient(circle at 74% 12%,rgba(91,46,242,.34),transparent 44%),
             radial-gradient(circle at 8% 78%,rgba(7,148,252,.24),transparent 40%)}
.hero-inner{position:relative;max-width:1080px;margin:0 auto;text-align:center}
.eyebrow{font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--blue);
  font-weight:600;margin:0 0 14px}
.hero .sub{color:var(--mid);font-size:clamp(14.5px,1.25vw,17px);line-height:1.6;
  max-width:58ch;margin:16px auto 0}

.startcard{max-width:410px;margin:26px auto 0;display:grid;gap:9px;text-align:left;
  background:rgba(15,20,48,.72);border:1px solid var(--line);border-radius:var(--r);
  padding:18px;box-shadow:0 30px 90px rgba(3,10,50,.55);backdrop-filter:blur(10px)}
.startcard input[type=text],.startcard input#gname{width:100%}
.startcard #gname{background:var(--deepbg);border:1px solid var(--line);border-radius:12px;
  padding:13px 15px;outline:none;font-size:16px;color:var(--ink)}
.startcard #gname:focus{border-color:var(--blue);box-shadow:0 0 0 3px rgba(7,148,252,.16)}
.cta{background:var(--grad);color:#fff;font-weight:600;font-size:16px;border-radius:12px;
  padding:14px;border:0;box-shadow:0 12px 34px rgba(7,85,235,.4);font-family:var(--display);letter-spacing:-.01em}
.cta:hover{filter:brightness(1.08)}
.cta:disabled{opacity:.55}
.startnote{color:var(--dim);font-size:12.5px;line-height:1.6;margin:2px 0 0}
.statusline{margin:14px 0 0;font-size:12px;color:var(--dim)}
.site .gate-toggles{display:flex;gap:14px;justify-content:flex-start;margin:2px 0 0;
  color:var(--mid);font-size:12.5px;flex-wrap:wrap}

.strip{border-block:1px solid rgba(28,35,80,.6);background:rgba(10,14,36,.5)}
.strip-in{max-width:1080px;margin:0 auto;padding:22px clamp(18px,4vw,52px);
  display:grid;grid-template-columns:repeat(4,1fr);gap:22px}
.strip-in div{display:grid;gap:3px}
.strip-in b{font-family:var(--display);font-size:15px;font-weight:600}
.strip-in span{color:var(--dim);font-size:12.5px;line-height:1.5}
@media (max-width:820px){ .strip-in{grid-template-columns:1fr 1fr} }

.modes{max-width:1140px;margin:0 auto;padding:clamp(56px,8vw,110px) clamp(18px,4vw,52px) 0;text-align:center}
.modes .lead{margin:0 auto 40px}
.cards{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;text-align:left}
.card-m{background:linear-gradient(180deg,rgba(15,20,48,.9),rgba(10,14,36,.75));
  border:1px solid var(--line);border-radius:var(--r);padding:22px;position:relative;overflow:hidden}
.card-m::after{content:'';position:absolute;inset:auto -30% -60% -30%;height:120px;
  background:radial-gradient(ellipse at center,rgba(7,148,252,.20),transparent 70%);pointer-events:none}
.card-m .tag{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--blue);font-weight:600}
.card-m h3{font-family:var(--display);font-size:21px;font-weight:600;margin:9px 0 9px;letter-spacing:-.02em}
.card-m p{color:var(--mid);font-size:13.5px;line-height:1.65;margin:0}
@media (max-width:1000px){ .cards{grid-template-columns:1fr 1fr} }
@media (max-width:620px){ .cards{grid-template-columns:1fr} }

.split{max-width:1140px;margin:0 auto;padding:clamp(56px,8vw,110px) clamp(18px,4vw,52px) 0;
  display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(30px,5vw,64px);align-items:center}
.split.rev .split-copy{order:2}
.ticks{list-style:none;margin:20px 0 0;padding:0;display:grid;gap:9px}
.ticks li{color:var(--mid);font-size:14px;padding-left:26px;position:relative;line-height:1.6}
.ticks li::before{content:'';position:absolute;left:0;top:7px;width:13px;height:13px;border-radius:4px;
  background:var(--grad)}
@media (max-width:900px){ .split{grid-template-columns:1fr} .split.rev .split-copy{order:0} }

.split-art{display:grid;place-items:center}
.markart{width:min(320px,74%);filter:drop-shadow(0 30px 70px rgba(91,46,242,.45))}
.frame{width:100%;max-width:460px;background:var(--deepbg);border:1px solid var(--line);
  border-radius:var(--r);overflow:hidden;position:relative;box-shadow:0 30px 90px rgba(3,10,50,.6)}
.frame-bar{display:flex;gap:6px;padding:11px 14px;border-bottom:1px solid var(--line);background:rgba(15,20,48,.7)}
.frame-bar i{width:9px;height:9px;border-radius:50%;background:#1C2350}
.frame-body{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:14px}
.ft{aspect-ratio:16/10;border-radius:10px;background:linear-gradient(150deg,rgba(7,148,252,.16),rgba(91,46,242,.14));
  border:1px solid rgba(28,35,80,.9)}
.ft:nth-child(2){background:linear-gradient(150deg,rgba(91,46,242,.2),rgba(7,148,252,.1))}
.ft:nth-child(3){background:linear-gradient(200deg,rgba(7,148,252,.1),rgba(29,47,198,.18))}
.frame-rec{position:absolute;left:14px;top:52px;display:flex;align-items:center;gap:7px;
  background:rgba(5,8,23,.85);border:1px solid #5b2b31;border-radius:8px;padding:5px 10px;
  font-size:11px;font-family:var(--mono);color:var(--mid)}
.frame-rec .dot{width:8px;height:8px;border-radius:50%;background:#FF6B7A;animation:blink 1.6s infinite}

.closer{text-align:center;padding:clamp(70px,10vw,140px) clamp(18px,4vw,52px) 0;max-width:760px;margin:0 auto}
.closer .lead{margin:16px auto 26px}
.closer .cta{padding:16px 40px;font-size:17px}

.foot{max-width:1140px;margin:0 auto;padding:clamp(50px,7vw,90px) clamp(18px,4vw,52px) 46px;
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  border-top:1px solid rgba(28,35,80,.6);margin-top:clamp(50px,7vw,90px)}
.footwm{height:12px;opacity:.7}
.foot span{color:var(--dim);font-size:12.5px;font-family:var(--mono)}

.statusline{display:flex;align-items:center;justify-content:center;gap:7px}
.statusline .pip{width:6px;height:6px;border-radius:50%;background:var(--good);box-shadow:0 0 8px var(--good)}

.sheet-card.narrow{width:min(520px,94vw)}
.sheet-body h3{color:var(--acc)}
.sheet-body label.row{display:flex;align-items:center;gap:9px;color:var(--mid);font-size:14px;margin:9px 0}
.sheet-body select.f{margin-top:4px}
.sheet-body .note{font-size:12.5px;color:var(--dim);margin-top:8px}


/* ═══════════════════════ small screens ═══════════════════════
   The room is the hard part on a phone: a video grid, a side panel and a control bar
   cannot all be on screen at once. So the panel becomes a sheet over the stage, the
   bar stays put above the home indicator, and the grid keeps the whole viewport. */

@media (max-width:860px){
  .hero{min-height:auto;padding-top:26px;padding-bottom:34px}
  .display{font-size:clamp(30px,8.4vw,44px)}
  .display br{display:none}
  .hero .sub{font-size:15px;margin-top:14px}
  .startcard{margin-top:22px;max-width:100%}
  .site .gate-toggles{gap:12px;font-size:12px}
  .strip-in{grid-template-columns:1fr 1fr;gap:16px;padding:20px}
  .modes,.split,.closer{padding-top:56px}
  .cards{gap:12px}
  .split-art{order:-1}
  .markart{width:52%}
  .frame{max-width:100%}
  .foot{flex-direction:column;gap:12px;text-align:center}
}

@media (max-width:900px){
  #app{grid-template-columns:1fr;grid-template-rows:1fr auto;
    grid-template-areas:"stage" "bar"}
  /* the panel slides over the meeting instead of squeezing it */
  #rail{position:fixed;inset:0 0 var(--barh) 0;z-index:30;border-left:0;
    border-top:1px solid var(--line);transform:translateY(100%);
    transition:transform .28s cubic-bezier(.2,.8,.2,1)}
  #app:not(.norail) #rail{transform:translateY(0)}
  #app.norail #rail{transform:translateY(100%)}
  #railOpen{display:inline-flex}
  #stage{padding:8px}
  #grid{gap:6px}
  .captions{bottom:6px;width:calc(100% - 16px)}
  .cap-pill{font-size:13px}
  .tile .chip{left:6px;bottom:6px;font-size:11px;padding:3px 7px}
  .tile .chip .badge{font-size:9px;padding:3px 5px}
  /* ── one row ────────────────────────────────────────────────────────────
     [stated] "the notes and chat should go up so now that bottom bar can just be one
     row". It wrapped because .bar-mid was forced to 100% and pushed to order:3, which
     cost a whole row of a phone screen to hold buttons that fit beside the others.
     Notes & chat moves onto the video where there is space going spare, the engine
     name is gone, and what remains is one row that does not wrap. */
  #bar{flex-wrap:nowrap;gap:6px;padding:7px 8px calc(9px + env(safe-area-inset-bottom));
    align-items:center}
  .bar-left{min-width:0;flex:0 1 auto;overflow:hidden}
  .bar-right{flex:none;margin-left:auto}
  .bar-mid{order:0;width:auto;flex:1 1 auto;justify-content:center;gap:3px;min-width:0;
    overflow-x:auto;scrollbar-width:none}
  .bar-mid::-webkit-scrollbar{display:none}
  .ctl{width:38px;height:38px;font-size:14px;flex:none}
  .ctl.leave{padding:0 11px;font-size:12px}
  .ctl.wide-ctl{padding:0;width:38px}
  /* Leave must never be the button that falls off the end. Seven round controls at
     38px plus a share icon plus Leave is 374px of content in a 390px phone, and the
     row was overflowing by exactly the width of the word. Below 400px everything
     tightens rather than the last button being cut in half. */
  .bar-right{flex:none;margin-left:auto;min-width:0}
  .ctl.leave{flex:none}
}
/* A narrower step of its own. Nesting this inside the 900px block would have been
   invalid CSS; opening it without closing that block first left every rule in the rest
   of the file trapped inside a media query, which is how the light theme lost its
   text colours for one commit. */
@media (max-width:400px){
  #bar{gap:4px;padding-left:6px;padding-right:6px}
  .bar-mid{gap:1px}
  .ctl{width:34px;height:34px}
  .ctl.wide-ctl{width:34px}
  .ctl.leave{padding:0 9px;font-size:11.5px}
  .recdotic{width:23px;height:23px}
}
@media (max-width:900px){

  .room{padding:7px 9px;gap:0;border-radius:9px}

  /* Notes & chat, lifted onto the video. It is the one control that opens a whole
     surface, so it earns a corner rather than a slot in a row of round buttons.
     FIXED, not absolute: it lives inside #bar, and a bar 59px tall makes `top:58px`
     mean "just past the bottom of the screen". Top-left, because the top-right corner
     already belongs to the theme and layout buttons. */
  /* [stated] "lets move the close button to the left side on mobile since thats where
     the open button is". Opening happens top-left and closing happened top-right, so a
     round trip crossed the whole screen with a thumb. `display:contents` on the tools
     wrapper lets its two children take their own places in the header rather than
     travelling together — Close leads, the tabs follow, and ? stays at the end where a
     rarely-pressed thing belongs. */
  .rail-tools{display:contents}
  #railClose{order:-1;margin-right:6px}
  .tabs-top{order:0}
  #guideBtn{order:1}
  .closebtn .chev-l{display:inline}
  .closebtn .chev-r{display:none}

  #railOpen{position:fixed;left:12px;top:12px;z-index:9;
    background:rgba(8,11,16,.72);border:1px solid rgba(255,255,255,.16);color:#fff;
    backdrop-filter:blur(8px);padding:8px 13px;font-size:12.5px;border-radius:10px}
  #app.norail #railOpen{display:inline-flex}

  /* [stated] "the clip text on mobile is too small to read" — the label is hidden at
     this width, so the word on the arc is the only thing saying which button this is,
     and at 11px it said nothing. */
  .recdotic{width:26px;height:26px}
  .recdotic .clipword{font-size:6.2px}

  .ctl.wide-ctl span{display:none}
  /* Once the room code came out of this button, all that was left was a 13px glyph in
     3px of padding — the smallest target in the whole bar, and it is the one that
     copies the invite link, which is the thing a person on a phone most often wants.
     Sized to match the other controls in the row. */
  .room{font-size:11px;padding:0;width:38px;height:38px;display:grid;place-items:center;
    border-radius:11px}
  .room .shareicon{width:19px;height:19px;opacity:.9}
  button.room:active .shareicon{opacity:1;color:var(--acc2)}
  .panelbtn{padding:6px 10px;font-size:11.5px}
  #peopleTag{display:none}
  .recdots{left:10px;top:10px}
  .recdot{font-size:11px;padding:5px 9px}
  .spotbar{left:8px;top:8px;font-size:11.5px;padding:5px 9px}
  #app.presenting #grid{height:92px}
  .sheet-card{padding:22px 20px;max-height:90vh}
  .sheet-body h2{font-size:19px}
  .modemenu{left:8px;right:8px}
}
:root{--barh:60px}
@media (max-width:900px){ :root{--barh:106px} }

/* ───────── the viewport, for good ─────────
   Three units have been tried here and all three lie somewhere. 100vh is the large
   viewport and hides the bar under Safari's toolbar. 100svh is the small one and
   leaves a band of background when the toolbar retracts. 100dvh is right until the
   keyboard opens, when Android reports a viewport that includes the keyboard and iOS
   does not resize at all.

   So the page does not ask CSS how tall the window is — it measures, from
   visualViewport, which is the only API that knows about the toolbar AND the
   keyboard. --vph is that measurement. The unit chain below is the fallback for the
   first paint and for anything without visualViewport, in worst-to-best order so the
   best supported one wins. */
#app{height:100vh}
@supports (height:100svh){ #app{height:100svh} }
@supports (height:100dvh){ #app{height:100dvh} }

/* Two measurements, not one. --vph is how tall the visible area is; --vtop is how far
   down the page that visible area STARTS.

   The second one is why this kept coming back. A `position:fixed` element on iOS is
   placed against the LAYOUT viewport, which includes the strip of page hidden behind
   Safari's toolbar — but --vph measures the VISUAL viewport, which does not. Pin a
   fixed element at top:0 and give it the visual height and it starts too high by
   exactly the toolbar's offset, so it ends that far below the bottom of the screen,
   and whatever is at its bottom edge — the control bar — is cut in half. Which is
   precisely what it did. Both numbers come from visualViewport; neither is guessed. */
#app{top:var(--vtop, 0px); bottom:auto; height:var(--vph, 100dvh)}

/* The control bar sits above the home indicator on every phone, and above the
   keyboard when one is open — --kb is measured too. */
#bar{padding-bottom:calc(9px + env(safe-area-inset-bottom, 0px) + var(--kb, 0px))}

/* ───────── the bar is not a grid row on a phone ─────────
   It used to be, and a grid row can be squeezed: the bar's track resolved to 0px
   while its buttons needed 106, so they hung off the bottom of the window with no
   scroll to reach them. A row that CAN collapse eventually will.

   So on a phone the bar is taken out of the grid and pinned to the bottom of the
   shell, where its height is whatever its content needs and nothing can compress it.
   The stage reserves room for it with padding, from --barh, which is MEASURED from
   the bar rather than guessed — add a button, wrap to a third line, translate the
   labels into German, and the reservation still matches. */
@media (max-width:900px){
  /* grid-auto-rows:0 matters as much as the rows above it. With the areas reduced to
     "stage", `grid-area:bar` and `grid-area:rail` no longer name anything, so the grid
     invents IMPLICIT rows for them — computed rows came out `784px 0px 60px`, and that
     stray 60px is the band of dead space between the video and the controls. The bar is
     absolutely positioned and the stage already reserves its height in padding, so the
     reservation was being made twice. */
  /* `.norail` is matched too, and that is the whole bug. `#app.norail` is an id plus a
     class, so it outranks a plain `#app` no matter which rule comes later — and on a
     phone the rail starts closed, so `.norail` is the state the room opens in. Its
     desktop rule is `1fr 0 60px`, which is exactly the `784px 0px 60px` the browser
     was computing: a 60px row still reserved for a bar that is absolutely positioned
     and already accounted for in the stage's padding. The video stopped 66px short of
     the controls because the room was paying for that bar twice.

     grid-auto-rows:0 covers the other half: with the areas reduced to "stage",
     `grid-area:bar` and `grid-area:rail` name nothing and the grid would otherwise
     invent implicit rows for them. */
  #app, #app.norail{grid-template-columns:1fr; grid-template-rows:1fr;
    grid-template-areas:"stage"; grid-auto-rows:0; grid-auto-columns:0}
  #stage{grid-row:1; grid-column:1; height:100%}
  #bar{position:absolute; left:0; right:0; bottom:0; z-index:20; height:auto}
  #stage{padding-bottom:calc(var(--barh, 106px) + 6px)}
  #rail{inset:0 0 var(--barh, 106px) 0}
}

/* ───────── dropping files in ───────── */
.dropveil{position:fixed;inset:0;z-index:60;display:none;place-items:center;pointer-events:none;
  background:rgba(5,8,23,.72);backdrop-filter:blur(6px)}
#app.dropping .dropveil{display:grid}
.dropveil>div{border:2px dashed var(--blue);border-radius:20px;padding:38px 52px;text-align:center;
  background:rgba(15,20,48,.8)}
.dropveil b{display:block;font-family:var(--display);font-size:22px;font-weight:600}
.dropveil span{display:block;color:var(--mid);font-size:13.5px;margin-top:7px}

.filecard{background:var(--panel2);border:1px solid var(--line);border-radius:12px;padding:11px 12px}
.filecard b{display:block;font-size:13px;font-style:normal;margin-bottom:8px}
.filecard img{width:100%;border-radius:9px;display:block;cursor:zoom-in;max-height:220px;object-fit:cover}
.filecard .row{display:flex;gap:9px;align-items:center;margin-top:9px}
.filecard .row a{font-size:12px;padding:6px 12px;border-radius:8px;text-decoration:none;
  background:var(--grad);color:#fff;font-weight:600}
.filecard .fname{color:var(--dim);font-size:11.5px;font-family:var(--mono);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;min-width:0}

/* ───────── the face bubble over a presentation ───────── */
.spotface{position:absolute;left:20px;bottom:20px;z-index:3;display:grid;justify-items:center;gap:8px;
  pointer-events:none}
.spotface[hidden]{display:none}
.spotface video{width:clamp(96px,11vw,164px);aspect-ratio:1;border-radius:50%;object-fit:cover;
  background:var(--panel2);border:3px solid transparent;
  background-image:linear-gradient(var(--panel2),var(--panel2)),var(--grad);
  background-origin:border-box;background-clip:content-box,border-box;
  box-shadow:0 14px 40px rgba(3,10,50,.65)}
.spotface span{background:rgba(5,8,23,.82);border-radius:8px;padding:3px 10px;font-size:12px;
  backdrop-filter:blur(6px);max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media (max-width:900px){ .spotface{left:10px;bottom:10px;gap:5px} .spotface span{font-size:11px} }

/* ───────── the QuickAlign roadmap ───────── */
.step{padding:9px 0 9px 0;border-bottom:1px solid rgba(28,35,80,.55)}
.step:last-of-type{border-bottom:0}
.step .sh{display:flex;align-items:center;gap:8px}
.step .n{width:19px;height:19px;border-radius:50%;display:grid;place-items:center;flex:none;
  font-size:10.5px;font-weight:700;background:var(--field);border:1px solid var(--line);color:var(--dim)}
.step b{font-size:13.5px}
.step p{margin:5px 0 0 27px;color:var(--dim);font-size:12.5px;line-height:1.55}
.step .here{font-size:9.5px;text-transform:uppercase;letter-spacing:.08em;color:#fff;
  background:var(--grad);border-radius:5px;padding:2px 6px;font-weight:700}
.step.now .n{background:var(--grad);border-color:transparent;color:#fff}
.step.now p{color:var(--mid)}
.step.done .n{color:var(--good);border-color:#1c5c48}
.step.done b{color:var(--mid)}

/* ───────── share / meeting code ───────── */
.bigcode{font-family:var(--mono);font-size:clamp(22px,4vw,30px);letter-spacing:.1em;text-align:center;
  color:var(--blue);background:rgba(7,148,252,.08);border:1px solid rgba(7,148,252,.35);
  border-radius:14px;padding:16px;margin:14px 0}
.linkrow2{display:flex;gap:8px}
.linkrow2 input{flex:1;min-width:0;background:var(--deepbg);border:1px solid var(--line);border-radius:10px;
  padding:11px 13px;font-size:12.5px;font-family:var(--mono);color:var(--mid)}
.knock{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid rgba(28,35,80,.55)}
.knock:last-child{border-bottom:0}
.knock .who2{flex:1;min-width:0;font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.knock button{font-size:12px;padding:7px 12px;border-radius:9px;border:1px solid var(--line);background:var(--field);color:var(--mid)}
.knock button.yes{background:var(--grad);color:#fff;border:0;font-weight:600}
.sheet-body .row span b{font-weight:600;color:var(--ink)}
.knockdot{position:absolute;top:5px;right:5px;width:9px;height:9px;border-radius:50%;background:var(--warn);
  box-shadow:0 0 8px var(--warn)}

/* ───────── phone: speaker focus ───────── */
@media (max-width:900px){
  /* the strip floats over the speaker instead of stealing height from a small screen */
  #app.focusmode #grid{position:relative}
  #app.focusmode #strip.dead{position:absolute;right:8px;bottom:8px;left:auto;height:auto;
    justify-content:flex-end;gap:6px;z-index:3}
  #app.focusmode #strip .tile{width:24vw;max-width:118px;height:auto;aspect-ratio:3/4;
    box-shadow:0 10px 26px rgba(0,0,0,.55)}
  #app.focusmode #strip .tile.me{border-color:rgba(7,148,252,.55)}
  #app.focusmode #strip .chip{font-size:10px;padding:2px 6px}
  #app.focusmode .captions{bottom:calc(8px + 24vw * 4 / 3 + 10px)}
  .viewbtn{right:10px;top:10px;width:34px;height:34px}
}

/* ───────── reactions ───────── */
.reactwrap{position:relative}
.reactbar{position:absolute;bottom:calc(100% + 10px);left:50%;transform:translateX(-50%);
  display:flex;gap:4px;padding:8px;background:var(--panel2);border:1px solid var(--line);
  border-radius:16px;box-shadow:0 20px 50px rgba(3,10,50,.7);z-index:20}
.reactbar[hidden]{display:none}
.reactbar button{width:44px;height:44px;border-radius:50%;font-size:24px;line-height:1;
  display:grid;place-items:center;transition:transform .12s}
.reactbar button:hover{transform:scale(1.25) translateY(-2px);background:var(--panel3)}
.reactbar button:active{transform:scale(.95)}

#floats{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:6}
.float{position:absolute;display:flex;flex-direction:column;align-items:center;gap:3px;
  animation:floatup var(--dur,3.6s) cubic-bezier(.22,.7,.3,1) forwards}
.float .em{font-size:var(--size,42px);line-height:1;filter:drop-shadow(0 6px 14px rgba(0,0,0,.5))}
.float .nm2{font-size:10.5px;color:var(--ink);background:rgba(5,8,23,.72);border-radius:6px;
  padding:1px 6px;backdrop-filter:blur(4px)}
@keyframes floatup{
  0%{opacity:0;transform:translateY(0) scale(.5) rotate(var(--r0,0deg))}
  12%{opacity:1;transform:translateY(-14px) scale(1.15) rotate(var(--r0,0deg))}
  100%{opacity:0;transform:translateY(-62vh) translateX(var(--dx,0px)) scale(.9) rotate(var(--r1,0deg))}
}
.tile.handup{border-color:var(--warn);box-shadow:inset 0 0 0 2px rgba(245,184,81,.6)}
.tile .hand{position:absolute;right:10px;top:10px;font-size:20px;
  animation:wave 1.6s ease-in-out infinite;transform-origin:70% 80%}
@keyframes wave{0%,100%{transform:rotate(-8deg)}50%{transform:rotate(12deg)}}

/* the file card was wrapping "Open" onto two lines */
.filecard .row a{white-space:nowrap;flex:none}
.filecard .row .dl{background:var(--field);border:1px solid var(--line);color:var(--mid);font-weight:500}

.chatbox-wrap{position:relative;flex:1;min-width:0}
.chatbox-wrap input{width:100%}
.atmenu{position:absolute;bottom:calc(100% + 6px);left:0;right:0;background:var(--panel2);
  border:1px solid var(--line);border-radius:12px;padding:5px;z-index:20;
  box-shadow:0 18px 44px rgba(3,10,50,.7)}
.atmenu[hidden]{display:none}
.atmenu button{display:block;width:100%;text-align:left;padding:8px 10px;border-radius:8px;font-size:13px;color:var(--mid)}
.atmenu button:hover{background:var(--panel3);color:var(--ink)}
.atmenu button.ai{color:var(--blue);font-weight:600}

/* Audio sinks: present, playing, and never laid out or moved. */
#sinks{position:fixed;width:0;height:0;overflow:hidden;pointer-events:none;opacity:0}
.audiowarn{position:absolute;left:50%;transform:translateX(-50%);top:12px;z-index:8;
  background:rgba(44,26,30,.92);border:1px solid #5b2b31;color:#f8cfcf;border-radius:10px;
  padding:8px 14px;font-size:13px;display:flex;align-items:center;gap:10px}
.audiowarn[hidden]{display:none}
.audiowarn button{background:var(--grad);color:#fff;border-radius:8px;padding:5px 11px;font-size:12px;font-weight:600}


/* ───────── pin, mute-for-me, and the agent row ───────── */
/* Top-right, not bottom-right. The picture-in-picture lives in the bottom-right corner
   of the room and sits above every tile, so pin and mute were unreachable on whichever
   tile happened to be underneath it — visible, and impossible to click. Two things that
   both want the same corner is a bug waiting for the day somebody has one guest. */
.tileacts{position:absolute;right:6px;top:6px;display:flex;gap:5px;opacity:0;transition:opacity .14s;z-index:3}
.tile:hover .tileacts,.tile:focus-within .tileacts{opacity:1}
@media (hover:none){ .tileacts{opacity:.75} }
.tileact{width:30px;height:30px;border-radius:9px;border:1px solid var(--line);
  background:rgba(5,8,23,.72);backdrop-filter:blur(6px);color:var(--ink);font-size:13px;
  cursor:pointer;display:grid;place-items:center;padding:0;line-height:1}
.tileact:hover{border-color:var(--blue)}
.tile.pinned .tileact.pin{background:var(--blue);border-color:var(--blue);color:#fff}
.tile.mutedforme .tileact.mute{background:#b3402f;border-color:#b3402f;color:#fff}
.tile.pinned{outline:2px solid var(--blue);outline-offset:-2px}
.tile.mutedforme video{filter:grayscale(.5) brightness(.82)}
.tile.mutedforme .chip .nm::after{content:' · muted for you';opacity:.75;font-weight:400}

/* Your own picture reads as a mirror; what is sent never is. */
video.mirror{transform:scaleX(-1)}

.person{display:flex;align-items:center;gap:9px;padding:8px 0;border-bottom:1px solid var(--line)}
.person:last-child{border-bottom:0}
.person .who2{flex:1;min-width:0;font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.person button{font-size:11.5px;padding:5px 10px;border-radius:8px;border:1px solid var(--line);
  background:var(--panel2);color:var(--ink);cursor:pointer;white-space:nowrap}
.person button:hover{border-color:var(--blue)}
.person button.yes{background:var(--blue);border-color:var(--blue);color:#fff}
.tagagent{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;padding:3px 7px;border-radius:6px;
  background:linear-gradient(120deg,var(--blue),var(--violet));color:#fff;font-weight:600}
.tile .badge.agentbadge{background:linear-gradient(120deg,var(--blue),var(--violet))}

.footnav{display:flex;gap:18px;flex-wrap:wrap;flex:1;justify-content:center}
.footnav a{color:var(--dim);font-size:13px;text-decoration:none}
.footnav a:hover{color:var(--ink)}
@media (max-width:700px){ .footnav{justify-content:flex-start;gap:14px;order:3;width:100%} }


/* ═══════════════════════ the key light ═══════════════════════
   A light theme lifts the interface. It does not light your face, because in a call
   most of the screen is other people's video and that is whatever colour their room
   is. What lights your face is a large, bright, close surface — which a laptop panel
   is, if you let it be one.

   So: a frame of warm white around the meeting, brightness on a slider, sitting above
   the video and below every control. It is yours alone. Nobody else sees it, nothing
   is sent, and it does not appear in a recording — it is a lamp, not a filter. */
#keylight{
  position:fixed; inset:0; z-index:8; pointer-events:none;
  opacity:0; transition:opacity .25s ease;
  --kl:0;                       /* 0…1, the slider */
  --klw:clamp(46px, 7vw, 120px);
}
#keylight.on{ opacity:1 }
#keylight::before{
  content:''; position:absolute; inset:0;
  /* brightest at the top, because that is the angle that flatters a face and the
     part of the panel closest to the camera */
  background:
    linear-gradient(180deg, rgba(255,247,236,calc(.96 * var(--kl))) 0%,
                            rgba(255,247,236,calc(.55 * var(--kl))) 42%,
                            rgba(255,247,236,0) 100%) top / 100% var(--klw) no-repeat,
    linear-gradient(0deg,   rgba(255,247,236,calc(.42 * var(--kl))) 0%,
                            rgba(255,247,236,0) 100%) bottom / 100% calc(var(--klw) * .7) no-repeat,
    linear-gradient(90deg,  rgba(255,247,236,calc(.62 * var(--kl))) 0%,
                            rgba(255,247,236,0) 100%) left / calc(var(--klw) * .8) 100% no-repeat,
    linear-gradient(270deg, rgba(255,247,236,calc(.62 * var(--kl))) 0%,
                            rgba(255,247,236,0) 100%) right / calc(var(--klw) * .8) 100% no-repeat;
}
#keylight.cool::before{ filter:hue-rotate(-14deg) saturate(.35) }

.klrow{display:flex;align-items:center;gap:12px;margin:10px 0 0}
.klrow input[type=range]{flex:1;accent-color:var(--acc)}
.klrow .klval{font-family:var(--mono);font-size:11.5px;color:var(--dim);
  min-width:3.2em;text-align:right;font-variant-numeric:tabular-nums}
.kltemp{display:flex;gap:8px;margin:10px 0 0}
.kltemp button{flex:1;font-size:12px;padding:7px 10px;border-radius:9px;
  border:1px solid var(--line);background:var(--panel2);color:var(--mid);cursor:pointer}
.kltemp button.on{border-color:var(--acc);color:var(--ink);background:var(--onbg)}

/* Theme picker — three states, because "match my system" is the honest default. */
.themepick{display:flex;gap:8px;margin:8px 0 0}
.themepick button{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;
  padding:11px 8px;border-radius:11px;border:1px solid var(--line);
  background:var(--panel2);color:var(--mid);cursor:pointer;font-size:12px}
.themepick button:hover{border-color:var(--edge2);color:var(--ink)}
.themepick button.on{border-color:var(--acc);color:var(--ink);background:var(--onbg)}
.themepick .sw{width:100%;height:26px;border-radius:7px;border:1px solid var(--line)}
.themepick .sw-dark{background:linear-gradient(160deg,#0A0E24 55%,#1C2350)}
.themepick .sw-light{background:linear-gradient(160deg,#FFFFFF 55%,#DCE2F2)}
.themepick .sw-auto{background:linear-gradient(105deg,#0A0E24 0 50%,#FFFFFF 50% 100%)}


/* The transcript is the thing people come back for, and today it is thrown away a few
   hours after the meeting. That is worth saying where they are reading it. */
.txkeep{margin:0;padding:14px 15px;border-top:1px solid var(--line);background:var(--panel2);
  display:flex;flex-direction:column;gap:7px}
.txkeep[hidden]{display:none}
.txkeep b{font-size:13px;color:var(--ink);font-weight:600;line-height:1.35}
.txkeep span{font-size:12.5px;color:var(--mid);line-height:1.5}
.txkeep .btn.go{align-self:flex-start;background:var(--grad);color:#fff;border:0;
  padding:8px 14px;border-radius:9px;font-size:12.5px;font-weight:600;cursor:pointer}


/* ───────── anything painted over video ─────────
   These keep their own contrast in both themes. A person's camera shows a bright
   window or a dark wall and neither follows your theme setting, so a name chip that
   flipped to dark-on-white would be unreadable half the time. */
.tile .chip, .tile .chip .nm{ color:#F4F7FF }
.tile .chip .role{ color:#AEB9D6 }
.tile .avatar{ background:rgba(10,14,36,.82); color:#C7D0E8 }
.tileact{ background:rgba(5,8,23,.78); border-color:rgba(255,255,255,.16); color:#F4F7FF }
.tileact:hover{ border-color:var(--acc); background:rgba(5,8,23,.92) }
/* Everything drawn over the video keeps light text in both themes. */
#grid, #grid *, .spotbar, .spotbar *, .recdots, .recdots *, .captions, .captions *,
#viewBtn, .float, .hand{ color:#F4F7FF }
#grid .chip .role{ color:#AEB9D6 }
#viewBtn{ background:rgba(8,11,16,.75); border-color:rgba(255,255,255,.16) }


/* ───────── one container, no tile ever moves ─────────
   The stage and the filmstrip are grid AREAS now, not separate parents. A tile keeps
   its place in the DOM for the whole call and only its grid-area changes, so nothing
   restarts when the speaker does: not the video, not the screen share, not the audio.
   Everything below is placement and size — the tile itself is untouched. */
#stagearea.focused{
  align-content:stretch;
  justify-content:stretch;
  grid-auto-flow:row;
}
#stagearea.focused .tile{ width:auto; height:auto; min-width:0; min-height:0 }
#stagearea .tile{
  transition:opacity .18s ease;
  contain:layout paint;          /* a tile resizing cannot reflow its neighbours */
}
/* the filmstrip row: a consistent shape, never a stretched rectangle */
#stagearea.focused .tile.instrip{
  aspect-ratio:16/9;
  height:100%;
  width:auto;
  justify-self:center;
  align-self:end;
}
#stagearea.focused .tile.stage{ aspect-ratio:auto }

/* Presenting: the spotlight owns the frame and every face sits in the strip. */
#app.presenting #stagearea{ grid-template-rows:var(--striph)!important; align-content:end }
#app.presenting #stagearea .tile{ grid-area:auto!important; aspect-ratio:16/9; height:100%;
  width:auto; justify-self:center; align-self:center; max-width:100% }
/* The presenter is not in the strip at all — their face is the circle over their own
   screen. See renderGrid. */

/* The old absolute-positioned phone strip fought the grid; the grid handles it. */
@media (max-width:900px){
  #grid{--striph:96px}
  #app.focusmode #strip{display:none}
}

/* ───────── the room shows everyone, and you are in the corner ─────────
   At every width. No stage that swaps, nothing that changes position when somebody
   starts talking, and your own tile out of the layout entirely so it cannot be moved
   by your own voice. The person speaking gets a ring. See renderGrid for why. */
#stagearea.roomgrid{
  align-content:stretch; justify-content:stretch;
  grid-auto-flow:row; gap:10px;
}
/* A tile is a 16:9 box, always, and the video fills it. It is NOT stretched to whatever
   shape the grid cell happens to be.
   [stated] "the cropping to these weird rectangle sizes should never be done, need to
   respect the size of video for people." Two people across a wide strip gave each of
   them a 750×155 slot, which crops a face to a letterbox band. Fixing the shape and
   centring what is left is what every conferencing product does, and it is why their
   grids look calm while a stretched one looks broken. */
#stagearea.roomgrid .tile{
  width:auto; height:auto; min-width:0; min-height:0;
  aspect-ratio:16/9;
  justify-self:center; align-self:center;
  max-width:100%; max-height:100%;
}
/* A tile with `width:auto;height:auto` and a fixed aspect-ratio has no size of its own
   to grow from — in a big empty cell it collapses to a stamp in the middle, which is
   what Jack saw sitting alone in a room. Filling the cell and letting aspect-ratio cap
   it is the way round that actually grows. */
#stagearea.roomgrid .tile{width:100%;height:100%}
#app.roomgrid #stagearea{align-items:center;justify-items:center}
/* One person in the room — yours takes the whole frame rather than a 16:9 letterbox
   inside it, because there is nothing to line it up against. */
#app.roomgrid.alone #stagearea .tile{aspect-ratio:auto}
/* The picture-in-picture is positioned against #grid, which is deliberately NOT a
   scroll container: an absolutely positioned child of a scroller is clipped by it and
   scrolls away with the content. */
#app.haspip #grid{position:relative}
#stagearea .tile.selfpip{
  position:absolute; right:14px; bottom:14px; z-index:6;
  width:clamp(112px, 15vw, 210px);
  height:auto; aspect-ratio:16/9;
  border-radius:12px; overflow:hidden;
  border:1px solid rgba(255,255,255,.16);
  box-shadow:0 12px 34px rgba(0,0,0,.6);
  transition:none;                     /* it never moves, so it never animates */
}
@media (max-width:900px){
  #stagearea.roomgrid{gap:6px}
  /* Portrait, because a phone camera is, and 16:9 crops your own chin off. */
  #stagearea .tile.selfpip{right:10px;bottom:10px;width:26vw;max-width:124px;
    min-width:84px;aspect-ratio:3/4}
}
#stagearea .tile.selfpip .chip{font-size:10px;padding:2px 6px;left:5px;bottom:5px}
#stagearea .tile.selfpip .chip .role,
#stagearea .tile.selfpip .tileacts{display:none}
#stagearea .tile.selfpip .avatar{font-size:20px}
/* Captions clear the picture-in-picture rather than sitting under it. */
#app.haspip .captions{bottom:calc(14px + clamp(112px,15vw,210px) * 9 / 16 + 10px)}
@media (max-width:900px){
  #app.haspip .captions{bottom:calc(10px + 26vw * 4 / 3 + 8px)}
}


/* ───────── goals ─────────
   A checklist the meeting fills in. The quote under a ticked goal is not decoration:
   it is the difference between a checklist you trust and one you have to re-check. */
#goalsPane{flex:1;min-height:0;overflow:auto;padding:14px}
.gempty h3{font-family:var(--display);font-size:16px;font-weight:600;margin:2px 0 8px;color:var(--ink)}
.gempty p{color:var(--mid);font-size:13px;line-height:1.6;margin:0 0 12px}
#goalsIn{width:100%;box-sizing:border-box;background:var(--sunk,rgba(255,255,255,.04));
  border:1px solid var(--line);border-radius:10px;color:var(--ink);padding:10px 12px;
  font:inherit;font-size:13px;line-height:1.55;resize:vertical;outline:none}
#goalsIn:focus{border-color:var(--blue)}
.growrow{display:flex;align-items:center;gap:12px;margin-top:10px;flex-wrap:wrap}
.growrow .primary{background:var(--grad);color:#fff;border:0;border-radius:9px;
  padding:9px 16px;font-size:13px;font-weight:600;cursor:pointer}
.growrow .primary:disabled{opacity:.55;cursor:default}
.gfile span{color:var(--dim);font-size:12.5px;cursor:pointer;text-decoration:underline;
  text-underline-offset:3px}
.gfile span:hover{color:var(--ink)}
.gnote{color:var(--dim);font-size:12px;margin:9px 0 0;min-height:1em}

.ghead{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.gbar{flex:1;height:5px;border-radius:3px;background:var(--sunk,rgba(255,255,255,.07));overflow:hidden}
.gbar i{display:block;height:100%;width:0;background:var(--grad);transition:width .5s cubic-bezier(.2,.8,.2,1)}
.gmeta{display:flex;flex-direction:column;line-height:1.25}
.gmeta b{font-size:12px;color:var(--ink);white-space:nowrap}
.gmeta span{font-family:var(--mono);font-size:10px;color:var(--dim)}

.goal{display:flex;gap:10px;padding:11px 0;border-bottom:1px solid var(--line)}
.goal:last-child{border-bottom:0}
.gtick{flex:none;width:20px;height:20px;margin-top:1px;border-radius:6px;cursor:pointer;
  border:1.5px solid var(--line);background:transparent;color:#fff;font-size:12px;
  line-height:1;display:grid;place-items:center;transition:.16s}
.gtick:hover{border-color:var(--blue)}
.goal.done .gtick{background:var(--good);border-color:var(--good)}
.goal.touched .gtick{border-color:var(--warn)}
.gmain{min-width:0;flex:1}
.gtitle{font-size:13.5px;line-height:1.45;color:var(--ink);font-weight:500}
.goal.done .gtitle{color:var(--mid)}
.gsub{display:flex;align-items:baseline;gap:8px;margin-top:3px;flex-wrap:wrap}
.gkind{font-family:var(--mono);font-size:9.5px;letter-spacing:.05em;text-transform:uppercase;
  padding:2px 6px;border-radius:5px;background:var(--sunk,rgba(255,255,255,.06));color:var(--dim)}
.gkind.decision{color:#8FB6FF}.gkind.action{color:#F0B37E}
.gkind.alignment{color:#C4A2FF}.gkind.information{color:#84CFC0}
.gwhen2{font-size:11.5px;color:var(--dim);line-height:1.5}
.gout{font-size:12.5px;color:var(--good);margin-top:5px;line-height:1.5}
.gowner{font-size:11.5px;color:var(--mid);margin-top:3px}
.gquote{margin-top:5px;font-size:11.5px;line-height:1.55;color:var(--dim);
  border-left:2px solid var(--line);padding-left:9px}
.gsaid{display:block;font-family:var(--mono);font-size:10px;margin-top:2px}
.ghand{font-family:var(--mono);font-size:9.5px;color:var(--warn);margin-top:5px;letter-spacing:.02em}
#goalsNew{margin-top:16px;padding-top:12px;border-top:1px dashed var(--line)}
#goalsNew h4{font-size:11px;font-family:var(--mono);letter-spacing:.05em;text-transform:uppercase;
  color:var(--dim);margin:0 0 8px;font-weight:500}
.gnew{padding:7px 0}
.gcount{position:absolute;top:3px;right:2px;padding:0 5px;height:15px;border-radius:8px;
  background:var(--sunk,rgba(255,255,255,.1));color:var(--mid);font-family:var(--mono);
  font-size:9.5px;line-height:15px;font-style:normal;box-shadow:0 0 0 2px var(--panel)}
.gcount.all{background:var(--good);color:#fff}


/* ───────── Clip past ─────────
   Two handles over a track that stands for the minutes held in this tab. Deliberately
   not a scrubbing preview: picking a moment you remember from thirty seconds ago does
   not need a frame-accurate viewer, it needs a drag that takes two seconds. */
.past-card{max-width:520px}
.past-sub{color:var(--mid);font-size:13.5px;line-height:1.6;margin:0 0 18px}
.ptrack{position:relative;height:54px;border-radius:12px;cursor:pointer;
  background:var(--sunk,rgba(255,255,255,.05));border:1px solid var(--line);
  background-image:repeating-linear-gradient(90deg,transparent 0 23px,var(--line) 23px 24px)}
.psel{position:absolute;top:-1px;bottom:-1px;border-radius:10px;
  background:linear-gradient(180deg,rgba(7,148,252,.30),rgba(81,36,216,.30));
  border:1.5px solid var(--blue);box-shadow:0 0 24px rgba(7,148,252,.28)}
.phandle{position:absolute;top:50%;transform:translateY(-50%);width:14px;height:34px;
  border-radius:7px;background:var(--blue);cursor:ew-resize;
  box-shadow:0 2px 10px rgba(0,0,0,.5)}
.phandle::after{content:'';position:absolute;inset:12px 5px;border-left:1px solid rgba(255,255,255,.65);
  border-right:1px solid rgba(255,255,255,.65)}
.phandle.l{left:-8px}
.phandle.r{right:-8px}
/* A 14px grip is a hard target on a phone, so the touch area is bigger than the paint. */
.phandle::before{content:'';position:absolute;inset:-12px -14px}
.prow{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:14px;flex-wrap:wrap}
.prow.end{justify-content:flex-end;margin-top:20px}
.plen{font-family:var(--mono);font-size:13px;color:var(--ink)}
.pquick{display:flex;gap:6px}
.pquick button{all:unset;cursor:pointer;font-size:11.5px;color:var(--dim);padding:5px 10px;
  border:1px solid var(--line);border-radius:999px}
.pquick button:hover{color:var(--ink);border-color:var(--blue)}
.past-card .primary{background:var(--grad);color:#fff;border:0;border-radius:10px;
  padding:10px 18px;font-size:13.5px;font-weight:600;cursor:pointer}
.past-card .primary:disabled{opacity:.55;cursor:default}
.past-card .ghost{background:none;border:1px solid var(--line);color:var(--mid);
  border-radius:10px;padding:10px 16px;font-size:13.5px;cursor:pointer}
.past-note{color:var(--dim);font-size:12px;margin:12px 0 0;min-height:1em}

/* ───────── the ask row's megaphone, and the image spotlight ───────── */
.rail-ask .loud{flex:none;padding:0 12px;font-size:15px}
.shot{position:relative}
.shot .showall{position:absolute;top:6px;right:6px;font-size:11px;padding:4px 9px;opacity:0;
  transition:opacity .14s;background:rgba(5,8,23,.82);backdrop-filter:blur(6px)}
.shot:hover .showall{opacity:1}
@media (hover:none){ .shot .showall{opacity:.9} }
/* [stated] "they should be pinnable to the video (but act like a video slot rather
   than remove all view)". This was inset:0 over a blurred backdrop, so putting a
   picture in front of people made everybody disappear — the opposite of what showing
   somebody something is for. It takes the stage the way a shared screen does: faces
   drop to the strip along the bottom and stay there the whole time. */
.imgspot{position:absolute;left:0;right:0;top:0;bottom:calc(var(--striph,118px) + 12px);
  z-index:24;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:10px;padding:12px}
.imgspot[hidden]{display:none}
#app.imgstage #stagearea{grid-template-rows:var(--striph)!important;align-content:end}
#app.imgstage #stagearea .tile{aspect-ratio:16/9;height:100%;width:auto;justify-self:center}
.imgspot .isp-frame{position:relative;display:inline-block;max-width:100%;max-height:100%;
  touch-action:none;cursor:crosshair}
.imgspot img{display:block;max-width:min(100%,1200px);max-height:100%;border-radius:14px;
  border:1px solid rgba(255,255,255,.14);box-shadow:0 24px 80px rgba(0,0,0,.6)}
/* The highlighter's surface, exactly over the picture. */
.imgspot .isp-ink{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;
  border-radius:14px}
.imgspot .isp-bar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;justify-content:center}
.imgspot .isp-cap{color:#EAF0FF;font-size:13.5px;max-width:60ch;text-align:center}
/* Was a hardcoded light hex, which is right on the dark overlay and invisible on the
   light one. Tokens follow the theme. */
.imgspot .ghosty{background:none;border:1px solid var(--line);color:var(--ink)}

/* the name chip carries the person's colour — a light border, as asked */
.tile .chip{border:1px solid var(--who, var(--line))}

/* who is looking at which pane: a dot per person on the tab itself */
.tabs-top button{position:relative}
.whodots{display:inline-flex;gap:3px;margin-left:6px;vertical-align:middle}
.whodots i{width:6px;height:6px;border-radius:50%;background:var(--d,#8aa);
  box-shadow:0 0 5px var(--d,#8aa)}

/* On a phone the view toggle floats over the video like the self-view does, instead of
   sitting in the bar — one more control in that row is what wraps it to a second line
   and knocks the whole menu down. */
@media (max-width:900px){
  #viewBtn{position:fixed;right:58px;top:calc(var(--vtop,0px) + 10px);z-index:15;
    width:38px;height:38px;border-radius:11px;background:rgba(5,8,23,.72);
    backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,.14)}
}


/* ───────── overlays on the video: theme, and the view toggle ─────────
   Controls that change how YOU see the room sit over the room, top-right — they never
   resize the video and never widen the control bar into wrapping. */
.stagebtn{position:absolute;right:12px;top:12px;z-index:9;width:36px;height:36px;
  border-radius:11px;background:rgba(8,11,16,.62);backdrop-filter:blur(8px);
  border:1px solid rgba(255,255,255,.16);color:#EAF0FF;font-size:15px;cursor:pointer}
.stagebtn:hover{border-color:rgba(255,255,255,.4)}
#app.presenting .stagebtn{top:12px}
@media (max-width:900px){
  /* The theme button takes the corner and the view toggle sits inboard of it. It used
     to be the other way round, which left the sun floating 60px short of the corner
     whenever the view toggle was not rendered — and in a two-person call it usually
     is not, so the gap read as the icon being misplaced. The corner belongs to the
     control that is always there. */
  .stagebtn{top:calc(var(--vtop,0px) + 10px);right:12px;position:fixed}
}

/* The Clip past icon draws with strokes like every other bar icon — an unstyled SVG
   path fills itself solid black, which is the dark blob Jack saw in light mode. */
#pastBtn svg{fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
#viewBtn svg{fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}

/* The record choice: one small popover, two honest options. */
.recpop{position:absolute;bottom:calc(100% + 10px);left:50%;transform:translateX(-50%);
  z-index:30;display:flex;flex-direction:column;gap:6px;padding:8px;border-radius:12px;
  background:var(--panel);border:1px solid var(--line);box-shadow:0 16px 44px rgba(0,0,0,.45);
  white-space:nowrap}
.recpop[hidden]{display:none}
.recpop button{all:unset;cursor:pointer;font-size:13px;color:var(--ink);padding:8px 12px;
  border-radius:8px;display:flex;align-items:center;gap:8px}
.recpop button:hover{background:var(--field)}
.recpop button i{font-style:normal;color:var(--dim);font-size:11.5px}


/* ───────── the account widget, styled EVERYWHERE ─────────
   This lived in docs.css, which the homepage does not load — so the signed-in menu
   rendered there as an unstyled white box with the landing page's pale link colours
   bleeding in: white on white. The rule this earns: a component that can appear on
   any page gets its styles in the stylesheet every page loads, in theme tokens, once.
   The copy in docs.css stays and is identical, so doc pages are unchanged. */
.acct-btn{all:unset;cursor:pointer;display:flex;align-items:center;gap:8px;padding:5px 11px 5px 5px;
  border-radius:999px;border:1px solid var(--line);transition:.15s}
.acct-btn:hover{border-color:var(--blue)}
.acct-btn img{width:26px;height:26px;border-radius:50%;display:block}
.acct-btn .initial{width:26px;height:26px;border-radius:50%;background:var(--grad);color:#fff;
  display:grid;place-items:center;font-size:12.5px;font-weight:600}
.acct-btn span:last-child{font-size:13.5px;color:var(--ink);max-width:12ch;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.acct-menu{position:absolute;right:0;top:calc(100% + 8px);min-width:224px;z-index:60;
  background:var(--panel);border:1px solid var(--line);border-radius:14px;overflow:hidden;
  box-shadow:0 18px 44px -18px rgba(0,0,0,.8)}
.acct-menu[hidden]{display:none}
.acct-who{padding:13px 15px;border-bottom:1px solid var(--line);display:flex;flex-direction:column;gap:2px}
.acct-who b{font-size:13.5px;color:var(--ink)}
.acct-who span{font-size:12px;color:var(--dim);overflow:hidden;text-overflow:ellipsis}
.acct-menu a,.acct-menu button{display:block;width:100%;text-align:left;padding:11px 15px;
  font-size:13.5px;color:var(--mid);text-decoration:none;background:none;border:0;cursor:pointer;
  font:inherit;font-size:13.5px}
.acct-menu a:hover,.acct-menu button:hover{background:var(--panel2);color:var(--ink);text-decoration:none}
.acct-menu button{border-top:1px solid var(--line);color:var(--dim)}

/* the panel */
.tm-modal{position:fixed;inset:0;z-index:200;display:grid;place-items:center;padding:20px;
  background:rgba(3,5,16,.76);backdrop-filter:blur(8px);overflow-y:auto}
.tm-modal[hidden]{display:none}
body.tm-modal-open{overflow:hidden}
.tm-card{position:relative;width:min(440px,100%);background:var(--panel);border:1px solid var(--line);
  border-radius:20px;padding:30px 28px 24px;box-shadow:0 30px 80px -30px rgba(0,0,0,.9)}
.tm-x{position:absolute;right:14px;top:12px;all:unset;cursor:pointer;font-size:22px;line-height:1;
  color:var(--dim);padding:6px 10px;border-radius:9px}
.tm-x:hover{color:var(--ink)}
.tm-card h2{font-family:var(--display);font-size:22px;font-weight:600;letter-spacing:-.015em;
  margin:0 0 18px;color:var(--ink);max-width:16ch;line-height:1.25}
ul.tm-reasons{list-style:none;padding:0;margin:0 0 22px;display:flex;flex-direction:column;gap:13px}
ul.tm-reasons li{display:flex;flex-direction:column;gap:2px;padding-left:20px;position:relative}
ul.tm-reasons li::before{content:'';position:absolute;left:0;top:7px;width:8px;height:8px;
  border-radius:2px;background:var(--grad)}
ul.tm-reasons b{font-size:14px;color:var(--ink);font-weight:600;line-height:1.35}
ul.tm-reasons span{font-size:13px;color:var(--mid);line-height:1.5}
.tm-gbtn{display:flex;justify-content:center;min-height:44px}
.tm-fine{font-size:12.5px;color:var(--dim);margin:14px 0 0;text-align:center;line-height:1.5}
.tm-note{font-size:11.5px;color:var(--dim);margin:16px 0 0;text-align:center;line-height:1.55;
  padding-top:15px;border-top:1px solid var(--line)}
.tm-note a{color:var(--dim);text-decoration:underline}
.tm-note a:hover{color:var(--blue)}
@media (max-width:520px){ .tm-card{padding:26px 20px 20px} .tm-card h2{font-size:20px} }

/* And nothing from the landing page's own link styling may reach inside the menu —
   #gate a is pale-on-dark, which is exactly wrong on the menu's panel. */
#gate .acct-menu a{color:var(--mid)}
#gate .acct-menu a:hover{color:var(--ink)}
.signin-btn{cursor:pointer;font:inherit;font-size:13.5px;color:var(--mid);background:none;
  border:1px solid var(--line);border-radius:999px;padding:7px 15px}
.signin-btn:hover{color:var(--ink);border-color:var(--blue)}


/* the code pill's own copied state — green, then back */
#roomLabel.copied{border-color:var(--good);color:var(--good);background:rgba(14,124,87,.12);
  transition:background .15s,border-color .15s}
#roomLabel.copied .shic{color:var(--good)}


/* ───────── buttons are SOLID ─────────
   The gradient fill retires from every control — [stated] twice, "a well known vibe
   code signal". One confident blue, a readable label, a subtle lift on hover. The
   gradient survives only as ART: the brand bar, thumbnails, the progress fill —
   surfaces, never buttons. Appended last on purpose: equal specificity, later wins. */
.btn.go, .btn.go.wide, .primary, .growrow .primary, .past-card .primary,
.bk-go, .bk-join, .empty .btn.go, .docnav-right .btn.go, .btn.go.big{
  background:#1256E8; border:1px solid rgba(255,255,255,.22);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22);
}
.btn.go:hover, .primary:hover, .bk-go:hover, .bk-join:hover, .btn.go.big:hover{
  background:#1B61F5;
}
:root[data-theme="light"] .btn.go, :root[data-theme="light"] .primary,
:root[data-theme="light"] .bk-go, :root[data-theme="light"] .bk-join{
  border-color:rgba(0,0,0,.08);
}


/* ───────── the phone's top edge, one visual system ─────────
   Two floating buttons with different shells plus two chips stacking on the same
   corner read as "boxes behind boxes". One shell for both buttons, one row; and when
   presenting, the REC chip steps below the presenter bar instead of on top of it. */
@media (max-width:900px){
  /* [stated] "top right see that this icon isnt fitting to the top right corner well".
     Two things were wrong. The backdrop was 62% opaque, so a bright camera showed
     through it and the button had no edge of its own — and it sat at the same 10px the
     tile does, which put it exactly on the tile's rounded corner where two borders ran
     through each other. Pushed in past the radius, and opaque enough to be a button
     sitting ON the video rather than a tint over it. */
  /* #themeBtn is named by id here because an earlier rule styles it by id, and an id
     beats a class no matter which sheet or which order — the same trap that put the
     dashboard rail 632px off-screen. Without it the theme button keeps a 34px width and
     a 9px radius while everything beside it is 36px and 11px. */
  #viewBtn, .stagebtn, #themeBtn.stagebtn, #railOpen{
    position:fixed;top:calc(var(--vtop,0px) + 16px);height:36px;
    border-radius:11px;background:rgba(8,11,16,.86);backdrop-filter:blur(10px);
    border:1px solid rgba(255,255,255,.18);color:#EAF0FF;z-index:15;
    display:grid;place-items:center;padding:0}
  #viewBtn, .stagebtn, #themeBtn.stagebtn{width:36px}
  .stagebtn, #themeBtn.stagebtn{right:16px} /* the corner — always present */
  #viewBtn{right:60px}                      /* inboard of it, when the room has one */
  /* Opposite them, on the same line and the same distance in, so the top of the screen
     reads as one row of controls rather than three things near the top. */
  #railOpen{left:16px;width:auto;padding:0 13px;font-size:12.5px}
  #app.presenting .recdots{top:52px}
  #app.presenting .spotbar{max-width:calc(100vw - 110px);overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap}
}


/* The theme button floats over the stage's top-right; a tile's own pin/mute live in
   the tile's top-right. When one tile fills the stage those are the same pixels, and
   the overlay won silently — the buttons were visible and unclickable.

   That was first fixed by raising the whole TILE on hover, which traded one bug for a
   worse one: [stated] "hovering over my own video for some reason still causes the
   overlay transcription and light mode dark mode to disappear" — reported three times.
   A tile at z-index 20 is above the captions and the theme button, so pointing at your
   own face blanked the subtitles everyone was reading.

   The collision was already solved by the line below, which moves the tile's actions
   clear of the corner the overlay occupies. Position was enough; the stacking bump was
   redundant and destructive. Only the ACTIONS rise now, and only far enough to be
   clickable — the tile itself stays under the things drawn on top of it, which is what
   "drawn on top of it" is supposed to mean. */
#app.roomgrid #stagearea .tile:not(.selfpip) .tileacts{right:52px}
#stagearea .tile:hover .tileacts, #stagearea .tile:focus-within .tileacts{z-index:12}

/* ── an agenda arriving as a file ──────────────────────────────────────────
   Tokens, not hexes: this sits in the call's rail, which is dark, but the same
   stylesheet dresses the light pages and a literal colour here would be the same
   bug that put three black slabs on the recording page. */
.agdrop{margin-top:8px;padding:11px 13px;border:1px dashed var(--edge);border-radius:10px;
  background:var(--field);color:var(--dim);font-size:12px;line-height:1.5;cursor:pointer;
  display:flex;align-items:center;gap:8px;transition:border-color .12s,background .12s,color .12s}
.agdrop:hover{border-color:var(--blue);color:var(--mid)}
.agdrop.over{border-color:var(--blue);border-style:solid;background:rgba(7,148,252,.10);color:var(--ink)}
.agdrop.busy{border-style:solid;border-color:var(--blue);color:var(--mid)}
.agdrop.bad{border-color:var(--bad);color:var(--bad);border-style:solid}
.agdropnote{min-width:0;flex:1}

/* The word riding over the record dot, so the button says CLIP rather than leaving
   the universal record glyph to imply the whole meeting starts here — it does not,
   the meeting is already being kept. Stroke is cleared because .recdotic's dot is a
   filled circle and a stroked letterform at 5px is mud. */
.recdotic .clipword{fill:currentColor;stroke:none;font-family:var(--mono),ui-monospace,monospace;
  font-size:6.6px;font-weight:700;letter-spacing:.06em}

/* The slider that reaches backwards from a finished clip. Its range is set from what
   was actually banked, so it never offers seconds that do not exist.
   clipgrow, not growrow: .growrow is already the goals row further up this file, and
   a class reused across two features is how #rail and #0d1218 both went wrong today. */
.clipgrow{display:flex;align-items:center;gap:12px;margin-top:8px;flex-wrap:wrap}
.clipgrow input[type=range]{flex:1;min-width:140px;accent-color:var(--blue)}
.clipgrowamt{font-family:var(--mono);font-size:13px;color:var(--ink);min-width:46px;text-align:right}

/* ── off means off ─────────────────────────────────────────────────────────
   [stated] "use an X through the camera and the video when its turned off not just
   red". Red on its own is genuinely ambiguous — on half the products in the world a
   red microphone means it is live. The slash is the convention nobody misreads, so
   both icons carry one that is simply invisible until the thing is switched off. */
.ctlic{width:21px;height:21px;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.ctlic .slash{opacity:0;stroke-width:2.1;transition:opacity .12s}
.ctl.off .ctlic .slash{opacity:1}
/* A slash reads as a slash only if it is not lost in the glyph it crosses, so the
   glyph steps back while it is there. */
.ctl.off .ctlic > use{opacity:.65}

/* The CC glyph: a caption bubble with lines in it, which reads as captions to somebody
   who has never been taught that "CC" means anything. */
.ccic{width:21px;height:21px;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.ccic .tail{stroke-linejoin:round}
.ctl.on .ccic{stroke:var(--blue)}

/* [stated] "allow making an account from homepage like a signup button". Sign in is
   the quiet one; sign up is the one a first-time visitor is looking for, so it carries
   the weight. Both open the same panel — Google creates the account or returns you to
   it — so what differs is what a stranger believes about whether they are welcome. */
.signup-btn{cursor:pointer;font:inherit;font-size:13.5px;font-weight:600;color:#fff;
  background:var(--acc);border:1px solid var(--acc);border-radius:9px;padding:7px 13px;
  margin-left:8px;white-space:nowrap}
.signup-btn:hover{background:var(--blue);border-color:var(--blue)}
@media (max-width:560px){ .signin-btn{display:none} }

/* A tabletop line has no known speaker. The label is set in italic so a reader can see
   at a glance that "Speaker 2" is a channel and "Sydney" is a person, without having
   to know what either means. */
.txline > b.sp-tabletop { font-style: italic; opacity: .82; }

/* Held while an invite link is being checked — see gbusy() in app.js. Not `disabled`,
   which on iOS greys the label to near-invisible; the point is that the button is
   still legible while it is not yet safe to press. */
.busy{pointer-events:none;opacity:.72;cursor:progress}

/* ───────── the clip trim bar ─────────
   Two handles over the clip already loaded in the sheet. Dragging seeks that video, so
   the frame on screen is the frame the cut lands on and nothing is fetched until Save.
   The handles are 26px wide over a 6px track: the track is what you read, the handle is
   what a thumb has to hit, and those are not the same size. */
.trimbar{position:relative;height:34px;margin:12px 0 8px;border-radius:8px;
  background:var(--sunk);border:1px solid var(--line);touch-action:none}
.trimheld{position:absolute;left:0;top:0;bottom:0;border-radius:7px 0 0 7px;
  background:repeating-linear-gradient(135deg,rgba(122,162,255,.16) 0 6px,transparent 6px 12px)}
.trimsel{position:absolute;top:0;bottom:0;background:rgba(58,124,255,.24);
  border-left:2px solid var(--blue);border-right:2px solid var(--blue)}
.trimh{position:absolute;top:50%;width:26px;height:38px;margin:-19px 0 0 -13px;padding:0;
  border:1px solid var(--line-bright,rgba(255,255,255,.34));border-radius:8px;
  background:var(--panel2);cursor:ew-resize;touch-action:none;z-index:2}
.trimh::after{content:'';position:absolute;left:50%;top:50%;width:2px;height:14px;
  margin:-7px 0 0 -1px;border-radius:1px;background:var(--mid)}
.trimh:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
.trimh:active{background:var(--blue);border-color:var(--blue)}
.trimrow{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.trimread{font-family:var(--mono);font-size:12.5px;color:var(--mid);flex:1 1 100%}
.trimrow .btn{flex:1 1 0;min-width:120px}
@media (max-width:700px){
  /* Bigger targets and a taller track: this is a drag done with a thumb on a moving
     train, not a mouse. */
  .trimbar{height:40px}
  .trimh{width:32px;height:46px;margin:-23px 0 0 -16px}
  .trimrow{justify-content:space-between}
  .trimrow .btn{flex:1;min-width:130px}
}

/* ───────── the rail header on a phone ─────────
   Five tabs plus Close plus ? do not fit on one 390px row at any size a person can
   read, so the row scrolled — and a tab you have to discover by swiping is a tab most
   people never find. "Chat" was sitting underneath the ? button.

   Two rows instead: the controls on top, the tabs beneath them with the full width to
   themselves. Nothing scrolls, and every tab is visible without being told to look. */
@media (max-width:700px){
  .rail-top{
    display:grid;
    grid-template-columns:auto 1fr auto;
    grid-template-areas:'close . help' 'tabs tabs tabs';
    row-gap:8px;
    align-items:center;
    padding:10px 12px 8px;
  }
  /* display:contents put these three in one row; with an explicit grid they are placed
     by name instead, so Close still leads and ? still ends — the arrangement Jack asked
     for — without them competing for one line. */
  #railClose{grid-area:close;order:0;margin-right:0}
  #guideBtn{grid-area:help;order:0}
  .tabs-top{
    grid-area:tabs;
    overflow-x:visible;          /* nothing left to scroll */
    gap:4px;
    justify-content:space-between;
  }
  .tabs-top button{
    flex:1 1 0;                  /* share the row evenly rather than each demanding its width */
    min-width:0;
    padding:8px 4px;
    font-size:12px;
    text-align:center;
    overflow:hidden;
    text-overflow:ellipsis;
  }
}


/* ───────── Notes & chat, as a control rather than a floating label ─────────
   [stated] "lets give notes and chat its own icon there" — there being the row of
   controls at the bottom. It used to be a text pill floating over the top-left of the
   video, which is a different kind of object from everything else that opens a panel,
   and it sat on top of whoever was in the top-left of the frame.

   It stays a labelled button on a wide screen, where there is room for the words. */
.notesic{display:none;width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
@media (max-width:900px){
  /* Into the bar, at the end of the control row, beside the other things that open
     something. display:contents on the wrapper so it can take its own place in the bar
     rather than travelling with the people-count it shares a div with. */
  .bar-right{display:contents}
  #railOpen{
    position:static !important; left:auto; top:auto; order:2; margin-left:2px;
    width:38px; height:38px; padding:0; display:grid; place-items:center;
    border-radius:11px; background:var(--panel2); border:1px solid var(--line);
    color:var(--ink); font-size:0; backdrop-filter:none;
  }
  #app.norail #railOpen{display:grid}
  #railOpen > span{display:none}
  #railOpen .notesic{display:block}
  /* The unread mark has to survive losing the text it used to sit beside. */
  #railOpen.hasunread::after{content:'';position:absolute;top:6px;right:6px;width:7px;
    height:7px;border-radius:50%;background:var(--blue);box-shadow:0 0 0 2px var(--panel)}
  #railOpen{position:relative !important}
}

/* ───────── the listening dot's glow was being clipped ─────────
   A box-shadow drawn OUTSIDE an element is cut off by any ancestor that clips, and
   .bar-left is overflow:hidden on a phone so the code label could ellipsis. The glow
   is what says transcription is live, and half a glow reads as a rendering fault.
   The label no longer needs clipping — it was removed from the bar — so the clip goes
   and the dot keeps its full halo. */
@media (max-width:900px){
  .bar-left{overflow:visible}
  #bar{overflow:visible}
}

/* ───────── iOS zooms into any input under 16px ─────────
   Not a preference — Safari on iPhone magnifies the page whenever a focused field's
   text is smaller than 16px, and because the room is a position:fixed shell, that
   magnification scrolls its top clean out of view. Tapping the chat box left a blank
   white screen above the input with the tabs, the messages and the video all gone.

   16px everywhere a person types, on phones only. This is the documented way to stop
   it: there is no property that disables the zoom, and `user-scalable=no` would also
   take pinch-zoom away from people who need it. */
@media (max-width:900px){
  input[type=text], input[type=search], input[type=email], input[type=url],
  input[type=tel], input[type=number], input:not([type]), textarea, select,
  .rail-ask input, .chat-send input, .invite input, .linkrow2 input,
  textarea.f, input.f, select.f{
    font-size:16px;
  }
}
