:root {
  --ink: #16324F;      --ink-soft: #3C5A78;
  --paper: #EDF1F5;    --card: #FFFFFF;
  --gold: #C98A2B;     --teal: #4C7A82;
  --green: #3E8A5B;    --line: #D3DCE4;
  --app-h: 100dvh;
  /* Review round 1 fix: dedicated tokens for the two patterns that broke in
     dark mode (see the dark-mode block at the bottom of this file for why).
     --invert-bg/--invert-ink are the "surface that deliberately contrasts
     with the page" pair (card back, filled buttons, active chips) — kept a
     constant navy/white in BOTH themes on purpose, so it never flips to a
     white flash the way reusing --ink for this purpose did. --correct-bg/
     --incorrect-bg are the answer-input feedback tints, themed so text
     using --ink on top of them (near-white in dark) stays readable.
     --danger is the destructive-action red, given a brighter dark-mode
     value because the light value (#A33) drops to ~2.4:1 on a dark card. */
  --invert-bg: #16324F;   --invert-ink: #FFFFFF;
  --correct-bg: #EAF6EF;  --incorrect-bg: #FBEAE9;
  --danger: #A33;
  /* Companion to the token block above: tell the UA which palettes this
     page supports so native form controls (the Notes textarea, scrollbars)
     don't default to light chrome underneath dark-themed text. Redeclared
     to "dark" inside both dark-mode guards below. */
  color-scheme: light dark;
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; overflow: hidden; }
body {
  background: var(--paper); color: var(--ink);
  font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
}
.app {
  height: var(--app-h);
  display: flex; flex-direction: column;
  padding-bottom: env(safe-area-inset-bottom);
}
.screen { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.screen[hidden] { display: none; }
.bar { display: flex; align-items: center; gap: 8px; padding: 10px 12px; }
.bar-gap { flex: 1; }
.progress-row { display: flex; align-items: center; gap: 8px; padding: 0 12px 8px; }
.progress-track { flex: 1; height: 3px; background: var(--line); border-radius: 2px; }
.progress-fill { height: 3px; background: var(--gold); border-radius: 2px; width: 0; }
.card-area { flex: 1; min-height: 0; margin: 0 12px; display: flex; }
.rate-row { display: flex; gap: 8px; padding: 12px; }
.rate-btn { flex: 1; min-height: 44px; border-radius: 10px; font-size: 15px; }
.icon-btn, .chip, .counter, .tab { min-height: 44px; touch-action: manipulation; }
.icon-btn.sm { min-height: 32px; min-width: 32px; }
.tabbar { display: flex; border-top: 1px solid var(--line); background: var(--card); }
.tabbar[hidden] { display: none; }
.tab { flex: 1; border: 0; background: none; padding: 10px 0; font-size: 11px; color: var(--ink-soft); }
.tab.active { color: var(--ink); }

/* ---- card faces / flip / answer input / feedback ------------------
   Step 6 requires .card-wrap { flex:1; min-height:0; display:flex;
   perspective:1400px; } and .card-inner { flex:1; display:grid; } —
   both applied directly below (not "from the shell rules above": there
   is no other .card-wrap/.card-inner rule in this file, so leaving
   these out is what let the two faces stack instead of overlap).
   These blocks also add back the 3D-flip mechanics and cursor that
   Step 6's snippet doesn't cover, plus (on .face, below) the explicit
   fill-height and content styling the shell's bare .face rule omits. */
.card-wrap { flex: 1; min-height: 0; display: flex; perspective: 1400px; cursor: pointer; }
.card-inner {
  flex: 1; display: grid;
  position: relative;
  transform-style: preserve-3d;
  transition: transform 0.45s cubic-bezier(.4,.2,.2,1);
}
.card-wrap.flipped .card-inner { transform: rotateY(180deg); }
/* Kartı bir sonrakine geçerken animasyonsuz sıfırlamak için:
   böylece dönme sırasında arka yüz (yeni kartın cevabı) görünmez. */
.card-inner.no-flip-anim { transition: none !important; }
.face {
  grid-area: 1 / 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: 16px;
  padding: 16px; overflow: hidden; backface-visibility: hidden;
  position: relative; height: 100%; text-align: center;
}
.face::before {
  content: "";
  position: absolute;
  top: 0; left: 0;
  width: 26px; height: 26px;
  background:
    linear-gradient(to bottom, transparent 40%, var(--ink) 40%, var(--ink) 60%, transparent 60%),
    linear-gradient(to right, transparent 40%, var(--ink) 40%, var(--ink) 60%, transparent 60%);
  opacity: 0.12;
  border-radius: 18px 0 0 0;
}
.status-dot {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
}
.back { transform: rotateY(180deg); background: var(--invert-bg); color: var(--invert-ink); border-color: var(--invert-bg); }
.prompt-label {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--teal);
  margin-bottom: 14px;
}
/* Original design used a --gold-soft (#F1D9AC) tint here, not solid --gold
   (#C98A2B) — solid gold at 11px on the --ink back face is too dark to
   read as a highlight. color-mix() recreates that light tint from --gold
   without introducing a 9th color token outside the approved palette. */
.back .prompt-label { color: color-mix(in srgb, var(--gold) 35%, white 65%); }
.fi-word {
  font-family: "Arial Black", Impact, sans-serif;
  font-weight: 900;
  font-size: clamp(20px, 4.6vw, 30px);
  line-height: 1.25;
  margin-bottom: 6px;
  max-width: 100%;
  overflow-wrap: break-word;
  hyphens: auto;
}
.fi-word.rule-text {
  font-family: inherit;
  font-weight: 700;
  font-size: clamp(16px, 3.4vw, 20px);
  line-height: 1.45;
  letter-spacing: 0;
}
.en-word {
  font-size: clamp(18px, 4vw, 25px);
  font-weight: 700;
  margin-bottom: 8px;
  max-width: 100%;
  overflow-wrap: break-word;
}
.prompt-hint {
  font-size: 12.5px;
  color: var(--ink-soft);
  max-width: 320px;
  line-height: 1.4;
  margin-bottom: 18px;
}
.note {
  font-size: 13px;
  color: var(--ink-soft);
  max-width: 340px;
  line-height: 1.4;
  margin-top: 4px;
}
.back .note { color: rgba(255,255,255,0.72); }
.answer-row {
  width: 100%;
  max-width: 320px;
  display: flex;
  gap: 8px;
  margin-top: 4px;
}
.answer-input {
  flex: 1;
  min-width: 0;
  border: 1.5px solid var(--line);
  border-radius: 10px;
  padding: 11px 14px;
  font-size: 16px;
  font-family: inherit;
  color: var(--ink);
  background: var(--card);
}
.answer-input:focus { outline: none; border-color: var(--gold); }
.answer-input.correct { border-color: var(--green); background: var(--correct-bg); }
.answer-input.incorrect { border-color: var(--danger); background: var(--incorrect-bg); }
.check-btn {
  border: none;
  background: var(--invert-bg);
  color: var(--invert-ink);
  padding: 0 20px;
  border-radius: 10px;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}
.check-btn:hover { background: var(--gold); }
.check-btn:disabled { opacity: 0.5; cursor: default; }
.skip-link {
  background: none;
  border: none;
  color: var(--ink-soft);
  font-size: 11px;
  text-decoration: underline;
  cursor: pointer;
  margin-top: 8px;
  padding: 0;
}
.feedback {
  font-size: 12px;
  font-weight: 700;
  margin-top: 8px;
  min-height: 15px;
}
.feedback.correct { color: var(--green); }
.feedback.incorrect { color: var(--danger); }
.empty-msg {
  text-align: center;
  color: var(--ink-soft);
  font-size: 14px;
  padding: 40px 20px;
}

/* ====================================================================
   screen-decks (Task 5, the real Decks home screen) below, followed by
   the deck-picker sheet, then screen-notes and screen-more (Task 6).
   All three are the app's real, styled screens now — nothing below
   this point is un-redesigned leftover markup.
   ==================================================================== */
.eyebrow {
  letter-spacing: 0.18em;
  font-size: 11px;
  font-weight: 700;
  color: var(--teal);
  text-transform: uppercase;
  margin-bottom: 6px;
}
h1 {
  font-family: "Arial Black", Impact, sans-serif;
  font-weight: 900;
  text-transform: uppercase;
  font-size: clamp(26px, 5vw, 40px);
  letter-spacing: -0.01em;
  margin: 0 0 24px;
  text-align: center;
  line-height: 1.05;
}

/* ---------- Decks home screen (Task 5) ---------- */
.home-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 14px 16px 2px; }
.home-head .eyebrow { margin-bottom: 2px; }
.home-head h1 { margin: 0; text-align: left; font-size: clamp(22px, 7vw, 32px); }
.avatar {
  flex-shrink: 0; width: 36px; height: 36px; border-radius: 50%;
  border: 1.5px solid var(--line); background: var(--card); color: var(--ink);
  font-size: 14px; font-weight: 800; cursor: pointer;
}
.continue {
  margin: 8px 16px 2px; padding: 13px 16px; border: 0; border-radius: 12px;
  background: var(--invert-bg); color: var(--invert-ink); font-size: 14px; font-weight: 700;
  text-align: left; cursor: pointer;
}
.continue[hidden] { display: none; }
.deck-list { flex: 1; min-height: 0; overflow-y: auto; padding: 0 16px 8px; -webkit-overflow-scrolling: touch; }
.tiles { display: flex; gap: 8px; padding: 8px 16px; }
/* color: var(--ink) is explicit here, not inherited, because <button> has a
   UA-stylesheet default "buttontext" colour that wins over inheritance —
   review round 1 caught this rendering the tile counts pure black on a
   dark card (1.35:1). Same reasoning on .deck-row below. */
.tile { flex: 1; background: var(--card); color: var(--ink); border: 0; border-radius: 10px; padding: 10px 4px; display: flex; flex-direction: column; cursor: pointer; }
.tile b { font-size: 17px; }
.tile span { font-size: 10px; color: var(--ink-soft); }
.tile:disabled { opacity: .5; cursor: default; }
.chips { display: flex; gap: 6px; padding: 8px 16px 4px; }
/* .chip is shared with Study's #deckChip (Task 4's shell markup, styled
   here since Task 4's own rule only set the 44px touch-target min-height
   via the .icon-btn/.chip/.counter/.tab selector above) — one rule styles
   both surfaces; changing it restyles both. Deliberate reuse, not an
   accidental collision. */
.chip { border: 1px solid var(--line); background: none; border-radius: 14px; padding: 4px 12px; font-size: 12px; color: var(--ink); cursor: pointer; }
.chip.on { background: var(--invert-bg); color: var(--invert-ink); border-color: var(--invert-bg); }
.deck-row { width: 100%; text-align: left; background: none; color: var(--ink); border: 0; border-bottom: 1px solid var(--line); padding: 10px 0; cursor: pointer; }
.deck-row .dr-top { display: flex; font-size: 13px; }
.deck-row .dr-name { flex: 1; }
.deck-row .dr-pct { color: var(--ink-soft); font-size: 12px; }
.deck-bar { height: 3px; background: var(--line); border-radius: 2px; margin-top: 5px; }
.deck-bar i { display: block; height: 3px; border-radius: 2px; background: var(--gold); }
.deck-bar i.full { background: var(--green); }

/* ---------- deck-picker sheet (Task 5's openDeckSheet/closeSheet,
   reached from Study's #deckChip). #sheetHost itself ships in Task 4's
   shell markup (hidden, no rules) — this is the first task to give it
   behaviour, so it owns all of this. html/body already have
   overflow:hidden (see top of file), so the page behind can't scroll;
   position:fixed + full inset keeps the backdrop from letting any touch
   reach screen-decks underneath, and only .sheet-body itself scrolls. */
#sheetHost {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(22,50,79,.45);
  display: flex; align-items: flex-end; justify-content: center;
}
#sheetHost[hidden] { display: none; }
.sheet {
  background: var(--card); border-radius: 18px 18px 0 0; width: 100%; max-width: 560px;
  max-height: 75vh; display: flex; flex-direction: column; overflow: hidden;
  box-shadow: 0 -12px 40px rgba(22,50,79,.3);
  padding-bottom: env(safe-area-inset-bottom);
}
.sheet-body { overflow-y: auto; padding: 6px 0; }
.sheet-body .deck-row { padding: 10px 20px; }

/* ---------- profiles ---------- */
.mini-btn {
  border: 1.5px solid var(--line); background: var(--card); color: var(--ink-soft);
  padding: 6px 12px; border-radius: 999px; font-size: 11px; font-weight: 700; cursor: pointer;
}
.mini-btn:hover { border-color: var(--gold); color: var(--ink); }
.mini-btn.danger { color: var(--danger); border-color: #E6C4C4; }
/* Armed (2-tap-confirm) state stays a fixed, theme-independent deep red —
   white text on #A33 already clears contrast in both themes on its own
   (it isn't sitting on --card, so it doesn't inherit the dark-card problem
   above), and tying it to --danger's brighter dark-mode value would break
   the white-text pairing the other way (see task-7-report.md round-1 fix
   notes for the arithmetic). */
.mini-btn.danger.armed { background: #A33; border-color: #A33; color: #fff; }

/* ---------- More screen (Task 6) ---------- */
.more-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0 16px 16px; }
/* .chips is reused from the Decks screen filter row (padding: 8px 16px 4px),
   which double-pads against .more-body's own 16px side padding, pulling the
   Profile/Appearance rows in further than .more-label and the danger
   buttons below them. Zero out its horizontal padding here so every row in
   .more-body shares one left/right edge. */
#screen-more .chips { padding-left: 0; padding-right: 0; }
.more-label { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--teal); margin: 18px 0 6px; }
/* Review fix (round 1): the brief's verbatim .mini-btn (padding:6px 12px,
   font-size:11px, no min-height) renders these two DESTRUCTIVE buttons at
   26px, side by side with a ~4.5px gutter — well under the shell's 44px
   touch-target minimum (line 29) that every other control gets, including
   the harmless theme chips two rows above. armButton()'s two-tap guard
   protects against one stray tap, not against a second tap landing 4.5px
   over on the other danger button, so this is the one place undersized
   targets can silently arm the wrong destructive action. Stack them full
   width at 44px instead; armButton()/ids are untouched. */
.more-body .mini-btn.danger { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 44px; margin-top: 8px; }

.sync-pill {
  position: fixed; left: 16px; bottom: 16px; z-index: 56; display: none;
  border: 1.5px solid var(--line); background: var(--card); color: var(--ink);
  padding: 7px 12px; border-radius: 999px; font-size: 11px; font-weight: 700;
  box-shadow: 0 4px 14px rgba(22,50,79,.10);
}
.sync-pill.warn { border-color: var(--gold); color: var(--gold); }
/* Kalıcı veri kaybı uyarısı: sync-pill ile aynı görsel dil, ama ayrı köşede
   (sağ-alt) — sol-alttaki sync-pill ile çakışmasın. syncNow() tarafından
   ASLA temizlenmez; bkz. dataLossMsg. */
.sync-pill.data-loss {
  left: auto; right: 16px; bottom: 16px;
  display: none; align-items: center; gap: 8px;
}
.sync-pill.data-loss.show { display: flex; }
/* Review fix (round 1 — Task 8): #updatePill reuses .sync-pill's visual
   language but needs its own on-screen slot. #syncPill owns bottom-left and
   #dataLossPill (see the Turkish comment above — that collision was already
   solved once) owns bottom-right; an update pill can be live at the same
   time as either (a fresh deploy reached while offline, syncPill already
   showing a pending-signal message). Anchor it to the top so none of the
   three can ever stack. Visibility itself is driven by app.js's registration
   handler setting el.style.display, the same pattern paintSync() uses for
   #syncPill in offline.js — not by the (harmless but insufficient) [hidden]
   attribute, which this base .sync-pill rule's unconditional display:none
   otherwise overrides. */
#updatePill { left: 50%; right: auto; bottom: auto; top: 16px; transform: translateX(-50%); }
.data-loss-close {
  border: none; background: none; color: inherit; cursor: pointer;
  font-size: 14px; font-weight: 800; line-height: 1; padding: 0 0 0 2px;
}
/* Notes tab badge (Task 6) — the count that used to sit on the old
   floating .notes-toggle button now lives on the tabbar's Notes tab
   instead; paintNotes() still just sets its text/display, unchanged. */
.tab .badge {
  display: inline-block; background: var(--gold); color: var(--invert-bg); border-radius: 999px;
  padding: 0 6px; margin-left: 4px; font-size: 10px; min-width: 16px; vertical-align: middle;
}

/* ---------- Notes screen (Task 6) ---------- */
#notesList { flex: 1; min-height: 0; overflow-y: auto; padding: 0 16px; }
.note-compose { padding: 8px 16px 12px; border-top: 1px solid var(--line); }
.cat-row { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px; }
.cat-btn {
  border: 1.5px solid var(--line); background: var(--card); color: var(--ink-soft);
  padding: 4px 9px; border-radius: 999px; font-size: 10.5px; font-weight: 700; cursor: pointer;
}
/* color: var(--invert-bg) (not var(--ink)) is deliberate: this tint mixes
   toward white regardless of theme, so pairing it with --ink (near-white in
   dark) produced 1.09:1 — see .note-cat below, same fix, same reason. */
.cat-btn.active { background: color-mix(in srgb, var(--gold) 35%, white 65%); border-color: var(--gold); color: var(--invert-bg); }
.note-input {
  width: 100%; border: 1.5px solid var(--line); border-radius: 12px; padding: 9px 11px;
  font: inherit; font-size: 13px; resize: vertical; min-height: 68px; color: var(--ink);
  background: var(--card);
}
.note-input:focus { outline: none; border-color: var(--gold); }
.ctx-line {
  font-size: 10.5px; color: var(--ink-soft); margin: 7px 0 8px; line-height: 1.4;
  background: var(--paper); border-radius: 8px; padding: 6px 9px;
}
.ctx-line b { color: var(--ink); }
.note-item { padding: 10px 0; border-bottom: 1px solid var(--paper); position: relative; }
.note-cat {
  display: inline-block; font-size: 9.5px; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; padding: 2px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--gold) 35%, white 65%); color: var(--invert-bg); margin-bottom: 5px;
}
.note-text { font-size: 13px; line-height: 1.45; white-space: pre-wrap; }
.note-ctx { font-size: 10.5px; color: var(--ink-soft); margin-top: 5px; }
.note-del {
  position: absolute; top: 9px; right: 14px; border: none; background: none;
  color: var(--ink-soft); cursor: pointer; font-size: 15px; line-height: 1; padding: 2px 4px;
}
.note-del:hover { color: var(--danger); }
.notes-actions { padding: 11px 16px; border-bottom: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: 6px; }
.notes-empty { padding: 26px 0; text-align: center; font-size: 12.5px; color: var(--ink-soft); }

/* ---------- keyboard handling (Task 7) ----------
   While the on-screen keyboard is up (sizeApp() toggles body.kb-open), the
   study card must keep the word, the input and Check visible in whatever
   sliver of height is left — so the row above the card and the card's own
   chrome are trimmed rather than the answer row. */
.kb-open .progress-row,
.kb-open .tiles { display: none; }
.kb-open .face { gap: 8px; padding: 10px; }
/* Review round 1: 17px sat ABOVE .fi-word.rule-text's own 16px (clamp(16px,
   3.4vw,20px) bottoms out at 16px at 390px width) — same specificity (0,2,0)
   as .fi-word.rule-text, so this rule's later source position made rule-text
   cards render *larger* exactly when kb-open is trying to save space, and
   .face's overflow:hidden (line 60) clips rather than scrolls, so that extra
   pixel was unrecoverable. 15px keeps this below rule-text's floor always. */
.kb-open .fi-word { font-size: 15px; }
.kb-open .prompt-label,
.kb-open .prompt-hint { display: none; }

/* ---------- dark mode (Task 7 + review round 1) ----------
   Two guards for the same token set: the media query covers the un-stamped
   "system" default, the attribute covers an explicit choice via applyTheme().
   Every name here already exists on bare :root above (light values) — see
   task-7-brief.md Step 5, which proves that mechanically.
   Dependency note: both blocks below have equal specificity ((0,2,0) for
   ":root:not(...)", (0,2,0) for ":root[data-theme=dark]" — pseudo-class and
   attribute selectors both count as one class each). When BOTH are active at
   once (system prefers dark AND data-theme="dark" is set explicitly), which
   one wins is decided purely by source order, not specificity — the
   attribute block below must stay physically after the media-query block
   for the explicit choice to keep winning. Don't reorder these two blocks. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ink: #E6EDF4;  --ink-soft: #9DB2C7;
    --paper: #0E1B2A; --card: #16243A;
    --gold: #E0A64A; --teal: #6FA3AC;
    --green: #4FA873; --line: #26364B;
    /* Same reasoning as the light :root block above: constant across
       themes on purpose (see round-1 report), and the two feedback tints /
       brighter danger red tuned for readable text on this dark card. */
    --invert-bg: #16324F;   --invert-ink: #FFFFFF;
    --correct-bg: #173A28;  --incorrect-bg: #3B1B1B;
    --danger: #E86A62;
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --ink: #E6EDF4;  --ink-soft: #9DB2C7;
  --paper: #0E1B2A; --card: #16243A;
  --gold: #E0A64A; --teal: #6FA3AC;
  --green: #4FA873; --line: #26364B;
  --invert-bg: #16324F;   --invert-ink: #FFFFFF;
  --correct-bg: #173A28;  --incorrect-bg: #3B1B1B;
  --danger: #E86A62;
  color-scheme: dark;
}

/* ---------- desktop shell (Task 11) ----------
   Design spec: "On desktop it's the same design, centered at ~460px
   wide" — no earlier task implemented it, so unconstrained .app stretched
   edge-to-edge on wide windows (deck chip at the far left, prev/next
   chevrons at the screen edges, ~1000px-wide rate buttons; see
   task-11-report.md for the measured before/after).
   .shell is the wrapper index.html adds around .app plus the four fixed
   overlays (#sheetHost, #syncPill, #updatePill, #dataLossPill — all
   .sync-pill or #sheetHost). Below this breakpoint .shell gets no rules
   at all: it's a plain block box, .app keeps its own height:var(--app-h),
   and the four overlays stay position:fixed against the viewport exactly
   as Tasks 4-9 built them. Phone layout is unchanged.
   At/above the breakpoint, .shell itself becomes the fixed, centred,
   phone-shaped panel (460x840 max), and the four overlays switch from
   position:fixed (viewport) to position:absolute (.shell, now their
   nearest positioned ancestor) — their existing offsets (inset:0 on
   #sheetHost; left:16px/bottom:16px on .sync-pill; the data-loss and
   updatePill corner overrides above) resolve against the PANEL instead
   of the window without a single offset value changing. .app is told to
   fill .shell (height:100%) instead of sizing itself from --app-h, so
   Task 7's kb-open/short-viewport math still runs unmodified beneath a
   panel that can never ask it to render taller than the panel allows.
   Gate: min-width:520px leaves the 460px panel room for its border/
   shadow without touching the viewport edge.
   Task 11 review round 1: min-height was originally 600px, on the theory
   that a short window should keep following the viewport like phone
   layout rather than be squeezed into a panel too short to read as one.
   But the user's actual bug report was an 830x570 windowed browser —
   exactly the case that gate excluded, so the "fix" produced no visible
   change for the one window that mattered. height:min(var(--app-h),840px)
   already caps-without-clipping at any height, so there was never a
   clipping risk to guard against; the gate was solving a problem that
   didn't exist while creating one that did. Lowered to 420px: comfortably
   below the user's 570px and the reviewer's cited 550px case, and below
   typical in-app desktop-window heights generally, while still sitting
   above common landscape-phone heights (~360-400px, e.g. a 390x844 phone
   turned sideways is 844x390) — a landscape phone should stay full-bleed
   like the rest of the phone layout, not be squeezed into a portrait-
   shaped panel sideways, and 420 keeps it excluded (see task-11-report.md
   round-1 fix section for the 740x360 check). The phone iframe regression
   check (390px wide) still never reaches the width gate regardless. */
@media (min-width: 520px) and (min-height: 420px) {
  .shell {
    position: fixed;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 460px;
    height: min(var(--app-h), 840px);
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: 28px;
    overflow: hidden;
    box-shadow: 0 20px 60px rgba(22,50,79,.3);
  }
  .app { width: 100%; height: 100%; }
  #sheetHost, .sync-pill { position: absolute; }
}
