/* DreamApp Ltd — editorial site styles
 * Single shared stylesheet for home, practice, about.
 * Legal pages (/privacy, /terms, /support) keep their own inline CSS.
 */

@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght,SOFT@0,9..144,300..700,0..100;1,9..144,300..700,0..100&family=Newsreader:ital,opsz,wght@0,6..72,300..600;1,6..72,300..600&family=JetBrains+Mono:wght@400;500&display=swap');

:root {
    --paper:       #f6f1e6;
    --paper-soft:  #ede6d3;
    --paper-card:  #fbf7ec;
    --ink:         #1d1a13;
    --ink-soft:    #6a6356;
    --ink-muted:   #9b9485;
    --rule:        #d8cfb8;
    --rule-soft:   #e6decc;
    --accent:      #8a4a32;
    --accent-soft: #b9745b;
    --highlight:   #e8d4ad;
}

@media (prefers-color-scheme: dark) {
    :root {
        --paper:       #14110c;
        --paper-soft:  #1c1812;
        --paper-card:  #1d1913;
        --ink:         #ece5d3;
        --ink-soft:    #908976;
        --ink-muted:   #5e574b;
        --rule:        #2b2620;
        --rule-soft:   #221e18;
        --accent:      #d8927a;
        --accent-soft: #a86a51;
        --highlight:   #3a2f1f;
    }
}

* { box-sizing: border-box; }
*::selection { background: var(--highlight); color: var(--ink); }

html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    scroll-behavior: smooth;
}

body {
    margin: 0;
    padding: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: 'Newsreader', 'Iowan Old Style', 'Charter', Georgia, serif;
    font-variation-settings: 'opsz' 18;
    font-size: 18px;
    line-height: 1.62;
    font-feature-settings: 'kern', 'liga', 'onum';
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

/* faint paper grain — subtle, only visible at close inspection */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 1;
    opacity: 0.035;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.7 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
    background-size: 180px 180px;
    mix-blend-mode: multiply;
}

@media (prefers-color-scheme: dark) {
    body::before { mix-blend-mode: screen; opacity: 0.06; }
}

/* ───────────────────────────────────────────── masthead */

.masthead {
    position: relative;
    z-index: 2;
    max-width: 1120px;
    margin: 0 auto;
    padding: 28px 40px 0;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 24px;
}

.masthead .wordmark {
    font-family: 'Fraunces', serif;
    font-variation-settings: 'opsz' 144, 'SOFT' 50, 'wght' 400;
    font-size: 22px;
    letter-spacing: -0.005em;
    color: var(--ink);
    text-decoration: none;
    line-height: 1;
}

.masthead .wordmark em {
    font-style: italic;
    font-variation-settings: 'opsz' 144, 'SOFT' 100, 'wght' 400;
}

/* .site-nav added to this selector list by name (the generated masthead's
   <nav class="site-nav"> would already inherit these via the bare ".masthead
   nav" descendant match, since it too sits directly under header.masthead --
   but styling it by name, not by that structural coincidence, is what keeps
   this rule from silently stopping working if either markup ever changes
   independently). Do not give .site-nav its own separate rule block: that
   duplicated every declaration here for zero behavioral difference. The
   same by-name addition is applied consistently to the other two bare
   ".masthead nav" rules below (the 640px and print breakpoints) rather
   than left relying on the same structural coincidence there. */
.masthead nav,
.site-nav {
    font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    display: flex;
    gap: 26px;
}

.masthead nav a,
.site-nav a {
    color: var(--ink-soft);
    text-decoration: none;
    transition: color 180ms ease;
}

.masthead nav a:hover,
.masthead nav a[aria-current="page"],
.site-nav a:hover,
.site-nav a[aria-current="page"] {
    color: var(--accent);
}

@media (max-width: 640px) {
    .masthead { padding: 22px 22px 0; flex-wrap: wrap; }
    .masthead nav,
    .site-nav { gap: 18px; font-size: 10px; }
}

/* ───────────────────────────────────────────── containers */

main {
    position: relative;
    z-index: 2;
    max-width: 720px;
    margin: 0 auto;
    padding: 80px 40px 96px;
}

main.wide { max-width: 880px; }

@media (max-width: 640px) {
    main { padding: 48px 24px 64px; }
}

/* ───────────────────────────────────────────── typography */

h1, h2, h3 {
    font-family: 'Fraunces', 'Iowan Old Style', Palatino, serif;
    color: var(--ink);
    margin: 0;
    text-wrap: balance;
}

.hero-title {
    font-variation-settings: 'opsz' 144, 'SOFT' 40, 'wght' 380;
    font-size: clamp(54px, 9.5vw, 104px);
    line-height: 0.96;
    letter-spacing: -0.025em;
    margin: 0 0 28px;
    color: var(--ink);
}

.hero-title em {
    font-style: italic;
    font-variation-settings: 'opsz' 144, 'SOFT' 100, 'wght' 380;
    color: var(--accent);
}

.page-title {
    font-variation-settings: 'opsz' 144, 'SOFT' 50, 'wght' 380;
    font-size: clamp(42px, 6.4vw, 68px);
    line-height: 1.02;
    letter-spacing: -0.022em;
    margin: 0 0 20px;
}

.page-title em {
    font-style: italic;
    font-variation-settings: 'opsz' 144, 'SOFT' 100, 'wght' 380;
}

h2 {
    font-variation-settings: 'opsz' 60, 'wght' 420;
    font-size: clamp(26px, 3.4vw, 34px);
    line-height: 1.15;
    letter-spacing: -0.012em;
    margin: 0 0 14px;
}

h3 {
    font-variation-settings: 'opsz' 36, 'wght' 500;
    font-size: 20px;
    line-height: 1.3;
    margin: 0 0 8px;
}

p {
    margin: 0 0 18px;
    color: var(--ink);
    hyphens: auto;
}

p.deck {
    font-size: clamp(20px, 2.6vw, 24px);
    line-height: 1.4;
    color: var(--ink-soft);
    font-variation-settings: 'opsz' 28;
    margin-bottom: 36px;
    max-width: 32ch;
    text-wrap: balance;
}

p.lede {
    font-size: 21px;
    line-height: 1.55;
    color: var(--ink);
    margin-bottom: 24px;
}

/* color: var(--accent) is a per-app override on article/blog pages
   (article.html.j2, blog_post.html.j2 set --accent to the app's own
   accent_hex) -- at 4.6em against an 18px body, this letter renders at
   82.8px, comfortably inside WCAG's large-text carve-out, so the floor
   here is 3:1, not 4.5:1. Named plainly rather than left implicit: Roostel
   orange (#C8801E) on light paper is 2.84:1, a marginal miss of even that
   lower floor. Accepted as decorative per controller ruling (the same
   call made for the app-card border-top and the eyebrow's ::before dash,
   see .eyebrow's comment below) -- one oversized letter reading slightly
   under AA is a different, smaller risk than the full line of label/CTA
   text that prompted the actual fix, and isn't being fixed here too. */
.drop p:first-of-type::first-letter {
    font-family: 'Fraunces', serif;
    font-variation-settings: 'opsz' 144, 'SOFT' 50, 'wght' 380;
    float: left;
    font-size: 4.6em;
    line-height: 0.86;
    padding: 0.08em 0.12em 0 0;
    margin-right: 0.04em;
    color: var(--accent);
}

a {
    color: var(--ink);
    text-decoration: underline;
    text-decoration-color: var(--rule);
    text-decoration-thickness: 1px;
    text-underline-offset: 4px;
    transition: text-decoration-color 200ms ease, color 200ms ease;
}

a:hover {
    text-decoration-color: var(--accent);
    color: var(--accent);
}

em { font-style: italic; }
strong { font-weight: 500; }

.tabular { font-variant-numeric: tabular-nums; }

/* ───────────────────────────────────────────── sections */

.section {
    margin: 80px 0 0;
}

.section-rule {
    display: flex;
    align-items: baseline;
    gap: 18px;
    margin-bottom: 36px;
}

.section-rule::after {
    content: '';
    flex: 1;
    border-top: 1px solid var(--rule);
    transform: translateY(-3px);
}

.section-number {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.18em;
    color: var(--accent);
    text-transform: uppercase;
    white-space: nowrap;
}

.section-label {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.18em;
    color: var(--ink-soft);
    text-transform: uppercase;
    white-space: nowrap;
}

.section-heading {
    margin-bottom: 24px;
}

/* ───────────────────────────────────────────── practice cards */

.practice-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0;
    margin-top: 8px;
}

.practice-item {
    display: grid;
    grid-template-columns: 110px 1fr;
    gap: 32px;
    padding: 36px 0;
    border-top: 1px solid var(--rule);
    align-items: start;
}

.practice-item:last-child {
    border-bottom: 1px solid var(--rule);
}

.practice-illo {
    width: 110px;
    height: 110px;
    display: block;
    object-fit: contain;
    margin-top: -8px;
}

@media (prefers-color-scheme: dark) {
    .practice-illo { filter: brightness(1.08) saturate(0.92); }
}

.practice-roman {
    font-family: 'Fraunces', serif;
    font-variation-settings: 'opsz' 144, 'SOFT' 80, 'wght' 350;
    font-style: italic;
    font-size: 44px;
    line-height: 1;
    color: var(--accent);
    padding-top: 4px;
}

.practice-body .practice-tag {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 6px;
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.practice-body .practice-tag .roman {
    font-family: 'Fraunces', serif;
    font-style: italic;
    font-size: 14px;
    letter-spacing: 0;
    text-transform: none;
}

.practice-body h3 {
    font-variation-settings: 'opsz' 60, 'wght' 480;
    font-size: 24px;
    margin-bottom: 8px;
    letter-spacing: -0.01em;
}

.practice-body p {
    color: var(--ink-soft);
    margin-bottom: 0;
}

@media (max-width: 640px) {
    .practice-item { grid-template-columns: 72px 1fr; gap: 18px; padding: 26px 0; }
    .practice-illo { width: 72px; height: 72px; }
    .practice-body h3 { font-size: 21px; }
}

/* ───────────────────────────────────────────── practice deep page */

.practice-deep {
    padding: 72px 0 8px;
    border-top: 1px solid var(--rule);
    position: relative;
}

.practice-deep:first-of-type { border-top: none; padding-top: 32px; }

.practice-deep-header {
    display: grid;
    grid-template-columns: 1fr 200px;
    gap: 40px;
    align-items: start;
    margin-bottom: 28px;
}

.practice-deep-text { min-width: 0; }

.practice-deep-illo {
    width: 200px;
    height: 200px;
    object-fit: contain;
    justify-self: end;
    margin-top: -16px;
}

@media (prefers-color-scheme: dark) {
    .practice-deep-illo { filter: brightness(1.08) saturate(0.92); }
}

@media (max-width: 720px) {
    .practice-deep-header { grid-template-columns: 1fr; gap: 8px; }
    .practice-deep-illo { width: 96px; height: 96px; justify-self: start; order: -1; margin-top: 0; margin-bottom: 4px; }
}

.practice-deep .practice-roman {
    font-size: 64px;
    padding-top: 0;
    display: block;
    margin-bottom: 14px;
}

.practice-deep h2 {
    margin-bottom: 8px;
}

.practice-deep .subtitle {
    font-style: italic;
    font-variation-settings: 'opsz' 28, 'SOFT' 80, 'wght' 380;
    font-family: 'Fraunces', serif;
    color: var(--ink-soft);
    font-size: 22px;
    margin: 0 0 0;
    line-height: 1.3;
}

.practice-deep .prose {
    margin-top: 4px;
}

.practice-deep .prose p {
    color: var(--ink);
    margin-bottom: 16px;
}

.feature-list,
.refusal-list {
    margin: 16px 0 0;
    padding: 0;
    list-style: none;
}

.feature-list li,
.refusal-list li {
    position: relative;
    padding: 4px 0 4px 22px;
    color: var(--ink);
    line-height: 1.5;
}

.feature-list li::before {
    content: '+';
    position: absolute;
    left: 0;
    top: 4px;
    font-family: 'JetBrains Mono', monospace;
    color: var(--accent);
    font-weight: 500;
}

.refusal-list li::before {
    content: '−';
    position: absolute;
    left: 0;
    top: 4px;
    font-family: 'JetBrains Mono', monospace;
    color: var(--ink-muted);
    font-weight: 500;
}

.two-col {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 28px;
    margin-top: 18px;
}

.two-col .col-label {
    font-family: 'JetBrains Mono', monospace;
    font-size: 10px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-muted);
    margin: 0 0 6px;
}

@media (max-width: 640px) {
    .two-col { grid-template-columns: 1fr; gap: 14px; }
    .practice-deep { padding-top: 48px; }
    .practice-deep:first-of-type { padding-top: 20px; }
    .practice-deep .practice-roman { font-size: 38px; margin-bottom: 8px; }
    .practice-deep .subtitle { font-size: 18px; }
    .practice-deep h2 { font-size: 26px; }
}

/* ───────────────────────────────────────────── detailed features list */

.features-block {
    margin-top: 36px;
}

.features-block .features-label {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 4px;
}

.features-block h4 {
    font-family: 'Fraunces', serif;
    font-variation-settings: 'opsz' 36, 'wght' 480;
    font-size: 20px;
    line-height: 1.3;
    margin: 0 0 14px;
    color: var(--ink);
}

.feature-list-detailed {
    list-style: none;
    padding: 0;
    margin: 8px 0 0;
}

.feature-list-detailed li {
    display: grid;
    grid-template-columns: 20px 1fr;
    gap: 10px;
    padding: 16px 0;
    border-top: 1px solid var(--rule-soft);
    align-items: baseline;
}

.feature-list-detailed li::before {
    content: '+';
    font-family: 'JetBrains Mono', monospace;
    color: var(--accent);
    font-weight: 500;
    font-size: 15px;
    line-height: 1.5;
}

.feature-list-detailed strong {
    display: block;
    font-family: 'Fraunces', serif;
    font-variation-settings: 'opsz' 28, 'wght' 500;
    font-size: 18px;
    color: var(--ink);
    margin-bottom: 4px;
    font-weight: 500;
    letter-spacing: -0.005em;
}

.feature-list-detailed .feat-desc {
    display: block;
    color: var(--ink-soft);
    font-size: 16px;
    line-height: 1.55;
}

.refusals-line {
    margin-top: 26px;
    padding: 18px 0 4px;
    border-top: 1px solid var(--rule-soft);
    font-size: 15px;
    color: var(--ink-soft);
    line-height: 1.55;
}

.refusals-line .refusals-label {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-muted);
    margin: 0 0 4px;
    display: block;
}

.refusals-line em {
    color: var(--ink);
    font-style: italic;
}

/* ───────────────────────────────────────────── contact grid */

.contact-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 28px;
    margin-top: 12px;
}

@media (max-width: 640px) {
    .contact-grid { grid-template-columns: 1fr; gap: 20px; }
}

.contact-card {
    padding: 22px 0 0;
    border-top: 1px solid var(--rule);
}

.contact-card .label {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin: 0 0 8px;
}

.contact-card .email {
    font-family: 'Fraunces', serif;
    font-variation-settings: 'opsz' 32, 'wght' 400;
    font-size: 19px;
    color: var(--ink);
    text-decoration: none;
    border-bottom: 1px solid var(--rule);
    padding-bottom: 1px;
}

.contact-card .email:hover {
    border-bottom-color: var(--accent);
    color: var(--accent);
}

.contact-card .descriptor {
    font-size: 14px;
    color: var(--ink-soft);
    margin: 8px 0 0;
}

/* ───────────────────────────────────────────── colophon (footer) */

.colophon {
    position: relative;
    z-index: 2;
    border-top: 1px solid var(--rule);
    background: var(--paper-soft);
    margin-top: 96px;
}

.colophon-inner {
    max-width: 1120px;
    margin: 0 auto;
    padding: 56px 40px 48px;
    display: grid;
    grid-template-columns: 1.4fr 1fr 1fr;
    gap: 40px;
    align-items: start;
}

.colophon h4 {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-muted);
    font-weight: 500;
    margin: 0 0 14px;
}

.colophon .mark {
    font-family: 'Fraunces', serif;
    font-variation-settings: 'opsz' 144, 'SOFT' 60, 'wght' 380;
    font-size: 28px;
    line-height: 1;
    margin-bottom: 14px;
    color: var(--ink);
    display: inline-block;
    text-decoration: none;
}

.colophon .mark em {
    font-style: italic;
    font-variation-settings: 'opsz' 144, 'SOFT' 100, 'wght' 380;
}

.colophon p {
    color: var(--ink-soft);
    font-size: 15px;
    line-height: 1.55;
    margin: 0 0 6px;
}

.colophon a {
    color: var(--ink-soft);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: border-color 180ms ease, color 180ms ease;
}

.colophon a:hover {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

.colophon ul { list-style: none; padding: 0; margin: 0; }
.colophon li { font-size: 15px; line-height: 1.9; color: var(--ink-soft); }

.colophon-baseline {
    border-top: 1px solid var(--rule);
    max-width: 1120px;
    margin: 0 auto;
    padding: 22px 40px 32px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 10.5px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-muted);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
}

@media (max-width: 720px) {
    .colophon-inner { grid-template-columns: 1fr; gap: 32px; padding: 40px 24px 32px; }
    .colophon-baseline { padding: 22px 24px 28px; flex-direction: column; align-items: flex-start; gap: 10px; }
}

/* ───────────────────────────────────────────── hero block */

.hero {
    padding-top: 24px;
    padding-bottom: 0;
}

.hero.has-art {
    display: grid;
    grid-template-columns: 1fr 240px;
    gap: 48px;
    align-items: start;
}

.hero .hero-text { min-width: 0; }

.hero-illo {
    width: 240px;
    height: 240px;
    margin-top: 8px;
    align-self: start;
    justify-self: end;
}

.hero-illo img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

@media (prefers-color-scheme: dark) {
    .hero-illo img { filter: brightness(1.08) saturate(0.92); }
}

@media (max-width: 720px) {
    .hero.has-art { grid-template-columns: 1fr; gap: 8px; }
    .hero-illo { order: -1; width: 128px; height: 128px; justify-self: start; margin: 0 0 4px -6px; }
}

.hero .eyebrow {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 28px;
    display: flex;
    align-items: center;
    gap: 10px;
}

.hero .eyebrow::before {
    content: '';
    width: 22px;
    height: 1px;
    background: var(--accent);
    display: inline-block;
}

/* ───────────────────────────────────────────── breadcrumb back link */

.back {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-soft);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 28px;
}

.back:hover { color: var(--accent); }

/* ───────────────────────────────────────────── meta panel (about) */

.facts-panel {
    margin-top: 18px;
    border-top: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
    padding: 28px 0;
    display: grid;
    grid-template-columns: 160px 1fr;
    row-gap: 16px;
    column-gap: 28px;
    font-size: 16px;
}

.facts-panel dt {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--ink-soft);
    padding-top: 3px;
}

.facts-panel dd {
    margin: 0;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}

@media (max-width: 640px) {
    .facts-panel { grid-template-columns: 1fr; row-gap: 4px; column-gap: 0; }
    .facts-panel dt { padding-top: 14px; }
    .facts-panel dt:first-of-type { padding-top: 0; }
}

/* ───────────────────────────────────────────── pull / quote */

.pull {
    margin: 40px 0;
    padding: 28px 0 28px 28px;
    border-left: 2px solid var(--accent);
    font-family: 'Fraunces', serif;
    font-variation-settings: 'opsz' 60, 'SOFT' 70, 'wght' 380;
    font-style: italic;
    font-size: 24px;
    line-height: 1.4;
    color: var(--ink);
    max-width: 32ch;
}

@media (max-width: 640px) {
    .pull { padding: 20px 0 20px 18px; font-size: 20px; margin: 28px 0; }
}

/* ───────────────────────────────────────────── spacing utilities */

.mt-md { margin-top: 28px; }
.mt-lg { margin-top: 48px; }

/* ───────────────────────────────────────────── print niceties */

@media print {
    body::before { display: none; }
    .masthead nav,
    .site-nav { display: none; }
    main { max-width: 100%; padding: 0; }
    .colophon { background: transparent; border-top: 1px solid #000; }
}

/* ═════════════════════════════════════════════════════════════════════
   generated pages (sitegen) -- app hubs, articles, blog: dist/apps/**,
   dist/blog/**. Everything above (masthead, colophon, .section, .hero,
   .eyebrow inside .hero, .label inside .contact-card, ...) already applies
   here unchanged; this block adds only what the generated templates emit
   that nothing above already covers.
   ═════════════════════════════════════════════════════════════════════ */

/* Generated pages' footer (base.html.j2) is just .colophon-baseline holding
   one flowing sentence -- no .colophon-inner sibling, unlike the hand
   pages' footer, where .colophon-baseline pairs two <span> siblings that
   DO want the flex/space-between look above. :only-child is what tells
   the two apart structurally (the hand-page one is never an only child).
   Left as flex with a mixed text+link+text+link body, that shared rule's
   justify-content:space-between spread all four -- the sentence, "Privacy",
   the "·", and "Terms" -- as separate anonymous flex items across the full
   1120px strip, stranding the "·" alone mid-row. Plain block flow is what
   a single sentence with two inline links wants; centering, padding, and
   the mono/uppercase treatment all still apply unchanged, but border-top
   does not (see below) -- it's the one .colophon-baseline property this
   selector overrides on purpose, not an oversight. */
.colophon-baseline:only-child {
    display: block;
    /* .colophon-baseline is .colophon's only child here, sitting flush
       against it (.colophon has no padding-top to separate them), and
       .colophon itself already carries its own border-top. Left alone,
       .colophon-baseline's own border-top -- correct on hand pages, where
       it separates the baseline strip from .colophon-inner above it --
       stacks a second hairline right under the first on generated pages,
       where there's no .colophon-inner for it to separate anything from. */
    border-top: none;
}

/* Bare .eyebrow / .label: the hand pages only ever nest these under .hero
   or .contact-card. The generated templates use both unnested too (an
   article's byline eyebrow, an index page's field-note count), so the
   base look lives here; .hero .eyebrow and .contact-card .label above
   still win wherever they apply (two classes beats one), unchanged.

   color is ink-soft, not accent: an article/hub page sets --accent inline
   to the app's own accent_hex (see article.html.j2, app_hub.html.j2), and
   that per-app color is never contrast-checked -- real registry values,
   not hypotheticals: Floraid's green (#556E40) is fine in light (5.05:1)
   but drops to 3.31:1 in dark, and Roostel's orange (#C8801E) is fine in
   dark (5.88:1) but drops to 2.84:1 in light -- both under the 4.5:1 AA
   text needs, just in different themes, so no single-theme check would
   have caught both. Accent stays reserved for purely decorative use (the
   app-card border-top, the eyebrow's own ::before dash, the article
   dropcap) where a large-text/non-text 3:1 floor applies instead, and is
   an accepted look-and-feel call, not a guarantee, even there (see the
   dropcap rule's own comment for the one case that actually misses that
   floor too). ink-soft is never overridden per-app, so it's the one
   that's actually safe here: 5.28:1 (light) / 5.40:1 (dark) against
   paper, both themes, every app. */
.eyebrow {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin: 0 0 20px;
    display: flex;
    align-items: center;
    gap: 10px;
}

.eyebrow::before {
    content: '';
    width: 22px;
    height: 1px;
    background: var(--accent);
    display: inline-block;
}

/* article.html.j2's byline ("Floraid · Foliage") and blog_post.html.j2's
   ("Studio notes · <date>") both nest a real <a> inside .eyebrow -- without
   this it falls through to the global `a` rule (ink text, full underline),
   which doesn't match the eyebrow's own mono/uppercase/ink-soft treatment
   sitting right next to it. currentColor (not a fixed token) so this stays
   correct if .eyebrow's own color ever changes again. Hover stays inherit,
   not accent: same per-app-override risk as the color rule above, and
   there's nothing this hover state needs to promise beyond "yes, a link". */
.eyebrow a {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px solid currentColor;
}

.eyebrow a:hover {
    color: inherit;
}

/* Explicit, not incidental: .eyebrow's own display:flex happens to also
   suppress ::first-letter (the pseudo-element isn't generated for flex
   containers), which is the only reason the dropcap rule below hasn't
   already put a giant first letter on "Floraid" / "Our apps" / etc. That's
   too fragile to lean on silently -- a future .eyebrow layout change that
   drops display:flex would silently reintroduce the bug -- so it joins the
   same all:unset reset family as .app-card's and .article-cta's leaks
   below, on its own terms. */
.drop p.eyebrow:first-of-type::first-letter {
    all: unset;
}

.label {
    display: block;
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

/* ---- /apps/ index ---- */

.app-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 28px;
    margin-top: 8px;
}

.app-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 22px 24px 24px;
    border: 1px solid var(--rule);
    border-top: 3px solid var(--accent);
    color: var(--ink);
    text-decoration: none;
    transition: border-color 200ms ease;
}

.app-card:hover {
    border-color: var(--accent);
}

.app-card h2 {
    font-size: 21px;
    margin: 0;
}

.app-card .deck {
    margin: 0;
    max-width: none;
    font-size: 15px;
    line-height: 1.4;
    color: var(--ink-soft);
    font-variation-settings: 'opsz' 20;
}

.app-card .label {
    /* Shorthand, not margin-top alone: .label is a bare <p> in
       apps_index.html.j2, so the sheet's global `p { margin: 0 0 18px }`
       was still contributing its 18px bottom margin underneath this --
       margin-top:auto pushed the label down, but that surviving 18px kept
       pushing the card's bottom border down past it, 18px of dead space
       the card had no visible reason to reserve. The 0 bottom here kills
       it; 0 left/right is a no-op restatement of what `p` already gave. */
    margin: auto 0 0;
    padding-top: 8px;
}

@media (max-width: 640px) {
    .app-grid { gap: 20px; }
}

/* ---- article-list: app hub "field notes" + blog index ---- */

.article-list {
    list-style: none;
    margin: 16px 0 0;
    padding: 0;
}

.article-list li {
    border-top: 1px solid var(--rule);
    padding: 20px 0;
}

.article-list li:last-child {
    border-bottom: 1px solid var(--rule);
}

.article-list a {
    display: block;
    text-decoration: none;
}

.article-title {
    display: block;
    font-family: 'Fraunces', serif;
    font-variation-settings: 'opsz' 32, 'wght' 480;
    font-size: 19px;
    line-height: 1.3;
    letter-spacing: -0.005em;
    color: var(--ink);
    margin: 0 0 4px;
}

.article-list a:hover .article-title {
    color: var(--accent);
}

.article-list .deck {
    display: block;
    max-width: none;
    font-size: 15px;
    line-height: 1.5;
    color: var(--ink-soft);
    font-variation-settings: 'opsz' 20;
    margin: 0 0 6px;
}

/* ---- article + blog post body ---- */

.article-hero {
    display: block;
    width: 100%;
    max-width: 480px;
    height: auto;
    margin: 8px auto 32px;
}

@media (max-width: 640px) {
    .article-hero { margin: 4px auto 24px; }
}

@media (prefers-color-scheme: dark) {
    /* NOT the same treatment as .hero-illo img / .practice-illo below:
       those are transparent PNGs sitting directly on the page's own paper,
       so lifting their brightness is what keeps them from going flat and
       muddy against dark paper. This hero is a WebP with an opaque cream
       background already baked in (render.py's _flatten_to_rgb bakes every
       hero onto CREAM_BG regardless of theme), so it was never going to go
       muddy -- lifting brightness on an already-light, hard-edged rectangle
       just made it glare harder against dark paper. Dimming it slightly
       instead softens that edge without fighting the bake. */
    .article-hero { filter: brightness(0.9); }
}

.article-body {
    max-width: 42rem;
    line-height: 1.65;
}

.article-body p {
    margin: 0 0 20px;
}

.article-body img {
    max-width: 100%;
    height: auto;
}

/* A body that opens with a "> " pull-quote as its very first block would
   otherwise hand the dropcap to the quote instead of the article's real
   first paragraph -- same underlying leak as .app-card's (see its
   comment), fixed the same way below. */
.article-body blockquote {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 28px 0;
    padding: 2px 0 2px 24px;
    border-left: 2px solid var(--accent);
    font-family: 'Fraunces', serif;
    font-variation-settings: 'opsz' 40, 'SOFT' 60, 'wght' 380;
    font-style: italic;
    color: var(--ink-soft);
}

.article-body blockquote p {
    margin: 0;
}

.article-page .article-body blockquote p:first-of-type::first-letter {
    all: unset;
}

.article-body code {
    background: var(--paper-soft);
    padding: 2px 6px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.9em;
}

.article-cta {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin: 40px 0;
    padding: 28px 32px;
    background: var(--paper-card);
    border-top: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
}

@media (max-width: 640px) {
    /* Same narrow-viewport shrink pattern as .pull / .facts-panel above.
       Placed after the base rule deliberately: at equal specificity
       (both are the same single-class ".article-cta" selector) the later
       rule wins regardless of a matching media query, so a copy of this
       sitting earlier in the sheet than the base rule -- as this one used
       to, next to .app-grid's own mobile tweak above -- was silently dead
       at every viewport, base rule always winning on source order. */
    .article-cta { padding: 20px 22px; }
}

.article-cta p {
    margin: 0;
}

/* Same dropcap leak as .app-card's, and the same fix -- see its comment.
   This callout's first <p> sits directly under .article-page, which also
   carries .drop, so the extra ".article-page" ancestor here (true in
   every template that uses .article-cta) is what pushes past the dropcap
   rule's specificity rather than tying it. */
.article-page .article-cta p:first-of-type::first-letter {
    all: unset;
}

/* ink, not accent: same per-app-override risk as .eyebrow's (see its
   comment for the real registry numbers) -- Floraid's green border/text
   would drop to 3.31:1 in dark mode, Roostel's orange to 2.84:1 in light,
   both under even the 3:1 non-text floor a border needs, let alone the
   4.5:1 text needs for the label inside it. ink clears both with room to
   spare (15.42:1 light / 14.99:1 dark on paper, 16.22:1 / 13.92:1 on
   paper-card, every app, no per-app risk) and the hover fill below is the
   same swap inverted, so it inherits the same numbers rather than needing
   its own check. */
.store-cta {
    display: inline-block;
    padding: 12px 22px;
    border: 1px solid var(--ink);
    color: var(--ink);
    font-family: 'JetBrains Mono', monospace;
    font-size: 12px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    text-decoration: none;
    transition: background 180ms ease, color 180ms ease;
}

.store-cta:hover {
    background: var(--ink);
    color: var(--paper);
}

.related {
    margin-top: 64px;
    padding-top: 8px;
    border-top: 1px solid var(--rule);
}

.related ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

.related li {
    border-top: 1px solid var(--rule-soft);
    padding: 14px 0;
}

.related li:first-child {
    border-top: none;
}

.related a {
    font-family: 'Fraunces', serif;
    font-variation-settings: 'opsz' 28, 'wght' 460;
    font-size: 17px;
    color: var(--ink);
    text-decoration: none;
}

.related a:hover {
    color: var(--accent);
}
/* ───────────────────────────────────────────── App Store Ads Basic cards */
.asa-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1.25rem;
}
/* Matches the sheet's own .practice-deep-header / max-width:720px
   convention (wide-screen default above, single column at/below the
   breakpoint) rather than inventing a min-width variant. */
@media (max-width: 720px) {
    .asa-grid { grid-template-columns: 1fr; }
}
.asa-teaser { margin-top: 1.5rem; }
.asa-card {
    background: #d9ebf7;
    border-radius: 14px;
    padding: 1rem 1.1rem 1.15rem;
    color: #111;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
@media (prefers-color-scheme: dark) {
    .asa-card { background: #1c3040; color: #f2f7fb; }
}
.asa-top { display: flex; gap: 0.75rem; align-items: flex-start; }
.asa-icon { width: 56px; height: 56px; border-radius: 12px; flex-shrink: 0; background: #ccc; }
.asa-meta { flex: 1; min-width: 0; }
.asa-name {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1.25;
    font-family: inherit;
}
.asa-subtitle { margin: 0.2rem 0 0; font-size: 0.85rem; color: #555; }
@media (prefers-color-scheme: dark) {
    .asa-subtitle { color: #a9b7c4; }
}
.asa-cta { text-align: center; flex-shrink: 0; }
.asa-get {
    display: inline-block;
    background: #0a84ff;
    color: #fff !important;
    font-size: 0.9rem;
    font-weight: 600;
    padding: 0.4rem 1.1rem;
    border-radius: 999px;
    text-decoration: none !important;
}
.asa-iap { margin-top: 0.3rem; font-size: 0.58rem; line-height: 1.2; color: #888; }
/* Cross-link into the field-notes hub for a store app that also has a
   guard-passing content app (sitegen's render_apps_index) -- same asa-cta
   column as GET, minimal treatment: plain small text link, no pill, no
   dark-mode override needed (asa-get's own #0a84ff already holds up on
   both the light and dark .asa-card backgrounds above). */
.asa-notes {
    display: block;
    margin-top: 0.4rem;
    font-size: 0.72rem;
    font-weight: 500;
    color: #0a84ff;
    text-decoration: none;
}
.asa-notes:hover { text-decoration: underline; }
.asa-stats {
    display: flex; flex-wrap: wrap; gap: 0.85rem;
    margin-top: 0.85rem; font-size: 0.78rem; color: #333; align-items: center;
}
@media (prefers-color-scheme: dark) {
    .asa-stats { color: #c5d0da; }
}
.asa-shots {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.5rem;
    margin-top: 0.9rem;
}
.asa-shot {
    width: 100%;
    aspect-ratio: 9 / 16;
    object-fit: cover;
    object-position: top;
    border-radius: 10px;
    border: 1px solid #c5d6e4;
    background: #fff;
}
.asa-disclaimer {
    max-width: 42rem;
    margin: 2rem auto 0;
    padding: 0 1.25rem 3rem;
    font-size: 0.85rem;
    color: var(--ink-muted);
}
