/* intake — app shell.
   The three screens are a verbatim port of the Claude Design prototype and carry their
   own inline styles. Everything here is either shell plumbing (scaling, routing) or a
   :hover/:active rule, which an inline style attribute cannot express. The prototype
   runtime emitted those same rules with !important, so they do too. */

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  /* iOS paints the strip behind the home indicator with the page's own background, so
     black here shows as a band under the canvas on a phone. The backdrop covers this
     everywhere else; they agree so that nothing but red can ever appear. */
  background: #FF0000;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
  touch-action: manipulation;
}

/* The page is the positioning box for both layers below, and layout() sets its height to
   the real screen's. Installed on iOS with a see-through status bar, the viewport iOS
   reports is a status bar short of the glass, and anything position:fixed is cut off at
   that height, leaving a band of bare background across the bottom of every screen.
   Content in the page itself is drawn all the way down, so that is where these live. */
body {
  position: relative;
  font-family: Archivo, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* Fills the letterbox left by contain-scaling the 402x874 canvas onto a viewport of a
   different aspect ratio, so the edges read as a continuation of the screen. */
#backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: #FF0000;
}

/* ── on a wide screen: the phone, on a quiet page ──
   Phones and the installed app never see any of this; it is for a browser window too
   wide to be the app's own screen. The canvas keeps the rounded glass of the phone it
   was drawn at (402x874, a 6.3" iPhone), sits in a bezel, and the page around it goes
   to a warm off-white that neither the red nor the sage fights with. */
#frame, #island { display: none; pointer-events: none; }

body.framed #frame {
  display: block;
  position: absolute;
  left: 0;
  top: 0;
  /* the canvas plus a 13px bezel all round, in canvas px; scaled with it by layout() */
  width: 428px;
  height: 900px;
  border-radius: 68px;
  transform-origin: 0 0;
  z-index: 0;
  background: #1A1414;
  box-shadow:
    inset 0 0 0 1.5px #3A3030,
    0 2px 4px rgba(39, 14, 14, .08),
    0 24px 60px rgba(39, 14, 14, .16);
}

body.framed #stage { clip-path: inset(0 round 55px); }

body.framed #island {
  display: block;
  position: absolute;
  left: 50%;
  top: 11px;
  width: 124px;
  height: 36px;
  margin-left: -62px;
  border-radius: 18px;
  background: #000;
  z-index: 100;
}

#stage {
  position: absolute;
  left: 0;
  top: 0;
  width: 402px;
  height: 874px;
  transform-origin: 0 0;
  z-index: 1;
  /* Sideways gestures belong to the app, which is what makes a swipe between screens
     possible; up and down is left alone so the day stack can still be scrolled. */
  touch-action: pan-y;
  /* None of this is text to be taken away and put somewhere else, and a selection left
     behind by a drag is worse than useless: the next drag beginning on top of one is
     read as picking the selection up and carrying it, and the browser cancels the
     gesture the swipe was riding on to do it. One swipe over a word would work and
     every one after it would be swallowed. */
  user-select: none;
  -webkit-user-select: none;
}

.screen { display: none; }
.screen.active { display: block; }

/* ── sliding between screens ──
   For the length of a slide both screens are on stage at once, laid over each other
   and moved apart by transform. The ported markup sets position:relative inline, so
   that has to be overridden outright; and the stage has to clip, or the screen on its
   way out would be seen crossing the letterbox. */
#stage.sliding { overflow: hidden; }

#stage.sliding .screen {
  position: absolute !important;
  left: 0;
  top: 0;
  width: 402px;
  will-change: transform;
}

/* Kept on show after losing .active, but no longer answering to a finger. */
.screen.leaving { display: block; pointer-events: none; }

/* Being pulled in by a swipe that has not been let go of yet. */
.screen.peek { display: block; pointer-events: none; }

@keyframes screen-from-right { from { transform: translateX(100%); } }
@keyframes screen-from-left  { from { transform: translateX(-100%); } }
@keyframes screen-to-left    { to   { transform: translateX(-100%); } }
@keyframes screen-to-right   { to   { transform: translateX(100%); } }

.screen.from-right { animation: screen-from-right var(--slide-ms, 260ms) cubic-bezier(.22,.61,.36,1) both; }
.screen.from-left  { animation: screen-from-left  var(--slide-ms, 260ms) cubic-bezier(.22,.61,.36,1) both; }
.screen.to-left    { animation: screen-to-left    var(--slide-ms, 260ms) cubic-bezier(.22,.61,.36,1) both; }
.screen.to-right   { animation: screen-to-right   var(--slide-ms, 260ms) cubic-bezier(.22,.61,.36,1) both; }

/* Prevents a flash of unstyled canvas before the first layout pass sizes the stage. */
#stage:not(.ready) { visibility: hidden; }

.nav-pill { cursor: pointer; user-select: none; -webkit-user-select: none; }

/* ── pseudo-class states carried over from the prototype's style-active/style-hover ── */

.btn-close:active { background: #270E0E !important; color: #FF0000 !important; }

.k:active { background: #270E0E !important; color: #FF0000 !important; }

.k-alt:active { background: #FF0000 !important; color: #270E0E !important; }

.dial-btn:active { background: rgba(255, 0, 0, .24) !important; transform: scale(.985) !important; }

/* Home's disc is red, so it presses dark rather than red - properly dark, so a press
   plainly lands. */
.dial-btn-home:active { background: rgba(39, 14, 14, .3) !important; transform: scale(.985) !important; }

/* ── hover ──
   Only where there is a pointer that can hover: on a phone a tap leaves :hover stuck on
   whatever was last touched, which would read as a button left on. Each thing moves away
   from its own ground - red goes darker, near-black goes lighter, an outline fills in,
   a word shifts - far enough to be seen at a glance rather than looked for. */
@media (hover: hover) and (pointer: fine) {
  /* Eased in, on the things with no transition of their own. The day stack, the LOG
     dots and the not-saving strip already transition for how they arrive, and a list
     here would replace theirs rather than add to it. */
  .k, .k-alt, .btn-close, .nav-pill, .dot-btn, .range-btn, .day-badge, .ask-btn {
    transition: filter .12s, background-color .12s, color .12s;
  }

  /* the two dials, over their own ground */
  .dial-btn:hover { background: rgba(255, 0, 0, .17) !important; }
  .dial-btn-home:hover { background: rgba(39, 14, 14, .09) !important; }
  /* Pressed has to win over hover, and a pressed dial is always also a hovered one. */
  .dial-btn:hover:active { background: rgba(255, 0, 0, .3) !important; }
  .dial-btn-home:hover:active { background: rgba(39, 14, 14, .34) !important; }

  /* red grounds: darker */
  .k:hover, .btn-close:hover, #stats-home-btn:hover, #calc-settings-btn:hover,
  .day-badge:hover, .day-opt:hover, .tile:not(.armed):hover, .cal-cell.is-red:hover,
  .meal-num:hover .lit {
    filter: brightness(.88);
  }

  /* near-black grounds: lighter */
  .k-alt:hover, #home-stats-btn:hover, .range-btn:hover, .cal-cell.is-dark:hover, .day-opt.on:hover,
  #home-cal-btn:hover, .ask-go:hover, #alarm:hover {
    filter: brightness(2.2);
  }

  /* outlines: filled in with their own colour */
  #home-settings-btn:hover { background: rgba(39, 14, 14, .13); }
  #stats-settings-btn:hover, #cal-settings-btn:hover { background: rgba(255, 0, 0, .12); }
  .ask-btn:not(.ask-go):hover { background: rgba(39, 14, 14, .09) !important; }
  .cal-cell:not(.is-red):not(.is-dark):hover { background: rgba(39, 14, 14, .23) !important; }

  /* words */
  .view-toggle:hover, .meal-num:hover div:not(.lit) { color: #FF8272 !important; }
  .set-act:hover { color: #6E2E26; }
  #set-delete:hover { color: #B80000; }
  .cal-nav:not(.off):hover { color: rgba(39, 14, 14, .75); }
}

.view-toggle:active { color: #FFFFFF !important; }

.range-btn:active { background: rgba(39, 14, 14, .7) !important; color: #FF0000 !important; }

.tile { cursor: pointer; user-select: none; -webkit-user-select: none; }

/* An armed tile shows its own clock running out: it holds black, then hands the colour
   back over the last stretch of the window. The state you can see is then always the
   state a second tap would act on, and the tile returns on its own even if a render is
   missed. --arm-ms is set from ARM_MS so the two cannot drift apart. */
@keyframes tile-arm {
  0%, 78% { background-color: #000000; color: #FF0000; }
  100%    { background-color: #FF0000; color: #270E0E; }
}

.tile.armed { animation: tile-arm var(--arm-ms, 2000ms) linear forwards; }

@media (prefers-reduced-motion: reduce) {
  .tile.armed { animation: none; }
}

/* ── day picker ── */

.noscrollbar { scrollbar-width: none; -ms-overflow-style: none; }
.noscrollbar::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* Pressed, the badge goes dark the way a day in the stack does when it is picked, and the
   circle that takes its place as the stack opens holds that for a moment (.flash). */
.day-badge:active { background-color: #270E0E !important; color: #FF0000 !important; }
@keyframes day-flash { 0%, 35% { background-color: #270E0E; color: #FF0000; } }
.day-opt.flash { animation: day-flash 220ms ease-out; }
/* The day on show, dark for as long as the stack is open. */
.day-opt.on { background-color: #270E0E !important; color: #FF0000 !important; }

.day-opt {
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  /* Each circle settles the last few pixels into place as it comes on, which is what
     keeps the run from pressing together while it unfolds. */
  transition: transform var(--pick-ms, 300ms) cubic-bezier(.22, .61, .36, 1),
              opacity var(--pick-ms, 300ms) ease-out;
}
/* Going dark is instant, like any press. Only the circle handing over to the red badge at
   the end of the fold eases back to red, so the hand-over does not blink. */
.day-opt.fading {
  transition: transform var(--pick-ms, 300ms) cubic-bezier(.22, .61, .36, 1),
              opacity var(--pick-ms, 300ms) ease-out,
              background-color var(--pick-ms, 300ms) ease-out,
              color var(--pick-ms, 300ms) ease-out;
}
.day-opt:active { background-color: #270E0E !important; color: #FF0000 !important; }

#home-cal-btn { transition: opacity var(--pick-ms, 300ms) linear; }

@media (prefers-reduced-motion: reduce) {
  .day-opt, #home-cal-btn { transition: none; }
}

#home-cal-btn:active span { background: #7A2A18 !important; }

/* The stats window is dragged horizontally, so the browser must not read the gesture as
   a scroll or a page swipe first. Nothing on this screen scrolls. */
.scrubbable {
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

/* Each of these holds one wide track that slides under it. Whatever hangs past the
   edge is simply cut - a bar or a date sliced down the middle is the point, not a
   defect, and it is what lets the drag be continuous instead of slot by slot.

   Sideways only: the meal columns are taller than their host by design and spill
   below it, and `overflow: hidden` has no way to say one axis without the other. */
.track { clip-path: inset(-400px 0 -400px 0); }

/* ── settings sheet ──
   Comes up from the bottom and is put away by pushing it back down, so the transform is
   the whole of its state: the class drives it while it is being animated, and a drag
   writes an inline transform that outranks the class for as long as the finger is down. */
#settings-sheet {
  transform: translateY(100%);
  transition: transform var(--sheet-ms, 300ms) cubic-bezier(.22, .61, .36, 1);
  will-change: transform;
  /* Vertical gestures belong to the sheet; without this the page takes them as a scroll. */
  touch-action: none;
}
#settings-scrim { opacity: 0; transition: opacity var(--sheet-ms, 300ms) linear; }
#settings.open #settings-sheet { transform: translateY(0); }
#settings.open #settings-scrim { opacity: 1; }
/* Under the finger there is nothing to ease towards - it is already where it is going. */
#settings.dragging #settings-sheet { transition: none; }

@media (prefers-reduced-motion: reduce) {
  #settings-sheet, #settings-scrim { transition: none; }
}

.set-cap {
  font: 600 9.5px/1 'IBM Plex Mono', monospace;
  letter-spacing: .2em;
  color: rgba(39, 14, 14, .45);
}

.set-val {
  font: 900 44px/.9 Archivo, sans-serif;
  letter-spacing: -.035em;
  color: #270E0E;
  margin: 0;
}

/* The figure is typed straight into the screen rather than into a field drawn on top of
   it, so the input has to give up everything that would make it look like one. */
input.set-val {
  width: 100%;
  text-align: right;
  background: transparent;
  border: 0;
  padding: 0;
  outline: none;
  border-radius: 0;
  -webkit-appearance: none;
  appearance: none;
  /* The sheet takes every other gesture on itself; the field keeps its own, so the
     caret can still be put where it is wanted. It is the one thing on the canvas that
     is typed into, so it is also the one thing that keeps its selection. */
  touch-action: auto;
  user-select: text;
  -webkit-user-select: text;
}
input.set-val:focus { color: #FF0000; }

.set-act { cursor: pointer; user-select: none; -webkit-user-select: none; }
.set-act:active { color: #FF0000; }

/* The one word on the sheet that takes something away wears the accent, so it is never
   read as just another item in the list. It presses the other way round, into the dark. */
#set-delete { color: #FF0000; }
#set-delete:active { color: #270E0E; }

/* ── are you sure ──
   Over everything, with the screen behind it put out. Both of the things it asks about
   are one tap from a word that does something else, so neither is asked on the sheet. */
#ask-scrim { opacity: 0; transition: opacity var(--ask-ms, 180ms) linear; }

#ask-panel {
  opacity: 0;
  transform: scale(.94);
  transition: opacity var(--ask-ms, 180ms) ease-out,
              transform var(--ask-ms, 180ms) cubic-bezier(.22, .61, .36, 1);
}

#ask.open #ask-scrim { opacity: 1; }
#ask.open #ask-panel { opacity: 1; transform: scale(1); }

.ask-btn { -webkit-tap-highlight-color: transparent; }
.ask-btn:active { box-shadow: inset 0 0 0 2.5px #270E0E, inset 0 0 0 54px #270E0E; color: #FF0000; }
.ask-go:active { background: #3A1513; }

@media (prefers-reduced-motion: reduce) {
  #ask-scrim, #ask-panel { transition: none; }
}

/* ── things that come up rather than appear ──
   Both live in the layout all the time and are raised into view, so what arrives has
   somewhere to have arrived from. Out of the way they take no taps, which is the whole
   of the difference display:none used to make. */
#stats-sel-times, #alarm {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--raise-ms, 200ms) ease-out,
              transform var(--raise-ms, 200ms) cubic-bezier(.22, .61, .36, 1);
}

#stats-sel-times { transform: translateY(22px); }
#alarm { transform: translateY(14px); }

#stats-sel-times.up, #alarm.up {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
  #stats-sel-times, #alarm { transition: none; }
}

.dot-btn { cursor: pointer; }
.dot-btn:active { opacity: .55; }

.cal-cell { cursor: pointer; user-select: none; -webkit-user-select: none; }
.cal-cell:active { transform: scale(.92); }

/* ── panes sliding across each other ──
   A month paged in the log, and the keypad giving way to the history. Not whole screens
   but a piece of one, so they cannot use the screen slide: these move only themselves
   and leave everything around them standing.

   402px is the width of the whole canvas, which is further than any of these panes has
   to go from where it sits to be off the screen entirely. The screen clips, so a pane
   is gone the moment it passes the edge rather than crossing the letterbox.

   A pane with a narrower window than the screen sets --pane-dx to the width of that
   window instead, so it leaves by its own edge and the next arrives from the same one,
   rather than waiting off-stage and appearing halfway in. */
@keyframes pane-in-right  { from { transform: translateX(var(--pane-dx, 402px)); } }
@keyframes pane-in-left   { from { transform: translateX(calc(var(--pane-dx, 402px) * -1)); } }
@keyframes pane-out-left  { to   { transform: translateX(calc(var(--pane-dx, 402px) * -1)); } }
@keyframes pane-out-right { to   { transform: translateX(var(--pane-dx, 402px)); } }

.pane-in-right, .pane-in-left, .pane-out-left, .pane-out-right {
  animation-duration: var(--pane-ms, 240ms);
  animation-timing-function: cubic-bezier(.22, .61, .36, 1);
  animation-fill-mode: both;
}
.pane-in-right  { animation-name: pane-in-right; }
.pane-in-left   { animation-name: pane-in-left; }
/* On its way out it is only a picture of where you were; a tap belongs to what is
   arriving, or to nothing. */
.pane-out-left  { animation-name: pane-out-left; pointer-events: none; }
.pane-out-right { animation-name: pane-out-right; pointer-events: none; }

/* The month and year: a short drift with a crossfade instead of a trip off the edge. The
   outgoing one is gone by 60% and the incoming one only starts to show at 40%, so the
   two are never both legible at once. */
@keyframes fade-in-right  { from { transform: translateX(var(--pane-dx)); opacity: 0; } 40% { opacity: 0; } }
@keyframes fade-in-left   { from { transform: translateX(calc(var(--pane-dx) * -1)); opacity: 0; } 40% { opacity: 0; } }
@keyframes fade-out-left  { 60% { opacity: 0; } to { transform: translateX(calc(var(--pane-dx) * -1)); opacity: 0; } }
@keyframes fade-out-right { 60% { opacity: 0; } to { transform: translateX(var(--pane-dx)); opacity: 0; } }
.pane-fade.pane-in-right  { animation-name: fade-in-right; }
.pane-fade.pane-in-left   { animation-name: fade-in-left; }
.pane-fade.pane-out-left  { animation-name: fade-out-left; }
.pane-fade.pane-out-right { animation-name: fade-out-right; }

@media (prefers-reduced-motion: reduce) {
  .pane-in-right, .pane-in-left, .pane-out-left, .pane-out-right { animation: none; }
}

/* Bare glyphs rather than filled pills - the month block is the only thing that
   should carry weight up there. The tap target stays 34px regardless. */
.cal-nav {
  width: 34px;
  height: 34px;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  color: rgba(39, 14, 14, .4);
  font: 900 22px/1 Archivo, sans-serif;
  display: flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
}
.cal-nav:active { color: #270E0E !important; }
.cal-nav.off { color: rgba(39, 14, 14, .14); pointer-events: none; }
