@sobree/core 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The narrow surface gesture controllers drive the Viewport through.
3
+ * Controllers translate raw input events into these calls; the Viewport
4
+ * owns all transform state, clamping, and raster behaviour.
5
+ */
6
+ export interface GestureHost {
7
+ /** Pan the stage by `(dx, dy)` CSS pixels. */
8
+ panBy(dx: number, dy: number): void;
9
+ /** Zoom to `scale`, anchoring the point at (clientX, clientY). */
10
+ zoomTo(scale: number, clientX: number, clientY: number): void;
11
+ /** Current visual scale (zoom deltas are multiplicative on this). */
12
+ getScale(): number;
13
+ }
@@ -0,0 +1,38 @@
1
+ import { GestureHost } from './gestureHost';
2
+ /**
3
+ * Touch gestures (mobile): one-finger drag pans, two-finger pinch zooms
4
+ * anchored at the finger midpoint, and translating both fingers pans.
5
+ * Mouse/pen pointers are deliberately ignored — their drag is text
6
+ * selection, not pan. The container has `touch-action: none`, so without
7
+ * this controller touch devices could neither scroll nor zoom.
8
+ */
9
+ export declare class TouchGestures {
10
+ private readonly container;
11
+ private readonly host;
12
+ /** Live touch pointers (pointerId → last client position). */
13
+ private readonly points;
14
+ /** `idle` → no touches; `tap` → one finger down, within slop; `pan` →
15
+ * one finger past slop; `pinch` → two fingers. */
16
+ private mode;
17
+ /** First touch's start position — slop is measured from here. */
18
+ private startX;
19
+ private startY;
20
+ /** Finger distance and scale captured when a pinch begins. */
21
+ private pinchStartDist;
22
+ private pinchStartScale;
23
+ /** Set when a pan/pinch actually moved the stage — the synthetic click
24
+ * browsers fire after the touch sequence must not reach the editor
25
+ * (it would teleport the caret to wherever the drag ended). */
26
+ private suppressNextClick;
27
+ private readonly onPointerDown;
28
+ private readonly onPointerMove;
29
+ private readonly onPointerEnd;
30
+ private readonly onClickCapture;
31
+ constructor(container: HTMLElement, host: GestureHost);
32
+ destroy(): void;
33
+ private handleDown;
34
+ private handleMove;
35
+ private movePinch;
36
+ private handleEnd;
37
+ private beginPinch;
38
+ }
@@ -1,3 +1,4 @@
1
+ import { GestureHost } from './gestureHost';
1
2
  export interface ViewportOptions {
2
3
  minScale?: number;
3
4
  maxScale?: number;
@@ -31,13 +32,18 @@ export interface ViewportOptions {
31
32
  * └ stage (absolutely positioned, transform: translate(tx,ty) scale(s))
32
33
  * └ slot (where the embedded content lives — caller mounts here)
33
34
  *
34
- * Gestures:
35
- * - Zoom: wheel with shiftKey OR ctrlKey (macOS pinch emits ctrlKey).
36
- * The point under the cursor stays under the cursor (zoom-to-cursor).
37
- * - Pan: wheel without modifiers two-finger trackpad scroll deltas move
38
- * the stage. Also supports click-drag with middle mouse or space.
35
+ * Input handling lives in two controllers driving this class through the
36
+ * GestureHost interface:
37
+ * - WheelGestures: zoom on shift/ctrl/meta+wheel (macOS pinch emits
38
+ * ctrlKey), pan on bare wheel with axis-locking.
39
+ * - TouchGestures (mobile): one-finger drag pans (after a small slop so
40
+ * taps still place the caret); two-finger pinch zooms anchored at the
41
+ * finger midpoint. Mouse/pen drag stays text selection.
42
+ *
43
+ * Viewport itself implements GestureHost — panBy / zoomTo / getScale are
44
+ * its public API, so the controllers just receive `this`.
39
45
  */
40
- export declare class Viewport {
46
+ export declare class Viewport implements GestureHost {
41
47
  readonly container: HTMLElement;
42
48
  readonly slot: HTMLElement;
43
49
  private readonly stage;
@@ -46,35 +52,19 @@ export declare class Viewport {
46
52
  private ty;
47
53
  private readonly minScale;
48
54
  private readonly maxScale;
49
- private readonly wheelZoomSensitivity;
50
- private readonly pinchZoomSensitivity;
51
55
  private readonly onScaleChange;
52
56
  private readonly onRenderTierChange;
53
57
  private readonly onTransformChange;
54
- private readonly onWheel;
58
+ private readonly wheelGestures;
59
+ private readonly touchGestures;
55
60
  /** Current layout-side zoom factor (integer ≥ 1). See ViewportOptions. */
56
61
  private renderTier;
57
62
  /** Suppresses `onTransformChange` during the constructor's initial
58
63
  * `applyTransform` so consumers can capture `viewport` in their
59
64
  * callback without TDZ traps. Flipped true at the end of the ctor. */
60
65
  private constructed;
61
- /** Timestamp of the last wheel event, used to delimit gestures. */
62
- private gestureLastTime;
63
- /** Dominant axis for the current gesture. Null until detected, cleared at gesture end. */
64
- private gesturePrimaryAxis;
65
- /**
66
- * Signed cumulative dx within the current gesture. Drives lock release:
67
- * wobble (±3-5px back-and-forth) cancels out; sustained one-way motion
68
- * accumulates past the threshold quickly. Reset at gesture end.
69
- */
70
- private gestureSignedDx;
71
- /**
72
- * Sticky horizontal-lock flag. Engaged by `fitTo` so alignment survives
73
- * gentle diagonal trackpad gestures; broken when the gesture's signed
74
- * cumulative dx crosses `X_RELEASE_THRESHOLD` — the user clearly intends
75
- * sustained horizontal motion.
76
- */
77
- private horizontalLock;
66
+ /** Debounce handle for the crisp-text settle pass (see scheduleSettle). */
67
+ private settleTimer;
78
68
  constructor(container: HTMLElement, options?: ViewportOptions);
79
69
  /** Reset pan to origin and scale to 1. */
80
70
  reset(): void;
@@ -102,20 +92,32 @@ export declare class Viewport {
102
92
  /** Zoom to `nextScale`, anchoring the point at (clientX, clientY) in container space. */
103
93
  zoomTo(nextScale: number, clientX: number, clientY: number): void;
104
94
  destroy(): void;
105
- private handleWheel;
95
+ private applyTransform;
106
96
  /**
107
- * Axis-lock for pan gestures:
108
- * - Within a gesture (events GESTURE_GAP_MS apart), a clear dominant
109
- * axis zeros the other axis so a nearly-vertical swipe doesn't also
110
- * drift the paper sideways.
111
- * - An explicit `horizontalLock` set by `fitTo` survives across gestures,
112
- * keeping fit-page / fit-width alignment stable.
113
- * - Both locks release when the gesture's signed cumulative dx crosses
114
- * `X_RELEASE_THRESHOLD`. Signed sum cancels wobble (back-and-forth
115
- * averages to zero) while sustained one-way motion accumulates fast.
97
+ * The stage transform, expressed in the render tier's coordinate space.
98
+ * Chrome applies transforms post-`zoom`, so a `translate(tx, ty)` on an
99
+ * element with `zoom: k` moves by `(tx*k, ty*k)` screen px. We store
100
+ * tx/ty in pre-zoom (container) pixels — the scheme cursor-anchored
101
+ * zoomTo relies on so divide by the tier to cancel zoom's translate
102
+ * multiplication. (Tier is permanently 1; kept for the single code path.)
103
+ *
104
+ * `threeD` selects the gesture-time form (`translate3d` — forces a
105
+ * compositor layer) vs the settled form (plain `translate` — lets the
106
+ * compositor drop the layer pin and re-rasterise text at the effective
107
+ * scale). Same matrix either way; only raster-cache behaviour differs.
116
108
  */
117
- private applyScrollAxisLock;
118
- private applyTransform;
109
+ private transformCss;
110
+ /**
111
+ * Crisp-text pass: once no transform has been written for SETTLE_MS,
112
+ * drop `will-change` and the 3D form so the compositor re-rasterises
113
+ * the (now static) stage at `devicePixelRatio × scale` — text is then
114
+ * as sharp at 3× as a 3×-laid-out page, with zero layout involvement.
115
+ * Both pins are needed: either `will-change: transform` or a 3D
116
+ * transform alone keeps browsers (Safari especially) stretching the
117
+ * stale 1× texture. The next gesture frame re-pins before moving.
118
+ */
119
+ private scheduleSettle;
120
+ private settle;
119
121
  /**
120
122
  * Apply the current transform with a CSS transition. Used only for
121
123
  * programmatic fits — wheel pan/zoom must stay instant or feel sluggish.
@@ -0,0 +1,54 @@
1
+ import { GestureHost } from './gestureHost';
2
+ export interface WheelGestureOptions {
3
+ /** Scale change per unit of wheel deltaY for shift+wheel. */
4
+ wheelZoomSensitivity: number;
5
+ /** Scale change per unit of wheel deltaY for pinch (ctrlKey). */
6
+ pinchZoomSensitivity: number;
7
+ }
8
+ /**
9
+ * Wheel gestures: zoom on shift/ctrl/meta+wheel (macOS trackpad pinch
10
+ * synthesises ctrlKey wheel events), pan on bare wheel — with an
11
+ * axis-lock so a nearly-vertical swipe doesn't drift the paper sideways.
12
+ */
13
+ export declare class WheelGestures {
14
+ private readonly container;
15
+ private readonly host;
16
+ private readonly opts;
17
+ /** Timestamp of the last wheel event, used to delimit gestures. */
18
+ private gestureLastTime;
19
+ /** Dominant axis for the current gesture. Null until detected, cleared at gesture end. */
20
+ private gesturePrimaryAxis;
21
+ /**
22
+ * Signed cumulative dx within the current gesture. Drives lock release:
23
+ * wobble (±3-5px back-and-forth) cancels out; sustained one-way motion
24
+ * accumulates past the threshold quickly. Reset at gesture end.
25
+ */
26
+ private gestureSignedDx;
27
+ /**
28
+ * Sticky horizontal-lock flag. Engaged by `fitTo` so alignment survives
29
+ * gentle diagonal trackpad gestures; broken when the gesture's signed
30
+ * cumulative dx crosses `X_RELEASE_THRESHOLD` — the user clearly
31
+ * intends sustained horizontal motion.
32
+ */
33
+ private horizontalLock;
34
+ private readonly onWheel;
35
+ constructor(container: HTMLElement, host: GestureHost, opts: WheelGestureOptions);
36
+ destroy(): void;
37
+ /** Engage the sticky horizontal lock (fit-* picked an X alignment). */
38
+ engageHorizontalLock(): void;
39
+ /** Clear all gesture and lock state (viewport reset). */
40
+ resetLocks(): void;
41
+ private handleWheel;
42
+ /**
43
+ * Axis-lock for pan gestures:
44
+ * - Within a gesture (events ≤ GESTURE_GAP_MS apart), a clear dominant
45
+ * axis zeros the other axis so a nearly-vertical swipe doesn't also
46
+ * drift the paper sideways.
47
+ * - An explicit `horizontalLock` set by `fitTo` survives across gestures,
48
+ * keeping fit-page / fit-width alignment stable.
49
+ * - Both locks release when the gesture's signed cumulative dx crosses
50
+ * `X_RELEASE_THRESHOLD`. Signed sum cancels wobble (back-and-forth
51
+ * averages to zero) while sustained one-way motion accumulates fast.
52
+ */
53
+ private applyScrollAxisLock;
54
+ }
package/dist/index.css CHANGED
@@ -1 +1 @@
1
- .paper-stack{display:flex;flex-direction:column;padding:48px;gap:28px;outline:none}.paper-row{display:flex;flex-direction:row;align-items:flex-start;gap:24px}.paper{position:relative;background:#fff;border:1px solid rgba(0,0,0,.3);box-shadow:0 1px 2px #00000014,0 18px 60px #0000001f;padding-top:var(--margin-top, 25mm);padding-right:var(--margin-right, 20mm);padding-bottom:var(--margin-bottom, 25mm);padding-left:var(--margin-left, 20mm);overflow:hidden;flex:none}.paper-header,.paper-footer{position:absolute;left:var(--margin-left, 20mm);right:var(--margin-right, 20mm);font-size:10pt;color:#555;white-space:pre-wrap}.paper-header{top:0;min-height:var(--margin-top, 25mm);padding-top:var(--header-offset-mm, 12.7mm);padding-bottom:4mm;border-bottom:1px dashed transparent}.paper-footer{bottom:0;min-height:var(--margin-bottom, 25mm);padding-top:4mm;text-align:center;border-top:1px dashed transparent}.paper-anchors{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:1}.paper-anchors.is-empty{display:none}.paper-anchors-behind{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:0}.paper-anchors-behind.is-empty{display:none}.paper-zone-anchors{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:2}.paper-zone-anchors.is-empty{display:none}.paper-anchor{position:absolute;box-sizing:border-box;overflow:hidden}.paper-header>p,.paper-footer>p,.paper-header>ul,.paper-header>ol,.paper-footer>ul,.paper-footer>ol{margin:0}.paper-footnotes{position:absolute;left:var(--margin-left, 20mm);right:var(--margin-right, 20mm);bottom:var(--margin-bottom, 25mm);font-size:.85em;padding-top:.5em;border-top:1pt solid currentColor;background:#fffffff5;z-index:1;max-height:40%;overflow:hidden}.paper-footnotes.is-empty{display:none}.paper-comments{flex:0 0 70mm;max-width:70mm;font-size:.85em;padding:0 .5em;align-self:stretch;overflow-y:auto}.paper-comments.is-empty{display:none}.paper-footnotes .sobree-footnotes__list{margin:0;padding-left:1.5em}.paper-footnotes .sobree-footnotes__item{margin:.25em 0}.paper-content a{color:inherit;text-decoration:none}.paper-content{position:relative;height:100%;min-height:100px;display:flow-root}.paper-content td p:not([style*=line-height]),.paper-content td li:not([style*=line-height]),.paper-content th p:not([style*=line-height]),.paper-content th li:not([style*=line-height]){line-height:1}.paper-content td p:not([style*=margin]),.paper-content th p:not([style*=margin]){margin:0}.paper-content .sobree-hang>li{list-style:none}.paper-content .sobree-hang>li:before{display:inline-block;box-sizing:border-box;width:var(--sobree-list-hang, 0);margin-left:calc(-1 * var(--sobree-list-hang, 0));white-space:nowrap;color:var(--sobree-marker-color, currentColor);font-family:var(--sobree-marker-font, inherit);font-size:var(--sobree-marker-size, inherit)}.paper-content .sobree-hang.lst-bullet>li:before{content:var(--sobree-bullet, "•")}.paper-content .sobree-hang.lst-decimal>li:before{content:var(--mk-pre, "") counter(list-item,decimal) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-decimal-zero>li:before{content:var(--mk-pre, "") counter(list-item,decimal-leading-zero) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-lower-latin>li:before{content:var(--mk-pre, "") counter(list-item,lower-latin) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-upper-latin>li:before{content:var(--mk-pre, "") counter(list-item,upper-latin) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-lower-roman>li:before{content:var(--mk-pre, "") counter(list-item,lower-roman) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-upper-roman>li:before{content:var(--mk-pre, "") counter(list-item,upper-roman) var(--mk-suf, ".")}.paper-content .sobree-tab-spread{display:flex;justify-content:space-between;align-items:baseline;gap:1em;white-space:nowrap}.paper-content .sobree-tab-spread .sobree-tab-spread__before,.paper-content .sobree-tab-spread .sobree-tab-spread__after{white-space:pre-wrap;min-width:0}.paper-content table.sobree-table-bordered td,.paper-content table.sobree-table-bordered th{border:var(--table-cell-border, none);padding:0 4px}.sobree-editor{position:relative;outline:none}.sobree-editor>:first-child{margin-top:0}.sobree-editor p,.sobree-editor h1,.sobree-editor h2,.sobree-editor h3,.sobree-editor h4,.sobree-editor h5,.sobree-editor h6,.sobree-editor ul,.sobree-editor ol,.sobree-editor li,.sobree-editor blockquote,.sobree-editor pre{margin:0;padding:0}.sobree-editor ul,.sobree-editor ol{padding-left:1.5em}.sobree-editor li.sobree-li-continuation{list-style-type:none}.sobree-editor li.sobree-li-continuation::marker{content:""}.sobree-editor .sobree-fragment-continued{text-align-last:justify}.sobree-editor .sobree-footnote-ref a{color:inherit;text-decoration:none}.sobree-editor .sobree-footnote-ref a:hover{text-decoration:underline}.sobree-editor .sobree-footnotes{margin-top:2em;padding-top:.5em;border-top:1pt solid currentColor;font-size:.85em}.sobree-editor .sobree-footnotes__list{padding-left:1.5em}.sobree-editor .sobree-footnotes__item{margin:.25em 0}.sobree-editor ins.sobree-revision-ins{text-decoration:underline}.sobree-editor del.sobree-revision-del{text-decoration:line-through}.sobree-editor .sobree-revision-format{text-decoration:underline dashed var(--sobree-format-revision-color, currentColor);text-underline-offset:3px}.sobree-editor [data-block-revision=ins]:after,.sobree-editor [data-block-revision=del]:after{content:" ¶";color:var(--sobree-block-revision-color, currentColor);opacity:.65;font-weight:600;-webkit-user-select:none;user-select:none}.sobree-editor [data-block-revision=del]:after{text-decoration:line-through}.sobree-editor .sobree-comment-range{background:var(--sobree-comment-range-bg, rgba(255, 217, 0, .25));border-bottom:1px dotted var(--sobree-comment-range-border, rgba(180, 130, 0, .5))}.sobree-editor .sobree-comment-ref{display:inline;font-size:.85em;margin:0 .1em}.sobree-editor .sobree-comment-ref a{color:inherit;text-decoration:none}.sobree-editor .sobree-comment-ref a:hover{filter:brightness(.7)}.sobree-editor h1,.sobree-editor h2,.sobree-editor h3,.sobree-editor h4,.sobree-editor h5,.sobree-editor h6{font-size:inherit;font-weight:inherit}.sobree-editor table{border-collapse:collapse;width:100%;position:relative}.sobree-editor th,.sobree-editor td{border:none;padding:0 .08in;vertical-align:top;transition:border-color .12s ease}.sobree-editor .sobree-section-break{display:flex;align-items:center;gap:8px;margin:8px 0;color:var(--fg-subtle, #7c7764);font-size:11px;letter-spacing:.04em;text-transform:uppercase;-webkit-user-select:none;user-select:none}.sobree-editor .sobree-section-break--continuous{visibility:hidden;height:0;margin:0;padding:0;border:none;overflow:hidden;font-size:0;line-height:0}.sobree-editor .sobree-section-break:before,.sobree-editor .sobree-section-break:after{content:"";flex:1;border-top:1px dashed var(--border, #dedbd0)}.sobree-editor .sobree-section-break__label{flex:none;padding:0 6px;background:var(--bg-elevated, #fff)}.sobree-editor .sobree-textbox-frame p{line-height:1.2}.sobree-editor .paper-content p,.sobree-editor .paper-content li,.sobree-editor .paper-header p,.sobree-editor .paper-header li,.sobree-editor .paper-footer p,.sobree-editor .paper-footer li,.sobree-editor .sobree-textbox-frame p{white-space:pre-wrap}.sobree-editor .sobree-section-trailer-empty{height:0;line-height:0;font-size:0;margin:0;padding:0;overflow:hidden}.sobree-editor .sobree-textbox-frame--placeholder{border:1px solid var(--border, #c8c4b8)}.sobree-editor .sobree-textbox-frame--placeholder.sobree-textbox-frame--filled{border:none;z-index:-1}.sobree-editor img.is-selected{outline:2px solid var(--sobree-primary, #d4521f);outline-offset:2px}.sobree-image-resize-handle{position:absolute;width:16px;height:16px;background:var(--sobree-primary, #d4521f);border:2px solid #fff;border-radius:3px;cursor:nwse-resize;z-index:1000;-webkit-user-select:none;user-select:none}.sobree-viewport{position:relative;overflow:hidden;overscroll-behavior:contain;touch-action:none;background:#ececee}.sobree-viewport__stage{position:absolute;top:0;left:0;transform-origin:0 0;will-change:transform}.sobree-viewport__stage.is-animating{transition:transform .32s cubic-bezier(.2,.7,.2,1)}.sobree-viewport__slot{display:block}.paper-stack .paper-content,.paper-stack .paper-header,.paper-stack .paper-footer{transition:opacity .2s ease}.paper-stack.is-zone-editing .paper-content,.paper-stack.is-zone-editing .paper-header,.paper-stack.is-zone-editing .paper-footer{opacity:.2}.paper-stack.is-zone-editing .paper-header[contenteditable=true],.paper-stack.is-zone-editing .paper-footer[contenteditable=true]{opacity:1}.paper-header[contenteditable=true],.paper-footer[contenteditable=true]{outline:2px dotted var(--primary, #c96f22);outline-offset:4px;background:var(--primary-soft, #fdf6ee);color:var(--fg-strong, #14130f);border-radius:2px}
1
+ .paper-stack{display:flex;flex-direction:column;padding:48px;gap:28px;outline:none}.paper-row{display:flex;flex-direction:row;align-items:flex-start;gap:24px}.paper{position:relative;background:#fff;border:1px solid rgba(0,0,0,.3);box-shadow:0 1px 2px #00000014,0 18px 60px #0000001f;padding-top:var(--margin-top, 25mm);padding-right:var(--margin-right, 20mm);padding-bottom:var(--margin-bottom, 25mm);padding-left:var(--margin-left, 20mm);overflow:hidden;flex:none}.paper-header,.paper-footer{position:absolute;left:var(--margin-left, 20mm);right:var(--margin-right, 20mm);font-size:10pt;color:#555;white-space:pre-wrap}.paper-header{top:0;min-height:var(--margin-top, 25mm);padding-top:var(--header-offset-mm, 12.7mm);padding-bottom:4mm;border-bottom:1px dashed transparent}.paper-footer{bottom:0;min-height:var(--margin-bottom, 25mm);padding-top:4mm;text-align:center;border-top:1px dashed transparent}.paper-anchors{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:1}.paper-anchors.is-empty{display:none}.paper-anchors-behind{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:0}.paper-anchors-behind.is-empty{display:none}.paper-zone-anchors{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:2}.paper-zone-anchors.is-empty{display:none}.paper-anchor{position:absolute;box-sizing:border-box;overflow:hidden}.paper-header>p,.paper-footer>p,.paper-header>ul,.paper-header>ol,.paper-footer>ul,.paper-footer>ol{margin:0}.paper-footnotes{position:absolute;left:var(--margin-left, 20mm);right:var(--margin-right, 20mm);bottom:var(--margin-bottom, 25mm);font-size:.85em;padding-top:.5em;border-top:1pt solid currentColor;background:#fffffff5;z-index:1;max-height:40%;overflow:hidden}.paper-footnotes.is-empty{display:none}.paper-comments{flex:0 0 70mm;max-width:70mm;font-size:.85em;padding:0 .5em;align-self:stretch;overflow-y:auto}.paper-comments.is-empty{display:none}.paper-footnotes .sobree-footnotes__list{margin:0;padding-left:1.5em}.paper-footnotes .sobree-footnotes__item{margin:.25em 0}.paper-content a{color:inherit;text-decoration:none}.paper-content{position:relative;height:100%;min-height:100px;display:flow-root}.paper-content td p:not([style*=line-height]),.paper-content td li:not([style*=line-height]),.paper-content th p:not([style*=line-height]),.paper-content th li:not([style*=line-height]){line-height:1}.paper-content td p:not([style*=margin]),.paper-content th p:not([style*=margin]){margin:0}.paper-content .sobree-hang>li{list-style:none}.paper-content .sobree-hang>li:before{display:inline-block;box-sizing:border-box;width:var(--sobree-list-hang, 0);margin-left:calc(-1 * var(--sobree-list-hang, 0));white-space:nowrap;color:var(--sobree-marker-color, currentColor);font-family:var(--sobree-marker-font, inherit);font-size:var(--sobree-marker-size, inherit)}.paper-content .sobree-hang.lst-bullet>li:before{content:var(--sobree-bullet, "•")}.paper-content .sobree-hang.lst-decimal>li:before{content:var(--mk-pre, "") counter(list-item,decimal) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-decimal-zero>li:before{content:var(--mk-pre, "") counter(list-item,decimal-leading-zero) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-lower-latin>li:before{content:var(--mk-pre, "") counter(list-item,lower-latin) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-upper-latin>li:before{content:var(--mk-pre, "") counter(list-item,upper-latin) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-lower-roman>li:before{content:var(--mk-pre, "") counter(list-item,lower-roman) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-upper-roman>li:before{content:var(--mk-pre, "") counter(list-item,upper-roman) var(--mk-suf, ".")}.paper-content .sobree-tab-spread{display:flex;justify-content:space-between;align-items:baseline;gap:1em;white-space:nowrap}.paper-content .sobree-tab-spread .sobree-tab-spread__before,.paper-content .sobree-tab-spread .sobree-tab-spread__after{white-space:pre-wrap;min-width:0}.paper-content table.sobree-table-bordered td,.paper-content table.sobree-table-bordered th{border:var(--table-cell-border, none);padding:0 4px}.sobree-editor{position:relative;outline:none}.sobree-editor>:first-child{margin-top:0}.sobree-editor p,.sobree-editor h1,.sobree-editor h2,.sobree-editor h3,.sobree-editor h4,.sobree-editor h5,.sobree-editor h6,.sobree-editor ul,.sobree-editor ol,.sobree-editor li,.sobree-editor blockquote,.sobree-editor pre{margin:0;padding:0}.sobree-editor ul,.sobree-editor ol{padding-left:1.5em}.sobree-editor li.sobree-li-continuation{list-style-type:none}.sobree-editor li.sobree-li-continuation::marker{content:""}.sobree-editor .sobree-fragment-continued{text-align-last:justify}.sobree-editor .sobree-footnote-ref a{color:inherit;text-decoration:none}.sobree-editor .sobree-footnote-ref a:hover{text-decoration:underline}.sobree-editor .sobree-footnotes{margin-top:2em;padding-top:.5em;border-top:1pt solid currentColor;font-size:.85em}.sobree-editor .sobree-footnotes__list{padding-left:1.5em}.sobree-editor .sobree-footnotes__item{margin:.25em 0}.sobree-editor ins.sobree-revision-ins{text-decoration:underline}.sobree-editor del.sobree-revision-del{text-decoration:line-through}.sobree-editor .sobree-revision-format{text-decoration:underline dashed var(--sobree-format-revision-color, currentColor);text-underline-offset:3px}.sobree-editor [data-block-revision=ins]:after,.sobree-editor [data-block-revision=del]:after{content:" ¶";color:var(--sobree-block-revision-color, currentColor);opacity:.65;font-weight:600;-webkit-user-select:none;user-select:none}.sobree-editor [data-block-revision=del]:after{text-decoration:line-through}.sobree-editor .sobree-comment-range{background:var(--sobree-comment-range-bg, rgba(255, 217, 0, .25));border-bottom:1px dotted var(--sobree-comment-range-border, rgba(180, 130, 0, .5))}.sobree-editor .sobree-comment-ref{display:inline;font-size:.85em;margin:0 .1em}.sobree-editor .sobree-comment-ref a{color:inherit;text-decoration:none}.sobree-editor .sobree-comment-ref a:hover{filter:brightness(.7)}.sobree-editor h1,.sobree-editor h2,.sobree-editor h3,.sobree-editor h4,.sobree-editor h5,.sobree-editor h6{font-size:inherit;font-weight:inherit}.sobree-editor table{border-collapse:collapse;width:100%;position:relative}.sobree-editor th,.sobree-editor td{border:none;padding:0 .08in;vertical-align:top;transition:border-color .12s ease}.sobree-editor .sobree-section-break{display:flex;align-items:center;gap:8px;margin:8px 0;color:var(--fg-subtle, #7c7764);font-size:11px;letter-spacing:.04em;text-transform:uppercase;-webkit-user-select:none;user-select:none}.sobree-editor .sobree-section-break--continuous{visibility:hidden;height:0;margin:0;padding:0;border:none;overflow:hidden;font-size:0;line-height:0}.sobree-editor .sobree-section-break:before,.sobree-editor .sobree-section-break:after{content:"";flex:1;border-top:1px dashed var(--border, #dedbd0)}.sobree-editor .sobree-section-break__label{flex:none;padding:0 6px;background:var(--bg-elevated, #fff)}.sobree-editor .sobree-textbox-frame p{line-height:1.2}.sobree-editor .paper-content p,.sobree-editor .paper-content li,.sobree-editor .paper-header p,.sobree-editor .paper-header li,.sobree-editor .paper-footer p,.sobree-editor .paper-footer li,.sobree-editor .sobree-textbox-frame p{white-space:pre-wrap}.sobree-editor .sobree-section-trailer-empty{height:0;line-height:0;font-size:0;margin:0;padding:0;overflow:hidden}.sobree-editor .sobree-textbox-frame--placeholder{border:1px solid var(--border, #c8c4b8)}.sobree-editor .sobree-textbox-frame--placeholder.sobree-textbox-frame--filled{border:none;z-index:-1}.sobree-editor img.is-selected{outline:2px solid var(--sobree-primary, #d4521f);outline-offset:2px}.sobree-image-resize-handle{position:absolute;width:16px;height:16px;background:var(--sobree-primary, #d4521f);border:2px solid #fff;border-radius:3px;cursor:nwse-resize;z-index:1000;-webkit-user-select:none;user-select:none}.sobree-viewport{position:relative;overflow:hidden;overscroll-behavior:contain;touch-action:none;background:#ececee}.sobree-viewport__stage{position:absolute;top:0;left:0;transform-origin:0 0}.sobree-viewport__stage.is-gesturing,.sobree-viewport__stage.is-animating{will-change:transform}.sobree-viewport__stage.is-animating{transition:transform .32s cubic-bezier(.2,.7,.2,1)}.sobree-viewport__slot{display:block}.paper-stack .paper-content,.paper-stack .paper-header,.paper-stack .paper-footer{transition:opacity .2s ease}.paper-stack.is-zone-editing .paper-content,.paper-stack.is-zone-editing .paper-header,.paper-stack.is-zone-editing .paper-footer{opacity:.2}.paper-stack.is-zone-editing .paper-header[contenteditable=true],.paper-stack.is-zone-editing .paper-footer[contenteditable=true]{opacity:1}.paper-header[contenteditable=true],.paper-footer[contenteditable=true]{outline:2px dotted var(--primary, #c96f22);outline-offset:4px;background:var(--primary-soft, #fdf6ee);color:var(--fg-strong, #14130f);border-radius:2px}