/* polish.css — the interaction layer.
 *
 * Loaded last, on purpose. Everything here is about how the app *feels* under
 * a finger or a keyboard rather than how it is laid out, and it is collected
 * in one file so the rules are auditable instead of scattered.
 *
 * Nothing in here changes layout. If a rule would move something, it belongs
 * in the stylesheet that owns that component.
 */

/* ---------------------------------------------------------------- typography
 * iOS inflates text in landscape unless told not to, which silently breaks
 * every carefully sized row. */
html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

body {
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

::selection {
    background: rgba(242, 115, 77, 0.28);
}

/* ------------------------------------------------------------------- focus
 * There are thirteen `outline: none` rules in this codebase and, before this
 * file, no :focus-visible anywhere — so a keyboard user had no idea where
 * they were. These put the ring back, but only for keyboard focus, so it
 * never flashes on tap. */

/* !important, deliberately and only here. The thirteen `outline: none` rules
   this is correcting live on selectors like `.form-group input:focus`, which
   outscores anything reasonable — and :where() has no specificity at all, so
   the first version of this rule lost silently and the ring never appeared.
   It is scoped to :focus-visible, so it can only ever affect keyboard focus. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 2.5px solid var(--accent, #f2734d) !important;
    outline-offset: 2px !important;
    border-radius: 6px;
}

/* Inputs already carry a border, so the ring sits tighter against it. */
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
    outline-offset: 1px !important;
}

/* A mouse click should not leave a ring behind. */
:where(a, button, [tabindex]):focus:not(:focus-visible) {
    outline: none;
}

/* --------------------------------------------------------------- the finger
 * Mobile Safari paints a grey box over anything tappable and waits 300ms to
 * see whether you meant to double-tap. Both make a web app feel like a web
 * page. */
:where(a, button, [role="button"], [data-tab], label, summary) {
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
}

/* Real feedback in place of the highlight that was just removed. 145 buttons
   in this app and seven of them had an :active state before this.
   Written at full specificity rather than inside :where(), since component
   rules would otherwise win and nothing would press. */
button:not(:disabled):active,
[role="button"]:not(:disabled):active {
    transform: scale(0.97);
}

/* Rows and cards press rather than shrink — scaling a full-width row looks
   like it is being squeezed. */
.chx-result:active,
.chx-qchip:active,
.cmdk-list [data-i]:active,
.picker-item:active,
.app-card:active {
    filter: brightness(0.96);
}

/* Only the transform is transitioned, and by name: several components already
   transition background or border, and a blanket `transition` here would
   replace theirs. */
button,
[role="button"] {
    transition-property: transform, filter, background, background-color, border-color, color, box-shadow;
    transition-duration: 0.1s;
    transition-timing-function: ease-out;
}

/* -------------------------------------------------------------- disabled
 * A disabled control that looks enabled is a bug report waiting to happen. */
:where(button, input, select, textarea):disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

:where(button):disabled:active {
    transform: none;
}

/* ------------------------------------------------------ selecting text
 * Long-pressing a button or a tab label should not raise a text selection and
 * the copy/paste bubble. Content stays selectable — the exception matters more
 * than the rule, since journals, recipes and stories are all things worth
 * copying out. */
:where(button, .chx-tab, .kge-tab, .il-tab, nav, header, .app-card-icon,
       .chx-stat-lbl, .chx-nl-groups-label, .chx-avatar-when, kbd) {
    -webkit-user-select: none;
    user-select: none;
}

:where(p, li, td, h1, h2, h3, h4, input, textarea, .chx-nl, .kge-line,
       .ckb-step, .ckb-ingredient, .dg-speech, .tr-card) {
    -webkit-user-select: text;
    user-select: text;
}

/* Canvases are games, not documents. Dragging in one should move the player,
   not start a selection or offer to save the image. */
:where(canvas) {
    -webkit-user-select: none;
    user-select: none;
    -webkit-user-drag: none;
    -webkit-touch-callout: none;
}

:where(.tk-canvas, .hf-canvas, .dg-canvas, .il-canvas) {
    touch-action: none;
}

/* ------------------------------------------------------------- scrolling */
:where(.chx-sheet-card, .modal-sheet, .cmdk-list, .chx-sheet-results,
       .tr-log, .ckb-list) {
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
}

/* A visible but unobtrusive scrollbar on desktop; iOS overlays its own. */
@media (hover: hover) and (pointer: fine) {
    :where(.chx-sheet-card, .modal-sheet, .cmdk-list)::-webkit-scrollbar {
        width: 10px;
    }
    :where(.chx-sheet-card, .modal-sheet, .cmdk-list)::-webkit-scrollbar-thumb {
        background: rgba(120, 92, 56, 0.28);
        border-radius: 99px;
        border: 3px solid transparent;
        background-clip: content-box;
    }
    :where(.chx-sheet-card, .modal-sheet, .cmdk-list)::-webkit-scrollbar-track {
        background: transparent;
    }
}

/* ------------------------------------------------------------ busy state
 * Set by UI.busy while an action is in flight, so a slow save cannot be
 * fired twice by an impatient second tap. */
[data-busy="true"] {
    position: relative;
    color: transparent !important;
    pointer-events: none;
}

[data-busy="true"]::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 15px;
    height: 15px;
    margin: -7.5px 0 0 -7.5px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    color: #fff;
    animation: polish-spin 0.6s linear infinite;
}

@keyframes polish-spin { to { transform: rotate(360deg); } }

/* --------------------------------------------------------- reduced motion
 * Honoured app-wide rather than per component. Two files did this for their
 * own animations; everything else animated regardless of the setting. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
}

/* ---------------------------------------------------------------- type
 * A baseline scale on the bare elements. Deliberately written as element
 * selectors, so anything a component sizes on purpose (a stat number, a
 * pixel-font label) still wins — this sets the default rather than
 * overruling decisions somebody made for a reason.
 *
 * Line height is part of each step. It is most of the difference between
 * text sitting on a rhythm and text drifting, and it was previously left to
 * the browser almost everywhere. */
body {
    font-size: var(--t-body);
    line-height: var(--lh-body);
}

h1 { font-size: var(--t-display); line-height: var(--lh-display); letter-spacing: -0.02em; }
h2 { font-size: var(--t-title);   line-height: var(--lh-title);   letter-spacing: -0.01em; }
h3 { font-size: var(--t-lead);    line-height: var(--lh-lead); }
h4 { font-size: var(--t-body);    line-height: var(--lh-body); }

h1, h2, h3, h4 { text-wrap: balance; }

small { font-size: var(--t-caption); line-height: var(--lh-caption); }

/* Long prose reads better a touch narrower than a card is wide; this stops a
   paragraph running the full width of a tablet. */
p { max-width: 68ch; }

/* Tabular figures everywhere a number is compared to another number, so
   columns of calories and grams line up instead of jittering. */
.chx-stat-num,
.chx-nl,
.ckb-macros,
.ckb-ing-kcal,
.tr-meters,
.il-hp {
    font-variant-numeric: tabular-nums;
}
