/* iOS system UI components on the web. Tokens from ios-tokens.css.
   Component metrics (hairline 1px, switch 51x31) are Apple's control dimensions,
   not spacing values — they don't come from the spacing scale. */

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--text-body);
  line-height: 1.294; /* 22/17 */
  color: var(--label);
  background: var(--bg-grouped);
  -webkit-font-smoothing: antialiased;
}

/* App shell -------------------------------------------------------------
   Compact: single column, no sidebar — the HIG says plainly "iOS: avoid using
   a sidebar". Regular: a split view with a persistent selection in the sidebar
   and a thin divider, which is macOS's preferred style. */

.detail__content {
  max-width: 28rem;
  margin-inline: auto;
  padding-block-end: var(--margin);
}

.sidebar {
  display: none;
}

@media (min-width: 48rem) {
  .app {
    display: grid;
    /* 19rem, not the 17rem this started at, because the wordmark is the whole
       name now. "Cairns Aviation Roster" measures 241px at the sidebar's type
       style and wants 273px with the head's padding — one pixel more than
       17rem gave it. 19rem is 304px, so there's about 26% slack for a font
       stack that renders wider than the one it was measured in.

       The cost is 32px of content column, and only at the tightest split-view
       width: at a 768px window the content area goes from 496px to 464px,
       still above the 448px the same content gets on a phone. */
    grid-template-columns: 19rem 1fr;
    align-items: start;
    transition: grid-template-columns 0.25s var(--ease-out);
  }

  .app--collapsed {
    grid-template-columns: 0 1fr;
  }

  .sidebar {
    display: block;
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
    overflow-x: hidden;
    background: var(--bg-grouped-secondary);
    border-right: 1px solid var(--separator); /* thin divider */
    padding-block-end: var(--margin);
  }

  .app--collapsed .sidebar {
    border-right-color: transparent;
  }

  .detail__content {
    max-width: 38rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .app {
    transition: none;
  }
}

/* Sidebar ---------------------------------------------------------------- */

.sidebar__head {
  display: flex;
  align-items: center;
  min-height: var(--tap);
  padding: var(--margin) var(--margin) 0.5rem;
}

/* Deliberately allowed to wrap, where it used to be nowrap. The column is sized
   to hold the name on one line in the font this was measured in, but a wider
   font stack on somebody else's machine would have run a nowrap wordmark
   straight out of the sidebar. Wrapping to "Cairns Aviation / Roster" is a
   worse-looking outcome than one line and a far better one than a clipped
   name. `balance` is what picks that break rather than leaving "Roster" alone
   on the second line by accident. */
.sidebar__title {
  font-size: var(--text-title2);
  line-height: 1.273; /* 28/22 */
  font-weight: 700;
  letter-spacing: 0.011em;
  text-wrap: balance;
}

.sidebar__nav {
  padding-inline: 0.5rem;
}

.sidebar__label {
  margin: var(--margin) 0 0.25rem;
  padding-inline: 0.5rem;
  font-size: var(--text-footnote);
  font-weight: 600;
  color: var(--label-secondary);
  white-space: nowrap;
}

.sidenav {
  display: flex;
  align-items: center;
  gap: 0.6875rem;
  /* Full 44px row. These sit flush against each other with no gap, so the hit
     region can't be extended past the box the way a spaced control's can —
     neighbouring rows would overlap and you'd land on the wrong section. */
  min-height: var(--tap);
  padding: 0.375rem 0.5rem;
  border-radius: 0.5rem;
  color: var(--label);
  text-decoration: none;
  font-size: var(--text-body);
  white-space: nowrap;
}

@media (hover: hover) and (pointer: fine) {
  .sidenav:hover {
    background: var(--fill-quaternary);
  }

  /* The selected row is already filled with the tint; a grey wash over it just
     dirties the colour. It brightens like a filled button instead. */
  .sidenav[aria-current="page"]:hover {
    background: var(--tint);
    filter: brightness(1.12);
  }
}

.sidenav__icon {
  flex: none;
  width: 1.25rem;
  height: 1.25rem;
  color: var(--tint);
}

/* Persistent selection, per the split-view guidance. */
.sidenav[aria-current="page"] {
  background: var(--tint);
  color: var(--on-tint);
}

.sidenav[aria-current="page"] .sidenav__icon {
  color: var(--on-tint);
}

/* Sidebar toggle --------------------------------------------------------- */

.navbar__toggle {
  display: none;
  margin-inline-end: auto; /* pushes the trailing action right; absent on compact */
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  margin-inline-start: calc(var(--margin) * -0.5);
  padding: 0;
  border: 0;
  border-radius: var(--radius-control);
  background: none;
  color: var(--tint);
  cursor: pointer;
}

@media (hover: hover) and (pointer: fine) {
  .navbar__toggle:hover {
    background: var(--fill-quaternary);
  }
}

@media (min-width: 48rem) {
  .navbar__toggle {
    display: inline-flex;
  }
}

/* Navigation bar with large title ---------------------------------------- */

.navbar {
  position: sticky;
  top: 0;
  z-index: 2;
  background: color-mix(in srgb, var(--bg-grouped) 80%, transparent);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  padding: var(--margin) var(--margin) 0;
}

/* iOS collapses a large title into a centred inline title as you scroll, and reveals
   a hairline under the bar. Both titles are always in the DOM; only opacity changes,
   so the header is correct with JS disabled (it simply stays expanded). */
.navbar__bar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-height: var(--tap);
}

.navbar__inline {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-size: var(--text-headline);
  font-weight: 600;
  opacity: 0;
  transition: opacity 0.2s var(--ease-out);
}

.navbar__action {
  min-height: var(--tap);
  padding-inline: 0;
}

.navbar__title {
  margin: 0;
  padding-block-end: 0.5rem;
  font-size: var(--text-large-title);
  line-height: 1.206; /* 41/34 */
  font-weight: 700;
  letter-spacing: 0.011em;
  transform-origin: left bottom;
  transition: opacity 0.2s var(--ease-out), transform 0.2s var(--ease-out);
}

.navbar::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--separator);
  opacity: 0;
  transition: opacity 0.2s var(--ease-out);
}

.navbar.is-collapsed .navbar__title {
  opacity: 0;
  transform: scale(0.6);
}

.navbar.is-collapsed .navbar__inline,
.navbar.is-collapsed::after {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .navbar__inline,
  .navbar__title,
  .navbar::after {
    transition: none;
  }
}

/* Section grouping -------------------------------------------------------- */

.group {
  margin-top: var(--margin);
}

.group__header {
  margin: 0 0 var(--margin);
  padding-inline: var(--margin);
  font-size: var(--text-footnote);
  line-height: 1.385;
  color: var(--label-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.group__footer {
  margin: 0.5rem 0 0;
  padding-inline: var(--margin);
  font-size: var(--text-footnote);
  line-height: 1.385;
  color: var(--label-secondary);
}

/* Inset grouped card + rows ---------------------------------------------- */

.card {
  background: var(--bg-grouped-secondary);
  border-radius: var(--radius-card);
  margin-inline: var(--margin);
  overflow: hidden;
}

.row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--margin);
  min-height: var(--row);
  padding: 0.6875rem var(--margin);
  width: 100%;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: start;
  text-decoration: none;
}

/* Hairline insets to align with the label, not the card edge — an iOS detail. */
.row:not(:last-child)::after {
  content: "";
  position: absolute;
  left: var(--margin);
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--separator);
}

a.row,
button.row {
  cursor: pointer;
}

a.row:active,
button.row:active {
  background: var(--fill-quaternary);
}

@media (hover: hover) and (pointer: fine) {
  a.row:hover,
  button.row:hover {
    background: var(--fill-quaternary);
  }

  /* A row that isn't going anywhere shouldn't light up as though it is. */
  .row[disabled]:hover,
  .row.is-locked:hover,
  .row.is-disabled:hover {
    background: none;
  }
}

.row__label {
  flex: 1;
  min-width: 0;
}

.row__label--fixed {
  flex: 0 0 auto;
}

.row__value {
  color: var(--label-secondary);
  text-align: end;
}

.row__sub {
  display: block;
  font-size: var(--text-footnote);
  line-height: 1.385;
  color: var(--label-secondary);
}

.row--destructive .row__label {
  color: var(--red);
}

/* Tint on a row label means "this label is the button" — Add Account, Sign Out.
   A row that pushes somewhere is not that: in Settings its title is plain, the
   value beside it is secondary, and the chevron is what says it's going
   somewhere. Tinting those too turns a card of four settings into four blue
   links, which is most of why Pay read as a wall of them.

   So the chevron decides. Rows carrying one keep a plain label; a row without
   one really is a button wearing a row, and keeps the tint. */
.row--action .row__label {
  color: var(--tint);
}

.row--action:has(.chevron) .row__label {
  color: var(--label);
}

/* Chevron — pure CSS, sized to the row */
.chevron {
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid var(--label-tertiary);
  border-bottom: 2px solid var(--label-tertiary);
  transform: rotate(-45deg);
}

/* Buttons ----------------------------------------------------------------- */

.buttons {
  display: grid;
  gap: 0.75rem;
  padding-inline: var(--margin);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: 0.75rem 1.25rem;
  border: 0;
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--text-body);
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}

.btn:active {
  opacity: 0.7;
}

/* Sheet bars have carried a disabled look since Done first needed one; a filled
   button had never been disabled until the sign-in page did it, and so looked
   entirely pressable while doing nothing. Same 0.45 as `.sheet__side[disabled]`
   rather than a second value for the same idea. */
.btn[disabled] {
  opacity: 0.45;
  cursor: default;
}

.btn--filled {
  background: var(--tint);
  color: var(--on-tint);
}

/* The tinted wash is mixed into an opaque surface, not into `transparent`.
   A translucent wash composites over whatever it lands on, so the same button
   measured differently on a plain row and on a zebra-striped one — 3.91:1 and
   3.22:1 for the same label. Mixing into --bg-grouped pins the result: one
   colour wherever the button sits, and because the dark page colour is black
   it also buys back the contrast the wash was spending. */
.btn--tinted {
  background: color-mix(in srgb, var(--tint) 15%, var(--bg-grouped));
  color: var(--tint);
}

.btn--gray {
  background: var(--fill-secondary);
  color: var(--label);
}

.btn--plain {
  background: none;
  color: var(--tint);
}

.btn--destructive {
  background: color-mix(in srgb, var(--red) 15%, var(--bg-grouped));
  color: var(--red-on-wash);
}

/* The hover itself is a fade, not a jump. Zero specificity via :where() so it
   sits under every rule that sets these properties, and inside the motion
   guard so reduced-motion gets the state change with no animation. */
@media (prefers-reduced-motion: no-preference) {
  :where(.btn, .row, .sidenav, .tab, .chip, .segmented__label, .stepper__btn,
    .actionsheet__btn, .alert__action, .sheet__side, .navbar__bell,
    .navbar__toggle, .monthnav__step, .monthnav__title, .avail__cell,
    .cal__cell, .row__hit, .dtable__act, .pubstrip__item) {
    transition:
      background-color 0.2s var(--ease-out),
      color 0.2s var(--ease-out),
      filter 0.2s var(--ease-out),
      opacity 0.2s var(--ease-out);
  }
}

/* Hover ---------------------------------------------------------------------
   Every control answers the pointer before it's clicked. Gated on a real
   pointer: on a touchscreen :hover latches after a tap and the control stays
   lit until you touch something else, which reads as a stuck selection.

   Each style deepens the surface it already has rather than borrowing a new
   one, so hover never changes what kind of button you're looking at. Filled is
   the exception — it has no wash to deepen, so it brightens instead, which
   works in both appearances without naming a second tint. */

@media (hover: hover) and (pointer: fine) {
  .btn--filled:hover {
    filter: brightness(1.12);
  }

  /* Deepening the wash raises the surface toward the label, so hover has less
     headroom than rest — 22% is the most that still clears AA. */
  .btn--tinted:hover {
    background: color-mix(in srgb, var(--tint) 22%, var(--bg-grouped));
  }

  /* --fill is only one step up from --fill-secondary (0.04 alpha), which on its
     own is too quiet to read as a response. The brightness lift carries it. */
  .btn--gray:hover {
    background: var(--fill);
    filter: brightness(1.18);
  }

  .btn--plain:hover {
    background: var(--fill-quaternary);
  }

  .btn--destructive:hover {
    background: color-mix(in srgb, var(--red) 22%, var(--bg-grouped));
  }

  .btn[disabled]:hover,
  .btn[aria-disabled="true"]:hover {
    filter: none;
  }
}

:where(a, button, input):focus-visible {
  outline: 2px solid var(--tint);
  outline-offset: 2px;
}

/* Segmented control ------------------------------------------------------- */

.segmented {
  display: flex;
  margin-inline: var(--margin);
  padding: 2px;
  background: var(--fill-tertiary);
  border-radius: 0.5625rem;
  border: 0;
}

.segmented legend {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.segmented__item {
  flex: 1;
  position: relative;
  display: flex;
}

.segmented__input {
  position: absolute;
  opacity: 0;
  inset: 0;
  margin: 0;
  cursor: pointer;
}

.segmented__label {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2rem;
  padding-inline: 0.5rem;
  border-radius: 0.4375rem;
  font-size: var(--text-subhead);
  font-weight: 500;
  color: var(--label);
  cursor: pointer;
}

.segmented__input:checked + .segmented__label {
  background: var(--segment-selected);
  font-weight: 600;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
}

@media (hover: hover) and (pointer: fine) {
  /* Only the unselected segments respond — the selected one is already the
     lit state, and washing it further just makes the track look broken. */
  .segmented__input:not(:checked) + .segmented__label:hover {
    background: var(--fill-quaternary);
  }
}

.segmented__input:focus-visible + .segmented__label {
  outline: 2px solid var(--tint);
  outline-offset: 1px;
}

/* Switch — Apple's control is 51x31 ---------------------------------------- */

.switch {
  flex: none;
  appearance: none;
  margin: 0;
  width: 51px;
  height: 31px;
  border-radius: var(--radius-capsule);
  background: var(--fill);
  position: relative;
  cursor: pointer;
  transition: background-color 0.2s var(--ease-out);
}

.switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 27px;
  height: 27px;
  border-radius: 50%;
  background: var(--switch-knob);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  transition: transform 0.2s var(--ease-out);
}

/* Vivid green is deliberate here: this is a fill, not text, so the contrast
   floor that rules it out for copy doesn't apply. */
.switch:checked {
  background: var(--green-vivid);
}

.switch:checked::after {
  transform: translateX(20px);
}

/* Text field -------------------------------------------------------------- */

.field {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--label);
  padding: 0;
}

.field::placeholder {
  color: var(--label-tertiary);
}

/* Deliberately no `outline: none` here. `.field:focus` would out-specify the
   zero-specificity :where() focus rule above and strip the ring entirely. */

/* Alert ------------------------------------------------------------------- */

.alert {
  margin: var(--margin);
  background: var(--bg-grouped-secondary);
  border-radius: var(--radius-alert);
  overflow: hidden;
  text-align: center;
  max-width: 17rem;
  margin-inline: auto;
}

.alert__body {
  padding: 1.25rem var(--margin);
}

.alert__title {
  margin: 0;
  font-size: var(--text-headline);
  font-weight: 600;
}

.alert__message {
  margin: 0.25rem 0 0;
  font-size: var(--text-footnote);
  line-height: 1.385;
  color: var(--label-secondary);
}

.alert__actions {
  display: flex;
  border-top: 1px solid var(--separator);
}

.alert__action {
  flex: 1;
  min-height: var(--tap);
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--text-body);
  color: var(--tint);
  cursor: pointer;
}

.alert__action + .alert__action {
  border-left: 1px solid var(--separator);
  font-weight: 600;
}

.alert__action--destructive {
  color: var(--red);
}

.alert__action:active {
  background: var(--fill-quaternary);
}

@media (hover: hover) and (pointer: fine) {
  .alert__action:hover {
    background: var(--fill-quaternary);
  }
}

/* Regular-width title alignment ------------------------------------------
   Must come after the .navbar rules above: `.navbar__title { margin: 0 }` has
   equal specificity, so source order decides. The title sits on the same
   column as the cards, with the bar carrying the inline padding. */

@media (min-width: 48rem) {
  .navbar {
    padding-inline: 0;
  }

  .navbar__bar,
  .navbar__title {
    max-width: 38rem;
    margin-inline: auto;
    padding-inline: var(--margin);
  }
}
