/* MODULE: css/panel.css — the Panel (PHASE4 §2).
   Owned by the panel agent, consumed only by src/panel.js.

   Depends on css/tokens.css for every colour, radius, duration, step and safe-area
   inset; on the .btn / .field / .field-group / .rule / .u-* primitives in
   css/base.css; and on .m-grip + .m-ceramic in css/materials.css. Loads nothing:
   no @import, no fonts, no network.

   ── TWO FORMS, ONE COMPONENT ──────────────────────────────────────────
   Mobile-first, so the default in this file IS the bottom sheet:

     < 48rem   a bottom sheet with three snap points. Its extent is capped so it can
               never reach the transport bar, and it is dragged by the knurled grip.
     ≥ 48rem   a drawer along the inline end, ~380px wide, over a dimmed but still
               visible instrument. The instrument does not move: the whole panel is
               `position: fixed`, so nothing behind it is ever reflowed.

   Modality is a separate axis and lives in src/panel.js: modal (focus trapped, body
   scroll locked) below 64rem, non-modal above it.

   ── WHY THERE IS A CLIP BOX ───────────────────────────────────────────
   "It must never cover the transport" is a hard constraint, and it has to hold
   DURING the animation too, not just at rest. So the sheet slides inside
   .tt-panel__clip, whose bottom edge is exactly the top of the reserved transport
   band: the closing sheet is cropped at that line rather than travelling over the
   controls. The clip is --tt-panel-lip taller than the sheet so the sheet's own top
   shadow still has somewhere to paint.

   ── THE ONE NUMBER JAVASCRIPT SUPPLIES ────────────────────────────────
   --tt-panel-reserve, in px: the band the transport occupies, measured from the live
   element. Everything else — the gap above it, the top breathing room, the resulting
   maximum extent — is arithmetic done here, in tokens, so there is exactly one
   hardcoded measurement in the whole component and it is one that cannot be
   expressed in CSS.

   ── MOTION ────────────────────────────────────────────────────────────
   transform and opacity only. Never width, height, top, left or inset — a bottom
   sheet that animates its height is a bottom sheet that drops frames. The .is-instant
   state (a drag in progress, or a geometry change that must land immediately) turns
   the transition off outright. */

/* ============================================================== *
 * 1. OVERLAY ROOT
 * ============================================================== */

.tt-panel {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay); /* below --z-modal: Settings still opens on top */
  overflow: hidden; /* crops the closed drawer parked off the inline edge */
  /* Without this, a touch-drag on the scrim finds a non-scrollable fixed overflow
     container and CHAINS to the document, so the app rubber-bands underneath. */
  overscroll-behavior: none;

  /* pointer-events is inherited: none here means the closing animation never eats a
     click on the instrument underneath, and `auto` on .is-open hands it back to the
     scrim and the sheet together. */
  pointer-events: none;

  /* ---- geometry -------------------------------------------------- */

  /* src/panel.js writes this one value. Everything below derives from it. */
  --tt-panel-reserve: 0px;

  /* Breathing room between the sheet and the transport it must never reach. Only
     paid when there is something to keep off: src/panel.js stamps data-reserved when
     it has actually measured a transport, and a sheet in CLOCK mode — where there is
     no transport at all — sits flush to the bottom of the screen where it belongs. */
  --tt-panel-gap: 0px;
  /* And above the sheet, so the instrument is never fully swallowed even at full
     extent — plus the notch on an iPhone. */
  --tt-panel-top: max(var(--space-6), calc(var(--safe-top) + var(--space-4)));
  /* Room inside the clip box for the sheet's own top shadow. */
  --tt-panel-lip: var(--space-5);

  --tt-panel-bottom: calc(var(--tt-panel-reserve) + var(--tt-panel-gap));

  /* 100dvh is essential — 100vh is wrong on iOS Safari and would let the sheet run
     under the browser chrome. The vh line is the fallback for engines without dvh;
     the dvh line simply wins where it parses. */
  --tt-panel-max: max(
    0px,
    calc(100vh - var(--tt-panel-bottom) - var(--tt-panel-top))
  );
  --tt-panel-max: max(
    0px,
    calc(100dvh - var(--tt-panel-bottom) - var(--tt-panel-top))
  );

  /* The drawer, ~380px = 23.75rem (PHASE4 §2), never wider than the viewport. */
  --tt-panel-w: min(23.75rem, calc(100% - var(--space-7)));

  /* A scrim is a cast shadow, so it must darken in BOTH themes. --ramp-900 is dark on
     ceramic; on graphite the same role is played by --ramp-300. The two theme
     selectors below mirror the cascade in css/tokens.css exactly.

     Deliberately light: PHASE4 asks for an instrument that is "dimmed but STILL
     VISIBLE". This is not a modal blackout, it is a drawer opening beside an object
     you can still read. */
  --tt-panel-scrim: color-mix(in oklab, var(--ramp-900) 26%, transparent);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="ceramic"]):not([data-theme="graphite"]) .tt-panel {
    --tt-panel-scrim: color-mix(in oklab, var(--ramp-300) 58%, transparent);
  }
}

[data-theme="graphite"] .tt-panel {
  --tt-panel-scrim: color-mix(in oklab, var(--ramp-300) 58%, transparent);
}

.tt-panel[data-reserved] {
  --tt-panel-gap: var(--space-2);
}

.tt-panel.is-open {
  pointer-events: auto;
}

/* ============================================================== *
 * 2. SCRIM
 * ============================================================== */

.tt-panel__scrim {
  position: absolute;
  inset: 0;
  background-color: var(--tt-panel-scrim);
  opacity: 0;
  transition: opacity var(--dur-med) var(--ease-out);
}

.tt-panel.is-open .tt-panel__scrim {
  opacity: 1;
}

/* ============================================================== *
 * 3. THE CLIP BOX AND THE SHEET
 *    Default = the mobile bottom sheet. The drawer is §8.
 * ============================================================== */

.tt-panel__clip {
  position: absolute;
  inset-inline: 0;
  inset-block-end: var(--tt-panel-bottom);
  block-size: calc(var(--tt-panel-max) + var(--tt-panel-lip));
  overflow: hidden;
  /* The lip is empty space above the sheet: it must not intercept a press aimed at
     the instrument. The sheet hands pointer events back to itself below. */
  pointer-events: none;
}

.tt-panel__sheet {
  /* Moulded ceramic (PHASE4 §4: metal for things you touch, ceramic for surfaces you
     don't). --m-base is set on this element rather than an ancestor because that is
     where css/materials.css §3a resolves the veil — see the rule at the head of its
     §3. Every background longhand carries a flat fallback so the sheet still renders
     correctly, just untextured, without that file. */
  --m-base: var(--bg);

  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  block-size: var(--tt-panel-max);
  pointer-events: auto;

  display: flex;
  flex-direction: column;
  min-block-size: 0;

  background-color: var(--m-base);
  background-image: var(--m-tile-image, none);
  background-size: var(--m-tile-size, auto);
  background-repeat: var(--m-tile-repeat, repeat);
  background-blend-mode: var(--m-tile-blend, normal);
  color: var(--ink);

  /* A moulded case: a larger radius than any control, and only on the leading edge —
     the trailing edge runs off the screen. */
  border-start-start-radius: calc(var(--radius-l) + var(--space-1));
  border-start-end-radius: calc(var(--radius-l) + var(--space-1));

  /* Light from above: a lit 1px top lip, a hairline, then the ambient occlusion that
     separates the sheet from the instrument behind it. --m-cast is dark in BOTH
     themes (css/materials.css); the raw rgba is only the no-materials fallback, and a
     cast shadow is the one thing in this app that legitimately cannot be a themed
     colour — css/tokens.css and css/layout.css record the same exception. */
  box-shadow:
    inset 0 var(--hairline-w) 0 var(--m-lift, rgba(255, 255, 255, 0.35)),
    0 calc(var(--hairline-w) * -1) 0 var(--hairline),
    0 -6px 18px var(--m-cast, rgba(0, 0, 0, 0.22));

  transform: translate3d(0, 100%, 0);
  transition: transform var(--dur-med) var(--ease-out);
}

/* The sheet carries `m-ceramic` so css/materials.css §3a declares --m-tile-image for
   it — that recipe only exists on the elements its selector list names, and this file
   may not edit that list. §3a's own instruction is to override --m-base "in a rule
   that matches the SAME element at equal-or-higher specificity", which is exactly
   what this is: one step above .m-ceramic, so the veil follows the body colour
   whatever order the two sheets end up loading in. */
.tt-panel .tt-panel__sheet {
  --m-base: var(--bg);
}

/* Promoted only while it is actually on screen — and NEVER while the sheet has
   surrendered its transform for a module drag. `will-change: transform` makes an
   element a containing block for fixed-position descendants exactly as a real
   transform does, so leaving it on would defeat the whole escape in src/panel.js and
   the dragged tile would be captured and clipped all over again. */
.tt-panel.is-open .tt-panel__sheet {
  will-change: transform;
}

.tt-panel .tt-panel__sheet.is-escaped {
  will-change: auto;
}

/* A drag, or a geometry change that must land immediately. */
.tt-panel__sheet.is-instant {
  transition: none;
}

.tt-panel__sheet:focus {
  outline: none; /* the sheet takes focus on open; the ring belongs on controls */
}

/* While arranging, the sheet's top lip carries the signal so the mode is legible
   even when the tiles are scrolled out of view. */
.tt-panel.is-editing .tt-panel__sheet {
  box-shadow:
    inset 0 calc(var(--hairline-w) * 2) 0
      color-mix(in oklab, var(--signal) 55%, transparent),
    0 calc(var(--hairline-w) * -1) 0 var(--hairline),
    0 -6px 18px var(--m-cast, rgba(0, 0, 0, 0.22));
}

/* ============================================================== *
 * 4. THE GRIP
 *    PERMITTED FLUTE 2 (PHASE4 §4). css/materials.css owns the knurling, the
 *    44px target and the press state; this only places it.
 * ============================================================== */

.tt-panel__grip {
  flex: 0 0 auto;
  touch-action: none; /* Pointer Events own this gesture outright */
}

.tt-panel__grip:focus-visible {
  outline: var(--ring-w) solid var(--signal);
  outline-offset: calc(var(--ring-offset) * -1);
  border-radius: var(--radius-m);
}

/* ============================================================== *
 * 5. HEAD: THE TABLIST AND THE TOOLS
 *
 *    A hairline rule under the head is structure, not decoration: it separates the
 *    control zone from the content zone and both sit on the 8px grid.
 * ============================================================== */

.tt-panel__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  flex: 0 0 auto;

  padding-inline: max(var(--space-4), var(--safe-left)) max(var(--space-4), var(--safe-right));
  padding-block-end: var(--space-3);
  border-block-end: var(--hairline-w) solid var(--hairline);
}

/* A recessed ceramic track with three keys, the same depth treatment the mode
   switcher uses — but NO chrome frame. PHASE4 §4 rations chrome to the dial bezel and
   the mode switch, and a tab strip is neither. */
.tt-panel__tabs {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: calc(var(--space-1) / 2);
  padding: var(--space-1);
  min-inline-size: 0;
  border-radius: var(--radius-pill);
  background-color: var(--bg-sunk);
  box-shadow: var(--shadow-well-s);
}

.tt-panel__tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: var(--tap);
  min-inline-size: 0;
  /* Uppercase tracking is added AFTER the last glyph too, so a centred tracked label
     reads as sitting half a track-step left of centre. The extra leading padding is
     that half-step, doubled — padding shifts a centred box by half its difference. */
  padding-inline: calc(var(--space-2) + var(--tr-label)) var(--space-2);
  border: 0;
  border-radius: var(--radius-pill);
  background-color: transparent;

  /* The engraved legend: 11px, 500, uppercase, and always carrying its tracking.
     Tight uppercase is unreadable and un-Swiss. */
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  font-weight: var(--wt-medium);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  font-variation-settings: var(--vf-label);
  font-feature-settings: var(--feat-caps, "calt" 1);
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;

  /* --ink-3 measures 4.98:1 on --bg-sunk, which is where an unengaged key sits. */
  color: var(--ink-3);
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition:
    background-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-med) var(--ease-out),
    transform var(--dur-med) var(--ease-out);
}

@media (hover: hover) {
  .tt-panel__tab[aria-selected="false"]:hover {
    color: var(--ink-2);
  }
}

/* The engaged key is the thing you touched, so it is brushed aluminium standing
   proud of the ceramic track. --ink on metal measures 13.8:1 / 10.7:1 (the sampled
   figures in css/materials.css); --ink-3 is never placed on a metal face. */
.tt-panel__tab[aria-selected="true"] {
  background-color: var(--m-face, var(--bg-raised));
  background-image: var(--m-metal-image, none);
  background-size: var(--m-metal-size, auto);
  background-repeat: var(--m-metal-repeat, no-repeat);
  background-position: var(--m-metal-pos, 0 0);
  background-blend-mode: var(--m-metal-blend, normal);
  color: var(--ink);
  box-shadow: var(--shadow-raise);
}

.tt-panel__tab:active {
  transform: translateY(1px);
  transition-duration: var(--dur-press);
}

.tt-panel__tab[aria-selected="true"]:active {
  background-color: var(--m-face-press, var(--surface-2));
  background-image: var(--m-metal-image-press, none);
  background-position: var(--m-metal-pos-press, 0 0);
}

.tt-panel__tab:focus-visible {
  outline-offset: -2px;
}

.tt-panel__tools {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  justify-self: end;
}

.tt-panel__tools .icon,
.tt-panel__tools svg {
  inline-size: calc(var(--space-4) + var(--space-1)); /* 20px */
  block-size: calc(var(--space-4) + var(--space-1));
}

/* ============================================================== *
 * 6. THE PANES
 *    One pane per tab, only one rendered at a time. Each is its own scroll
 *    container, so the sheet's chrome never scrolls away from under the user.
 * ============================================================== */

.tt-panel__panes {
  flex: 1 1 auto;
  min-block-size: 0;
  display: flex;
  flex-direction: column;
}

.tt-panel__pane {
  flex: 1 1 auto;
  min-block-size: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;

  padding-inline: max(var(--space-4), var(--safe-left)) max(var(--space-4), var(--safe-right));
  padding-block-start: var(--space-4);
  /* The home indicator only needs clearing when nothing is reserved beneath the sheet
     — when the transport IS down there, it has already paid that inset. */
  padding-block-end: max(
    var(--space-4),
    calc(var(--safe-bottom) - var(--tt-panel-reserve))
  );
}

/* A scroll container clips, so it cannot show an offset ring. */
.tt-panel__pane:focus-visible {
  outline: var(--ring-w) solid var(--signal);
  outline-offset: calc(var(--ring-offset) * -1);
}

.tt-panel__empty {
  color: var(--ink-3);
  font-size: var(--step--1);
  line-height: var(--lh-snug);
}

/* src/layout.js mounts into this. It gets no styling of its own: the engine's own
   sheet (css/layout.css) owns the grid, and the Panel forces every module to span
   the full width in src/panel.js — by overriding minW on its copy of the module def,
   not by reaching into another agent's CSS — so the grid resolves to a single column
   and the drag becomes the vertical reordering PHASE4 §2 asks for.

   CONTRACT-GAP (layout agent): the engine's edge auto-scroll during a drag calls
   window.scrollBy(), which does nothing when the tiles live in a scrolling pane
   rather than in the page. Dragging works; dragging past the bottom of the pane does
   not scroll it. `.tt-panel__pane` is the scroller if you ever want to teach the
   engine about a scroll parent. */
.tt-panel__host {
  min-inline-size: 0;
}

/* ============================================================== *
 * 7. MODULE: THE WAKING-DAY WINDOW
 *    The Panel's own module, in the DAY tab beside the ribbon.
 * ============================================================== */

.tt-dw {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-inline-size: 0;
}

.tt-dw__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

.tt-dw__title {
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  font-weight: var(--wt-medium);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  font-variation-settings: var(--vf-label);
  font-feature-settings: var(--feat-caps, "calt" 1);
  text-transform: uppercase;
  color: var(--ink-3);
}

/* The one figure allowed in a title row. Tabular, because it changes. */
.tt-dw__span {
  flex: 0 0 auto;
  font-size: var(--step-0);
  color: var(--ink);
}

.tt-dw__times {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  margin-block-start: var(--space-1);
}

.tt-dw__time {
  font-family: var(--font-num);
  font-variant-numeric: var(--font-num-variant);
  font-feature-settings: var(--font-num-feat);
}

.tt-dw__error {
  color: var(--signal-text);
}

/* ============================================================== *
 * 8. THE DRAWER  (≥ 48rem)
 *    Same component, different form: it slides in from the inline end, full
 *    height, over an instrument that does not move.
 * ============================================================== */

@media (min-width: 48rem) {
  .tt-panel__clip {
    inset-block: 0;
    inset-inline-start: auto;
    inset-inline-end: 0;
    inline-size: var(--tt-panel-w);
    block-size: auto;
    overflow: visible; /* nothing to crop: the transport is not below this */
  }

  .tt-panel__sheet {
    inset: 0;
    block-size: auto;
    border-radius: 0;
    border-start-start-radius: calc(var(--radius-l) + var(--space-1));
    border-end-start-radius: calc(var(--radius-l) + var(--space-1));
    border-inline-start: var(--hairline-w) solid var(--hairline);

    /* Light still comes from above and slightly left, so a drawer standing off the
       right-hand edge casts to its inline start. */
    box-shadow:
      inset var(--hairline-w) 0 0 var(--m-lift, rgba(255, 255, 255, 0.28)),
      -8px 0 24px var(--m-cast, rgba(0, 0, 0, 0.22));

    padding-block-start: var(--safe-top);
    transform: translate3d(100%, 0, 0);
    transition: transform var(--dur-med) var(--ease-spring);
  }

  [dir="rtl"] .tt-panel__sheet {
    transform: translate3d(-100%, 0, 0);
  }

  .tt-panel.is-editing .tt-panel__sheet {
    box-shadow:
      inset calc(var(--hairline-w) * 2) 0 0
        color-mix(in oklab, var(--signal) 55%, transparent),
      -8px 0 24px var(--m-cast, rgba(0, 0, 0, 0.22));
  }

  /* No sheet, no grip. */
  .tt-panel__grip {
    display: none;
  }

  .tt-panel__head {
    padding-block-start: var(--space-4);
  }

  .tt-panel__pane {
    padding-block-end: max(var(--space-6), calc(var(--safe-bottom) + var(--space-4)));
  }
}

/* ============================================================== *
 * 9. THE FALLBACK TOGGLE
 *    Rendered by src/panel.js only when the shell has no panel control of its
 *    own. It sits under the overlay on purpose: while the panel is open the
 *    scrim is in front of it, and a press there dismisses the panel anyway.
 * ============================================================== */

.tt-panel__toggle {
  position: fixed;
  inset-block-start: calc(var(--safe-top) + var(--space-3));
  inset-inline-end: calc(var(--safe-right) + var(--space-3));
  z-index: var(--z-sticky);
}

/* Focus mode is chrome away: the panel key goes with the rest of it. */
html[data-focus="true"] .tt-panel__toggle {
  display: none;
}

/* ============================================================== *
 * 10. PREFERENCE OVERRIDES
 * ============================================================== */

/* Reduced motion: no slide, no fade — the panel is simply there or not there.
   css/base.css already collapses every duration; this states the intent outright so
   it survives a future change to that rule. Note that src/panel.js still measures,
   still snaps and still announces: reduced MOTION is not reduced feedback. */
@media (prefers-reduced-motion: reduce) {
  .tt-panel__sheet,
  .tt-panel__scrim,
  .tt-panel__tab {
    transition: none;
  }

  .tt-panel__sheet {
    will-change: auto;
  }
}

@media (prefers-contrast: more) {
  .tt-panel__sheet {
    border-block-start: var(--hairline-w) solid var(--hairline-strong);
  }

  .tt-panel__tab[aria-selected="true"] {
    box-shadow:
      var(--shadow-raise),
      inset 0 0 0 var(--hairline-w) var(--hairline-strong);
  }
}

@media (forced-colors: active) {
  .tt-panel__sheet {
    background-image: none;
    box-shadow: none;
    border: 1px solid ButtonBorder;
  }

  .tt-panel__scrim {
    background-color: Canvas;
    opacity: 0.85;
  }

  .tt-panel__tabs {
    box-shadow: none;
    border: 1px solid ButtonBorder;
  }

  .tt-panel__tab {
    background-image: none;
  }

  .tt-panel__tab[aria-selected="true"] {
    background-color: Highlight;
    color: HighlightText;
    box-shadow: none;
  }
}
