/* ═══════════════════════════════════════════════════════════════════════════
   FEEL.CSS - how the interface answers a finger.

   Before this file, Aluma had no touch layer at all: not one
   -webkit-tap-highlight-color and not one touch-action in ~35,000 lines of
   CSS. On an iPhone that means every tap painted the default grey flash box
   over the control, and double-tap-to-zoom armed a 300ms wait before any tap
   registered. The app was a third of a second behind the finger, and flashed
   grey while it waited.

   It also had nine different :active transforms - scale 0.9, 0.93, 0.94, 0.96,
   0.97, translateY 0, 0.5px, 1px, -1px - so no two controls pressed alike.

   Three things make a control feel native:

     1. It responds the instant the finger lands, not on release.
     2. It moves a little, and the same amount everywhere.
     3. It comes back slower than it went down.

   That last one is the whole trick. Going down is mechanical and immediate;
   coming back is elastic. Equal timings in both directions read as a web page,
   which is why --press-in is a quarter of --press-out.

   Loads after the design system and before rounded.css, so shape stays the
   final word on shape. Motion only - this file sets no colours and no layout.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Get out of the way of the finger ─────────────────────────────────────
   The grey flash is a platform default, not a design decision anyone made, and
   it lands ON TOP of a carefully rounded control as a hard rectangle. */
*,
*::before,
*::after { -webkit-tap-highlight-color: transparent; }

/* manipulation keeps scroll and pinch, drops double-tap zoom - and dropping
   double-tap zoom is what removes the 300ms wait before a tap is delivered. */
button,
a,
label,
summary,
select,
input,
textarea,
[role="button"],
[role="tab"],
[role="option"],
[role="menuitem"],
[role="switch"],
[data-view],
[data-action],
.nav-item,
.chip,
.seg-btn,
.toggle-btn,
.settings-tab,
.suggestion-chip { touch-action: manipulation; }

/* A label that drags out of a button reads as the control coming apart.
   Text the student needs to copy - a message, an assignment body - is exempt
   further down. */
button,
.nav-item,
.seg-btn,
.chip,
.settings-tab,
.toggle-btn,
[role="button"],
[role="tab"] {
  -webkit-user-select: none;
  user-select: none;
}

/* ── 2. One press, everywhere ────────────────────────────────────────────────
   Down is 90ms and linear-ish: the control is already there when the finger
   arrives. Up is 380ms on a spring that overshoots slightly, so it settles
   rather than snaps.

   This sheet defines NO tokens. --press-scale, --press-scale-sm, --press-in,
   --press-out, --ease-press and --ease-settle are declared in redesign.css,
   which owns the design system; a component sheet that minted its own would
   fork it. Motion is spent here, never minted. */
/* Specificity, stated plainly rather than escalated by accident.

   These controls already carry transitions from four other sheets, several
   with !important and higher specificity than a plain class: `.sidebar
   .nav-item`, `body[data-view="settings"] .settings-tab`, `.send-btn,
   #send-btn, #project-send-btn`, and .primary-btn set four separate times -
   once as `all`. A plain `.nav-item` rule here loses every one of those, and
   when it loses, `transform` is absent from the winning transition list, so
   the control snaps down and snaps straight back with no return animation at
   all. That is precisely the web-page feel this file exists to remove.

   :is() takes the specificity of its most specific argument, so repeating the
   list three times reads as (0,0,3,0) - enough to beat a two-class or
   class+attribute selector without an !important arms race, and paired with
   !important it settles the rest. The one thing it does not beat is an ID with
   !important (#send-btn), which is correct: that rule already animates
   transform, so the press works there anyway.

   The property list is a superset of what those rules animated, so nothing
   that used to fade now jumps - only the timing is unified. */
:is(button, .primary-btn, .ghost-btn, .danger-btn, .icon-btn, .send-btn,
      .oauth-btn, .nav-item, .seg-btn, .chip, .suggestion-chip,
      .settings-tab, .tutor-mode-btn, .account-card, .toggle-btn,
      .dash-tutor-send, .hub-next-action, .dash-primary-action,
      [role="button"], [role="tab"], [role="option"]):is(button, .primary-btn, .ghost-btn, .danger-btn, .icon-btn, .send-btn,
      .oauth-btn, .nav-item, .seg-btn, .chip, .suggestion-chip,
      .settings-tab, .tutor-mode-btn, .account-card, .toggle-btn,
      .dash-tutor-send, .hub-next-action, .dash-primary-action,
      [role="button"], [role="tab"], [role="option"]):is(button, .primary-btn, .ghost-btn, .danger-btn, .icon-btn, .send-btn,
      .oauth-btn, .nav-item, .seg-btn, .chip, .suggestion-chip,
      .settings-tab, .tutor-mode-btn, .account-card, .toggle-btn,
      .dash-tutor-send, .hub-next-action, .dash-primary-action,
      [role="button"], [role="tab"], [role="option"]) {
  transition: transform var(--press-out) var(--ease-settle),
              background-color 200ms var(--ease-press),
              background 200ms var(--ease-press),
              border-color 200ms var(--ease-press),
              color 200ms var(--ease-press),
              box-shadow 200ms var(--ease-press),
              opacity 200ms var(--ease-press) !important;
  transform-origin: center;
  /* No `will-change` here. This selector matches every button, nav item,
     chip and [role="button"] in the app, and `will-change: transform` on
     each one asks the compositor for a layer per control whether it is
     about to move or not - on a phone that is real memory for a press that
     lasts 90ms and promotes itself anyway. */
}

/* The press. Explicit list rather than a blanket rule, so a control that wants
   its own press - a drag handle, a canvas - keeps it by being left out. */
:is(button, .primary-btn, .ghost-btn, .danger-btn, .icon-btn, .send-btn,
      .oauth-btn, .nav-item, .seg-btn, .chip, .suggestion-chip,
      .settings-tab, .tutor-mode-btn, .account-card, .toggle-btn,
      .dash-tutor-send, .hub-next-action, .dash-primary-action,
      [role="button"], [role="tab"], [role="option"]):is(button, .primary-btn, .ghost-btn, .danger-btn, .icon-btn, .send-btn,
      .oauth-btn, .nav-item, .seg-btn, .chip, .suggestion-chip,
      .settings-tab, .tutor-mode-btn, .account-card, .toggle-btn,
      .dash-tutor-send, .hub-next-action, .dash-primary-action,
      [role="button"], [role="tab"], [role="option"]):is(button, .primary-btn, .ghost-btn, .danger-btn, .icon-btn, .send-btn,
      .oauth-btn, .nav-item, .seg-btn, .chip, .suggestion-chip,
      .settings-tab, .tutor-mode-btn, .account-card, .toggle-btn,
      .dash-tutor-send, .hub-next-action, .dash-primary-action,
      [role="button"], [role="tab"], [role="option"]):active:not(:disabled) {
  transform: scale(var(--press-scale)) !important;
  transition-duration: var(--press-in) !important;
  transition-timing-function: var(--ease-press) !important;
}

/* Small round controls need a deeper press to read at all at 36-44px. */
:is(.icon-btn, .send-btn, .rail-toggle, .notif-bell-btn, .td-close,
    .close-btn, .cal-arrow, .dash-tutor-send):is(.icon-btn, .send-btn,
    .rail-toggle, .notif-bell-btn, .td-close, .close-btn, .cal-arrow,
    .dash-tutor-send):active:not(:disabled) {
  transform: scale(var(--press-scale-sm)) !important;
  transition-duration: var(--press-in) !important;
  transition-timing-function: var(--ease-press) !important;
}

/* The old rules lifted some buttons 1px on hover and dropped them on press.
   A control that moves on hover then moves again on press has two states to
   read; the press is the one that matters, so hover no longer moves anything. */
.primary-btn:hover,
.ghost-btn:hover,
.danger-btn:hover,
.hub-next-action:hover,
.dash-primary-action:hover { transform: none; }

/* ── 3. Hover belongs to pointers ────────────────────────────────────────────
   On a touch screen a :hover style latches after a tap and stays until the
   next one, so a button a student has finished with goes on looking armed.
   Anything that paints on hover gets scoped to devices that can actually
   hover. */
@media (hover: none) {
  button:hover,
  .nav-item:hover,
  .chip:hover,
  .seg-btn:hover,
  .settings-tab:hover,
  .smore-item:hover,
  .account-card:hover,
  .toggle-btn:hover,
  [role="button"]:hover { background-color: inherit; }
}

/* ── 4. Scrolling ────────────────────────────────────────────────────────────
   Momentum inside panes, and no rubber-banding the whole page when a list
   inside it reaches its end. */
.sidebar,
.settings-pane,
.dash-shell,
.chat,
.tasks-body,
.hist-list,
.mem-feed,
.disc-scroll,
.sidebar-scroll,
.messages {
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

/* ── 5. Text the student needs ───────────────────────────────────────────────
   Everything above turned selection off for controls. Content is not a
   control: an answer, an assignment body or a code sample must stay
   selectable and copyable. */
.msg,
.msg *,
.td-source,
.artifact-panel-body,
.set-readonly,
pre, code, p, li, td, th,
input, textarea {
  -webkit-user-select: text;
  user-select: text;
}

/* ── 6. Reduced motion ───────────────────────────────────────────────────────
   Someone who asked the OS for less motion gets the state change without the
   travel. The control still confirms the press - it just does not move. */
@media (prefers-reduced-motion: reduce) {
  /* The press is spent from tokens - --press-scale, --press-in, --press-out -
     and redesign.css, which mints them, zeroes them here. That is the whole
     fix, and it is why this block is three lines rather than the two
     specificity-matched selector chains it used to need.

     It used to need them because the answers lost. `transition: none` at
     (0,1,0) lost to the transition shorthand above, which repeats its :is()
     chain three times and carries !important; `button:active` at (0,1,1) lost
     to the press rule at (0,5,0), and !important on both sides means the more
     specific selector wins, so shouting did not help either. Both rules read
     as coverage and were dead: a student who asked the OS for less motion got
     the full 380ms settle and the full 0.97 scale.

     Overriding the tokens instead needs no specificity at all, and it reaches
     soft.css's three copies of the same transition too, which no amount of
     selector-matching in this file ever would have.

     What is left here is the part that is not a token: the press still has to
     confirm itself, so it dims instead of moving. */
  button:active,
  .nav-item:active,
  .seg-btn:active,
  .chip:active,
  .icon-btn:active,
  .send-btn:active,
  [role="button"]:active,
  [role="tab"]:active { opacity: 0.72; }
}

/* ── 7. The haptic switch ────────────────────────────────────────────────────
   haptics.js parks a real <input type="checkbox" switch> here. It has to stay
   rendered to fire the Taptic Engine, so it cannot be display:none or
   visibility:hidden - it is moved off-screen at zero opacity instead, out of
   the tab order and out of the accessibility tree. */
.haptic-host {
  position: fixed;
  top: -9999px;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
  z-index: -1;
}


