/* ── Tria design tokens ───────────────────────────────────────────────────
   An editorial, portfolio-minded system. The UI is a cool, near-neutral
   greyscale — a deep off-black on a cool off-white — and the ONLY chromatic
   color is a pastel quintet, reserved for post types and surfaced on the
   filter menu and the Publish button. Type is Instrument Serif (display) over Oxygen (the quiet
   functional layer). The grid and its whitespace carry the design. */

/* ── The two faces, self-hosted ───────────────────────────────────────────────
   Both families used to arrive from fonts.googleapis.com. The type IS the
   design here, and renting it from a third party meant the first screen waited
   on two extra handshakes on the web and on the network itself in the bundled
   iOS app — where a cold launch with no signal drew Tria in system sans. These
   are Google's own woff2 subsets, byte for byte, served from css/fonts (112KB
   for all ten, and the iOS build already bundles the css folder).

   Kept as separate latin / latin-ext files with their unicode-range intact, the
   way Google serves them: a name with an accent in it still resolves, and the
   ~40% of the payload that only exists for those accents is fetched only by the
   people who need it. `swap` because a self-hosted file is either there or
   something is very wrong — better a frame of fallback than invisible text. */
@font-face {
  font-family: 'Instrument Serif';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/instrument-serif-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/instrument-serif-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: italic; font-weight: 400; font-display: swap;
  src: url(fonts/instrument-serif-400i-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: italic; font-weight: 400; font-display: swap;
  src: url(fonts/instrument-serif-400i-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Oxygen';
  font-style: normal; font-weight: 300; font-display: swap;
  src: url(fonts/oxygen-300-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Oxygen';
  font-style: normal; font-weight: 300; font-display: swap;
  src: url(fonts/oxygen-300-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Oxygen';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/oxygen-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Oxygen';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/oxygen-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Oxygen';
  font-style: normal; font-weight: 700; font-display: swap;
  src: url(fonts/oxygen-700-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Oxygen';
  font-style: normal; font-weight: 700; font-display: swap;
  src: url(fonts/oxygen-700-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* Neutrals — cool off-white, deep cool off-black. The whole UI lives here. */
  --bg:        #edeef0;   /* cool paper */
  --surface:   #f6f7f8;   /* barely-lifted panels (sidebar, chips) */
  --surface-2: #e6e8ea;
  --text:      #14171a;   /* deep, slightly cool off-black */
  --muted:     #5c636b;
  --rule:      #dadde1;   /* hairlines */

  /* Action accent — kept monochrome (ink). Publish button, active nav, the All
     filter, links. The only chromatic color stays reserved for post types. */
  --accent:       var(--text);
  --accent-press: color-mix(in srgb, var(--text) 84%, var(--muted));
  --on-accent:    var(--bg);

  /* Post types — a pastel quintet: lavender / peach-coral / baby cyan / lime /
     rose. Each type carries two faces: the pastel itself is the FILL (active
     filter chips, the shifting Publish button), and a deepened -ink twin carries
     text/labels on paper (AA). --on-type is the ink that sits on any pastel
     fill — near-black, AA on all five in both modes. */
  --type-note:         #b7a6e8;  /* lavender */
  --type-note-ink:     #6247b8;
  --type-find:         #f2a58c;  /* peach / coral */
  --type-find-ink:     #b0472a;
  --type-photo:        #9fd6e8;  /* baby cyan */
  --type-photo-ink:    #1d7391;
  --type-activity:     #b9df7d;  /* lime green */
  --type-activity-ink: #4d7519;
  --type-poll:         #ea86ae;  /* rose pink */
  --type-poll-ink:     #b23a68;
  --on-type:           #14171a;

  /* Liked-heart quintet — the ink twins nudged toward their pastels so the
     filled heart reads brighter on paper (it's an icon, not text, so it can
     sit lighter than AA). Dark mode resets these to the inks (= pastels). */
  --heart-note:     color-mix(in srgb, var(--type-note-ink)     70%, var(--type-note));
  --heart-find:     color-mix(in srgb, var(--type-find-ink)     70%, var(--type-find));
  --heart-photo:    color-mix(in srgb, var(--type-photo-ink)    70%, var(--type-photo));
  --heart-activity: color-mix(in srgb, var(--type-activity-ink) 70%, var(--type-activity));
  --heart-poll:     color-mix(in srgb, var(--type-poll-ink)     70%, var(--type-poll));

  /* Geometry + rhythm */
  --radius:      3px;     /* small radius — focus rings, incidental corners */
  --radius-pill: 999px;   /* fully rounded — pills, tags, buttons */
  --radius-img:  8px;      /* soft radius — small containers, chips */
  /* The rest of the corner scale, tokenised so the ladder is enforced not
     remembered (see the design system note in CLAUDE.md). */
  --radius-field: 12px;   /* composer + edit inputs and their grouped rows */
  --radius-card:  14px;   /* photos, and floating glass menus / cards */
  --radius-nav:   18px;   /* the nav rail */
  --radius-modal: 20px;   /* glass modals + sheets */
  /* Concentric corners (the iOS 26 rule): a nested corner is its PARENT's radius
     minus the padding between them, so the two curves stay parallel instead of
     the inner one bending tighter and reading as a different corner language.
     It only applies when the child actually reaches the parent's corner — a form
     field 1.6rem inside a modal is nowhere near it and keeps its own radius.
     --pad-panel is the inner pad every row-holding glass panel shares, so the two
     derived radii below stay correct if it ever moves. */
  --pad-panel:      0.3rem;
  --radius-row:     calc(var(--radius-card)  - var(--pad-panel));   /* rows in a menu  */
  --radius-row-lg:  calc(var(--radius-modal) - var(--pad-panel));   /* rows in a sheet */
  --sidebar-w: 268px;
  --topbar-h:  88px;
  --feed-width: 660px;
  --content-max: 900px;   /* masthead + rule width (wider than the feed measure) */
  --inset:     1.6rem;    /* text inset past the left type-rule */

  /* Type scale — Instrument Serif carries the display sizes (mastheads, headlines,
     names); Oxygen carries the functional layer. Bigger, bolder, editorial. */
  --display:  clamp(2.6rem, 4.6vw, 4.6rem);   /* page nameplates */
  --headline: clamp(1.7rem, 2.6vw, 2.05rem);  /* post titles */
  --body:     1rem;                            /* captions / notes */
  --kicker:   0.78rem;                         /* the uppercase date/section label */

  /* ── Liquid glass — the material for the layer that floats above content ────
     The WHOLE recipe lives here now: fill, blur, edge, rim, lift. It used to be
     copy-pasted at ~20 sites in five slightly different flavours (fills from 62%
     to 88%, rims from #fff 12% to 40%, each with its own dark-mode block
     restating all of it), so "our glass" was really nine glasses that happened to
     look alike. A surface now names its TIER and inherits the rest, and dark mode
     is answered once, at the bottom of this file, instead of per component.

     THE TIERS ARE ABOUT WHAT'S BEHIND THEM, not about what the element is called.
     A backdrop-filter only costs anything when its backdrop CHANGES, so the bill
     is area x radius x frames-that-move:

       chrome  — fixed over a feed that scrolls under it (top bar, nav pill,
                 seg-tabs, search, the daily card). Re-samples every frame of
                 every scroll. Stays lean.
       panel   — sits over a page that is frozen behind it (modals, sheets,
                 menus, the lightbox). Re-samples once and then holds. Can afford
                 the deep blur that actually reads as glass.

     That keeps the July 2026 battery pass intact rather than reverting it. The
     heat came from thirty surfaces re-rasterizing during scroll; those are still
     the cheap ones, and the deep blur went where nothing is moving. Chrome is at
     15px rather than the 24 it ran before that pass, and saturate() comes back
     only on the static tier.

     Fill is THIN, and that is the single biggest reason the old glass read as
     frosted plastic: a 62-88% fill behind an 8px blur is the inverse of every
     native material, which is a thin fill over a deep blur. You are meant to see
     content through glass. The blur is what keeps it from competing with the
     text, not the fill. */
  --glass-blur:       15px;
  --glass-blur-panel: 30px;
  /* saturate() is what makes a native material pick up the colour of whatever
     passes behind it. Static tier only — it rides on the blur that's already
     happening, but a colour matrix is still per-pixel work and chrome pays that
     bill on every scroll frame. */
  --glass-filter-panel: blur(var(--glass-blur-panel)) saturate(1.7);

  --glass-bg:       color-mix(in srgb, var(--bg) 58%, transparent);
  --glass-bg-panel: color-mix(in srgb, var(--bg) 70%, transparent);

  /* The specular edge. Native glass lights its whole perimeter and varies around
     it: brightest where the light strikes, nearly gone on the far side. The old
     rim was one `inset 0 1px 0` line, which is a lit LIP — a bright top and three
     dead sides. Two insets at opposite offsets give the full perimeter WITH the
     falloff, at the same cost of nothing (a box-shadow is not a filter). The
     light is top-left, the same fixed source the lit dome uses. */
  --glass-rim:
    inset  1px  1px 0 color-mix(in srgb, #fff 52%, transparent),
    inset -1px -1px 0 color-mix(in srgb, #fff 14%, transparent);
  --glass-edge: 1px solid color-mix(in srgb, var(--text) 9%, transparent);
  --glass-lift:       0 10px 30px rgba(0, 0, 0, 0.14);
  --glass-lift-panel: 0 24px 60px rgba(0, 0, 0, 0.28);

  /* iOS installed-shell status-bar scrim — a top wash that backs the OS clock and
     battery glyphs where content scrolls under them (black-translucent bar, so the
     app runs full-bleed beneath it; see .statusbar-scrim). Light paper takes a
     WHITE wash: iOS paints dark glyphs to match the light theme-color, so they
     need a light backing. Dark mode flips it below. */
  --statusbar-scrim: linear-gradient(180deg,
                       rgba(255, 255, 255, 0.80) 0%,
                       rgba(255, 255, 255, 0.36) 55%,
                       transparent 100%);

  /* Grain — a static, greyscale film-grain tile (inline SVG feTurbulence, so no
     asset and no network). Laid over the whole viewport via a fixed overlay and
     blended (see .grain in app.css) so flat fills and gradients read as tactile
     paper rather than digital-smooth. Intentionally visible, not just anti-band
     dithering. --grain-opacity tunes intensity per theme. */
  --grain: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='140'%20height='140'%3E%3Cfilter%20id='g'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.72'%20numOctaves='2'%20stitchTiles='stitch'/%3E%3CfeColorMatrix%20type='saturate'%20values='0'/%3E%3C/filter%3E%3Crect%20width='100%25'%20height='100%25'%20filter='url%28%23g%29'/%3E%3C/svg%3E");
  --grain-opacity: 0.1;

  /* Lit surfaces — one shared "light from above" cue for every filled control,
     so buttons and active chips read as lit objects rather than flat swatches
     (the same light the + FAB's radial dome uses). --lit-top is a sheen layered
     OVER a solid fill (bright top fading to a base shade); the shadows give the
     rim highlight, the pooled base shade, and a soft lift. Kept low so it stays
     editorial, not glossy. */
  --lit-top:  linear-gradient(180deg,
                rgba(255, 255, 255, 0.18) 0%,
                rgba(255, 255, 255, 0) 44%,
                rgba(0, 0, 0, 0.12) 100%);
  --lit-rim:  inset 0 1px 0 rgba(255, 255, 255, 0.24);
  --lit-base: inset 0 -3px 6px rgba(0, 0, 0, 0.10);
  --lit-drop: 0 2px 8px rgba(0, 0, 0, 0.14);

  /* Motion — one signature easeOut carries every considered move (pages, cards,
     reveals). Soft, never springy; durations step in a small, calm scale. */
  --ease:      cubic-bezier(0.16, 1, 0.3, 1);  /* signature easeOut — settles gently */
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);   /* symmetric — for cross-fades */
  /* The playful counterpart: a real spring for press releases and reward beats.
     This bezier is the broad-support stand-in (a touch of overshoot); the
     @supports block below upgrades it to a sampled underdamped spring where
     linear() exists (Safari 17.2+). Reserved for RESPONSES to a touch — press
     release, count rolls, reward pops — never for page-level moves, which keep
     the calm signature ease. */
  --spring: cubic-bezier(0.3, 1.35, 0.45, 1);
  --dur-micro: 0.15s;   /* hovers, colour shifts */
  --dur-quick: 0.24s;   /* small reveals, modal rise, fades */
  --dur-move:  0.36s;   /* page + big transitions */
  --dur-slow:  0.5s;    /* card rise, generous eases */

  color-scheme: light;
}

/* Where linear() easing exists (Safari 17.2+ / modern engines), --spring becomes
   a sampled underdamped spring: ~10% overshoot, a whisper of settle. A bezier
   can only overshoot once; this is what makes releases feel physical. */
@supports (animation-timing-function: linear(0, 1)) {
  :root {
    --spring: linear(
      0, 0.008, 0.035, 0.078, 0.136, 0.207, 0.288, 0.377, 0.47, 0.565, 0.658,
      0.746, 0.828, 0.9, 0.962, 1.013, 1.052, 1.079, 1.095, 1.101, 1.099, 1.09,
      1.075, 1.057, 1.038, 1.02, 1.005, 0.993, 0.985, 0.98, 0.979, 0.981,
      0.984, 0.988, 0.993, 0.997, 1, 1.002, 1.003, 1.002, 1.001, 1);
  }
}

/* ── Dark mode — deep cool black, cool off-white ink; RYB lifted to stay vivid. */
@media (prefers-color-scheme: dark) {
  :root {
    --bg:        #0e1012;
    --surface:   #171a1d;
    --surface-2: #1f2327;
    --text:      #e9ebed;
    --muted:     #969ca3;
    --rule:      #272c30;

    --accent:       var(--text);
    --accent-press: color-mix(in srgb, var(--text) 84%, var(--muted));
    --on-accent:    var(--bg);

    /* Pastel fills read light against the dark paper, so the -ink twins can
       simply BE the pastels — vivid text without a second ramp. --on-type stays
       near-black: the fills themselves don't change. */
    --type-note:         #b7a6e8;
    --type-note-ink:     #b7a6e8;
    --type-find:         #f2a58c;
    --type-find-ink:     #f2a58c;
    --type-photo:        #9fd6e8;
    --type-photo-ink:    #9fd6e8;
    --type-activity:     #b9df7d;
    --type-activity-ink: #b9df7d;
    --type-poll:         #ea86ae;
    --type-poll-ink:     #ea86ae;

    --heart-note:     var(--type-note-ink);
    --heart-find:     var(--type-find-ink);
    --heart-photo:    var(--type-photo-ink);
    --heart-activity: var(--type-activity-ink);
    --heart-poll:     var(--type-poll-ink);

    /* Lit surfaces on dark paper: the top-white sheen barely reads on the
       near-white fills, so lean on a deeper base shade and a stronger lift. */
    --lit-top:  linear-gradient(180deg,
                  rgba(255, 255, 255, 0.14) 0%,
                  rgba(255, 255, 255, 0) 40%,
                  rgba(0, 0, 0, 0.16) 100%);
    --lit-base: inset 0 -3px 6px rgba(0, 0, 0, 0.14);
    --lit-drop: 0 3px 13px rgba(0, 0, 0, 0.5);

    /* Dark glass, answered once for every surface. Chrome runs a shade thinner
       than light (dark paper already suppresses what's behind it, so the same
       fill reads heavier), and panels a shade THICKER — they carry form fields
       and dense rows, and a bright photo behind an under-filled panel is where
       dark mode loses its text. The rim is the real difference: a near-white
       specular line on a near-black surface is a neon strip, so it drops to a
       sixth of its light-mode strength and the falloff side nearly vanishes.
       Shadows deepen, because a 14% black shadow on #0e1012 is not a shadow. */
    --glass-bg:       color-mix(in srgb, var(--bg) 55%, transparent);
    --glass-bg-panel: color-mix(in srgb, var(--bg) 74%, transparent);
    --glass-rim:
      inset  1px  1px 0 color-mix(in srgb, #fff 17%, transparent),
      inset -1px -1px 0 color-mix(in srgb, #fff 5%, transparent);
    --glass-edge: 1px solid color-mix(in srgb, var(--text) 11%, transparent);
    --glass-lift:       0 10px 30px rgba(0, 0, 0, 0.5);
    --glass-lift-panel: 0 24px 60px rgba(0, 0, 0, 0.5);

    /* Flip the status-bar scrim dark: iOS paints light glyphs on the dark
       theme-color, so they need a dark backing to read over light content. */
    --statusbar-scrim: linear-gradient(180deg,
                         rgba(0, 0, 0, 0.50) 0%,
                         rgba(0, 0, 0, 0.20) 55%,
                         transparent 100%);

    /* Grain reads stronger on dark paper (soft-light lifts it), so ease it back. */
    --grain-opacity: 0.07;

    color-scheme: dark;
  }
}
