/* MODULE: css/type.css — the Swiss typographic system, applied.
   PHASE4 §3. Depends on css/tokens.css (the @font-face, the scale tokens,
   the weight vocabulary) and expects css/base.css to have run first.
   Loads nothing: no @import, no font fetch, no network.

   tokens.css declares the type; this file is what makes the app obey it.
   Three concerns, in order:

     1. element defaults — what unclassed markup looks like
     2. .t-display / .t-title / .t-body / .t-label / .t-micro — the scale
     3. .rule — the hairline as structure
     4. weight discipline — the three-weight rule, enforced

   LOAD ORDER MATTERS. This sheet must come after css/base.css, and it is
   safest last of all, because several modes inject their own <style> at
   runtime and a runtime <style> always sits after a <link>. Section 5 is
   written with a :root prefix so it wins on specificity rather than on
   source order, which makes it correct in either position.

   // CONTRACT-GAP: index.html (shell agent) must add, after base.css:
   //     <link rel="stylesheet" href="css/type.css">
   //   and ideally, in <head> before the stylesheets:
   //     <link rel="preload" as="font" type="font/woff2"
   //           href="assets/fonts/inter-var.woff2" crossorigin>
   //   The font is same-origin, but a preload without crossorigin is
   //   fetched twice — fonts are always CORS-mode requests.
   // CONTRACT-GAP: sw.js (shell agent) must precache BOTH
   //   'assets/fonts/inter-var.woff2' and 'css/type.css'. Without the font
   //   in the cache the app falls back to the system stack offline, which
   //   is the one thing PHASE4 §5 says must not happen.

   THE FOUR RULES THIS FILE ENFORCES, so nobody has to remember them:
     · Three weights: 300 / 400 / 500. Plus 600 on the START key alone.
     · No italics. `em` and `i` are set upright and emphasised by weight.
     · Every changing number is tabular. Verified at the font binary:
       tnum resolves all ten digits to one 1328/2048em advance.
     · Flush left, ragged right. The numerals under the dial are the only
       centred element in the app. Justified text does not exist here.
*/

/* ============================================================== *
 * 1. ELEMENT DEFAULTS
 *    What the app looks like before anyone reaches for a class.
 * ============================================================== */

body {
  /* A variable font has real weights at every stop, so synthesis is never
     needed — and switching it off is also how "no italics" is enforced at
     the rendering level: nothing can fake an oblique. */
  font-synthesis: none;
  font-synthesis-weight: none;
  font-synthesis-style: none;
  font-synthesis-small-caps: none;
  /* Opt in to the optical-size axis for everything. The .t-* classes below
     pin exact opsz values where the scale calls for them; everywhere else
     the browser tracks the axis to the rendered size, which is the correct
     behaviour and costs nothing.
     Deliberately NOT setting font-variation-settings here: it inherits, and
     an inherited opsz would override the axis for every descendant. */
  font-optical-sizing: auto;
}

/* Headings are the title role. Sizes come from the scale, never from the
   browser's h1..h6 defaults, and never from a class-free guess. */
h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-sans);
  font-size: var(--fs-title);
  font-weight: var(--wt-medium);
  line-height: var(--lh-title);
  letter-spacing: var(--tr-title);
  font-variation-settings: var(--vf-title);
  font-feature-settings: var(--feat-text);
  /* Short lines: even the rag rather than a one-word last line. */
  text-wrap: balance;
}

p,
li,
dd,
dt,
blockquote,
figcaption {
  /* Ragged right is the default and stays the default. `pretty` only
     improves the shape of the rag; it never justifies. */
  text-wrap: pretty;
  hyphens: manual;
}

/* Emphasis is weight, not slope. There are no italics in this app. */
strong,
b {
  font-weight: var(--wt-medium);
}

em,
i,
cite,
dfn,
var,
address {
  font-style: normal;
  font-weight: var(--wt-medium);
}

small {
  font-size: var(--fs-label);
  font-weight: var(--wt-regular);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-body);
  font-variation-settings: var(--vf-label);
  text-transform: none;
}

/* Form furniture reads as engraved legend, sentence case left to the author. */
label,
legend,
caption {
  font-weight: var(--wt-medium);
  line-height: var(--lh-body);
}

caption,
th {
  text-align: start;
}

th {
  font-weight: var(--wt-medium);
}

code,
kbd,
samp,
pre {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-adjust);
  font-variant-numeric: var(--font-num-variant);
}

::placeholder {
  font-weight: var(--wt-regular);
  letter-spacing: var(--tr-body);
}

abbr[title] {
  text-decoration: underline dotted;
  text-underline-offset: 0.18em;
  cursor: help;
}

/* Superscripts and subscripts normally push a line box taller and break the
   baseline grid. `line-height: 0` takes them out of the line-box maths and
   the offsets put them back optically. */
sub,
sup {
  font-size: var(--fs-sup-adjust);
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

sup {
  inset-block-start: -0.5em;
}

sub {
  inset-block-end: -0.25em;
}

/* ============================================================== *
 * 2. THE SCALE
 *
 *    Longhands throughout, never the `font` shorthand: the shorthand
 *    resets font-feature-settings, font-variant-numeric and
 *    font-variation-settings, which would silently strip tabular figures
 *    off anything it touched. The --type-* composites in tokens.css exist
 *    for one-line use elsewhere and carry that warning with them.
 * ============================================================== */

/* --- .t-display ------------------------------------------------ *
   THE TIMER NUMERALS, AND NOTHING ELSE. This is the composition.
   300 weight at 56–136px, tracked in to -0.035em, opsz 32 so the cut
   closes up at size. Its authority is scale and restraint: no shadow,
   no gradient, no outline, no animation. Ever. Section 5 pins the same
   treatment onto the live readouts, which live in another agent's file.
   Vertical optical centring is free: the font is symmetric about cap
   height (see the note above the @font-face in tokens.css), so at
   line-height 1 the figures sit dead centre in their line box with no
   nudging. Horizontal centring on the dial's axis is text-align below,
   plus the sign compensation for the overtime state. */
.t-display {
  font-family: var(--font-num);
  font-size: var(--fs-display);
  font-weight: var(--wt-light);
  line-height: var(--lh-display);
  letter-spacing: var(--tr-display);
  font-variation-settings: var(--vf-display);
  font-variant-numeric: var(--font-num-variant);
  font-feature-settings: var(--feat-num);
  /* The one centred element in the app. */
  text-align: center;
  /* Ornament ban, made structural rather than advisory. */
  text-shadow: none;
  -webkit-text-stroke-width: 0;
  background-image: none;
  -webkit-background-clip: border-box;
  background-clip: border-box;
  animation: none;
  filter: none;
}

/* Overtime prefixes a "+". Mirror the space it takes on the other side so
   the digits hold the dial's axis instead of sliding sideways at the moment
   the timer crosses zero. See --type-display-sign for why this is an end
   margin and not a negative text-indent. */
.t-display[data-overtime="1"],
.t-display[data-sign] {
  margin-inline-end: var(--type-display-sign);
}

.t-title {
  font-family: var(--font-sans);
  font-size: var(--fs-title);
  font-weight: var(--wt-medium);
  line-height: var(--lh-title);
  letter-spacing: var(--tr-title);
  font-variation-settings: var(--vf-title);
  font-variant-numeric: var(--font-num-variant);
  font-feature-settings: var(--feat-text);
  text-align: start;
  text-wrap: balance;
}

.t-body {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: var(--wt-regular);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-body);
  font-variation-settings: var(--vf-body);
  font-variant-numeric: var(--font-num-variant);
  font-feature-settings: var(--feat-text);
  text-align: start;
  text-wrap: pretty;
}

/* --- .t-label / .t-micro --------------------------------------- *
   The engraved legends. Uppercase ALWAYS carries its tracking: tight
   uppercase is unreadable and un-Swiss, so the tracking is part of the
   class and cannot be forgotten. `case` lifts punctuation to cap height,
   `cv08` gives the capital I a serif so it cannot be read as an l. */
.t-label {
  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-variant-numeric: var(--font-num-variant);
  font-feature-settings: var(--feat-caps);
  text-transform: uppercase;
  text-align: start;
}

.t-micro {
  font-family: var(--font-sans);
  font-size: var(--fs-micro);
  font-weight: var(--wt-medium);
  line-height: var(--lh-micro);
  letter-spacing: var(--tr-micro);
  font-variation-settings: var(--vf-micro);
  font-variant-numeric: var(--font-num-variant);
  font-feature-settings: var(--feat-caps);
  text-transform: uppercase;
  text-align: start;
}

/* Letter-spacing is added AFTER the last glyph too, so a tracked label
   sitting at the end of a row reads as indented from the edge it should
   align to. These claw the phantom space back. Use on the trailing item
   of a flex row, or on anything right-aligned. */
.t-label--end {
  margin-inline-end: calc(var(--tr-label) * -1);
}

.t-micro--end {
  margin-inline-end: calc(var(--tr-micro) * -1);
}

/* A column of prose gets a measure. The Panel is narrow enough that this
   rarely bites, which is exactly when a measure should be invisible. */
.t-measure {
  max-inline-size: var(--measure);
}

/* ============================================================== *
 * 3. NUMERALS
 *    Every number that changes is tabular. A digit that changes width
 *    while a timer runs is a bug, not a detail.
 * ============================================================== */

.t-num {
  font-family: var(--font-num);
  font-variant-numeric: var(--font-num-variant);
  font-feature-settings: var(--feat-num);
  letter-spacing: var(--tr-title);
  line-height: var(--lh-body);
}

/* Slashed zero, for a number that must not be misread as a letter — an
   ID, a hex value, a lone 0 in a dense column. Opt-in: the retained
   `zero` feature keeps the tabular advance, so mixing it in never breaks
   alignment, but a slash across the display numerals would fight the
   geometry, so it is never the default. */
.t-zero {
  font-feature-settings: var(--feat-zero);
}

/* ============================================================== *
 * 4. HAIRLINE RULES — structure, not decoration
 *
 *    A rule separates zones and anchors labels, the way Müller-Brockmann
 *    uses them: it is part of the grid, so IT MUST ALIGN TO THE GRID IT
 *    ORGANISES. That is the whole discipline. A hairline dropped between
 *    two blocks at an arbitrary offset is decoration and should be
 *    deleted instead.
 *
 *    The modifiers below do the arithmetic so nobody eyeballs it: the
 *    rule's own 1px is subtracted from the space beneath it, and the
 *    total — margin + hairline + margin — is a whole number of 8px
 *    units. Set a rule without a modifier and you are responsible for
 *    that sum yourself.
 *
 *    Kept byte-identical to the .rule in css/base.css for its three core
 *    declarations, so the two definitions agree whichever order they load.
 * ============================================================== */

.rule {
  display: block;
  inline-size: 100%;
  block-size: var(--hairline-w);
  background-color: var(--hairline);
  border: 0;
  /* A 1px flex item shrinks to nothing in a tight column. It must not. */
  flex: 0 0 auto;
}

.rule--strong {
  background-color: var(--hairline-strong);
}

/* 8 + 1 + 7 = 16px = 2 units */
.rule--tight {
  margin-block-start: var(--baseline);
  margin-block-end: calc(var(--baseline) - var(--hairline-w));
}

/* 16 + 1 + 15 = 32px = 4 units */
.rule--gap {
  margin-block-start: calc(var(--baseline) * 2);
  margin-block-end: calc(var(--baseline) * 2 - var(--hairline-w));
}

/* 24 + 1 + 23 = 48px = 6 units */
.rule--zone {
  margin-block-start: calc(var(--baseline) * 3);
  margin-block-end: calc(var(--baseline) * 3 - var(--hairline-w));
}

/* Anchors a label to the zone below it: rule, then the legend sitting on
   the next grid line. No margin below, because the label's own line box
   is already a whole number of units. */
.rule--anchor {
  margin-block-start: calc(var(--baseline) * 2);
  margin-block-end: calc(var(--baseline) - var(--hairline-w));
}

/* A column divider. Stretches to the row it divides rather than carrying
   a height of its own, so it can never poke past the grid. */
.rule--vertical {
  inline-size: var(--hairline-w);
  block-size: auto;
  min-block-size: var(--baseline);
  align-self: stretch;
}

/* ============================================================== *
 * 5. WEIGHT DISCIPLINE — the three-weight rule, enforced
 *
 *    PHASE4 §3: "Three weights maximum in the entire app: 300, 400, 500.
 *    A 600 is permitted for the single START control and nowhere else."
 *
 *    Roughly forty rules across eleven files predate that sentence and
 *    still ask for 600 or 700. Those files belong to other agents, so the
 *    correction lives here instead, one line per site, each tagged with
 *    where it came from. Every selector is prefixed with :root purely to
 *    add a specificity step — several of these targets are styled from a
 *    <style> element that modes inject at runtime, which always lands
 *    after this stylesheet in the cascade, so matching specificity would
 *    lose on source order.
 *
 *    This block is meant to shrink. As each owning agent adopts
 *    var(--wt-medium), delete its line here. When the block is empty the
 *    app is enforcing the rule on its own.
 * ============================================================== */

/* --- 600 → 500 ------------------------------------------------- */

/* css/base.css: .btn, .chip, .field-label, .panel__title, .u-caps */
:root .btn,
:root .chip,
:root .field-label,
:root .panel__title,
:root .u-caps,
/* css/base.css:289 — .btn--lg was 700; it also dresses the alarm's Stop key,
   so it cannot be the exception. START claims the exception below. */
:root .btn--lg,
/* css/layout.css */
:root .tt-mod__name,
:root .tt-layout__tray-label,
/* css/modules.css */
:root .module__title,
:root .module__value,
:root .module__unit,
/* css/settings.css */
:root .settings__title,
:root .set-section__title,
:root .set-seg__legend,
:root .set-seg__opt span,
:root .set-sub__title,
:root .set-mark__label,
:root .set-marks__unit,
:root .set-volume__value,
/* css/mod-*.css */
:root .dr__amount,
:root .est-actual,
:root .mth-name,
:root .ps-chip__n,
:root .ps-row__n,
:root .ps-row__pen,
:root .sl__total,
:root .sl-day.is-on,
:root .tk.is-focus .tk-input,
/* index.html — the shell's inlined above-the-fold chrome */
:root .brand,
:root .segmented__key,
/* src/modes/timer.js — injected at runtime */
:root .tt-timer__meta,
:root .tt-timer__grouplabel,
:root .tt-timer__stepname,
:root .tt-timer__stepcount,
/* src/modes/clockface.js — injected at runtime */
:root .tt-clock__suffix,
:root .tt-clock__date,
:root .tt-ribbonfb__head,
/* src/modes/alarm.js — injected at runtime */
:root .tt-alarm__notetitle,
:root .tt-alarm__ringingtime,
:root .tt-alarm__off,
:root .tt-alarm__time {
  font-weight: var(--wt-medium);
}

/* --- the one exception ----------------------------------------- *
   START. Ordered after .btn--lg above so it wins at equal specificity.
   This is the only 600 in the app; if a second one ever appears, one of
   them is a bug. */
:root .tt-timer__primary {
  font-weight: var(--wt-start);
}

/* --- the readouts are display type ----------------------------- *
   .tt-timer__readout (src/modes/timer.js) and .tt-clock__hm
   (src/modes/clockface.js) are the same numerals in two modes — the
   instrument's face. Both already carry --step-5, which now resolves to
   --fs-display, so they only need the rest of the display role: the 300
   weight, the -0.035em tracking, opsz 32, and the ornament ban.
   Everything else about them stays the property of their own agents. */
:root .tt-timer__readout,
:root .tt-clock__hm {
  font-weight: var(--wt-light);
  letter-spacing: var(--tr-display);
  font-variation-settings: var(--vf-display);
  font-variant-numeric: var(--font-num-variant);
  font-feature-settings: var(--feat-num);
  text-shadow: none;
  -webkit-text-stroke-width: 0;
  background-image: none;
  -webkit-background-clip: border-box;
  background-clip: border-box;
  animation: none;
  filter: none;
}

/* Overtime reads "+M:SS". Reserve the sign's width on the trailing side so
   the digits hold the axis across the transition into overtime. */
:root .tt-timer__readout[data-overtime="1"] {
  margin-inline-end: var(--type-display-sign);
}
