/* ==========================================================================
   Giant Heartbeat — site styles
   A modern, clean starter. Brand/colour choices are deliberately simple and
   easy to change: tweak the custom properties below.

   Colour lives in two themes:
     1. :root — the LIGHT theme (default), plus the theme-independent tokens
        (type scale, spacing, fonts).
     2. [data-theme="dark"] (further down) — the DARK theme.
   The theme follows the OS by default; the footer control lets visitors force
   Light or Dark, remembered per device.
   ========================================================================== */

/* IBM Plex Sans — self-hosted variable font (weights 400–700), used for
   both body text and headings. Two subsets: latin and latin-ext. */
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/fonts/ibm-plex-sans-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: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/fonts/ibm-plex-sans-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* --- ORIGINAL DEFAULT PALETTE — kept for posterity, not in use ---
  --bg: #ffffff;
  --fg: #16171a;
  --muted: #5b6169;
  --accent: #d1342c;
  --accent-dark: #a82821;
  --gold: #c9a24b;
  --gold-light: #e4c878;
  --surface: #f6f6f4;
  --border: #e6e6e2;
  --on-accent: #ffffff;
  --scrim: linear-gradient(90deg, rgb(181 12 18 / 92%) 0%, rgba(120, 40, 24, 0.72) 50%, rgba(176, 132, 32, 0.80) 100%);
  */

  /* --- LIGHT THEME (default) --- */
  color-scheme: light;
  --bg: #fdf6ec;
  --fg: #16110f;
  --muted: #6b6259;
  --accent: #c2410c;
  --accent-dark: #a8380a;
  --gold: #2f5bd8;        /* secondary accent — use on dark surfaces / decorative */
  --gold-light: #7f9cf0;  /* lighter variant for text over dark */
  --surface: #f4e9d8;
  --field-bg: #ffffff;   /* form-field fill — set off from --bg so inputs read as fields */
  --border: #ecdcc4;
  --on-accent: #ffffff;   /* text/glyph colour drawn on top of an --accent fill */
  --scrim: linear-gradient(90deg, rgb(194 65 12 / 90%) 0%, rgba(140, 70, 60, 0.70) 50%, rgba(47, 91, 216, 0.74) 100%);

  /* --- Structural tokens (theme-independent) ---
     Type scale — every text size on the site comes from these seven steps.
     Choose a step by role, not by context. */
  --fs-xs: 0.78rem;                       /* eyebrow / labels */
  --fs-sm: 0.85rem;                       /* captions, footnotes */
  --fs-base: 1.0rem;                   /* body */
  --fs-md: 1.125rem;                      /* card titles, h3 */
  --fs-lg: clamp(1.05rem, 2vw, 1.25rem);  /* lede / large body */
  --fs-xl: clamp(1.2rem, 2.2vw, 1.5rem);  /* h2 / section headings */
  --fs-2xl: clamp(1.5rem, 3vw, 1.95rem);  /* h1 / hero */

  --max-width: 1080px;
  --radius: 12px;
  --img-shadow: 0 4px 14px rgba(28, 22, 16, 0.12); /* shared drop shadow for content imagery */
  --header-h: 64px;
  --section-pad: clamp(2.5rem, 5vw, 3.5rem); /* vertical padding for content bands */
  --col-gap: clamp(1.5rem, 3vw, 2.25rem);    /* horizontal gutter between paired grid columns */
  --font: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-heading: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: 1.65;
  color: var(--fg);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  /* Sticky footer: the page column fills the viewport so short pages push the
     footer to the bottom instead of leaving a bare strip below it. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

img { max-width: 100%; height: auto; display: block; }

/* A paragraph that directly follows a content image needs its own top space.
   Paragraphs carry no top margin and images carry no bottom margin, so a bare
   `img` + `p` would otherwise sit flush. Matches the ~1.1em gap that precedes
   an image (from the preceding paragraph's bottom margin). */
img + p { margin-top: 1.1em; }

/* Short content screenshots stay at natural size and are centred in the column
   so any shortfall is symmetric instead of left-hugging. Wider images already
   fill the column, so the auto margins are a no-op for them. */
.section > img { margin-inline: auto; }

a { color: var(--accent); text-decoration-line: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--accent-dark); }

h1, h2, h3 { font-family: var(--font-heading); font-weight: 700; line-height: 1.2; letter-spacing: -0.015em; margin: 0 0 0.5em; }
h1 { font-size: var(--fs-2xl); }
h2 { font-size: var(--fs-xl); margin-top: 1.8em; }
h3 { font-size: var(--fs-md); margin-top: 1.5em; }
p { margin: 0 0 1.1em; }

.content-wrap { max-width: var(--max-width); margin: 0 auto; padding: 0 1.25rem; }

/* --- Accessibility --- */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--on-accent); padding: 0.6rem 1rem; border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus { left: 0; }

/* --- Header --- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(180%) blur(10px);
  border-bottom: 1px solid var(--border);
}
.header-inner { display: flex; align-items: center; justify-content: space-between; height: var(--header-h); }
.header-brand { display: flex; align-items: center; gap: 0.6rem; color: var(--accent); text-decoration: none; font-weight: 700; letter-spacing: -0.01em; }
.header-brand:hover { color: var(--accent-dark); }
.brand-mark { width: 42px; height: 42px; }
.site-title { font-family: var(--font-heading); font-weight: 700; font-size: var(--fs-md); }

.desktop-nav { display: flex; gap: 1.75rem; }
.desktop-nav a { color: var(--accent); text-decoration: none; font-weight: 500; padding: 0.1rem 0; border-bottom: 2px solid transparent; }
.desktop-nav a:hover { color: var(--accent-dark); }
.desktop-nav a[aria-current="page"] { border-bottom-color: var(--accent); }

/* --- Hamburger --- */
.hamburger {
  display: none; background: none; border: 0; cursor: pointer; padding: 0.5rem;
  flex-direction: column; gap: 5px;
}
.hamburger span { display: block; width: 24px; height: 2px; background: var(--fg); transition: transform 0.25s ease, opacity 0.25s ease; }
.hamburger.is-open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger.is-open span:nth-child(2) { opacity: 0; }
.hamburger.is-open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.mobile-nav {
  display: none; flex-direction: column;
  padding: 0.75rem 1.25rem;
  background: var(--bg);
}
.mobile-nav.is-open { display: flex; }
.mobile-nav a { color: var(--accent); text-decoration: none; font-weight: 600; padding: 0.75rem 0; }
/* Separators only *between* items — none above the first or below the last. */
.mobile-nav a + a { border-top: 1px solid var(--border); }
.mobile-nav a[aria-current="page"] { color: var(--accent-dark); }

@media (max-width: 720px) {
  .desktop-nav { display: none; }
  .hamburger { display: flex; }
}

/* --- Main / hero --- */
main { display: block; flex: 1; }
/* Vertical padding per content band. Because it applies to both top and bottom,
   two stacked sections show double this value between them. */
.section { padding: 2rem 0; }
/* A heading that opens a section shouldn't add its own top margin on top of the
   section's padding, otherwise the gap between sections is dominated by it. */
.section > h2:first-child, .section > h3:first-child { margin-top: 0; }
/* An H2 that isn't the section opener (e.g. a second heading mid-section, like
   "Export what you have changed") falls back to the small heading margin and
   reads tighter than the 4rem gaps between sections. Match that spacing so all
   heading breaks look consistent. */
.section > h2:not(:first-child) { margin-top: 4rem; }
/* Lists that open a content band (e.g. the privacy policy). */
.section > ul { margin: 0 0 1.1em; padding-left: 1.25rem; }
.section > ul > li { margin-bottom: 0.4rem; }

.hero { padding: clamp(3rem, 8vw, 6rem) 0; }
.hero__eyebrow { text-transform: uppercase; letter-spacing: 0.12em; font-size: var(--fs-xs); font-weight: 700; color: var(--accent); margin-bottom: 1rem; }
.hero__eyebrow:empty { display: none; }
.hero__lede { font-size: var(--fs-lg); color: var(--fg); max-width: 60ch; }
.hero__sub { color: var(--muted); max-width: 70ch; }

/* A page-title hero that is followed by a content band (Privacy, Blog, the
   Practice Desk policy) shares a .content-wrap with that band. The hero's
   generous bottom padding plus the band's top padding would otherwise leave a
   large void under the subtitle, so drop the hero's bottom padding when it is
   not the last thing in the wrap. Standalone heroes (404, confirmation) keep
   their full padding. */
.content-wrap > .hero:not(:last-child) { padding-bottom: 0; }

/* Overlay hero (home): the image is the backdrop and the text sits on top of
   it, over a scrim gradient for legibility. */
.hero--image {
  position: relative;
  display: flex;
  align-items: center;
  min-height: clamp(360px, 50vh, 620px);
  padding: clamp(3rem, 8vw, 5rem) 0;
  overflow: hidden;
  background: #14161a; /* fallback colour while the image loads */
}
.hero__bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; z-index: 0; }
.hero__scrim {
  position: absolute; inset: 0; z-index: 1;
  /* Scrim: red on the left bridging through a deep warm red into a saturated
     gold on the right (bridged via red, not olive, so it reads as gold rather
     than yellow-green). Keeps white text legible over the photo. Defined as
     --scrim in :root so schemes can retint it. */
  background: var(--scrim);
}
.hero__content { position: relative; z-index: 2; width: 100%; }
.hero--image .hero__eyebrow { color: var(--gold-light); opacity: 1; }
.hero--image h1 { color: #fff; max-width: 30ch; text-shadow: 0 1px 18px rgba(0,0,0,0.4); }
.hero--image .hero__lede { color: rgba(255,255,255,0.92); max-width: 52ch; text-shadow: 0 1px 18px rgba(0,0,0,0.4); margin-bottom: 0; }

/* Keep the hero compact on small screens. */
@media (max-width: 720px) {
  .hero--image { min-height: 260px; padding: 3rem 0; }
}

/* Intro block that follows the hero. Equal top/bottom padding, and the cards
   section below has its top padding zeroed, so the gap above the paragraphs
   equals the gap below them (no doubled-up spacing). */
.section--intro { padding: var(--section-pad) 0; }
.section--intro p:last-child { margin-bottom: 0; }
.section--cards { padding-top: 0; }

/* --- Buttons --- */
.button, .btn {

  display: inline-block; background: var(--accent); color: var(--on-accent); text-decoration: none;
  font-weight: 600; padding: 0.7rem 1.3rem; border: 0; border-radius: 8px;
  cursor: pointer; transition: background 0.15s ease;
}

.button:hover, .btn:hover { background: var(--accent-dark); color: var(--on-accent); }
.button--ghost { background: transparent; color: var(--fg); border: 1px solid var(--border); }
.button--ghost:hover { background: var(--surface); color: var(--fg); }

/* --- Cards / feature grid --- */
/* N columns on wide screens, dropping straight to a single stacked column
   below the breakpoint. Deliberately never produces a 2-up middle state
   (which would strand an odd card on its own row). To change the desktop
   count, edit --cols only. */
.grid { --cols: 3; display: grid; gap: 1.5rem; grid-template-columns: repeat(var(--cols), 1fr); }
@media (max-width: 860px) { .grid { --cols: 1; } }
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  /* Soft, layered elevation (warm-tinted), plus a top highlight for a gentle
     emboss — depth without the heavy drop shadow. Static: these cards are
     containers, not links, so they don't react on hover. */
  box-shadow:
    0 1px 2px rgba(28, 22, 16, 0.05),
    0 12px 32px -14px rgba(28, 22, 16, 0.18),
    inset 0 1px 0 rgba(255, 255, 255, 0.55);
}
.card__title { font-size: var(--fs-md); margin-top: 0; }
.card h3 { margin-top: 0; }

/* Mailing-list sign-up panel: a static card that spans the normal content
   column (no narrower cap). */
.signup-card { padding: clamp(1.5rem, 4vw, 2.5rem); }
.signup-card h2 { margin-top: 0; }
.signup-card > :last-child { margin-bottom: 0; }
.signup-intro { padding-right: 20px; }
/* The final content section on a page (e.g. a signup band) sits directly above
   the footer, so give it extra room below. Covers both nesting patterns: a
   section that is the last child of <main> (Practice Desk signup) and one
   nested inside a .content-wrap (contact page signup). */
main > .section:last-child,
main > .content-wrap > .section:last-child { padding-bottom: clamp(2.5rem, 5vw, 3.5rem); }

/* --- Store badges --- */
.store-badges { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; margin: 1.5rem 0; }
.store-badges img { height: 56px; width: auto; }
/* The Mac badge ships with a built-in light-grey keyline (#a6a6a6); the
   Microsoft badge's edge is black at 10% opacity, which vanishes on dark
   backgrounds. Give the Microsoft badge a matching keyline + corner radius. */
.store-badges img[src*="microsoft-store"] { border: 1px solid #a6a6a6; border-radius: 13px; }

/* --- Feature list (Practice Desk) --- */
.feature { margin: 3rem 0; }
.feature img { box-shadow: var(--img-shadow); margin-top: 1rem; }
.feature ul { padding-left: 1.1rem; }
.feature li { margin-bottom: 0.4em; }

/* --- Forms --- */
form { margin: 1.5rem 0; }
label { display: block; font-weight: 600; margin: 1rem 0 0.35rem; }
input[type="text"], input[type="email"], input[type="url"], textarea {
  width: 100%; padding: 0.7rem 0.85rem; font: inherit; color: var(--fg);
  background: var(--field-bg); border: 1px solid var(--border); border-radius: 8px;
}
input:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.signup-fields { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.signup-fields input { flex: 1 1 240px; }
/* Kit's script writes its own success/error notes into the form. */
.formkit-alert { margin: 1rem 0 0; padding: 0; list-style: none; font-size: var(--fs-base); color: var(--fg); }
.formkit-alert:empty { display: none; }
.signup-note { color: var(--muted); font-size: var(--fs-sm); margin: 0; }
/* reCAPTCHA badge, injected by Kit's form script. Hidden per Google's terms,
   which require the linked attribution shown in .signup-note. */
.grecaptcha-badge { visibility: hidden; }

/* --- Contact page --- */
/* Wide: the intro and message form sit in the left column with the portrait
   photo alongside on the right; the mailing-list signup spans the full width
   below. Narrow: source order, so the photo drops in above the signup. */
.contact-media { margin: 0 0 3rem; }
.contact-media img { width: 100%; height: auto; box-shadow: var(--img-shadow); }

/* Tuck the message form up under the opening paragraph, with a little air. */
.contact-intro { padding-bottom: 0; }
.contact-message { padding-top: 0; }

/* Nothing sits between the message field and the send button now that the
   captcha widget is gone, so restore the gap it used to provide. */
.contact-form button[type="submit"] { margin-top: 1.25rem; }

/* Inline result shown on /contact/ after an in-page send. */
.form-success, .form-error {
  margin: 0 0 1.5rem;
  padding: 0.85rem 1.1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.form-success { border-left: 3px solid var(--accent); }
.form-error { border-left: 3px solid #b4472f; }

/* Honeypot: off-screen for people, tempting to bots. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

@media (min-width: 960px) {
  .contact-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 44%);
    column-gap: var(--col-gap);
    padding-top: clamp(3rem, 8vw, 6rem);
    align-items: start;
  }
  .contact-main  { grid-column: 1; }
  .contact-media { grid-column: 2; margin-bottom: 0; }
  .contact-intro { padding: 0; }
}

/* --- Blog --- */
.post-list { list-style: none; padding: 0; margin: 2rem 0; }
.post-list li { padding: 1.25rem 0; border-bottom: 1px solid var(--border); }
.post-list time { display: block; color: var(--muted); font-size: var(--fs-sm); }
.post-list a { font-size: var(--fs-md); font-weight: 600; text-decoration: none; }
.post-list a:hover { text-decoration: underline; }
.post-header { margin: 2rem 0; }
.post-header time { color: var(--muted); }

/* --- Footer --- */
.site-footer { border-top: 1px solid var(--border); background: var(--surface); padding: 2.5rem 0; }
.footer-inner { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 2rem; }
.footer-brand { display: flex; align-items: center; gap: 0.5rem; font-family: var(--font-heading); font-weight: 700; }
.footer-nav { display: flex; flex-wrap: wrap; gap: 1.25rem; }
.footer-nav a { color: var(--muted); text-decoration: none; }
.footer-nav a:hover { color: var(--accent); }
/* Small legal links that ride along with the copyright line. */
.footer-legal { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem; margin-left: 0.4rem; }
.footer-legal a { color: var(--muted); }
.footer-legal a:hover { color: var(--accent); }
.footer-legal span { color: var(--border); }
/* Copyright + theme control share the bottom line; the theme links sit right. */
.footer-note {
  flex-basis: 100%;
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 0.4rem 1rem;
  color: var(--muted); font-size: var(--fs-sm); margin: 0.5rem 0 0;
}
/* Theme links: the current one reads as engaged and isn't clickable. */
.footer-theme { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem; }
.footer-theme__sep { color: var(--border); }
.footer-theme__opt {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: var(--muted); cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.footer-theme__opt:hover { color: var(--accent); }
.footer-theme__opt[aria-current="true"],
.footer-theme__opt:disabled {
  color: var(--fg); -webkit-text-fill-color: var(--fg);
  font-weight: 600; text-decoration: none; cursor: default; opacity: 1;
}

/* --- Practice Desk / book specifics --- */
.app-icon { width: 256px; max-width: 100%; height: auto; }

/* Practice Desk hero: full-bleed screenshot backdrop (via .hero--image) with
   the app icon and intro copy layered on top. The icon/copy pair is a grid. */
.practice-hero__layout {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--col-gap);
  align-items: center;
}
.practice-hero + .section { padding-top: 1.5rem; }
/* Centre the copy block in the icon's row: drop the trailing paragraph margin
   so the box is symmetric and the grid's align-items centres it truly. */
.practice-hero__intro > p:last-child { margin-bottom: 0; }
/* Equal gaps above the lede, below it, and below the badges. The h1's empty
   descender space and the lede's leading make the top gap ~11px larger than the
   raw margin, so the badges take 17px (vs the h1's 6px); the link needs 13px. */
.practice-hero h1 { margin-bottom: 6px; }
.practice-hero .store-badges { margin-top: 17px; margin-bottom: 13px; }
/* Hero link: white so it stays legible over the photo in any theme (the
   accent is theme-dependent against the scrim), with a clear underline and a
   soft shadow so it still reads as a link rather than plain text. */
.practice-hero .hero__content a {
  color: #fff;
  font-size: var(--fs-lg);   /* match the lede so the hero reads as two sizes, not three */
  text-decoration-color: rgba(255,255,255,0.85);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
  text-shadow: 0 1px 18px rgba(0,0,0,0.45);
}
.practice-hero .hero__content a:hover { color: var(--gold-light); }
@media (max-width: 860px) {
  .practice-hero__layout { grid-template-columns: 1fr; row-gap: 1.5rem; }
  .practice-hero + .section { padding-top: 0; }
}
/* Book page: the cover sits in the left column with the title, subtitle and
   opening paragraph alongside it; the rest of the copy flows below. Mirrors
   the contact page's two-column layout, but with the image on the left.
   Stacks to a single column on narrow screens. */
.book-hero {
  display: grid;
  grid-template-columns: minmax(0, 42%) minmax(0, 1fr);
  column-gap: var(--col-gap);
  align-items: start;
  padding-bottom: 0;
}
.book-hero + .section { padding-top: 1.5rem; }
.book-cover { width: 100%; box-shadow: var(--img-shadow); }
.book-byline { font-size: var(--fs-sm); color: var(--muted); margin-top: -0.5em; }

@media (max-width: 860px) {
  .book-hero { grid-template-columns: 1fr; row-gap: 1.5rem; }
  .book-hero + .section { padding-top: 0; }
}
/* Photos in content: shared frame + caption treatment used across the site. */
figure { margin: 2rem 0; }
figure img { box-shadow: var(--img-shadow); }
figcaption { color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; margin-top: 0.6rem; }
.disclaimer { color: var(--muted); margin-top: 2rem; }
.post-back { margin-top: 3rem; }

/* ==========================================================================
   DARK THEME
   --------------------------------------------------------------------------
   The light theme is in :root above; this is its dark counterpart, applied
   when <html> has data-theme="dark". The theme follows the OS by default — the
   footer control sets data-theme to "light" or "dark" (or clears it for Auto).
   Every rule in this file reads colour from these custom properties, so nothing
   else needs to change.
   ========================================================================== */
[data-theme="dark"] {
  color-scheme: dark;
  --bg: #14100e;
  --fg: #f3ede3;
  --muted: #a79e90;
  --accent: #ff5b3a;
  --accent-dark: #ff8367;      /* hover reads lighter on dark */
  --gold: #d8b25c;
  --gold-light: #eccf8f;
  --surface: #1e1915;
  --field-bg: #262019;
  --border: #332b24;
  --on-accent: #1a0f09;
  --img-shadow: 0 6px 20px rgba(0, 0, 0, 0.55);
  --scrim: linear-gradient(90deg, rgb(150 24 12 / 88%) 0%, rgba(90, 34, 22, 0.70) 50%, rgba(150, 112, 30, 0.72) 100%);
}
