/* ==========================================================================
   EVAL · base.css — reset, body, the two frames, the page crossfade.
   Owner: engine agent.
   iPad frame  (>= 1000px): full-viewport canvas, no page scroll, top bar.
   Phone frame (<  1000px): safe-area header, scrollable pages, floating nav.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  color: var(--ink);
  background:
    var(--dots) 0 0 / 26px 26px,   /* INTERACTION-LAWS §7: 26px grid, 7% white */
    radial-gradient(820px 400px at 10% -10%, var(--wash1) 0%, transparent 62%),
    radial-gradient(760px 380px at 94% -6%, var(--wash2) 0%, transparent 60%),
    var(--canvas);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overscroll-behavior-y: none;
}

button, input, textarea, select {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
  background: none;
  border: 0;
  margin: 0;
  /* Chrome's UA sheet puts 1px 6px on every <button>. The reset zeroed margin
     and border but not this, so with border-box every fixed-size button lost
     12px of inline content box and squeezed whatever sat inside it — the done
     circle drew as a 32x40 egg on every task row. Zero it here, once, rather
     than in each primitive; every primitive that wants padding declares it. */
  padding: 0;
}
button { cursor: pointer; -webkit-tap-highlight-color: transparent; }
h1, h2, h3, h4, p { margin: 0; font-weight: inherit; }
ul, ol { margin: 0; padding: 0; list-style: none; }
svg { display: block; }
:focus-visible { outline: 2px solid var(--pur); outline-offset: 2px; }

/* --------------------------------------------------------------------------
   Shell
   -------------------------------------------------------------------------- */
.app { display: flex; flex-direction: column; }
.pages { position: relative; min-height: 0; }

.page {
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-page) var(--ease), visibility 0s linear var(--dur-page);
}
.page.on {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur-page) var(--ease), visibility 0s;
}

/* --------------------------------------------------------------------------
   Top bar (both frames; the pieces it shows differ)
   -------------------------------------------------------------------------- */
/* A double tap is never a zoom (Quinn 09-27: the WebView zoomed in and locked there).
   touch-action: manipulation removes the double-tap-to-zoom gesture and the 300ms
   tap delay everywhere; the viewport meta pins the scale as the second lock. */
html, body { touch-action: manipulation; }

.topbar { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }

.brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
/* the mark's four squares are drawn in currentColor, so the ink that sits ON the
   purple gradient is spelled once, in tokens.css, and never in the shell's svg */
.mark {
  width: 32px; height: 32px; border-radius: 11px; background: var(--grad);
  display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
  color: var(--onGrad);
  box-shadow: 0 5px 14px -5px rgba(107, 78, 230, .60);
}
.brand .wordmark {
  font-family: var(--font-head); font-size: 18px; font-weight: 800;
  letter-spacing: 0.4px; color: var(--ink);
}
.brand .aitag {
  font-family: var(--font-mono); font-size: var(--fs-pill); font-weight: 600;
  letter-spacing: .6px; color: var(--purInk); background: var(--lav);
  border-radius: var(--r-xs); padding: 3px 6px; line-height: 1; flex: 0 0 auto;
}
.brand .store {
  font-size: var(--fs-label); font-weight: 500; color: var(--muted);
  padding-left: 11px; border-left: 1px solid var(--line3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.daypill {
  display: flex; align-items: center; gap: 11px; flex: 0 0 auto;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-pill); padding: 7px 15px; box-shadow: var(--shadow);
}
.daypill .d1 { font-family: var(--font-head); font-size: var(--fs-sub); font-weight: 700; white-space: nowrap; }
.daypill .d2 { font-size: var(--fs-sub); font-weight: 500; color: var(--muted); white-space: nowrap; }
/* the honest mark when the app is running on the copy that shipped in the bundle
   (EVAL.data.stale()): fmt.ABSENT's own toneless word, drawn where the GM is already
   reading the date. It is a fact, not a verdict, so it carries no tone. */
.daypill .dstale { flex: 0 0 auto; background: var(--surf2); }
.monthbar { width: 58px; height: 7px; border-radius: var(--r-pill); background: var(--line); overflow: hidden; flex: 0 0 auto; }
.monthbar > i { display: block; height: 100%; border-radius: var(--r-pill); background: var(--gradR); }

.avatarchip {
  width: 44px; height: 44px; border-radius: var(--r-pill); flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  background: var(--card); border: 1px solid var(--line); box-shadow: var(--shadow);
}
.avatarchip .avatar { width: 30px; height: 30px; font-size: var(--fs-pill); }

/* --------------------------------------------------------------------------
   iPad frame
   -------------------------------------------------------------------------- */
@media (min-width: 1000px) {
  html, body { height: 100%; overflow: hidden; }
  /* the iPad status bar (clock, Wi-Fi, battery) owns the top inset; EVAL's bar
     starts under it on the same canvas, so the clock reads as part of the frame */
  .app { height: 100vh; padding: calc(var(--sat) + 10px) var(--gutter) 14px; gap: var(--gap); }
  /* brand | pills | date+avatar. The pills take what is left and scroll inside it if a
     narrow iPad cannot hold all nine; the brand and the right side never get squeezed. */
  .topbar { height: var(--topbar-h); display: grid; grid-template-columns: auto minmax(0, 1fr) auto; }
  .topbar .brandside { display: flex; align-items: center; gap: 10px; min-width: 0; }
  .topbar .rightside { display: flex; align-items: center; gap: 10px; justify-self: end; }
  .pages { flex: 1; }
  .page { position: absolute; inset: 0; overflow: hidden; }
  .navpill { display: none; }
}
/* the 13-inch iPad held upright: a page is taller than one screen, so it scrolls
   the way every iOS page does; the one-screen rule belongs to landscape */
@media (min-width: 1000px) and (orientation: portrait) {
  .page { overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
}

/* --------------------------------------------------------------------------
   Phone frame
   -------------------------------------------------------------------------- */
@media (max-width: 999px) {
  body { min-height: 100vh; }
  .app { min-height: 100vh; }
  .topbar {
    position: sticky; top: 0; z-index: 20;
    padding: calc(var(--sat) + 12px) var(--gutter) 10px;
    background: none;
  }
  /* The fade used to be the bar's own background, and its see-through tail let
     a card scroll under it crisply, half a title at a time. The wash is a layer
     behind the bar instead: canvas at the top, blurred and masked out at the
     foot, so what passes under it dissolves. The bar's own content is never
     masked — only this layer is.

     The ramp has to START BELOW the bar's own content row or the bar dissolves
     itself. The row is the 44px avatar chip: 12..56 of a 66px box, 71..115 of
     125 with the notch — and a mask that went transparent from 58% (38px, 72px)
     put the wordmark and the chip inside their own fade, so a card title read at
     near-full strength straight through EVAL and a pill survived half-erased
     behind the avatar. The two offsets under the row are fixed and frame-free:
     the bar's own 10px of bottom padding and the page's 4px of top padding. So
     the layer hangs 4px past the bar and stays solid to 14px from its foot —
     opaque across the whole content row, zero exactly where the page's first
     pixel begins, and a 14px dissolve in between that belongs to neither. */
  .topbar::before {
    content: ''; position: absolute; inset: 0 0 -4px; z-index: -1; pointer-events: none;
    background: linear-gradient(to bottom, var(--canvas) calc(100% - 14px), transparent);
    -webkit-backdrop-filter: blur(14px) saturate(1.3);
    backdrop-filter: blur(14px) saturate(1.3);
    -webkit-mask-image: linear-gradient(to bottom, black calc(100% - 14px), transparent);
    mask-image: linear-gradient(to bottom, black calc(100% - 14px), transparent);
  }
  .topbar .brandside { display: flex; align-items: center; gap: 9px; min-width: 0; flex: 1; }
  .topbar .rightside { display: flex; align-items: center; gap: 8px; }
  .topbar .navpills { display: none; }
  .topbar .store { display: none; }
  .daypill { padding: 6px 12px; gap: 8px; }
  .daypill .d2 { display: none; }
  .monthbar { width: 40px; }
  .avatarchip { width: 44px; height: 44px; }

  .pages { flex: 1; }
  .page {
    display: none;
    padding: 4px var(--gutter) calc(var(--sab) + 108px);
  }
  .page.on { display: block; position: relative; z-index: 1; animation: pageIn var(--dur-page) var(--ease) both; }

  /* A page change is a cross-dissolve (INTERACTION-LAWS §1), not a fade up from
     bare canvas. The phone's pages are display:none when they are off, so
     dropping .on from the old one in the same frame the new one starts at
     opacity 0 left the content area EMPTY for the first ~90ms — filmed at 0ms
     and 40ms with nothing but the bar and the nav pill. The outgoing page keeps
     painting for the same 180ms: lifted out of flow (two block pages in flow
     would stack, not overlap) and pinned by an inline `top` to the scroll offset
     it was read at, so it holds still while the window returns to the top of the
     page that is arriving. The iPad's pages are absolute and always crossed. */
  .page.leaving {
    display: block; visibility: visible; opacity: 1; pointer-events: none;
    position: absolute; left: 0; right: 0; overflow: hidden;
    animation: pageOut var(--dur-page) var(--ease) both;
  }

  /* The foot of the phone gets the same dissolve the top bar has. The floating
     nav pill is glass, so without it a card scrolling underneath was bisected —
     half a figure above the pill, the other half below. The layer sits under the
     pill (z 40 < 50) and over the page, and never takes a tap.

     And the ramp obeys the same law the bar's own comment states 40 lines up:
     IT HAS TO BE SOLID ACROSS THE CONTROL ROW AND DISSOLVE ONLY BEYOND IT, or
     the wash dissolves the thing it is protecting instead of what passes under
     it. The bar got that care; the foot did not. The nav pill is 60px tall
     sitting `--sab + 16px` off the bottom, so its top edge is `--sab + 76px` up
     — but the wash went solid only to 46% of its own `--sab + 96px` height, i.e.
     `--sab + 44px`. Thirty-two pixels of the pill's own band sat inside the
     ramp, at roughly 60% ink, so a card head arriving there was erased from the
     top by the fade and covered from the bottom by the glass: on phone Home at
     rest that is the word "Follow-ups" and its two pills, unreadable in the one
     card INTERACTION-LAWS §7 says is never a footnote.

     `100% - 20px` IS the pill's top edge on any device (both are measured from
     the same `--sab`), so content is fully dissolved BEFORE it reaches the pill
     and the 20px of ramp belongs to neither. Nothing moves; the wash just stops
     doing its fade in the wrong 32 pixels. */
  body::after {
    content: ''; position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    height: calc(var(--sab) + 96px); pointer-events: none;
    background: linear-gradient(to top, var(--canvas) calc(100% - 20px), transparent);
    -webkit-backdrop-filter: blur(12px) saturate(1.3);
    backdrop-filter: blur(12px) saturate(1.3);
    -webkit-mask-image: linear-gradient(to top, black calc(100% - 20px), transparent);
    mask-image: linear-gradient(to top, black calc(100% - 20px), transparent);
  }
  /* a curtain owns the whole screen: the foot wash may not sit over its scrim */
  html.locked body::after { display: none; }
  /* Ask is a fixed surface, not a scrolling page: the input pill is the bottom
     of its layout, not something passing under the nav. The wash was dissolving
     the one control it exists to protect (48 of the pill's 58px with the home
     indicator, 14 flat), so the page that has nothing to scroll has no wash. */
  html[data-page="ask"] body::after { display: none; }
}

@keyframes pageIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes pageOut { from { opacity: 1; } to { opacity: 0; } }

/* entrance used by EVAL.motion.stagger */
@keyframes gridEnter {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* body scroll lock while a curtain or pop-up is open */
html.locked, html.locked body { overflow: hidden !important; }
html.locked body { position: relative; }
html.locked.phone body { position: fixed; left: 0; right: 0; width: 100%; }

/* --------------------------------------------------------------------------
   Reduced motion (INTERACTION-LAWS §3: everything becomes an instant switch)
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-delay: 0ms !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- 09-27 late: nine pills in the top bar, and the phone bar -------- */
@media (min-width: 1000px) {
  .topbar .navpills { justify-self: center; max-width: 100%; min-width: 0; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; }
  .topbar .navpills::-webkit-scrollbar { display: none; }
  .topbar .navpills button { padding: 0 18px; flex: 0 0 auto; height: 44px; font-size: 15px; }
  /* the store name steps aside until there is room for it beside nine pills */
  .topbar .store { display: none; }
}
@media (min-width: 1440px) { .topbar .store { display: inline; } }   /* eight pills need the room under 1440 (09-29) */
@media (max-width: 999px) {
  /* the wordmark was clipped by the date pill: the AI tag goes, the pill stops claiming the row */
  .topbar .aitag { display: none; }
  .topbar .brandside { flex: 0 1 auto; }
  .topbar .rightside { flex: 1 1 auto; justify-content: flex-end; min-width: 0; }
  .daypill { min-width: 0; flex: 0 1 auto; }
  .daypill .dstale { max-width: 96px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}
@media (min-width: 1000px) and (max-width: 1279px) {
  /* nine pills under 1280: the pills tighten, the day count and the AI tag step aside */
  .topbar .navpills button { padding: 0 12px; font-size: 14px; }
  .topbar .navpills { gap: 0; }
  .daypill .d2 { display: none; }
  .topbar .aitag { display: none; }
}

/* ==========================================================================
   09-28 · WHERE THE TABS SIT (html[data-tabs], iPad only) + THE SWIPE
   engine.js TABS + SWIPE sets data-tabs from EVAL.settings 'tabs'. 'top' is the
   pills above; 'left' / 'right' put a rail of tiles beside the pages; 'bottom'
   floats the same eight under them. The rail's look is in engine.css; the
   phone ignores all of it and keeps its own pill.
   ========================================================================== */
@media (min-width: 1000px) {
  /* the bar's three columns are named, so an empty middle never pulls the date
     pill into it */
  .topbar .brandside { grid-column: 1; }
  .topbar .navpills { grid-column: 2; }
  .topbar .rightside { grid-column: 3; }
  html[data-tabs="left"] .topbar .navpills,
  html[data-tabs="right"] .topbar .navpills,
  html[data-tabs="bottom"] .topbar .navpills { display: none; }
  /* with the pills gone the bar has the room it was saving: the store's name,
     the AI tag and the day count come back at every width */
  html[data-tabs="left"] .topbar .store,
  html[data-tabs="right"] .topbar .store,
  html[data-tabs="bottom"] .topbar .store { display: inline; }
  html[data-tabs="left"] .topbar .aitag,
  html[data-tabs="right"] .topbar .aitag,
  html[data-tabs="bottom"] .topbar .aitag { display: inline; }
  html[data-tabs="left"] .daypill .d2,
  html[data-tabs="right"] .daypill .d2,
  html[data-tabs="bottom"] .daypill .d2 { display: inline; }

  /* a rail: the bar across the top, the rail and the pages side by side under it */
  html[data-tabs="left"] .app,
  html[data-tabs="right"] .app {
    display: grid;
    grid-template-rows: var(--topbar-h) minmax(0, 1fr);
  }
  html[data-tabs="left"] .app { grid-template-columns: 104px minmax(0, 1fr); grid-template-areas: "top top" "rail pages"; }
  html[data-tabs="right"] .app { grid-template-columns: minmax(0, 1fr) 104px; grid-template-areas: "top top" "pages rail"; }
  html[data-tabs="left"] .app > .topbar, html[data-tabs="right"] .app > .topbar { grid-area: top; }
  html[data-tabs="left"] .app > .pages, html[data-tabs="right"] .app > .pages { grid-area: pages; min-width: 0; }
  html[data-tabs="left"] .app > .navrail, html[data-tabs="right"] .app > .navrail { grid-area: rail; }

  /* a bottom bar: in the flow under the pages, so nothing can ever hide under
     it, and clear of the home indicator */
  html[data-tabs="bottom"] .app { padding-bottom: calc(var(--sab) + 10px); }
  html[data-tabs="bottom"] .app > .navrail { flex: 0 0 auto; align-self: center; }
}

/* ---- the swipe between tabs ------------------------------------------------
   The browser keeps the vertical pan (the page scroll); the sideways one is
   the app's. A scroller inside a page that scrolls sideways resets this for
   itself, so a schedule grid still scrolls under the finger. */
.pages, .page { touch-action: pan-y; }
/* mid-swipe: no text selection under a mouse drag, and the page being carried
   and the one riding in are both on screen and both on their own layer */
html.swiping, html.swiping * { -webkit-user-select: none; user-select: none; }
.page.swipe-cur, .page.swipe-inc { will-change: transform, opacity; transform-origin: 50% 40%; }
.page.swipe-inc { visibility: visible; }
@media (min-width: 1000px) {
  /* the page slides under the rail, never over it — and stops at the rail's
     side of the page area, so no sliver of it shows in the gutter beyond the
     rail. The other three sides stay open: a page still leaves by the screen's
     own edge. */
  .navrail { position: relative; z-index: 2; }
  html.swiping[data-tabs="left"] .pages { clip-path: inset(-100vh -100vw -100vh -6px); }
  html.swiping[data-tabs="right"] .pages { clip-path: inset(-100vh -6px -100vh -100vw); }
}
@media (max-width: 999px) {
  /* the phone's pages are one column in the document: the incoming page is
     lifted out of it (engine.js pins its top to the scroll offset) and the
     column clips sideways so nothing widens the document mid-drag */
  html.swiping .pages { overflow-x: clip; }
  .page.swipe-inc {
    display: block; position: absolute; left: 0; right: 0; z-index: 1;
    overflow: hidden; opacity: 1;
  }
  /* a page a swipe carried in has already arrived: no fade-in on top of it */
  .page.landed { animation: none; }
}

/* eight pills incl. Manufacturer: a touch tighter under 1440 so the last one never clips (09-29) */
@media (min-width: 1000px) and (max-width: 1439px) {
  .topbar .navpills button { padding: 0 13px; font-size: 14.5px; }
}

/* 09-29 (Quinn): no scrollbars inside cards, panels or curtains — a list that scrolls inside a card
   shows a black bar down its edge on the iPad, over the task check circles; a swipe is the scroll */
.card, .card *, .panel, .panel *, .curtain, .curtain *, .popup, .popup * { scrollbar-width: none; -ms-overflow-style: none; }
.card ::-webkit-scrollbar, .card::-webkit-scrollbar, .panel ::-webkit-scrollbar, .panel::-webkit-scrollbar,
.curtain ::-webkit-scrollbar, .curtain::-webkit-scrollbar, .popup ::-webkit-scrollbar, .popup::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* nine pills (Deals + Team, 09-29): under 1100px they tighten so the last one never clips at 1024 */
@media (min-width: 1000px) and (max-width: 1099px) { .topbar .navpills button { padding: 0 10px; } }
