/* FlightSnatcher - the family pages ('/' and /calendar), 2026-09 redesign.
 *
 * The tokens and the pieces both pages draw. Every value comes from
 * docs/design/2026-09-redesign/answer/SPEC.md §2 (the calendar's tile tokens
 * from docs/design/2026-09-redesign/calendar/build.py): the iOS type ramp
 * (17, 22, 28, 34, 56 and the display price), one accent that means "a good
 * price" and nothing else, no borders anywhere.
 *
 * Scoped to body.fam, never :root. tokens.css still loads first (the Inter
 * face, the reset) and /pro reads its --card, --ink, --line, --good, --thumb,
 * --r and --shadow with other values: on :root these would restyle it.
 *
 * Every selector below carries body.fam (or :where(body.fam), where the
 * mockup's own cascade order must be preserved unaltered by extra
 * specificity) so no rule here can ever match an element outside a family
 * page, even one /pro shares a class name with (tests/test_family_partials.py
 * ::test_every_rule_in_family_css_is_scoped_to_body_fam).
 */
body.fam{
  color-scheme:light dark;
  --ground:#f2f2f7;--card:#fff;--fill:#f2f2f7;--fill2:#e3e3e8;--thumb:#fff;
  --ink:#1d1d1f;--ink2:#55555a;--ink3:#c0c0c5;--line:#8e8e93;--sep:rgba(60,60,67,.15);
  --go:#11692f;--book:#16833f;--on-book:#fff;--tint:rgba(22,131,63,.13);
  --btn:#1d1d1f;--btn-ink:#fff;--scrim:rgba(0,0,0,.3);--xc:#c0c0c5;--pill:#fff;
  --sky:#d3eedc;--sky2:#f4fbf6;
  --mist:linear-gradient(180deg,#f3f3f6,#e9e9ee);
  --lum:radial-gradient(120% 120% at 32% 18%,#62d98f 0%,#23a354 44%,#12793a 100%);
  --lum-h:0 5px 14px -4px rgba(22,131,63,.62);
  --soft:linear-gradient(150deg,rgba(52,199,89,.36),rgba(52,199,89,.13));
  --soft-h:0 4px 12px -5px rgba(22,131,63,.35);
  --shadow:0 12px 36px rgba(0,0,0,.13),0 1px 3px rgba(0,0,0,.06);
  --r:22px;
  font-family:-apple-system,BlinkMacSystemFont,Inter,system-ui,"Segoe UI",Roboto,sans-serif;
  font-feature-settings:"cv05","cv08";
}
@media (prefers-color-scheme:dark){body.fam{
  --ground:#000;--card:#1c1c1e;--fill:#2c2c2e;--fill2:#39393d;--thumb:#636366;
  --ink:#f5f5f7;--ink2:#a1a1a6;--ink3:#48484a;--line:#8e8e93;--sep:rgba(84,84,88,.6);
  --go:#30d158;--book:#30d158;--on-book:#000;--tint:rgba(48,209,88,.17);
  --btn:#f5f5f7;--btn-ink:#000;--scrim:rgba(0,0,0,.6);--xc:#8e8e93;--pill:#39393d;
  --sky:#0f3f23;--sky2:#123d25;
  --mist:linear-gradient(180deg,#2c2c2f,#252528);
  --lum:radial-gradient(120% 120% at 32% 18%,#8af5ae 0%,#34d35c 46%,#1f9d45 100%);
  --lum-h:0 0 18px -3px rgba(48,209,88,.62);
  --soft:linear-gradient(150deg,rgba(48,209,88,.38),rgba(48,209,88,.14));
  --soft-h:0 0 14px -6px rgba(48,209,88,.5);
  --shadow:0 12px 36px rgba(0,0,0,.65);
}}
/* the ground where the body does not reach (overscroll, a short page); html
   has no descendant combinator into body.fam, so :has() is the only way to
   scope an ancestor - it still never matches a page whose body lacks .fam */
html:has(>body.fam){background:#f2f2f7;-webkit-text-size-adjust:100%;text-size-adjust:100%}
@media (prefers-color-scheme:dark){html:has(>body.fam){background:#000}}
/* iOS Dynamic Type (the iPhone's Larger Text) reaches web content only
   through this system font keyword; without it the parents' text size never
   reaches either page. Touch WebKit only: desktop Safari would shrink the
   base to 13px. It sets the whole font on <html>; body.fam below takes the
   family back, and its rem sizes carry only the reader's size. At iOS's
   default size (17px root) the pages read ~6% larger than the mockup. */
@supports (font:-apple-system-body) and (-webkit-touch-callout:none){html:has(>body.fam){font:-apple-system-body}}
/* the mockup's reset, at zero specificity so every rule below still wins */
:where(body.fam *){margin:0;padding:0}
body.fam [hidden]{display:none!important}
body.fam{margin:0;background:var(--ground);color:var(--ink);font-size:1.0625rem;line-height:1.41;letter-spacing:-.012em;
  -webkit-font-smoothing:antialiased;min-height:100vh;position:relative;overflow-x:hidden;
  padding:0 max(16px,env(safe-area-inset-right)) env(safe-area-inset-bottom) max(16px,env(safe-area-inset-left));
  /* not in the mockup: at 320 px with the text doubled (Dynamic Type, Android's
     font size) a no-break date may break rather than push the page sideways
     (tests/test_parent_redesign.py::test_no_sideways_scrolling) */
  overflow-wrap:break-word}
:where(body.fam) a{color:inherit;text-decoration:none;-webkit-tap-highlight-color:transparent}
:where(body.fam) :focus-visible{outline:2.5px solid var(--ink);outline-offset:3px;border-radius:10px}
:where(body.fam) :is(h1,h2,h3,.claim,.gone){text-wrap:balance}
:where(body.fam) .sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
:where(body.fam) :is(.nb,.amt,.c,time){white-space:nowrap}
:where(body.fam) .go{color:var(--go)}
:where(body.fam) .ar{color:var(--ink2);font-weight:400}
:where(body.fam) .cur{opacity:.6}
/* a leading euro (English) reads as part of the number, like apple.com prices */
:where(body.fam) .cur.pre{opacity:1}
:where(body.fam) svg.g{width:1.18em;height:1.18em;flex:none;vertical-align:-.22em}
/* a caveat's glyph, inline before its words */
:where(body.fam) svg.gi{width:1em;height:1em;margin-right:.32em;vertical-align:-.14em}
:where(body.fam) svg.chev{width:8px;height:14px;flex:none;color:var(--ink3)}
:where(body.fam) svg.ext{width:.7em;height:.7em;flex:none;opacity:.7}
:where(body.fam) time{font-variant-numeric:tabular-nums}
/* "a · b · c" that never opens a line with a separator: each item brings its
   own dot, and the first dot of every line sits in the clipped margin. An
   item moves to the next line whole; only one wider than a whole line (large
   text on a phone: "de persoană, direct dus-întors") wraps inside itself,
   its later lines hanging under its words, never cut off by the clip. A
   pair ("Preț bun · −38 €") so stays on one line wherever it fits. */
:where(body.fam) .ln{display:block;overflow:hidden}
:where(body.fam) .ln>span{display:flex;flex-wrap:wrap;align-items:baseline;margin-left:-.95em}
:where(body.fam) .ln>span>span{padding-left:.95em;text-indent:-.95em}
:where(body.fam) .ln>span>span::before{content:"·";display:inline-block;width:.95em;text-align:center;text-indent:0;color:var(--ink2);font-weight:400}
:where(body.fam) .ln>span>span *{text-indent:0}
/* an age in such an item may wrap too, only after "acum" or before "ago":
   its count and unit are held by no-break spaces (i18n hours_ago) */
:where(body.fam) .ln time{white-space:normal}
:where(body.fam) .ln.old{color:var(--ink2);font-weight:400}
:where(body.fam) .page{position:relative;z-index:1;margin:0 auto}
/* the one top bar every page shares: the back link or the age left, language
   right; it may wrap (not in the mockup) so doubled text never pushes the page sideways */
:where(body.fam) .bar{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:0 12px;min-height:44px;
  padding:calc(env(safe-area-inset-top,0px) + 6px) 2px 0;color:var(--ink2)}
:where(body.fam) .bar>a{color:var(--ink);font-weight:600;padding:10px 2px;display:inline-flex;align-items:center;gap:7px;min-height:44px}
:where(body.fam) .bar .back svg.chev{transform:scaleX(-1);color:var(--ink)}
:where(body.fam) .bar .age{min-width:0}
/* the stale banner: pre-rendered hidden, revealed by the age script after 30 h */
:where(body.fam) .stale{display:flex;gap:12px;align-items:flex-start;background:var(--card);border-radius:16px;padding:13px 16px;margin-top:10px}
:where(body.fam) .stale svg.g{width:22px;height:22px;margin-top:1px;color:var(--ink)}
:where(body.fam) .stale b{display:block;font-weight:600}
:where(body.fam) .stale span{display:block;color:var(--ink2)}
:where(body.fam) .stale.check span{color:var(--ink);font-weight:600}
body.fam:has(.stale:not([hidden])) .bar .age{visibility:hidden}
body.fam:has(.stale:not([hidden])) .ln .agei{display:none}
/* the inset grouped list: soft fill, no outlines, hairlines inset like iOS */
:where(body.fam) .group{list-style:none;background:var(--card);border-radius:var(--r);overflow:hidden}
:where(body.fam) .group>li{position:relative}
:where(body.fam) .group>li+li::before{content:"";position:absolute;top:0;left:18px;right:0;height:.5px;background:var(--sep)}
:where(body.fam) .sec-h{font-size:1.375rem;font-weight:700;letter-spacing:-.022em;margin:44px 18px 12px}
/* a list row, Stocks style: title and price on one baseline, then one quiet line */
:where(body.fam) .row{display:grid;grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"rr rp" "rd rd" "rv rv" "ro ro";
  column-gap:12px;padding:13px 16px 14px 18px;align-items:baseline;transition:background .15s}
:where(body.fam) a.row:active{background:var(--fill)}
:where(body.fam) :is(.rr,.rp){font-weight:600;letter-spacing:-.018em}
:where(body.fam) .rr{grid-area:rr;line-height:1.3}
:where(body.fam) .rp{grid-area:rp;font-variant-numeric:tabular-nums;white-space:nowrap;display:flex;align-items:baseline;gap:10px}
:where(body.fam) .rp svg.chev{align-self:center}
:where(body.fam) .rd{grid-area:rd;color:var(--ink2);margin-top:1px}
:where(body.fam) .rv{grid-area:rv;color:var(--ink2)}
:where(body.fam) :is(.rd .go,.rv .go){font-weight:600}
:where(body.fam) .ro{grid-area:ro;margin-top:1px}
:where(body.fam) .vq{display:inline-flex;align-items:center;gap:7px;color:var(--ink2)}
:where(body.fam) .vq svg.g{width:1.1em;height:1.1em}
/* a link row in a grouped list */
:where(body.fam) .lrow{display:flex;align-items:center;gap:14px;padding:13px 16px 13px 18px;min-height:66px;transition:background .15s}
:where(body.fam) .lrow:active{background:var(--fill)}
:where(body.fam) .lrow .tx{flex:1;min-width:0}
:where(body.fam) .lrow b{display:block;font-weight:600}
:where(body.fam) .lrow .s{display:block;color:var(--ink2)}
/* 56 px tall at the reader's default size, taller where larger text needs
   two lines: a fixed height let "Vedeți zborul pe Google" spill out of the
   button, white on the page, from iOS's XXXL text on. Its corners are half
   of 56 px, so one line is the capsule and more lines a rounded rectangle,
   never an oval. */
:where(body.fam) .cta{display:flex;align-items:center;justify-content:center;gap:.5em;min-height:56px;padding:10px 24px;margin:26px auto 0;max-width:380px;
  border-radius:28px;background:var(--btn);color:var(--btn-ink);font-weight:600;letter-spacing:-.015em;text-align:center;text-wrap:balance;
  transition:transform .15s ease,opacity .15s ease}
:where(body.fam) .cta:active{transform:scale(.98);opacity:.86}
/* the quiet form, under a "better to wait" or "too early" verdict */
:where(body.fam) .cta.quiet{display:inline-flex;min-height:0;background:none;color:var(--ink);padding:12px 8px;margin-top:20px}
:where(body.fam) .cta.quiet svg.ext{display:none}
:where(body.fam) .cap{display:inline-block;padding:0 10px;border-radius:999px;background:var(--tint);color:var(--go);font-weight:600;
  font-variant-numeric:tabular-nums;white-space:nowrap}
:where(body.fam) .pill{display:inline-flex;align-items:center;gap:8px;padding:6px 10px 6px 16px;border-radius:999px;background:var(--pill);font-weight:600;min-height:44px}
:where(body.fam) .pill svg{width:20px;height:20px}
:where(body.fam) .top-pill{text-align:center;margin-top:12px}
/* a warning that is a sentence, never a colour or a slab: the separate-tickets risk */
:where(body.fam) .risk{color:var(--ink);text-wrap:pretty}
/* one empty state, used by every page: a glyph, a heading, one sentence */
:where(body.fam) .none{padding:56px 8px 8px;text-align:center}
:where(body.fam) .none svg.g{width:44px;height:44px;color:var(--ink2)}
:where(body.fam) :is(.none h1,.none h2){font-size:1.375rem;font-weight:700;letter-spacing:-.022em;margin-top:14px;line-height:1.22}
:where(body.fam) .none p{color:var(--ink2);margin:8px auto 0;max-width:22em;text-wrap:pretty}
:where(body.fam) :is(.none+.group,.none+.others){margin-top:28px}
@media (min-width:700px){:where(body.fam) .none+.group{max-width:560px;margin-left:auto;margin-right:auto}}
:where(body.fam) .foot{text-align:center;color:var(--ink2);padding:56px 8px 44px}
:where(body.fam) .foot p{text-wrap:pretty}
