/* Phone layout. Loaded on every surface, but every selector sits under .phone (test/public.test.js
   enforces it), so it can never touch the PC screen. Positions are the 390x844 logical frame of the
   approved mockup, times --pu. --u feeds the shared card rules in app.css (88u = 96pu). */
.phone { position: fixed; inset: 0; overflow: hidden; background: radial-gradient(ellipse at 50% 40%, #0a1b2a 0%, #03070c 55%, #010204 100%); -webkit-tap-highlight-color: transparent; }
.phone .p-frame {
  --pu: min(calc(100vw / 390), calc(100dvh / 844));
  --u: calc(1.09 * var(--pu));
  position: absolute; left: 50%; top: 0; width: calc(390 * var(--pu)); height: calc(844 * var(--pu)); transform: translateX(-50%);
  font-family: var(--font-display);
}
.phone .p-home { position: absolute; inset: 0; transition: filter 400ms var(--ease-out), opacity 400ms var(--ease-out), transform 500ms var(--ease-spring); }
.phone[data-sheet="open"] .p-home, .phone[data-view="life"] .p-home, .phone[data-view="history"] .p-home { filter: blur(8px); opacity: 0.35; transform: scale(0.98); }
.phone .p-hud { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.phone .p-orb { position: absolute; left: calc(64 * var(--pu)); top: calc(185 * var(--pu)); width: calc(262 * var(--pu)); height: calc(262 * var(--pu)); display: block; pointer-events: none; transition: transform 600ms var(--ease-spring), opacity 500ms var(--ease-out); }
/* Room-to-pocket handoff (Task 8): the orb springs in when this phone takes the answer, and
   leaves - up and back toward the room screen - when it hands it away. It marks the MOMENT the
   voice moves and then clears; what says where the answer is afterwards is the screen's own
   controls (Stop is up on the surface that is talking, "Continue here" on the one that is not).
   The text keeps mirroring either way; only the voice moved. */
.phone[data-handoff="in"] .p-orb { animation: p-handoff-in 600ms var(--ease-spring); }
.phone[data-handoff="out"] .p-orb { transform: translate(0, calc(-40 * var(--pu))) scale(0.6); opacity: 0.25; }
.phone[data-handoff="out"] .p-home { opacity: 0.35; }
@keyframes p-handoff-in { from { transform: scale(0.6); opacity: 0.4; } to { transform: none; opacity: 1; } }
.phone .p-scan { position: absolute; inset: 0; pointer-events: none; z-index: 80; background: repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.12) 0 1px, transparent 1px 3px), radial-gradient(ellipse at 50% 42%, transparent 60%, rgba(0, 0, 0, 0.62)); }

/* App bar: date only (the iPhone shows the time), status on the right. */
.phone .p-appbar { position: absolute; left: calc(24 * var(--pu)); right: calc(24 * var(--pu)); top: max(calc(56 * var(--pu)), calc(env(safe-area-inset-top) + 4px)); display: flex; justify-content: space-between; align-items: flex-start; z-index: 20; transition: opacity 400ms var(--ease-out); }
.phone[data-island="result"] .p-appbar { opacity: 0; }
.phone .p-date { font-size: max(12px, calc(15 * var(--pu))); letter-spacing: 0.16em; text-transform: uppercase; color: #eef8ff; margin-top: calc(5 * var(--pu)); text-shadow: 0 0 8px rgba(var(--blue-rgb), 0.55); }
.phone .p-statusblk { text-align: right; }
.phone .p-status { font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.14em; text-transform: uppercase; white-space: nowrap; color: rgb(var(--blue-rgb)); text-shadow: 0 0 8px rgba(var(--blue-rgb), 0.7); transition: color 400ms; }
.phone .p-status::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: currentColor; box-shadow: 0 0 8px currentColor; margin-right: 7px; vertical-align: 1px; }
.phone .p-status[data-tone="life"] { color: rgb(var(--life-rgb)); text-shadow: 0 0 8px rgba(var(--life-rgb), 0.6); }
.phone .p-status[data-tone="warn"] { color: rgb(var(--warn-rgb)); text-shadow: 0 0 8px rgba(var(--warn-rgb), 0.6); }
.phone .p-status[data-tone="fault"] { color: rgb(var(--fault-rgb)); text-shadow: none; }
.phone .p-mark { font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.16em; text-transform: uppercase; color: rgba(var(--blue-rgb), 0.62); margin-top: 6px; }

/* The core is the talk button. Quiet sits bottom-left of the ring, clear of the status line. */
.phone .p-core { position: absolute; left: calc(125 * var(--pu)); top: calc(246 * var(--pu)); width: calc(140 * var(--pu)); height: calc(140 * var(--pu)); border-radius: 50%; border: 0; background: transparent; cursor: pointer; z-index: 15; }
.phone .p-core:focus-visible { outline: 2px solid rgba(var(--blue-rgb), 0.8); outline-offset: 4px; }
/* While the room owns the microphone the core is not the personal talk button - the banner says
   so, and the cursor agrees rather than inviting a tap that deliberately does nothing. */
.phone .p-core[aria-disabled="true"] { cursor: default; }
.phone .p-quiet { position: absolute; left: calc(12 * var(--pu)); top: calc(432 * var(--pu)); z-index: 21; min-width: 64px; min-height: 44px; padding: 0 12px; border: 0; background: transparent; font-family: var(--font-display); font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.14em; text-transform: uppercase; color: rgba(170, 195, 215, 0.6); cursor: pointer; }
.phone .p-quiet::before { content: ""; position: absolute; left: 4px; right: 4px; top: 50%; height: 26px; transform: translateY(-50%); border-radius: 999px; box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.2); z-index: -1; }
.phone .p-quiet[aria-pressed="true"] { color: #dff3ff; }
.phone .p-quiet[aria-pressed="true"]::before { background: rgba(var(--blue-rgb), 0.12); box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.45); }
/* Mirrors Quiet on the other side of the ring - the warning tone when pressed, since it is
   turning ON a lock (personal answers stay off this phone's own mic while it's armed), not a
   plain preference toggle. */
.phone .p-room { position: absolute; right: calc(12 * var(--pu)); top: calc(432 * var(--pu)); z-index: 21; min-width: 84px; min-height: 44px; padding: 0 12px; border: 0; background: transparent; font-family: var(--font-display); font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.14em; text-transform: uppercase; color: rgba(170, 195, 215, 0.6); cursor: pointer; }
.phone .p-room::before { content: ""; position: absolute; left: 4px; right: 4px; top: 50%; height: 26px; transform: translateY(-50%); border-radius: 999px; box-shadow: inset 0 0 0 1px rgba(var(--warn-rgb), 0.25); z-index: -1; }
.phone .p-room[aria-pressed="true"] { color: rgb(var(--warn-rgb)); }
.phone .p-room[aria-pressed="true"]::before { background: rgba(var(--warn-rgb), 0.14); box-shadow: inset 0 0 0 1px rgba(var(--warn-rgb), 0.55); }
/* The one persistent, always-legible statement of what room mode is doing and why - anchored to
   the bottom (above the home indicator's safe area) rather than squeezed into the already-packed
   band between the ring and the card row, and given a z-index that lets it sit on top of the
   glance cards while it is shown, exactly the way a system "recording" banner would. */
/* z-index above the bottom nav (55) as well as the cards it sits over (12) - found live: at 30
   the nav bar's own background rendered on top of this note's lower lines instead of under them. */
.phone .p-room-note { position: absolute; left: calc(18 * var(--pu)); right: calc(18 * var(--pu)); bottom: max(calc(100 * var(--pu)), calc(env(safe-area-inset-bottom) + 92px)); z-index: 56; padding: calc(10 * var(--pu)) calc(14 * var(--pu)); background: rgba(4, 12, 20, 0.92); box-shadow: inset 0 0 0 1px rgba(var(--warn-rgb), 0.5); font-family: var(--font-body); font-size: max(10px, calc(11 * var(--pu))); line-height: 1.4; color: rgba(235, 248, 255, 0.94); clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); }
.phone .p-room-note[data-tone="warn"] { box-shadow: inset 0 0 0 1px rgba(var(--warn-rgb), 0.8); color: rgb(var(--warn-rgb)); }
.phone .p-room-note[data-tone="fault"] { box-shadow: inset 0 0 0 1px rgba(var(--fault-rgb), 0.8); color: rgb(var(--fault-rgb)); }
.phone .p-room-note[data-tone="blue"] { box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.55); }
.phone .p-room-note[data-tone="dim"] { color: rgba(200, 210, 220, 0.7); }

/* Zone under the ring */
.phone .p-zone { position: absolute; left: calc(20 * var(--pu)); right: calc(20 * var(--pu)); top: calc(484 * var(--pu)); height: calc(92 * var(--pu)); z-index: 12; text-align: center; }
.phone .p-zone > div { position: absolute; left: 0; right: 0; animation: p-rise 420ms var(--ease-out); }
@keyframes p-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.phone .p-standby { top: calc(46 * var(--pu)); font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.24em; text-transform: uppercase; color: rgba(var(--blue-rgb), 0.66); }
.phone .p-words { top: calc(40 * var(--pu)); font-family: var(--font-body); font-weight: 300; font-size: max(16px, calc(22 * var(--pu))); line-height: 1.25; color: #fff; }
.phone .p-words .w { opacity: 0.18; transition: opacity 240ms var(--ease-out); }
.phone .p-words .w.lit { opacity: 1; text-shadow: 0 0 10px rgba(var(--blue-rgb), 0.45); }
.phone .p-words.is-held { top: calc(34 * var(--pu)); font-size: max(14px, calc(17 * var(--pu))); }
.phone .p-words.is-held .w { opacity: 0.72; text-shadow: none; }
.phone .p-exchange { top: calc(14 * var(--pu)); }
.phone .p-you { font-size: max(12px, calc(13 * var(--pu))); color: rgba(150, 180, 205, 0.92); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.phone .p-jarvis { margin-top: 5px; font-size: max(14px, calc(16 * var(--pu))); line-height: 1.32; color: rgba(235, 248, 255, 0.96); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; text-wrap: balance; transition: opacity .18s var(--ease-out); }
.phone[data-fault="true"] .p-jarvis { color: rgb(var(--fault-rgb)); }
/* Sentences arrive rather than jump: a soft fade while the reply is still streaming in. */
.phone[data-mode="thinking"] .p-jarvis { opacity: .75; }
.phone[data-quiet="true"] .p-exchange { top: calc(2 * var(--pu)); text-align: left; cursor: pointer; padding: calc(9 * var(--pu)) calc(14 * var(--pu)) calc(10 * var(--pu)) calc(16 * var(--pu)); background: linear-gradient(180deg, rgba(var(--blue-rgb), 0.15), rgba(var(--blue-rgb), 0.03)); box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.5); clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px); }
.phone[data-quiet="true"] .p-you::before { content: "You  "; color: rgba(var(--blue-rgb), 0.95); letter-spacing: 0.14em; }
.phone[data-quiet="true"] .p-jarvis { font-family: var(--font-body); font-size: max(14px, calc(15 * var(--pu))); -webkit-line-clamp: 3; }
.phone .p-offline { top: calc(4 * var(--pu)); }
.phone .p-off-title { font-size: max(16px, calc(19 * var(--pu))); font-weight: 500; color: rgb(var(--fault-rgb)); }
.phone .p-off-reason { font-family: var(--font-body); font-size: max(12px, calc(12 * var(--pu))); color: rgba(165, 180, 195, 0.85); margin-top: 2px; }
.phone .p-off-row { display: flex; justify-content: center; align-items: center; gap: 14px; margin-top: calc(6 * var(--pu)); }
.phone .p-off-count { font-size: max(12px, calc(13 * var(--pu))); letter-spacing: 0.12em; text-transform: uppercase; color: rgba(200, 210, 220, 0.85); font-variant-numeric: tabular-nums; }
.phone .p-retry { min-height: 44px; min-width: 88px; font-family: var(--font-display); font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.18em; text-transform: uppercase; color: #fff; background: rgba(var(--fault-rgb), 0.14); border: 0; box-shadow: inset 0 0 0 1px rgba(var(--fault-rgb), 0.7); cursor: pointer; clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); }
.phone .p-retry:active { background: rgba(var(--fault-rgb), 0.3); }
/* Sits clear of the needs-you card's LOCK tag, which hangs 14px above the card (top 556 in
   phone units). At 522 the bar ran to 566 and the Send button printed straight through the
   tag - found live in Task 9 check 3, and guarded by a test in test/public.test.js. */
.phone .p-ask { position: absolute; left: calc(20 * var(--pu)); right: calc(20 * var(--pu)); top: calc(504 * var(--pu)); z-index: 13; display: flex; gap: 8px; padding-left: 12px; background: rgba(4, 12, 20, 0.72); box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.35); clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); }
.phone .p-ask:focus-within { box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.8); }
.phone .p-ask input { flex: 1; min-width: 0; min-height: 44px; background: transparent; border: 0; font-family: var(--font-body); font-size: 16px; color: var(--ink); }
.phone .p-ask input:focus-visible { outline: none; }
.phone .p-send { min-width: 64px; min-height: 44px; border: 0; background: rgba(var(--blue-rgb), 0.14); font-family: var(--font-display); font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase; cursor: pointer; }

/* Sits under the exchange zone, full width of the card row below it (same 18pu gutters as
   .p-cards). The fault-adjacent warning colour, not the fault red - Stop is a normal control, not
   an error. z-index above the cards it may sit near: whenever it is showing, Jarvis is mid-reply
   and reaching him is more urgent than the row underneath. */
.phone .p-stop { position: absolute; left: calc(18 * var(--pu)); right: calc(18 * var(--pu)); top: calc(578 * var(--pu)); z-index: 14; min-height: 44px; border: 0; background: rgba(var(--warn-rgb), 0.16); box-shadow: inset 0 0 0 1px rgba(var(--warn-rgb), 0.55); color: rgb(var(--warn-rgb)); font-family: var(--font-display); font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.16em; text-transform: uppercase; cursor: pointer; clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); }
.phone .p-stop:active { background: rgba(var(--warn-rgb), 0.3); }
/* "Continue here": the same full-width, 44px-tall target as Stop, in the same slot - blue, because
   moving the voice here is a normal thing to do, not an interruption. Hidden unless there is
   genuinely an answer running somewhere else. It used to sit "just above" Stop at 526pu, which is
   inside the zone under the ring (484-576pu): on screen it covered the question while he was
   thinking and the reply's last line while the PC spoke it (Task 10). The two never show together
   - Stop needs this phone speaking, the offer needs someone else speaking - so they share a slot. */
.phone .p-handoff { position: absolute; left: calc(18 * var(--pu)); right: calc(18 * var(--pu)); top: calc(578 * var(--pu)); z-index: 14; min-height: 44px; border: 0; background: rgba(var(--blue-rgb), 0.16); box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.55); color: rgb(var(--blue-rgb)); font-family: var(--font-display); font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.16em; text-transform: uppercase; cursor: pointer; clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); }
.phone .p-handoff:active { background: rgba(var(--blue-rgb), 0.3); }

/* Cards: needs-you on top only when something waits, then the row of three. */
.phone .p-cards { position: absolute; left: calc(18 * var(--pu)); right: calc(18 * var(--pu)); top: calc(568 * var(--pu)); height: calc(180 * var(--pu)); z-index: 12; }
.phone .p-row-wrap { position: absolute; left: 0; right: 0; top: calc(82 * var(--pu)); height: calc(96 * var(--pu)); }
.phone .p-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: calc(8 * var(--pu)); height: 100%; transition: opacity 300ms var(--ease-out); }
.phone .p-row.is-empty, .phone .p-needs.is-empty { opacity: 0; }
.phone .p-row .card { padding: calc(10 * var(--pu)) calc(8 * var(--pu)) 0 calc(13 * var(--pu)); }
.phone .p-row .card-title, .phone .p-row .card-sub, .phone .p-needs .card-title, .phone .p-needs .card-sub { font-size: max(12px, calc(12 * var(--pu))); }
.phone .p-row .card-sub, .phone .p-needs .card-sub { font-family: var(--font-body); }
.phone .p-row .card-value { font-size: max(12px, calc(27 * var(--pu))); }
.phone .p-row .card-value.is-text { font-size: max(12px, calc(19 * var(--pu))); }
.phone .p-row.is-stale .card-wrap { filter: grayscale(1); opacity: 0.5; }
.phone .p-hits { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: calc(8 * var(--pu)); }
.phone .p-hit { display: block; width: 100%; height: 100%; min-height: 44px; border: 0; padding: 0; background: transparent; cursor: pointer; }
.phone .p-hit:focus-visible { outline: 2px solid rgba(var(--blue-rgb), 0.8); outline-offset: 2px; }
.phone .p-needs-wrap { position: absolute; left: 0; right: 0; top: calc(2 * var(--pu)); height: calc(76 * var(--pu)); --c: var(--warn-rgb); animation: p-drop 700ms var(--ease-spring); }
@keyframes p-drop { from { opacity: 0; transform: translateY(-14px) scale(0.98); } to { opacity: 1; transform: none; } }
.phone .p-needs-wrap .p-hit { position: absolute; inset: 0; }
.phone .p-needs, .phone .p-needs .card-wrap { height: 100%; }
.phone .p-needs .card { padding: calc(9 * var(--pu)) calc(14 * var(--pu)) 0 calc(16 * var(--pu)); }
.phone .p-needs .card-value { font-size: max(12px, calc(21 * var(--pu))); }
.phone .p-answer-wrap { --c: var(--blue-rgb); }
.phone .p-brk { position: absolute; inset: -7px; pointer-events: none; }
.phone .p-brk span { position: absolute; width: 14px; height: 14px; border: 0 solid rgba(var(--c), 0.95); filter: drop-shadow(0 0 5px rgba(var(--c), 0.8)); }
.phone .p-brk span:nth-child(1) { left: 0; top: 0; border-left-width: 2px; border-top-width: 2px; }
.phone .p-brk span:nth-child(2) { right: 0; top: 0; border-right-width: 2px; border-top-width: 2px; }
.phone .p-brk span:nth-child(3) { left: 0; bottom: 0; border-left-width: 2px; border-bottom-width: 2px; }
.phone .p-brk span:nth-child(4) { right: 0; bottom: 0; border-right-width: 2px; border-bottom-width: 2px; }
.phone .p-brk em { position: absolute; right: 18px; top: -7px; font-style: normal; font-size: 12px; line-height: 14px; letter-spacing: 0.18em; text-transform: uppercase; color: rgba(var(--c), 0.95); background: #02060a; padding: 0 5px; }
.phone .p-answer-wrap .p-brk { inset: -8px; animation: p-fade 600ms var(--ease-out) 1100ms both; }
@keyframes p-fade { from { opacity: 0; } to { opacity: 1; } }
.phone .p-allquiet { position: absolute; left: 0; right: 0; top: calc(24 * var(--pu)); display: flex; align-items: center; gap: 12px; font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.2em; text-transform: uppercase; color: rgba(var(--life-rgb), 0.78); }
.phone .p-allquiet::before, .phone .p-allquiet::after { content: ""; flex: 1; height: 1px; background: linear-gradient(90deg, transparent, rgba(var(--life-rgb), 0.35)); }
.phone .p-allquiet::after { background: linear-gradient(270deg, transparent, rgba(var(--life-rgb), 0.35)); }
.phone .p-allquiet i { width: 6px; height: 6px; border-radius: 50%; background: rgb(var(--life-rgb)); box-shadow: 0 0 8px rgb(var(--life-rgb)); margin-right: -4px; }
.phone .p-stale { position: absolute; left: 0; right: 0; top: calc(52 * var(--pu)); font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.16em; text-transform: uppercase; color: rgba(170, 180, 190, 0.7); text-align: center; }

/* Live status island */
/* Must clear the device's OWN Dynamic Island, not sit under it. env(safe-area-inset-top)
   is ~59px on an iPhone with one and 0 in a desktop browser, so the max() keeps the
   designed 11-unit position everywhere it is safe and drops below the notch where it is not.
   Found on the user's real iPhone 2026-09-16 - no simulated phone shows this. */
.phone .p-island { position: absolute; top: max(calc(11 * var(--pu)), calc(env(safe-area-inset-top) + 6px)); left: 50%; width: calc(126 * var(--pu)); height: calc(37 * var(--pu)); transform: translateX(-50%); border-radius: calc(19 * var(--pu)); background: #000; z-index: 60; overflow: hidden; --area-rgb: var(--blue-rgb); transition: width 600ms var(--ease-spring), height 600ms var(--ease-spring), border-radius 600ms var(--ease-spring), box-shadow 500ms var(--ease-out); }
.phone .p-island[data-island="think"] { width: calc(238 * var(--pu)); height: calc(42 * var(--pu)); border-radius: calc(21 * var(--pu)); box-shadow: 0 0 0 1px rgba(var(--blue-rgb), 0.18), 0 8px 24px rgba(0, 0, 0, 0.6); }
.phone .p-island[data-island="compact"] { width: calc(156 * var(--pu)); box-shadow: 0 0 0 1px rgba(var(--area-rgb), 0.22), 0 6px 18px rgba(0, 0, 0, 0.6); }
.phone .p-island[data-island="result"] { width: calc(362 * var(--pu)); height: calc(88 * var(--pu)); border-radius: calc(30 * var(--pu)); box-shadow: 0 0 0 1px rgba(var(--area-rgb), 0.28), 0 12px 34px rgba(0, 0, 0, 0.7); }
.phone .p-mini { position: absolute; left: calc(9 * var(--pu)); top: calc(7 * var(--pu)); width: calc(28 * var(--pu)); height: calc(28 * var(--pu)); opacity: 0; transition: left 600ms var(--ease-spring), top 600ms var(--ease-spring), width 600ms var(--ease-spring), height 600ms var(--ease-spring), opacity 300ms var(--ease-out); }
.phone .p-island[data-island="think"] .p-mini { opacity: 1; }
.phone .p-island[data-island="compact"] .p-mini { opacity: 1; left: calc(8 * var(--pu)); top: calc(6 * var(--pu)); width: calc(25 * var(--pu)); height: calc(25 * var(--pu)); }
.phone .p-island[data-island="result"] .p-mini { opacity: 1; left: calc(12 * var(--pu)); top: calc(17 * var(--pu)); width: calc(54 * var(--pu)); height: calc(54 * var(--pu)); }
.phone .p-isl-think { position: absolute; left: calc(46 * var(--pu)); right: calc(12 * var(--pu)); top: 0; line-height: calc(42 * var(--pu)); font-size: max(12px, calc(14 * var(--pu))); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; opacity: 0; transition: opacity 300ms var(--ease-out); }
.phone .p-island[data-island="think"] .p-isl-think { opacity: 1; transition-delay: 200ms; }
.phone .p-shimmer { background: linear-gradient(90deg, rgba(140, 190, 220, 0.6) 0%, rgba(140, 190, 220, 0.6) 35%, #fff 50%, rgba(140, 190, 220, 0.6) 65%, rgba(140, 190, 220, 0.6) 100%); background-size: 250% 100%; -webkit-background-clip: text; background-clip: text; color: transparent; animation: p-shimmer 1.8s linear infinite; }
@keyframes p-shimmer { from { background-position: 100% 0; } to { background-position: -150% 0; } }
.phone .p-isl-compact { position: absolute; right: calc(15 * var(--pu)); top: 0; line-height: calc(37 * var(--pu)); font-family: var(--font-number); font-weight: 700; font-size: max(14px, calc(17 * var(--pu))); color: rgb(var(--area-rgb)); font-variant-numeric: tabular-nums; white-space: nowrap; opacity: 0; transition: opacity 300ms var(--ease-out); }
.phone .p-island[data-island="compact"] .p-isl-compact { opacity: 1; transition-delay: 300ms; }
.phone .p-isl-result { position: absolute; left: calc(80 * var(--pu)); right: calc(16 * var(--pu)); top: 0; height: calc(88 * var(--pu)); display: flex; align-items: center; justify-content: space-between; gap: 10px; opacity: 0; transform: translateY(-6px); transition: opacity 350ms var(--ease-out), transform 500ms var(--ease-spring); }
.phone .p-island[data-island="result"] .p-isl-result { opacity: 1; transform: none; transition-delay: 250ms; }
.phone .p-isl-text { min-width: 0; }
.phone .p-isl-label { font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.14em; text-transform: uppercase; color: rgba(var(--area-rgb), 0.95); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.phone .p-isl-value { font-family: var(--font-number); font-weight: 700; font-size: max(12px, calc(26 * var(--pu))); line-height: 1.05; color: #fff; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.phone .p-isl-value small { font-family: var(--font-body); font-weight: 400; font-size: max(12px, calc(13 * var(--pu))); color: rgba(165, 192, 214, 0.92); margin-left: 4px; }
.phone .p-isl-spark path { stroke: rgb(var(--area-rgb)); }

/* Cards that open up, and the Life / History pages */
.phone .p-sheet { position: absolute; inset: 0; z-index: 50; transform-origin: 0 0; padding: calc(110 * var(--pu)) calc(22 * var(--pu)) calc(100 * var(--pu)); overflow-y: auto; background: linear-gradient(180deg, rgba(var(--blue-rgb), 0.1), rgba(2, 8, 14, 0.94) 30%); box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.35); transition: transform 560ms var(--ease-spring); }
.phone .p-sheet-body { --area-rgb: var(--blue-rgb); }
.phone .p-detail-title { margin: 0 0 calc(18 * var(--pu)); font-weight: 500; font-size: max(18px, calc(22 * var(--pu))); letter-spacing: 0.14em; text-transform: uppercase; color: rgb(var(--area-rgb)); }
.phone .p-detail-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 60%); column-gap: 12px; padding: 12px 0; border-top: 1px solid rgba(var(--area-rgb), 0.18); animation: p-rise 420ms var(--ease-out) both; }
.phone .p-detail-label { font-size: max(12px, calc(12 * var(--pu))); letter-spacing: 0.14em; text-transform: uppercase; color: rgba(165, 192, 214, 0.92); align-self: center; }
.phone .p-detail-value { font-family: var(--font-number); font-weight: 700; font-size: max(18px, calc(24 * var(--pu))); color: #fff; font-variant-numeric: tabular-nums; text-align: right; overflow-wrap: anywhere; }
.phone .p-detail-sub { grid-column: 1 / -1; font-family: var(--font-body); font-size: max(12px, calc(13 * var(--pu))); color: rgba(165, 192, 214, 0.85); }
.phone .p-detail-note { font-family: var(--font-body); font-size: max(13px, calc(14 * var(--pu))); color: rgba(200, 215, 228, 0.9); margin: 16px 0 0; }
/* Empty-sheet gauge: same idiom as the PC's left-column empty gauge (a faint ring, an em-dash),
   filling the space an offline card sheet would otherwise leave as dead black. Static — no
   fraction is ever drawn, so nothing here can be mistaken for a real reading. */
.phone .p-detail-gauge { position: relative; width: calc(104 * var(--pu)); height: calc(104 * var(--pu)); margin: calc(18 * var(--pu)) auto calc(30 * var(--pu)); }
.phone .p-detail-gauge-ring { position: absolute; inset: 0; border-radius: 50%; background: conic-gradient(from 135deg, rgba(var(--area-rgb), 0.32) 0deg 270deg, transparent 270deg 360deg); -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 7px), #000 calc(100% - 7px)); mask: radial-gradient(farthest-side, transparent calc(100% - 7px), #000 calc(100% - 7px)); }
.phone .p-detail-gauge-value { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-family: var(--font-number); font-weight: 700; font-size: max(18px, calc(30 * var(--pu))); color: rgba(var(--area-rgb), 0.85); }
.phone .p-close { position: absolute; right: calc(18 * var(--pu)); top: calc(56 * var(--pu)); min-width: 64px; min-height: 44px; border: 0; background: rgba(var(--blue-rgb), 0.12); box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.45); font-family: var(--font-display); font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; cursor: pointer; }
.phone .p-page { position: absolute; left: 0; right: 0; top: 0; bottom: calc(90 * var(--pu)); z-index: 40; padding: calc(100 * var(--pu)) calc(22 * var(--pu)) 16px; overflow-y: auto; animation: p-rise 420ms var(--ease-out); }
.phone .p-page-title { margin: 0 0 12px; font-weight: 500; font-size: max(18px, calc(22 * var(--pu))); letter-spacing: 0.16em; text-transform: uppercase; color: #eaf6ff; }
.phone .p-life-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; width: 100%; min-height: 56px; padding: 0 14px 0 16px; margin-bottom: 8px; border: 0; text-align: left; cursor: pointer; background: linear-gradient(180deg, rgba(var(--life-rgb), 0.12), rgba(var(--life-rgb), 0.02)); box-shadow: inset 0 0 0 1px rgba(var(--life-rgb), 0.4); clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px); }
/* A door that answered with something actually wrong (bad status, bad body) keeps the alarming
   fault red. A door that was simply never reached — timed out, unreachable — reads the same
   muted grey the nav already uses for "not connected", so the common case (every life app
   unreachable from a dev machine) reads calm rather than uniformly broken. */
.phone .p-life-row[data-state="fault"] { background: rgba(var(--fault-rgb), 0.06); box-shadow: inset 0 0 0 1px rgba(var(--fault-rgb), 0.45); }
.phone .p-life-row[data-state="unreachable"] { background: rgba(120, 132, 145, 0.08); box-shadow: inset 0 0 0 1px rgba(120, 132, 145, 0.35); }
.phone .p-life-row[data-state="checking"] { background: rgba(var(--blue-rgb), 0.05); box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.25); }
.phone .p-life-label { font-size: max(13px, calc(14 * var(--pu))); letter-spacing: 0.14em; text-transform: uppercase; }
/* Never clip the reason: the one thing this row promises is that it's telling the truth about
   why. Wraps and grows the row instead of ellipsis-truncating (matches .p-detail-value's own
   never-clip rule below). */
.phone .p-life-sub { min-width: 0; font-family: var(--font-body); font-size: max(12px, calc(13 * var(--pu))); color: rgba(165, 192, 214, 0.92); text-align: right; overflow-wrap: anywhere; }
/* Task 9, the notifications row. Same never-clip rule as .p-life-sub: when notifications are
   off, the reason is the entire point of the row, so it wraps rather than truncating. No
   animation, so nothing to turn off under prefers-reduced-motion. */
.phone .p-notif { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; min-height: 56px; margin-top: 18px; padding: 10px 14px 10px 16px; background: linear-gradient(180deg, rgba(var(--blue-rgb), 0.1), rgba(var(--blue-rgb), 0.02)); box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.35); clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px); }
.phone .p-notif-lines { min-width: 0; }
.phone .p-notif-text { font-size: max(13px, calc(14 * var(--pu))); letter-spacing: 0.14em; text-transform: uppercase; color: #eaf6ff; overflow-wrap: anywhere; }
.phone .p-notif-detail { margin-top: 4px; font-family: var(--font-body); font-size: max(12px, calc(13 * var(--pu))); line-height: 1.35; color: rgba(165, 192, 214, 0.92); overflow-wrap: anywhere; }
.phone .p-notif-btn { flex: 0 0 auto; min-height: 44px; padding: 0 16px; border: 0; background: rgba(var(--blue-rgb), 0.16); box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.5); color: #eaf6ff; font-family: var(--font-display); font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; cursor: pointer; }
.phone .p-notif-btn[disabled] { color: rgba(120, 132, 145, 0.7); box-shadow: inset 0 0 0 1px rgba(120, 132, 145, 0.35); background: rgba(120, 132, 145, 0.08); cursor: default; }
.phone .p-day { margin: 18px 0 6px; font-weight: 500; font-size: 12px; letter-spacing: 0.2em; text-transform: uppercase; color: rgba(var(--blue-rgb), 0.8); }
.phone .p-ex { padding: 10px 0; border-top: 1px solid rgba(var(--blue-rgb), 0.12); }
.phone .p-ex-time { font-size: 12px; letter-spacing: 0.12em; color: rgba(150, 180, 205, 0.7); }
.phone .p-ex-you { font-family: var(--font-body); font-size: 14px; color: rgba(150, 180, 205, 0.95); margin-top: 2px; }
.phone .p-ex-jarvis { font-family: var(--font-body); font-size: 15px; line-height: 1.4; color: #f2f9ff; margin-top: 4px; white-space: pre-wrap; }
.phone .p-ex.is-fault .p-ex-jarvis, .phone .p-empty.is-fault { color: rgb(var(--fault-rgb)); }
.phone .p-empty { font-family: var(--font-body); font-size: 14px; color: rgba(200, 215, 228, 0.8); }
.phone .p-more { display: block; margin: 14px auto 0; min-height: 44px; padding: 0 20px; border: 0; background: rgba(var(--blue-rgb), 0.1); box-shadow: inset 0 0 0 1px rgba(var(--blue-rgb), 0.4); font-family: var(--font-display); font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; cursor: pointer; }

/* Bottom bar */
.phone .p-nav { position: absolute; left: 0; right: 0; bottom: 0; height: calc(90 * var(--pu)); padding: 0 10px env(safe-area-inset-bottom); z-index: 55; display: grid; grid-template-columns: repeat(4, 1fr); align-items: start; background: linear-gradient(180deg, rgba(2, 6, 10, 0.2), rgba(2, 6, 10, 0.92) 30%); }
.phone .p-nav::before { content: ""; position: absolute; left: 22px; right: 22px; top: 0; height: 1px; background: linear-gradient(90deg, transparent, rgba(var(--blue-rgb), 0.35), transparent); }
.phone .p-nav button { position: relative; display: flex; flex-direction: column; align-items: center; min-height: 56px; padding-top: 11px; border: 0; background: transparent; font-family: var(--font-display); font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(170, 195, 215, 0.72); cursor: pointer; }
.phone .p-nav svg { width: 22px; height: 22px; margin-bottom: 4px; }
.phone .p-nav button[aria-current="page"] { color: #fff; }
.phone .p-nav button[aria-current="page"] svg { color: var(--blue); filter: drop-shadow(0 0 6px rgba(var(--blue-rgb), 0.8)); }
.phone .p-nav button[aria-current="page"]::before { content: ""; position: absolute; top: -1px; width: 28px; height: 2px; background: var(--blue); box-shadow: 0 0 10px var(--blue); }
.phone .p-nav button.is-off { color: rgba(120, 132, 145, 0.55); cursor: default; }
.phone .p-nc { display: none; font-family: var(--font-body); font-size: 12px; letter-spacing: 0; text-transform: none; color: rgba(140, 152, 165, 0.82); line-height: 1.1; margin-top: 1px; }
.phone .p-nav button.is-off .p-nc { display: block; }

@media (prefers-reduced-motion: reduce) {
  .phone .p-home, .phone .p-sheet, .phone .p-island, .phone .p-mini, .phone .p-isl-result, .phone .p-jarvis { transition: none; }
  .phone .p-zone > div, .phone .p-needs-wrap, .phone .p-answer-wrap .p-brk, .phone .p-detail-row, .phone .p-page { animation: none; }
  .phone .p-shimmer { animation: none; background: none; color: rgba(200, 225, 240, 0.9); }
  /* The handoff is not animated here: nothing translates, scales or transitions. The dim and the
     fly-out rules above are still defined, but phone-main.js clears the attribute in the same tick
     under this setting (0ms instead of 600ms), so neither is ever on screen long enough to see.
     What carries the handoff instead is the controls - Stop is only up on the surface that is
     talking, "Continue here" only on the one that is not. */
  .phone .p-orb { transition: none; }
  .phone[data-handoff="in"] .p-orb { animation: none; }
  .phone[data-handoff="out"] .p-orb { transform: none; }
}
