/* The field face. Loads after base.css, which owns every token used here.
 *
 * Who this is for: Micah, standing in a gravel driveway at 4:40pm in July, sun
 * behind him, phone in one hand and a wrench in the other. Sometimes it is 6am
 * in the truck instead. Every decision below answers to that: big targets,
 * instant clarity, no ambiguity about whether something saved.
 *
 * Three consequences of that scene that are easy to miss:
 *   - No --label-3 anywhere. 3.26:1 does not exist in sunlight, so the two
 *     places base.css reaches for it (the disclosure chevron, the empty-state
 *     glyph) are lifted to --label-2 here.
 *   - Nothing below 15px renders. The shared .tl-chip and .tl-group-header ship
 *     at --t-footnote because the desk reads them at arm's length under office
 *     light; this face lifts both to --t-subhead.
 *   - The accent is per-client and injected at runtime. Nothing brand-specific
 *     is hardcoded here: every brand-coloured pixel comes from --accent (fills),
 *     --tint-text (type) or --tint-soft (washes).
 *
 * Layout shape: a fixed app shell. The nav bar overlays two absolutely
 * positioned panes that slide past each other for the push transition, so both
 * panes stay in the DOM and `hidden` lands on the outgoing one afterwards.
 */

/* base.css publishes --scheme but does not set color-scheme itself. Opting in
   here gets dark form controls, dark scrollbars and a dark caret in the notes
   field, which is the difference between "styled dark" and "actually dark". */
:root {
  color-scheme: var(--scheme);

  /* The field-only secondary label, and the reason it exists: this face reuses
     the shared --label-2, which the brief itself documents at 5.23:1, while the
     brief ALSO requires 7:1 for field primary content because the face is read
     in direct sun. Both cannot be true. Measured on the shared token, the line
     that says what the job actually is ("Door off track, Advance") came in at
     5.23:1 and the day summary at 4.69:1 on the grouped ground.

     #515155 clears 7:1 on every surface this face puts it on: 7.90:1 on the
     white sheet, 7.08:1 on the grouped ground, 7.19:1 on the inset fill.
     #A8A8AF is the dark counterpart: 7.20:1 on the card, 8.47:1 on the ground.
     Scoped here so the desk face, read at arm's length under office light,
     keeps the shared ramp.

     The three dark declarations mirror base.css exactly. A plain :root here
     would lose to base's :root[data-theme="dark"] on specificity, so the
     override has to be written at matching weight and simply arrive later. */
  --label-2: #515155;
}

:root[data-theme="dark"] {
  --label-2: #A8A8AF;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --label-2: #A8A8AF;
  }
}

/* ==========================================================================
   1. Face-wide corrections to the shared layer
   ========================================================================== */

/* The disclosure chevron ships as --label-3, which is banned on this face. The
   id selector is deliberate: base's dark-theme rule for .tl-chevron carries
   three classes' worth of specificity and would otherwise win. */
#app .tl-chevron {
  color: var(--label-2);
}

/* Chips and group headers lift off --t-footnote to clear the 15px field floor. */
.tl-chip {
  font: var(--t-subhead);
  letter-spacing: var(--tr-subhead);
  font-weight: 600;
  padding: 3px 10px;
}

.tl-chip .tl-icon {
  width: 17px;
  height: 17px;
}

.tl-chip-count {
  font-family: var(--font-round);
  font-variant-numeric: tabular-nums;
}

.tl-group-header {
  font: var(--t-subhead);
  letter-spacing: var(--tr-subhead);
  margin-block-end: 7px;
}

/* The brief assigns --t-callout to the secondary row line on this face. */
.tl-row-secondary {
  font: var(--t-callout);
  letter-spacing: var(--tr-callout);
  color: var(--label-2);
}

/* The empty-state glyph is --label-3 on the desk and --label-2 here. */
.tl-empty {
  color: var(--label-2);
}

/* ==========================================================================
   2. App shell
   ========================================================================== */

html {
  height: 100%;
}

body {
  height: 100%;
  /* The second declaration wins where dvh is understood and is simply dropped
     where it is not, so a shrinking phone toolbar never clips the action bar
     off the bottom of the detail view. */
  height: 100dvh;
  overflow: hidden;
}

#gate,
#app {
  position: absolute;
  inset: 0;
}

#app {
  overflow: hidden;
}

/* ==========================================================================
   3. Gate
   ========================================================================== */

#gate {
  display: flex;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  padding-inline: max(var(--inset), var(--safe-l)) max(var(--inset), var(--safe-r));
  padding-block: max(var(--s6), var(--safe-t)) max(var(--s6), var(--safe-b));
}

/* margin:auto rather than justify-content:center, so a landscape phone can
   still scroll to the top of the card instead of having it clipped. */
.gate-card {
  width: 100%;
  max-width: 420px;
  margin: auto;
}

.gate-mark {
  padding: 5px var(--s3) 5px 10px;
}

.gate-title {
  margin-block-start: var(--s5);
}

.gate-line {
  margin-block-start: var(--s3);
  max-width: 40ch;
}

.gate-note {
  margin-block-start: var(--s5);
  max-width: 40ch;
}

/* Name, PIN, Sign in: stacked full width at the note's measure, so every
   target is a whole-width 44px row on a phone and nothing squeezes the PIN
   field down to nothing. */
.gate-form {
  display: grid;
  gap: var(--s2);
  margin-block-start: var(--s4);
  max-width: 40ch;
}

/* 17px body type on purpose: iOS zooms any field set under 16px. */
.gate-input {
  width: 100%;
  min-width: 0;
  min-height: 44px;
  margin: 0;
  padding: 0 var(--s3);
  border: none;
  border-radius: var(--r-input);
  background: var(--surface);
  color: var(--label);
  font: var(--t-body);
  letter-spacing: var(--tr-body);
  appearance: none;
  -webkit-appearance: none;
}

@supports (corner-shape: squircle) {
  .gate-input {
    corner-shape: squircle;
  }
}

/* --label-3 is the shared placeholder colour and is banned on this face. */
.gate-input::placeholder {
  color: var(--label-2);
}

/* The name picker wears the field's own shell. With the native arrow gone it
   needs a sign that it opens, so it carries a small caret drawn from two
   gradients in a label token, which follows light and dark with the rest. */
.gate-select {
  padding-inline-end: calc(var(--s3) + 18px);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--label-2) 50%),
    linear-gradient(135deg, var(--label-2) 50%, transparent 50%);
  background-position:
    right calc(var(--s3) + 5px) center,
    right var(--s3) center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  cursor: pointer;
}

/* Until a name is chosen the picker reads as a placeholder, like the PIN field
   beside it. The options themselves keep the full label colour. */
.gate-select:has(option[value=""]:checked) {
  color: var(--label-2);
}

.gate-select option {
  color: var(--label);
}

.gate-connect {
  width: 100%;
}

/* Only ever populated by field.js. */
.gate-error {
  display: block;
  margin-block-start: var(--s3);
  max-width: 40ch;
}

/* ==========================================================================
   4. Nav bar
   One of the two places blur exists in this product.
   ========================================================================== */

.nav {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 3;
  padding-top: var(--safe-t);
  background: var(--nav-bg);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .nav {
    background: var(--bg-grouped);
  }
}

.nav::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 0.5px;
  background: var(--separator);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-micro) var(--ease-glide);
}

@media (resolution: 1dppx) {
  .nav::after {
    height: 1px;
    transform: scaleY(0.5);
    transform-origin: bottom;
  }
}

#app[data-scrolled="1"] .nav::after {
  opacity: 1;
}

.nav-inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--s2);
  height: 44px;
  padding-inline: max(var(--s2), var(--safe-l)) max(var(--s2), var(--safe-r));
}

.nav-lead {
  justify-self: start;
  min-width: 0;
}

.nav-trail {
  justify-self: end;
  display: flex;
  align-items: center;
  /* The queue chip and the add control share this slot, and a chip pressed
     against a 44px target reads as one wider control. */
  gap: var(--s1);
  min-width: 0;
}

/* The inline title is invisible until the large title has scrolled away. Text
   swaps between views happen while it sits at opacity 0, so a push never shows
   a title changing under the user's thumb. */
.nav-title {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  text-align: center;
  opacity: 0;
  transition: opacity var(--dur-fade) var(--ease-glide);
}

#app[data-scrolled="1"] .nav-title {
  opacity: 1;
}

/* UIKit crossfades bar items with the push rather than swapping them. The
   button used to appear at full opacity the instant the gesture started, while
   the pane it belongs to was still about 55% of the way across. It now arrives
   and leaves with the pane, and `hidden` still lands at the end of the pop, so
   the fade has something to fade and the accessibility tree stays honest. */
.nav-back {
  gap: var(--s1);
  padding-inline: var(--s2);
  max-width: 100%;
  overflow: hidden;
  white-space: nowrap;
  opacity: 0;
  transition: opacity var(--dur-fade) var(--ease-glide);
}

#app[data-view="detail"] .nav-back {
  opacity: 1;
}

/* Invisible for the length of a pop, so it must not be pressable in that window
   even though `hidden` has not landed yet. */
#app:not([data-view="detail"]) .nav-back {
  pointer-events: none;
}

/* Deliberately not .tl-chevron: this one takes the tint because it is an action
   rather than chrome, and inheriting the colour avoids a specificity fight. */
.nav-chevron {
  width: 9px;
  height: 15px;
  flex: none;
}

/* The add control, and the mirror image of the back button above it: it leaves
   with the pane it belongs to instead of snapping away the instant a push
   starts. .tl-btn-glyph carries the 44x44 target around the 22px plus. A plus
   over an open job would add nothing, so the list is the only view it shows on.
   It is not hidden outright, because `hidden` cannot be faded. */
.nav-add {
  transition: opacity var(--dur-fade) var(--ease-glide);
}

#app[data-view="detail"] .nav-add {
  opacity: 0;
  /* Invisible for the length of a push, so it must not be pressable in that
     window either. */
  pointer-events: none;
}

/* The offline queue indicator. Its presence is never animated: it has to be
   instantly truthful about work that exists only on this phone. Colour is
   allowed to cross-fade, and does. */
.pending {
  gap: 5px;
  transition:
    background-color var(--dur-micro) var(--ease-glide),
    color var(--dur-micro) var(--ease-glide);
}

/* ==========================================================================
   5. Panes and the push transition
   ========================================================================== */

.pane {
  position: absolute;
  inset: 0;
  background: var(--bg-grouped);
}

.pane-scroll {
  overflow-y: auto;
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  padding-top: calc(var(--safe-t) + 44px);
}

.pane-body {
  max-width: 560px;
  margin-inline: auto;
  padding-inline: max(var(--inset), var(--safe-l)) max(var(--inset), var(--safe-r));
  padding-block: var(--s3) calc(var(--safe-b) + var(--s7));
}

#pane-list {
  transform: translateX(0);
  opacity: 1;
}

#detail {
  overflow: hidden;
  transform: translateX(100%);
  /* The pushed view's leading edge is one of exactly three elevations in this
     product. It only reads while the pane is over the list. */
  box-shadow: var(--shadow-lead);
}

#app[data-view="detail"] #pane-list {
  transform: translateX(-30%);
  opacity: 0.5;
}

#app[data-view="detail"] #detail {
  transform: translateX(0);
}

/* Transitions exist only while a push or a pop is in flight, which is what
   guarantees nothing moves on first paint or on a poll-driven repaint. */
#app[data-anim="push"] .pane,
#app[data-anim="pop"] .pane {
  transition-property: transform, opacity;
  transition-timing-function: var(--ease-out);
  will-change: transform;
}

#app[data-anim="push"] .pane {
  transition-duration: var(--dur-view);
}

#app[data-anim="pop"] .pane {
  transition-duration: var(--dur-view-out);
}

/* One pixel of nothing, watched by an IntersectionObserver. When it leaves the
   scrollport the large title has gone with it. */
.scroll-sentinel {
  height: 1px;
  margin-block-start: -1px;
  pointer-events: none;
}

.pane-title {
  transition:
    opacity var(--dur-fade) var(--ease-glide),
    transform var(--dur-fade) var(--ease-glide);
}

#app[data-scrolled="1"] .pane-title {
  opacity: 0;
  transform: translateY(-4px);
}

/* ==========================================================================
   6. Today list
   ========================================================================== */

.list-summary {
  margin-block-start: var(--s5);
}

/* The row is a plain container so the tap band can be a real <button> and the
   two actions can be real links. Nesting either inside the other would be
   invalid, and role="button" on the row would hide the actions from a screen
   reader entirely. The row keeps its .tl-row class so the shared hairline rule
   still draws the separators and the last row still has none. */
.job-row {
  display: block;
  padding: 0;
}

.job-open {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr) auto;
  grid-template-areas: "rail content trailing";
  align-items: center;
  width: 100%;
  min-height: 72px;
  padding: 11px var(--s4);
  appearance: none;
  border: none;
  border-radius: 0;
  background: transparent;
  color: inherit;
  text-align: start;
  /* Instant on, slow off. .tl-row-tap's :active rule sets the duration to 0, so
     the wash lands the moment the finger does and releases over 320ms. */
  transition: background-color var(--dur-wash-out) var(--ease-glide);
}

.job-open:focus-visible {
  outline-offset: -3px;
}

/* Spans inside a button are not grid items of their own parents, so the two
   content lines and the two rail lines have to be told to stack. */
.job-open .tl-row-primary,
.job-open .tl-row-secondary,
.rail-time {
  display: block;
}

.rail-suffix {
  display: block;
  font: var(--t-subhead);
  letter-spacing: var(--tr-subhead);
  font-family: var(--font-round);
  color: var(--label-2);
}

.job-actions {
  display: flex;
  gap: var(--s5);
  padding-inline: calc(var(--s4) + var(--rail)) var(--s4);
  padding-block-end: var(--s2);
}

.job-actions .tl-btn {
  padding-inline: 0;
  gap: 6px;
}


.job-actions .tl-icon {
  width: 19px;
  height: 19px;
}

/* ==========================================================================
   7. Empty and finished states
   No illustration: one 44px glyph, a title3 headline, one subhead line. A
   finished day and a day with no work are different facts and read differently.
   ========================================================================== */

.field-empty {
  padding-block: var(--s7);
}

/* An empty day is the whole screen, so it sits in the middle of one rather
   than clinging to the title with a void underneath. A finished day follows
   the list it is commenting on, so it stays where it falls. */
#empty {
  justify-content: center;
  min-height: 55vh;
}

.empty-glyph {
  display: block;
  color: var(--label-2);
}

.empty-glyph .tl-icon {
  width: 44px;
  height: 44px;
}

.daydone-glyph {
  color: var(--positive-text);
}

/* ==========================================================================
   8. Alert sheets: failed saves, and a full disk
   No left stripe. The fill carries the state.
   ========================================================================== */

.alert-sheet {
  margin-block-start: var(--s5);
  padding: var(--s4);
  border-radius: var(--r-alert);
  background: var(--critical-soft);
}

@supports (corner-shape: squircle) {
  .alert-sheet {
    corner-shape: squircle;
  }
}

.alert-line {
  margin-block-start: var(--s2);
  color: var(--label);
}

.alert-list {
  margin-block-start: var(--s2);
}

.alert-item {
  font: var(--t-subhead);
  letter-spacing: var(--tr-subhead);
  color: var(--label);
  margin-block-start: var(--s1);
}

.alert-actions {
  display: flex;
  align-items: center;
  gap: var(--s4);
  margin-block-start: var(--s3);
}

.alert-actions .tl-btn-filled {
  padding-inline: var(--s5);
}

/* The banner plays its entrance the first time the failed set becomes
   non-empty, and never again on a repaint that finds the same set. */
@keyframes alert-enter {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.alert-sheet[data-enter="1"] {
  animation: alert-enter var(--dur-fade) var(--ease-out) both;
}

/* ==========================================================================
   9. Job detail
   ========================================================================== */

.detail-scroll {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  padding-top: calc(var(--safe-t) + 44px);
  /* Clears the action bar, which overlays the bottom of the pane. */
  padding-bottom: calc(var(--safe-b) + 88px);
}

.detail-scroll .pane-body {
  padding-block-end: 0;
}

.detail-sub {
  margin-block-start: var(--s1);
}

.detail-status {
  margin-block-start: var(--s3);
}

.detail-note {
  margin-block-start: var(--s3);
  color: var(--label);
}

/* Every group on this pane sits at rail 0, so all of its headers land on one x
   with the content beneath them. Only the today list carries a real rail. */
.detail-group {
  --rail: 0px;
  margin-block-start: var(--s6);
}

.detail-head + .detail-group {
  margin-block-start: var(--s5);
}

/* Contact rows carry copyable text plus a 44x44 glyph button, which is how
   Contacts does it: the number stays selectable, the action stays huge. */
.contact-action {
  color: var(--tint-text);
}

/* Photos: 3-up squares inside a sheet, so the group header lines up with the
   first tile. The tile IS the placeholder, so there is never a broken-image
   glyph while the authed blob is still in flight on bad LTE. */
.photo-sheet {
  padding: var(--s4);
}

.shots {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s2);
}

.shot {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  padding: 0;
  appearance: none;
  border: none;
  border-radius: var(--r-photo);
  background: var(--surface-3);
  color: var(--label-2);
  overflow: hidden;
  transition: opacity var(--dur-release) var(--ease-glide);
}

@supports (corner-shape: squircle) {
  .shot {
    corner-shape: squircle;
  }
}

.shot[data-state="ready"]:active {
  opacity: 0.72;
  transition-duration: var(--dur-press);
}

.shot[data-state="loading"] {
  cursor: default;
}

.shot-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A photo can be taken back off the job it was uploaded to, so every tile is
   wrapped in a cell: the tile is a button, a button cannot contain one, and the
   remove control has to lie over the corner of the shot. The cell is the grid
   item; the square is still the tile's own aspect-ratio. */
.shot-cell {
  position: relative;
}

/* 44x44 of target around a 26px disc, the same trade the nav glyph buttons
   make: the finger gets the corner of the tile, the eye gets something small
   enough not to cover the photograph. */
.shot-delete {
  position: absolute;
  inset-block-start: 0;
  inset-inline-end: 0;
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;
  width: 44px;
  height: 44px;
  padding: var(--s1);
  appearance: none;
  border: none;
  background: none;
  transition: opacity var(--dur-release) var(--ease-glide);
}

.shot-delete:active {
  opacity: 0.35;
  transition-duration: var(--dur-press);
}

/* The disc sits on a photograph, so it carries its own contrast instead of
   borrowing a surface it cannot see. Armed, it fills with the critical colour
   and the x becomes a checkmark: the confirm is a colour AND a shape, so it
   still reads in direct sun and to a colour-blind reader. */
.shot-delete-disc {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--r-chip);
  background: var(--scrim);
  color: #FFFFFF;
  transition: background-color var(--dur-micro) var(--ease-glide);
}

.shot-delete[data-armed="1"] .shot-delete-disc {
  background: var(--critical);
}

.shot-delete .tl-icon {
  width: 15px;
  height: 15px;
}

/* Failure is visible and retryable rather than a permanently broken square. */
.shot-retry {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s1);
  font: var(--t-subhead);
  letter-spacing: var(--tr-subhead);
  color: var(--critical-text);
}

.shot-retry .tl-icon {
  width: 24px;
  height: 24px;
}

/* The camera control. The real input is the focusable element and the label is
   the 56px target, so the tap, the keyboard focus and the focus ring all land
   on the thing that looks like a button. */
.camera {
  position: relative;
  margin-block-start: var(--s3);
}

.camera-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.camera:focus-within {
  outline: 2px solid var(--tint-text);
  outline-offset: 2px;
}

/* Checklist. The box rides in the content column rather than in the rail, so
   the separators and the group header stay on the same x as everything else on
   this pane. */
.check-row {
  min-height: 56px;
  cursor: pointer;
}

.check-content {
  display: flex;
  align-items: center;
  gap: var(--s3);
}

.check-box {
  position: relative;
  width: 28px;
  height: 28px;
  flex: none;
}

.check-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.check-fill {
  position: absolute;
  inset: 0;
  border: 1.75px solid var(--gray-2);
  border-radius: var(--r-chip);
  background: transparent;
  pointer-events: none;
  transition:
    background-color var(--dur-micro) var(--ease-glide),
    border-color var(--dur-micro) var(--ease-glide);
}

.check-input:checked ~ .check-fill {
  background: var(--accent);
  border-color: var(--accent);
}

.check-mark {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 18px;
  height: 18px;
  color: var(--accent-ink);
  opacity: 0;
  transform: scale(0.7);
  pointer-events: none;
  transition:
    opacity var(--dur-micro) var(--ease-glide),
    transform var(--dur-micro) var(--ease-out);
}

.check-input:checked ~ .check-mark {
  opacity: 1;
  transform: scale(1);
}

.check-input:focus-visible ~ .check-fill {
  outline: 2px solid var(--tint-text);
  outline-offset: 2px;
}

.check-label {
  min-width: 0;
}

.notes {
  display: block;
  width: 100%;
  min-height: 112px;
  padding: var(--s3) var(--s4);
  border: none;
  border-radius: var(--r-input);
  background: var(--surface);
  color: var(--label);
  font: var(--t-body);
  letter-spacing: var(--tr-body);
  /* No resize grip: raw OS chrome in an otherwise clean sheet, and a grip is
     not something a hand holding a wrench is going to drag anyway. */
  resize: none;
}

@supports (corner-shape: squircle) {
  .notes {
    corner-shape: squircle;
  }
}

/* --label-3 is the shared placeholder colour and is banned on this face. */
.notes::placeholder {
  color: var(--label-2);
}

/* Text buttons carry no inline padding, which would leave this label 16px to
   the left of every other label on the pane. The padding puts it back on the
   column without shrinking the target. */
.detail-save {
  margin-block-start: var(--s4);
  padding-inline-start: var(--s4);
}

/* The action bar overlays the bottom of the pane. Opaque, because the only
   blur in this product lives in the nav bar and the sheet scrim. */
.detail-bar {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  background: var(--bg-grouped);
  padding-inline: max(var(--inset), var(--safe-l)) max(var(--inset), var(--safe-r));
  padding-block: var(--s3) max(var(--s4), var(--safe-b));
}

.detail-bar-inner {
  max-width: 560px;
  margin-inline: auto;
}

/* ==========================================================================
   10. The new-job form
   Three rows on the same pane the job detail uses, so adding a job is the push
   transition, the back button and the filled bar this face already has, and not
   a modal sheet built a second time. The label sits in the rail the sheet
   reserves, which is what keeps the three inputs on one column.
   ========================================================================== */

.new-row {
  /* The whole row is the target: the label is part of the tap that focuses the
     input beside it, so the caret is where the thumb landed. */
  cursor: text;
  padding-block: 0;
}

.new-name {
  grid-area: rail;
  font: var(--t-body);
  letter-spacing: var(--tr-body);
  color: var(--label);
  padding-inline-end: var(--s2);
}

.new-input {
  grid-area: content;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  min-height: 44px;
  padding: 0;
  border: none;
  border-radius: 0;
  background: transparent;
  color: var(--label);
  font: var(--t-body);
  letter-spacing: var(--tr-body);
  appearance: none;
  -webkit-appearance: none;
}

.new-input:focus-visible {
  outline-offset: -2px;
}

/* --label-3 is the shared placeholder colour and is banned on this face. */
.new-input::placeholder {
  color: var(--label-2);
}

/* iOS centres a date or time input's value and gives the empty control no
   height once appearance is off. The value sits where every other field's text
   starts, on the same 44px line, so the Day and Time rows read as rows. */
.new-input::-webkit-date-and-time-value {
  display: flex;
  align-items: center;
  min-height: 44px;
  text-align: start;
}

/* ==========================================================================
   11. Photo viewer
   ========================================================================== */

.viewer {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  padding:
    calc(var(--safe-t) + var(--s8))
    max(var(--s4), var(--safe-r))
    max(var(--s4), var(--safe-b))
    max(var(--s4), var(--safe-l));
  background: var(--bg-grouped);
  animation: viewer-in var(--dur-fade) var(--ease-out) both;
}

/* Arrives over --dur-fade, leaves over --dur-fade with the exit curve. The
   attribute selector outranks the reduced-motion rule below on purpose: under
   reduce the entrance is instant, but the exit keeps its opacity, because a
   full-screen photo disappearing between two frames reads as a crash. */
.viewer[data-state="out"] {
  animation: viewer-out var(--dur-fade) var(--ease-in) both;
}

@keyframes viewer-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes viewer-out {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

.viewer-img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: var(--r-photo);
}

.viewer-close {
  position: absolute;
  inset-block-start: calc(var(--safe-t) + var(--s2));
  inset-inline-end: max(var(--s2), var(--safe-r));
  background: var(--surface);
  color: var(--label);
}

/* ==========================================================================
   12. Toast
   In and out are one animation on the one timer, so a second toast arriving
   mid flight cannot inherit the first one's countdown.
   ========================================================================== */

.toast {
  position: fixed;
  z-index: 5;
  inset-inline: max(var(--s4), var(--safe-l)) max(var(--s4), var(--safe-r));
  bottom: max(var(--s4), var(--safe-b));
  margin-inline: auto;
  width: fit-content;
  max-width: 30rem;
  padding: var(--s3) var(--s5);
  border-radius: var(--r-alert);
  background: var(--surface);
  color: var(--label);
  font: var(--t-headline);
  letter-spacing: var(--tr-headline);
  text-align: center;
  box-shadow: var(--shadow-toast);
}

@supports (corner-shape: squircle) {
  .toast {
    corner-shape: squircle;
  }
}

/* Clears the detail view's action bar. */
#app[data-view="detail"] .toast {
  bottom: calc(max(var(--s4), var(--safe-b)) + 76px);
}

/* toast-out fills FORWARDS, not both. A backwards fill would apply its own
   `from { opacity: 1 }` for the whole 2300ms delay and, being later in the
   list, beat toast-in: the toast would land at full opacity with only its
   translate animating, and the one channel that confirms an offline save
   would pop in from nothing. Forwards holds the faded-out end state and
   contributes nothing before its delay elapses. */
.toast.is-live {
  animation:
    toast-in var(--dur-fade) var(--ease-out) both,
    toast-out var(--dur-fade) var(--ease-in) 2300ms forwards;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes toast-out {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

/* ==========================================================================
   13. Wider screens
   Phone first, but the office laptop opens this too and it must not look
   broken. The column stays 560px; only the breathing room grows.
   ========================================================================== */

@media (min-width: 768px) {
  .pane-body {
    padding-block-start: var(--s5);
  }

  .detail-bar {
    padding-block-start: var(--s4);
  }
}

/* ==========================================================================
   14. Reduced motion
   base.css clamps every duration. What is left here is removing the two
   transform-based movements this face adds, and making the two scroll-linked
   changes switch instantly rather than fade.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  #app[data-view="detail"] #pane-list {
    transform: none;
    opacity: 1;
  }

  #detail {
    transform: none;
    opacity: 0;
  }

  #app[data-view="detail"] #detail {
    transform: none;
    opacity: 1;
  }

  #app[data-anim="push"] .pane,
  #app[data-anim="pop"] .pane {
    transition-property: transform, opacity;
    transition-duration: 1ms, var(--dur-fade);
  }

  .pane-title,
  .nav-title,
  .nav::after {
    transition-duration: 1ms;
  }

  /* The checkbox keeps its colour fill on --dur-micro and loses its scale: the
     mark is identical in both states, so there is nothing left to transition. */
  .check-mark,
  .check-input:checked ~ .check-mark {
    transform: none;
  }

  .alert-sheet[data-enter="1"],
  .viewer,
  .toast.is-live {
    animation-name: none;
  }
}
